summaryrefslogtreecommitdiffstats
path: root/arch/sparc64/prom
diff options
context:
space:
mode:
Diffstat (limited to 'arch/sparc64/prom')
-rw-r--r--arch/sparc64/prom/Makefile23
-rw-r--r--arch/sparc64/prom/bootstr.c23
-rw-r--r--arch/sparc64/prom/console.c128
-rw-r--r--arch/sparc64/prom/devops.c41
-rw-r--r--arch/sparc64/prom/init.c79
-rw-r--r--arch/sparc64/prom/memory.c152
-rw-r--r--arch/sparc64/prom/misc.c134
-rw-r--r--arch/sparc64/prom/p1275.c170
-rw-r--r--arch/sparc64/prom/printf.c46
-rw-r--r--arch/sparc64/prom/ranges.c107
-rw-r--r--arch/sparc64/prom/tree.c328
11 files changed, 1231 insertions, 0 deletions
diff --git a/arch/sparc64/prom/Makefile b/arch/sparc64/prom/Makefile
new file mode 100644
index 000000000..1cec2111e
--- /dev/null
+++ b/arch/sparc64/prom/Makefile
@@ -0,0 +1,23 @@
+# $Id: Makefile,v 1.2 1997/02/25 12:40:25 jj Exp $
+# Makefile for the Sun Boot PROM interface library under
+# Linux.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now in the main makefile...
+
+OBJS = bootstr.o devops.o init.o memory.o misc.o \
+ ranges.o tree.o console.o printf.o p1275.o
+
+all: promlib.a
+
+promlib.a: $(OBJS)
+ $(AR) rcs promlib.a $(OBJS)
+ sync
+
+dep:
+ $(CPP) -M *.c > .depend
+
+include $(TOPDIR)/Rules.make
diff --git a/arch/sparc64/prom/bootstr.c b/arch/sparc64/prom/bootstr.c
new file mode 100644
index 000000000..e226c6e95
--- /dev/null
+++ b/arch/sparc64/prom/bootstr.c
@@ -0,0 +1,23 @@
+/* $Id: bootstr.c,v 1.3 1997/03/04 16:27:06 jj Exp $
+ * bootstr.c: Boot string/argument acquisition from the PROM.
+ *
+ * Copyright(C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright(C) 1996 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ */
+
+#include <linux/string.h>
+#include <asm/oplib.h>
+
+#define BARG_LEN 256
+static char barg_buf[BARG_LEN];
+static char fetched = 0;
+
+char *
+prom_getbootargs(void)
+{
+ /* This check saves us from a panic when bootfd patches args. */
+ if (fetched) return barg_buf;
+ prom_getstring(prom_chosen_node, "bootargs", barg_buf, BARG_LEN);
+ fetched = 1;
+ return barg_buf;
+}
diff --git a/arch/sparc64/prom/console.c b/arch/sparc64/prom/console.c
new file mode 100644
index 000000000..a4aea63b8
--- /dev/null
+++ b/arch/sparc64/prom/console.c
@@ -0,0 +1,128 @@
+/* $Id: console.c,v 1.6 1997/03/18 17:59:59 jj Exp $
+ * console.c: Routines that deal with sending and receiving IO
+ * to/from the current console device using the PROM.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <asm/openprom.h>
+#include <asm/oplib.h>
+#include <asm/system.h>
+#include <linux/string.h>
+
+extern int prom_stdin, prom_stdout;
+
+/* Non blocking get character from console input device, returns -1
+ * if no input was taken. This can be used for polling.
+ */
+__inline__ int
+prom_nbgetchar(void)
+{
+ char inc;
+
+ if (p1275_cmd("read", P1275_ARG(1,P1275_ARG_OUT_BUF)|
+ P1275_INOUT(3,1),
+ prom_stdin, &inc, P1275_SIZE(1)) == 1)
+ return inc;
+ else
+ return -1;
+}
+
+/* Non blocking put character to console device, returns -1 if
+ * unsuccessful.
+ */
+__inline__ int
+prom_nbputchar(char c)
+{
+ char outc;
+
+ outc = c;
+ if (p1275_cmd("write", P1275_ARG(1,P1275_ARG_IN_BUF)|
+ P1275_INOUT(3,1),
+ prom_stdout, &outc, P1275_SIZE(1)) == 1)
+ return 0;
+ else
+ return -1;
+}
+
+/* Blocking version of get character routine above. */
+char
+prom_getchar(void)
+{
+ int character;
+ while((character = prom_nbgetchar()) == -1) ;
+ return (char) character;
+}
+
+/* Blocking version of put character routine above. */
+void
+prom_putchar(char c)
+{
+ prom_nbputchar(c);
+ return;
+}
+
+void
+prom_puts(char *s, int len)
+{
+ p1275_cmd("write", P1275_ARG(1,P1275_ARG_IN_BUF)|
+ P1275_INOUT(3,1),
+ prom_stdout, s, P1275_SIZE(len));
+}
+
+/* Query for input device type */
+enum prom_input_device
+prom_query_input_device()
+{
+ int st_p;
+ char propb[64];
+
+ st_p = prom_inst2pkg(prom_stdin);
+ if(prom_node_has_property(st_p, "keyboard"))
+ return PROMDEV_IKBD;
+ prom_getproperty(st_p, "device_type", propb, sizeof(propb));
+ if(strncmp(propb, "serial", sizeof("serial")))
+ return PROMDEV_I_UNK;
+ /* FIXME: Is there any better way how to find out? */
+ st_p = prom_finddevice ("/options");
+ prom_getproperty(st_p, "input-device", propb, sizeof(propb));
+ if (strncmp (propb, "tty", 3) || !propb[3] || propb[4])
+ return PROMDEV_I_UNK;
+ switch (propb[3]) {
+ case 'a': return PROMDEV_ITTYA;
+ case 'b': return PROMDEV_ITTYB;
+ default: return PROMDEV_I_UNK;
+ }
+}
+
+/* Query for output device type */
+
+enum prom_output_device
+prom_query_output_device()
+{
+ int st_p;
+ char propb[64];
+ int propl;
+
+ st_p = prom_inst2pkg(prom_stdout);
+ propl = prom_getproperty(st_p, "device_type", propb, sizeof(propb));
+ if (propl >= 0 && propl == sizeof("display") &&
+ strncmp("display", propb, sizeof("display")) == 0)
+ return PROMDEV_OSCREEN;
+ if(strncmp("serial", propb, sizeof("serial")))
+ return PROMDEV_O_UNK;
+ /* FIXME: Is there any better way how to find out? */
+ st_p = prom_finddevice ("/options");
+ prom_getproperty(st_p, "output-device", propb, sizeof(propb));
+ if (strncmp (propb, "tty", 3) || !propb[3] || propb[4])
+ return PROMDEV_O_UNK;
+ switch (propb[3]) {
+ case 'a': return PROMDEV_OTTYA;
+ case 'b': return PROMDEV_OTTYB;
+ default: return PROMDEV_O_UNK;
+ }
+}
diff --git a/arch/sparc64/prom/devops.c b/arch/sparc64/prom/devops.c
new file mode 100644
index 000000000..980717928
--- /dev/null
+++ b/arch/sparc64/prom/devops.c
@@ -0,0 +1,41 @@
+/* $Id: devops.c,v 1.2 1997/02/25 12:40:20 jj Exp $
+ * devops.c: Device operations using the PROM.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ */
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+
+#include <asm/openprom.h>
+#include <asm/oplib.h>
+
+/* Open the device described by the string 'dstr'. Returns the handle
+ * to that device used for subsequent operations on that device.
+ * Returns -1 on failure.
+ */
+int
+prom_devopen(char *dstr)
+{
+ return p1275_cmd ("open", P1275_ARG(0,P1275_ARG_IN_STRING)|
+ P1275_INOUT(1,1),
+ dstr);
+}
+
+/* Close the device described by device handle 'dhandle'. */
+int
+prom_devclose(int dhandle)
+{
+ p1275_cmd ("close", P1275_INOUT(1,0), dhandle);
+ return 0;
+}
+
+/* Seek to specified location described by 'seekhi' and 'seeklo'
+ * for device 'dhandle'.
+ */
+void
+prom_seek(int dhandle, unsigned int seekhi, unsigned int seeklo)
+{
+ p1275_cmd ("seek", P1275_INOUT(3,1), dhandle, seekhi, seeklo);
+}
diff --git a/arch/sparc64/prom/init.c b/arch/sparc64/prom/init.c
new file mode 100644
index 000000000..7dcef7642
--- /dev/null
+++ b/arch/sparc64/prom/init.c
@@ -0,0 +1,79 @@
+/* $Id: init.c,v 1.7 1997/03/24 17:43:59 jj Exp $
+ * init.c: Initialize internal variables used by the PROM
+ * library functions.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/string.h>
+
+#include <asm/openprom.h>
+#include <asm/oplib.h>
+
+enum prom_major_version prom_vers;
+unsigned int prom_rev, prom_prev;
+
+/* The root node of the prom device tree. */
+int prom_root_node;
+int prom_stdin, prom_stdout;
+int prom_chosen_node;
+
+/* 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);
+extern void prom_cif_init(void *, void *);
+
+__initfunc(void prom_init(void *cif_handler, void *cif_stack))
+{
+ char buffer[80];
+ int node;
+
+ prom_vers = PROM_P1275;
+
+ prom_cif_init(cif_handler, cif_stack);
+
+ prom_root_node = prom_getsibling(0);
+ if((prom_root_node == 0) || (prom_root_node == -1))
+ prom_halt();
+
+ prom_chosen_node = prom_finddevice("/chosen");
+ if (!prom_chosen_node || prom_chosen_node == -1)
+ prom_halt();
+
+ prom_stdin = prom_getint (prom_chosen_node, "stdin");
+ prom_stdout = prom_getint (prom_chosen_node, "stdout");
+
+ node = prom_finddevice("/openprom");
+ if (!node || node == -1)
+ prom_halt();
+
+ prom_getstring (node, "version", buffer, sizeof (buffer));
+
+ prom_printf ("\n");
+
+ if (strncmp (buffer, "OBP ", 4) || buffer[5] != '.' || buffer[7] != '.') {
+ prom_printf ("Strange OBP version `%s'.\n", buffer);
+ prom_halt ();
+ }
+ /* Version field is expected to be 'OBP x.y.z date...' */
+
+ prom_rev = buffer[6] - '0';
+ prom_prev = ((buffer[4] - '0') << 16) |
+ ((buffer[6] - '0') << 8) |
+ (buffer[8] - '0');
+
+ printk ("PROMLIB: Sun IEEE Boot Prom %s\n", buffer + 4);
+
+ prom_meminit();
+
+ prom_ranges_init();
+
+ /* Initialization successful. */
+}
diff --git a/arch/sparc64/prom/memory.c b/arch/sparc64/prom/memory.c
new file mode 100644
index 000000000..bb3e50a92
--- /dev/null
+++ b/arch/sparc64/prom/memory.c
@@ -0,0 +1,152 @@
+/* $Id: memory.c,v 1.3 1997/03/04 16:27:10 jj Exp $
+ * memory.c: Prom routine for acquiring various bits of information
+ * about RAM on the machine, both virtual and physical.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+
+#include <asm/openprom.h>
+#include <asm/oplib.h>
+
+/* This routine, for consistency, returns the ram parameters in the
+ * V0 prom memory descriptor format. I choose this format because I
+ * think it was the easiest to work with. I feel the religious
+ * arguments now... ;) Also, I return the linked lists sorted to
+ * prevent paging_init() upset stomach as I have not yet written
+ * the pepto-bismol kernel module yet.
+ */
+
+struct linux_prom64_registers prom_reg_memlist[64];
+struct linux_prom64_registers prom_reg_tmp[64];
+
+struct linux_mlist_p1275 prom_phys_total[64];
+struct linux_mlist_p1275 prom_prom_taken[64];
+struct linux_mlist_p1275 prom_phys_avail[64];
+
+struct linux_mlist_p1275 *prom_ptot_ptr = prom_phys_total;
+struct linux_mlist_p1275 *prom_ptak_ptr = prom_prom_taken;
+struct linux_mlist_p1275 *prom_pavl_ptr = prom_phys_avail;
+
+struct linux_mem_p1275 prom_memlist;
+
+
+/* Internal Prom library routine to sort a linux_mlist_p1275 memory
+ * list. Used below in initialization.
+ */
+__initfunc(static void
+prom_sortmemlist(struct linux_mlist_p1275 *thislist))
+{
+ int swapi = 0;
+ int i, mitr, tmpsize;
+ unsigned long tmpaddr;
+ unsigned long lowest;
+
+ for(i=0; thislist[i].theres_more != 0; i++) {
+ lowest = thislist[i].start_adr;
+ for(mitr = i+1; thislist[mitr-1].theres_more != 0; mitr++)
+ if(thislist[mitr].start_adr < lowest) {
+ lowest = thislist[mitr].start_adr;
+ swapi = mitr;
+ }
+ if(lowest == thislist[i].start_adr) continue;
+ tmpaddr = thislist[swapi].start_adr;
+ tmpsize = thislist[swapi].num_bytes;
+ for(mitr = swapi; mitr > i; mitr--) {
+ thislist[mitr].start_adr = thislist[mitr-1].start_adr;
+ thislist[mitr].num_bytes = thislist[mitr-1].num_bytes;
+ }
+ thislist[i].start_adr = tmpaddr;
+ thislist[i].num_bytes = tmpsize;
+ }
+}
+
+/* Initialize the memory lists based upon the prom version. */
+__initfunc(void prom_meminit(void))
+{
+ int node = 0;
+ unsigned int iter, num_regs;
+
+ node = prom_finddevice("/memory");
+ num_regs = prom_getproperty(node, "available",
+ (char *) prom_reg_memlist,
+ sizeof(prom_reg_memlist));
+ num_regs = (num_regs/sizeof(struct linux_prom64_registers));
+ for(iter=0; iter<num_regs; iter++) {
+ prom_phys_avail[iter].start_adr =
+ prom_reg_memlist[iter].phys_addr;
+ prom_phys_avail[iter].num_bytes =
+ (unsigned long) prom_reg_memlist[iter].reg_size;
+ prom_phys_avail[iter].theres_more =
+ &prom_phys_avail[iter+1];
+ }
+ prom_phys_avail[iter-1].theres_more = 0x0;
+
+ num_regs = prom_getproperty(node, "reg",
+ (char *) prom_reg_memlist,
+ sizeof(prom_reg_memlist));
+ num_regs = (num_regs/sizeof(struct linux_prom64_registers));
+ for(iter=0; iter<num_regs; iter++) {
+ prom_phys_total[iter].start_adr =
+ prom_reg_memlist[iter].phys_addr;
+ prom_phys_total[iter].num_bytes =
+ (unsigned long) prom_reg_memlist[iter].reg_size;
+ prom_phys_total[iter].theres_more =
+ &prom_phys_total[iter+1];
+ }
+ prom_phys_total[iter-1].theres_more = 0x0;
+
+ node = prom_finddevice("/virtual-memory");
+ num_regs = prom_getproperty(node, "available",
+ (char *) prom_reg_memlist,
+ sizeof(prom_reg_memlist));
+ num_regs = (num_regs/sizeof(struct linux_prom64_registers));
+
+ /* Convert available virtual areas to taken virtual
+ * areas. First sort, then convert.
+ */
+ for(iter=0; iter<num_regs; iter++) {
+ prom_prom_taken[iter].start_adr =
+ prom_reg_memlist[iter].phys_addr;
+ prom_prom_taken[iter].num_bytes =
+ (unsigned long) prom_reg_memlist[iter].reg_size;
+ prom_prom_taken[iter].theres_more =
+ &prom_phys_total[iter+1];
+ }
+ prom_prom_taken[iter-1].theres_more = 0x0;
+
+ prom_sortmemlist(prom_prom_taken);
+
+ /* Finally, convert. */
+ for(iter=0; iter<num_regs; iter++) {
+ prom_prom_taken[iter].start_adr =
+ prom_prom_taken[iter].start_adr +
+ prom_prom_taken[iter].num_bytes;
+ prom_prom_taken[iter].num_bytes =
+ prom_prom_taken[iter+1].start_adr -
+ prom_prom_taken[iter].start_adr;
+ }
+ prom_prom_taken[iter-1].num_bytes =
+ ((unsigned long)-1) - (unsigned long) prom_prom_taken[iter-1].start_adr;
+
+ /* Sort the other two lists. */
+ prom_sortmemlist(prom_phys_total);
+ prom_sortmemlist(prom_phys_avail);
+
+ /* Link all the lists into the top-level descriptor. */
+ prom_memlist.p1275_totphys=&prom_ptot_ptr;
+ prom_memlist.p1275_prommap=&prom_ptak_ptr;
+ prom_memlist.p1275_available=&prom_pavl_ptr;
+}
+
+/* This returns a pointer to our libraries internal p1275 format
+ * memory descriptor.
+ */
+struct linux_mem_p1275 *
+prom_meminfo(void)
+{
+ return &prom_memlist;
+}
diff --git a/arch/sparc64/prom/misc.c b/arch/sparc64/prom/misc.c
new file mode 100644
index 000000000..fe9bf9c6b
--- /dev/null
+++ b/arch/sparc64/prom/misc.c
@@ -0,0 +1,134 @@
+/* $Id: misc.c,v 1.6 1997/04/10 05:13:05 davem Exp $
+ * misc.c: Miscellaneous prom functions that don't belong
+ * anywhere else.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <asm/openprom.h>
+#include <asm/oplib.h>
+
+/* Reset and reboot the machine with the command 'bcommand'. */
+void
+prom_reboot(char *bcommand)
+{
+ p1275_cmd ("boot", P1275_ARG(0,P1275_ARG_IN_STRING)|
+ P1275_INOUT(1,0), bcommand);
+}
+
+/* Forth evaluate the expression contained in 'fstring'. */
+void
+prom_feval(char *fstring)
+{
+ if(!fstring || fstring[0] == 0)
+ return;
+ p1275_cmd ("interpret", P1275_ARG(0,P1275_ARG_IN_STRING)|
+ P1275_INOUT(1,1), fstring);
+}
+
+/* We want to do this more nicely some day. */
+#ifdef CONFIG_SUN_CONSOLE
+extern void console_restore_palette(void);
+extern void set_palette(void);
+extern int serial_console;
+#endif
+
+/* Drop into the prom, with the chance to continue with the 'go'
+ * prom command.
+ */
+/* XXX Fix the pre and post calls as it locks up my Ultra at the moment -DaveM */
+void
+prom_cmdline(void)
+{
+ extern void kernel_enter_debugger(void);
+ extern void install_obp_ticker(void);
+ extern void install_linux_ticker(void);
+ unsigned long flags;
+
+ /* kernel_enter_debugger(); */
+#ifdef CONFIG_SUN_CONSOLE
+#if 0
+ if(!serial_console)
+ console_restore_palette ();
+#endif
+#endif
+ /* install_obp_ticker(); */
+ save_flags(flags); cli();
+ p1275_cmd ("enter", P1275_INOUT(0,0));
+ restore_flags(flags);
+ /* install_linux_ticker(); */
+#ifdef CONFIG_SUN_CONSOLE
+#if 0
+ if(!serial_console)
+ set_palette ();
+#endif
+#endif
+}
+
+/* Drop into the prom, but completely terminate the program.
+ * No chance of continuing.
+ */
+void
+prom_halt(void)
+{
+again:
+ p1275_cmd ("exit", P1275_INOUT(0,0));
+ goto again; /* PROM is out to get me -DaveM */
+}
+
+/* Set prom sync handler to call function 'funcp'. */
+void
+prom_setsync(sync_func_t funcp)
+{
+ if(!funcp) return;
+ p1275_cmd ("set-callback", P1275_ARG(0,P1275_ARG_IN_FUNCTION)|
+ P1275_INOUT(1,1), funcp);
+}
+
+/* Get the idprom and stuff it into buffer 'idbuf'. Returns the
+ * format type. 'num_bytes' is the number of bytes that your idbuf
+ * has space for. Returns 0xff on error.
+ */
+unsigned char
+prom_get_idprom(char *idbuf, int num_bytes)
+{
+ int len;
+
+ len = prom_getproplen(prom_root_node, "idprom");
+ if((len>num_bytes) || (len==-1)) return 0xff;
+ if(!prom_getproperty(prom_root_node, "idprom", idbuf, num_bytes))
+ return idbuf[0];
+
+ return 0xff;
+}
+
+/* Get the major prom version number. */
+int
+prom_version(void)
+{
+ return PROM_P1275;
+}
+
+/* Get the prom plugin-revision. */
+int
+prom_getrev(void)
+{
+ return prom_rev;
+}
+
+/* Get the prom firmware print revision. */
+int
+prom_getprev(void)
+{
+ return prom_prev;
+}
+
+/* Install Linux trap table so PROM uses that instead of it's own. */
+void prom_set_trap_table(unsigned long tba)
+{
+ p1275_cmd("SUNW,set-trap-table", P1275_INOUT(1, 0), tba);
+}
diff --git a/arch/sparc64/prom/p1275.c b/arch/sparc64/prom/p1275.c
new file mode 100644
index 000000000..3eb0311df
--- /dev/null
+++ b/arch/sparc64/prom/p1275.c
@@ -0,0 +1,170 @@
+/* $Id: p1275.c,v 1.8 1997/04/03 09:29:21 davem Exp $
+ * p1275.c: Sun IEEE 1275 PROM low level interface routines
+ *
+ * Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/string.h>
+
+#include <asm/openprom.h>
+#include <asm/oplib.h>
+#include <asm/system.h>
+#include <asm/spitfire.h>
+#include <asm/pstate.h>
+
+/* If you change layout of this structure, please change the prom_doit
+ function below as well. */
+typedef struct {
+ unsigned prom_doit_code [24]; /* 0x8000 */
+ long prom_sync_routine; /* 0x8060 */
+ void (*prom_cif_handler)(long *); /* 0x8068 */
+ unsigned long prom_cif_stack; /* 0x8070 */
+ unsigned long prom_args [23]; /* 0x8078 */
+ char prom_buffer [7888];
+} at0x8000;
+
+static void (*prom_do_it)(void);
+
+void prom_cif_interface (void) __attribute__ ((__section__ (".p1275")));
+
+/* At most 14 insns */
+void prom_cif_interface (void)
+{
+ __asm__ __volatile__ ("
+ sethi %%hi(0x8000), %%o0
+ ldx [%%o0 + 0x070], %%o1 ! prom_cif_stack
+ save %%o1, -0xc0, %%sp
+ ldx [%%i0 + 0x068], %%l2 ! prom_cif_handler
+ rdpr %%pstate, %%l4
+ mov %%g4, %%l0
+ mov %%g6, %%l1
+ wrpr %%l4, %0, %%pstate ! turn on address masking
+ call %%l2
+ or %%i0, 0x078, %%o0 ! prom_args
+ wrpr %%l4, 0, %%pstate ! put pstate back
+ mov %%l0, %%g4
+ ret
+ restore %%l1, 0, %%g6
+ save %%sp, -0xc0, %%sp ! If you change the offset of the save
+ rdpr %%pstate, %%l4 ! here, please change the 0x8038
+ andn %%l4, %0, %%l3 ! constant below as well
+ wrpr %%l3, %%pstate
+ ldx [%%o0 + 0x060], %%l2
+ call %%l2
+ nop
+ wrpr %%l4, 0, %%pstate
+ ret
+ restore
+ " : : "i" (PSTATE_AM));
+}
+
+long p1275_cmd (char *service, long fmt, ...)
+{
+ char *p, *q;
+ unsigned long flags;
+ int nargs, nrets, i;
+ va_list list;
+ long attrs, x;
+ long ctx = 0;
+ at0x8000 *low = (at0x8000 *)(0x8000);
+
+ p = low->prom_buffer;
+ save_and_cli(flags);
+ ctx = spitfire_get_primary_context ();
+ if (ctx) {
+ flushw_user ();
+ spitfire_set_primary_context (0);
+ }
+ low->prom_args[0] = (unsigned long)p; /* service */
+ strcpy (p, service);
+ p = (char *)(((long)(strchr (p, 0) + 8)) & ~7);
+ low->prom_args[1] = nargs = (fmt & 0x0f); /* nargs */
+ low->prom_args[2] = nrets = ((fmt & 0xf0) >> 4); /* nrets */
+ attrs = fmt >> 8;
+ va_start(list, fmt);
+ for (i = 0; i < nargs; i++, attrs >>= 3) {
+ switch (attrs & 0x7) {
+ case P1275_ARG_NUMBER:
+ low->prom_args[i + 3] = (unsigned)va_arg(list, long); break;
+ case P1275_ARG_IN_STRING:
+ strcpy (p, va_arg(list, char *));
+ low->prom_args[i + 3] = (unsigned long)p;
+ p = (char *)(((long)(strchr (p, 0) + 8)) & ~7);
+ break;
+ case P1275_ARG_OUT_BUF:
+ (void) va_arg(list, char *);
+ low->prom_args[i + 3] = (unsigned long)p;
+ x = va_arg(list, long);
+ i++; attrs >>= 3;
+ p = (char *)(((long)(p + (int)x + 7)) & ~7);
+ low->prom_args[i + 3] = x;
+ break;
+ case P1275_ARG_IN_BUF:
+ q = va_arg(list, char *);
+ low->prom_args[i + 3] = (unsigned long)p;
+ x = va_arg(list, long);
+ i++; attrs >>= 3;
+ memcpy (p, q, (int)x);
+ p = (char *)(((long)(p + (int)x + 7)) & ~7);
+ low->prom_args[i + 3] = x;
+ break;
+ case P1275_ARG_OUT_32B:
+ (void) va_arg(list, char *);
+ low->prom_args[i + 3] = (unsigned long)p;
+ p += 32;
+ break;
+ case P1275_ARG_IN_FUNCTION:
+ low->prom_args[i + 3] = 0x8038;
+ low->prom_sync_routine = va_arg(list, long); break;
+ }
+ }
+ va_end(list);
+
+ (*prom_do_it)();
+
+ attrs = fmt >> 8;
+ va_start(list, fmt);
+ for (i = 0; i < nargs; i++, attrs >>= 3) {
+ switch (attrs & 0x7) {
+ case P1275_ARG_NUMBER:
+ (void) va_arg(list, long); break;
+ case P1275_ARG_IN_STRING:
+ (void) va_arg(list, char *); break;
+ case P1275_ARG_IN_FUNCTION:
+ (void) va_arg(list, long); break;
+ case P1275_ARG_IN_BUF:
+ (void) va_arg(list, char *);
+ (void) va_arg(list, long);
+ i++; attrs >>= 3;
+ break;
+ case P1275_ARG_OUT_BUF:
+ p = va_arg(list, char *);
+ x = va_arg(list, long);
+ memcpy (p, (char *)(low->prom_args[i + 3]), (int)x);
+ i++; attrs >>= 3;
+ break;
+ case P1275_ARG_OUT_32B:
+ p = va_arg(list, char *);
+ memcpy (p, (char *)(low->prom_args[i + 3]), 32);
+ break;
+ }
+ }
+ va_end(list);
+ x = low->prom_args [nargs + 3];
+
+ if (ctx)
+ spitfire_set_primary_context (ctx);
+ restore_flags(flags);
+ return x;
+}
+
+void prom_cif_init(void *cif_handler, void *cif_stack)
+{
+ at0x8000 *low = (at0x8000 *)(0x8000);
+
+ low->prom_cif_handler = (void (*)(long *))cif_handler;
+ low->prom_cif_stack = (unsigned long)cif_stack;
+ prom_do_it = (void (*)(void))(0x8000);
+}
diff --git a/arch/sparc64/prom/printf.c b/arch/sparc64/prom/printf.c
new file mode 100644
index 000000000..33428e9f6
--- /dev/null
+++ b/arch/sparc64/prom/printf.c
@@ -0,0 +1,46 @@
+/* $Id: printf.c,v 1.3 1997/03/18 18:00:00 jj Exp $
+ * printf.c: Internal prom library printf facility.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ */
+
+/* This routine is internal to the prom library, no one else should know
+ * about or use it! It's simple and smelly anyway....
+ */
+
+#include <linux/kernel.h>
+
+#include <asm/openprom.h>
+#include <asm/oplib.h>
+
+static char ppbuf[1024];
+
+extern void prom_puts (char *, int);
+
+void
+prom_printf(char *fmt, ...)
+{
+ va_list args;
+ char ch, *bptr, *last;
+ int i;
+
+ va_start(args, fmt);
+ i = vsprintf(ppbuf, fmt, args);
+
+ bptr = ppbuf;
+ last = ppbuf;
+
+ while((ch = *(bptr++)) != 0) {
+ if(ch == '\n') {
+ if (last < bptr - 1)
+ prom_puts (last, bptr - 1 - last);
+ prom_putchar('\r');
+ last = bptr - 1;
+ }
+ }
+ if (last < bptr - 1)
+ prom_puts (last, bptr - 1 - last);
+ va_end(args);
+ return;
+}
diff --git a/arch/sparc64/prom/ranges.c b/arch/sparc64/prom/ranges.c
new file mode 100644
index 000000000..323539237
--- /dev/null
+++ b/arch/sparc64/prom/ranges.c
@@ -0,0 +1,107 @@
+/* $Id: ranges.c,v 1.3 1997/03/21 12:33:36 jj Exp $
+ * ranges.c: Handle ranges in newer proms for obio/sbus.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ */
+
+#include <linux/init.h>
+#include <asm/openprom.h>
+#include <asm/oplib.h>
+#include <asm/sbus.h>
+#include <asm/system.h>
+
+struct linux_prom_ranges promlib_obio_ranges[PROMREG_MAX];
+int num_obio_ranges;
+
+/* Adjust register values based upon the ranges parameters. */
+inline void
+prom_adjust_regs(struct linux_prom_registers *regp, int nregs,
+ struct linux_prom_ranges *rangep, int nranges)
+{
+ int regc, rngc;
+
+ for(regc=0; regc < nregs; regc++) {
+ for(rngc=0; rngc < nranges; rngc++)
+ if(regp[regc].which_io == rangep[rngc].ot_child_space)
+ break; /* Fount it */
+ if(rngc==nranges) /* oops */
+ prom_printf("adjust_regs: Could not find range with matching bus type...\n");
+ regp[regc].which_io = rangep[rngc].ot_parent_space;
+ regp[regc].phys_addr += rangep[rngc].ot_parent_base;
+ }
+}
+
+inline void
+prom_adjust_ranges(struct linux_prom_ranges *ranges1, int nranges1,
+ struct linux_prom_ranges *ranges2, int nranges2)
+{
+ int rng1c, rng2c;
+
+ for(rng1c=0; rng1c < nranges1; rng1c++) {
+ for(rng2c=0; rng2c < nranges2; rng2c++)
+ if(ranges1[rng1c].ot_child_space ==
+ ranges2[rng2c].ot_child_space) break;
+ if(rng2c == nranges2) /* oops */
+ prom_printf("adjust_ranges: Could not find matching bus type...\n");
+ ranges1[rng1c].ot_parent_space = ranges2[rng2c].ot_parent_space;
+ ranges1[rng1c].ot_parent_base += ranges2[rng2c].ot_parent_base;
+ }
+}
+
+/* Apply probed sbus ranges to registers passed, if no ranges return. */
+void prom_apply_sbus_ranges(struct linux_sbus *sbus, struct linux_prom_registers *regs,
+ int nregs, struct linux_sbus_device *sdev)
+{
+ if(sbus->num_sbus_ranges) {
+ if(sdev && (sdev->ranges_applied == 0)) {
+ sdev->ranges_applied = 1;
+ prom_adjust_regs(regs, nregs, sbus->sbus_ranges,
+ sbus->num_sbus_ranges);
+ }
+ }
+}
+
+__initfunc(void prom_ranges_init(void))
+{
+}
+
+__initfunc(void prom_sbus_ranges_init(int iommund, struct linux_sbus *sbus))
+{
+ int success;
+
+ sbus->num_sbus_ranges = 0;
+ success = prom_getproperty(sbus->prom_node, "ranges",
+ (char *) sbus->sbus_ranges,
+ sizeof (sbus->sbus_ranges));
+ if (success != -1)
+ sbus->num_sbus_ranges = (success/sizeof(struct linux_prom_ranges));
+}
+
+void
+prom_apply_generic_ranges (int node, int parent, struct linux_prom_registers *regs, int nregs)
+{
+ int success;
+ int num_ranges;
+ struct linux_prom_ranges ranges[PROMREG_MAX];
+
+ success = prom_getproperty(node, "ranges",
+ (char *) ranges,
+ sizeof (ranges));
+ if (success != -1) {
+ num_ranges = (success/sizeof(struct linux_prom_ranges));
+ if (parent) {
+ struct linux_prom_ranges parent_ranges[PROMREG_MAX];
+ int num_parent_ranges;
+
+ success = prom_getproperty(parent, "ranges",
+ (char *) parent_ranges,
+ sizeof (parent_ranges));
+ if (success != -1) {
+ num_parent_ranges = (success/sizeof(struct linux_prom_ranges));
+ prom_adjust_ranges (ranges, num_ranges, parent_ranges, num_parent_ranges);
+ }
+ }
+ prom_adjust_regs(regs, nregs, ranges, num_ranges);
+ }
+}
diff --git a/arch/sparc64/prom/tree.c b/arch/sparc64/prom/tree.c
new file mode 100644
index 000000000..ae4baf858
--- /dev/null
+++ b/arch/sparc64/prom/tree.c
@@ -0,0 +1,328 @@
+/* $Id: tree.c,v 1.5 1997/03/24 17:44:01 jj Exp $
+ * tree.c: Basic device tree traversal/scanning for the Linux
+ * prom library.
+ *
+ * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ */
+
+#include <linux/string.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+
+#include <asm/openprom.h>
+#include <asm/oplib.h>
+
+/* Return the child of node 'node' or zero if no this node has no
+ * direct descendent.
+ */
+__inline__ int
+__prom_getchild(int node)
+{
+ return p1275_cmd ("child", P1275_INOUT(1, 1), node);
+}
+
+__inline__ int
+prom_getchild(int node)
+{
+ long cnode;
+
+ if(node == -1) return 0;
+ cnode = __prom_getchild(node);
+ if(cnode == -1) return 0;
+ return (int)cnode;
+}
+
+__inline__ int
+prom_getparent(int node)
+{
+ long cnode;
+
+ if(node == -1) return 0;
+ cnode = p1275_cmd ("parent", P1275_INOUT(1, 1), node);
+ if(cnode == -1) return 0;
+ return (int)cnode;
+}
+
+/* Return the next sibling of node 'node' or zero if no more siblings
+ * at this level of depth in the tree.
+ */
+__inline__ int
+__prom_getsibling(int node)
+{
+ return p1275_cmd ("peer", P1275_INOUT(1, 1), node);
+}
+
+__inline__ int
+prom_getsibling(int node)
+{
+ long sibnode;
+
+ if(node == -1) return 0;
+ sibnode = __prom_getsibling(node);
+ if(sibnode == -1) return 0;
+ return (int)sibnode;
+}
+
+/* Return the length in bytes of property 'prop' at node 'node'.
+ * Return -1 on error.
+ */
+__inline__ int
+prom_getproplen(int node, char *prop)
+{
+ if((!node) || (!prop)) return -1;
+ return p1275_cmd ("getproplen",
+ P1275_ARG(1,P1275_ARG_IN_STRING)|
+ P1275_INOUT(2, 1),
+ node, prop);
+}
+
+/* Acquire a property 'prop' at node 'node' and place it in
+ * 'buffer' which has a size of 'bufsize'. If the acquisition
+ * was successful the length will be returned, else -1 is returned.
+ */
+__inline__ int
+prom_getproperty(int node, char *prop, char *buffer, int bufsize)
+{
+ int plen;
+
+ plen = prom_getproplen(node, prop);
+ if((plen > bufsize) || (plen == 0) || (plen == -1))
+ return -1;
+ else {
+ /* Ok, things seem all right. */
+ return p1275_cmd ("getprop",
+ P1275_ARG(1,P1275_ARG_IN_STRING)|
+ P1275_ARG(2,P1275_ARG_OUT_BUF)|
+ P1275_INOUT(4, 1),
+ node, prop, buffer, P1275_SIZE(plen));
+ }
+}
+
+/* Acquire an integer property and return its value. Returns -1
+ * on failure.
+ */
+__inline__ int
+prom_getint(int node, char *prop)
+{
+ int intprop;
+
+ if(prom_getproperty(node, prop, (char *) &intprop, sizeof(int)) != -1)
+ return intprop;
+
+ return -1;
+}
+
+/* Acquire an integer property, upon error return the passed default
+ * integer.
+ */
+
+int
+prom_getintdefault(int node, char *property, int deflt)
+{
+ int retval;
+
+ retval = prom_getint(node, property);
+ if(retval == -1) return deflt;
+
+ return retval;
+}
+
+/* Acquire a boolean property, 1=TRUE 0=FALSE. */
+int
+prom_getbool(int node, char *prop)
+{
+ int retval;
+
+ retval = prom_getproplen(node, prop);
+ if(retval == -1) return 0;
+ return 1;
+}
+
+/* Acquire a property whose value is a string, returns a null
+ * string on error. The char pointer is the user supplied string
+ * buffer.
+ */
+void
+prom_getstring(int node, char *prop, char *user_buf, int ubuf_size)
+{
+ int len;
+
+ len = prom_getproperty(node, prop, user_buf, ubuf_size);
+ if(len != -1) return;
+ user_buf[0] = 0;
+ return;
+}
+
+
+/* Does the device at node 'node' have name 'name'?
+ * YES = 1 NO = 0
+ */
+int
+prom_nodematch(int node, char *name)
+{
+ char namebuf[128];
+ prom_getproperty(node, "name", namebuf, sizeof(namebuf));
+ if(strcmp(namebuf, name) == 0) return 1;
+ return 0;
+}
+
+/* Search siblings at 'node_start' for a node with name
+ * 'nodename'. Return node if successful, zero if not.
+ */
+int
+prom_searchsiblings(int node_start, char *nodename)
+{
+
+ int thisnode, error;
+ char promlib_buf[128];
+
+ for(thisnode = node_start; thisnode;
+ thisnode=prom_getsibling(thisnode)) {
+ error = prom_getproperty(thisnode, "name", promlib_buf,
+ sizeof(promlib_buf));
+ /* Should this ever happen? */
+ if(error == -1) continue;
+ if(strcmp(nodename, promlib_buf)==0) return thisnode;
+ }
+
+ return 0;
+}
+
+/* Gets name in the {name@x,yyyyy|name (if no reg)} form */
+int
+prom_getname (int node, char *buffer, int len)
+{
+ int i, sbus = 0;
+ struct linux_prom_registers *reg;
+ struct linux_prom64_registers reg64[PROMREG_MAX];
+
+ for (sbus = prom_getparent (node); sbus; sbus = prom_getparent (sbus)) {
+ i = prom_getproperty (sbus, "name", buffer, len);
+ if (i > 0) {
+ buffer [i] = 0;
+ if (!strcmp (buffer, "sbus"))
+ break;
+ }
+ }
+ i = prom_getproperty (node, "name", buffer, len);
+ if (i <= 0) {
+ buffer [0] = 0;
+ return -1;
+ }
+ buffer [i] = 0;
+ len -= i;
+ i = prom_getproperty (node, "reg", (char *)reg64, sizeof (reg64));
+ if (i <= 0) return 0;
+ if (len < 16) return -1;
+ buffer = strchr (buffer, 0);
+ if (sbus) {
+ reg = (struct linux_prom_registers *)reg64;
+ sprintf (buffer, "@%x,%x", reg[0].which_io, (uint)reg[0].phys_addr);
+ } else {
+ sprintf (buffer, "@%x,%x", (unsigned int)(reg64[0].phys_addr >> 36), (unsigned int)(reg64[0].phys_addr));
+ }
+ return 0;
+}
+
+/* Return the first property type for node 'node'.
+ * buffer should be at least 32B in length
+ */
+__inline__ char *
+prom_firstprop(int node, char *buffer)
+{
+ *buffer = 0;
+ if(node == -1) return buffer;
+ p1275_cmd ("nextprop", P1275_ARG(2,P1275_ARG_OUT_32B)|
+ P1275_INOUT(3, 0),
+ node, (char *) 0x0, buffer);
+ return buffer;
+}
+
+/* Return the property type string after property type 'oprop'
+ * at node 'node' . Returns NULL string if no more
+ * property types for this node.
+ */
+__inline__ char *
+prom_nextprop(int node, char *oprop, char *buffer)
+{
+ char buf[32];
+
+ if(node == -1) {
+ *buffer = 0;
+ return buffer;
+ }
+ if (oprop == buffer) {
+ strcpy (buf, oprop);
+ oprop = buf;
+ }
+ p1275_cmd ("nextprop", P1275_ARG(1,P1275_ARG_IN_STRING)|
+ P1275_ARG(2,P1275_ARG_OUT_32B)|
+ P1275_INOUT(3, 0),
+ node, oprop, buffer);
+ return buffer;
+}
+
+int
+prom_finddevice(char *name)
+{
+ if(!name) return 0;
+ return p1275_cmd ("finddevice", P1275_ARG(0,P1275_ARG_IN_STRING)|
+ P1275_INOUT(1, 1),
+ name);
+}
+
+int prom_node_has_property(int node, char *prop)
+{
+ char buf [32];
+
+ *buf = 0;
+ do {
+ prom_nextprop(node, buf, buf);
+ if(!strcmp(buf, prop))
+ return 1;
+ } while (*buf);
+ return 0;
+}
+
+/* Set property 'pname' at node 'node' to value 'value' which has a length
+ * of 'size' bytes. Return the number of bytes the prom accepted.
+ */
+int
+prom_setprop(int node, char *pname, char *value, int size)
+{
+ if(size == 0) return 0;
+ if((pname == 0) || (value == 0)) return 0;
+
+ return p1275_cmd ("setprop", P1275_ARG(1,P1275_ARG_IN_STRING)|
+ P1275_ARG(2,P1275_ARG_IN_BUF)|
+ P1275_INOUT(4, 1),
+ node, pname, value, P1275_SIZE(size));
+}
+
+__inline__ int
+prom_inst2pkg(int inst)
+{
+ int node;
+
+ node = p1275_cmd ("instance-to-package", P1275_INOUT(1, 1), inst);
+ if (node == -1) return 0;
+ return node;
+}
+
+/* Return 'node' assigned to a particular prom 'path'
+ * FIXME: Should work for v0 as well
+ */
+int
+prom_pathtoinode(char *path)
+{
+ int node, inst;
+
+ inst = prom_devopen (path);
+ if (inst == -1) return 0;
+ node = prom_inst2pkg (inst);
+ prom_devclose (inst);
+ if (node == -1) return 0;
+ return node;
+}