aboutsummaryrefslogtreecommitdiff
path: root/arch/sparc
diff options
context:
space:
mode:
authorSam Ravnborg <sam@ravnborg.org>2008-11-29 22:16:52 -0800
committerDavid S. Miller <davem@davemloft.net>2008-12-04 09:17:17 -0800
commit5de18cde3b748aafc6d187d7655ba42f2260501d (patch)
treefa4805081e6663b10be678c924a0adc3c7a95107 /arch/sparc
parent708d4f09647106d549c646dc459c7ccf2c237cc8 (diff)
sparc,sparc64: unify prom/
- all files with identical names copied and renamed to *_64.c - the remaning files copied as is - added sparc64 specific files to sparc/prom/Makefile - teach sparc64 Makefile to look into sparc/prom/ - delete unused Makefile from sparc64/prom/ linking order was not kept for sparc64 with this change. It was not possible to keep linking order for both sparc and sparc64 and as sparc64 see more testing than sparc it was natural to break linking order on sparc64. Should it have any effect it would be detected sooner this way. printf_32.c and printf_64.c are obvious candidates to be merged but they are not 100% equal so that was left for later Signed-off-by: Sam Ravnborg <sam@ravnborg.org> Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'arch/sparc')
-rw-r--r--arch/sparc/Makefile3
-rw-r--r--arch/sparc/prom/Makefile4
-rw-r--r--arch/sparc/prom/bootstr_64.c40
-rw-r--r--arch/sparc/prom/cif.S48
-rw-r--r--arch/sparc/prom/console_64.c74
-rw-r--r--arch/sparc/prom/devops_64.c41
-rw-r--r--arch/sparc/prom/init_64.c57
-rw-r--r--arch/sparc/prom/misc_64.c335
-rw-r--r--arch/sparc/prom/p1275.c151
-rw-r--r--arch/sparc/prom/printf_64.c47
-rw-r--r--arch/sparc/prom/tree_64.c299
11 files changed, 1097 insertions, 2 deletions
diff --git a/arch/sparc/Makefile b/arch/sparc/Makefile
index 3c043baf92b..317aad600ed 100644
--- a/arch/sparc/Makefile
+++ b/arch/sparc/Makefile
@@ -76,9 +76,8 @@ core-$(CONFIG_SPARC32) += arch/sparc/kernel/
core-$(CONFIG_SPARC64) += arch/sparc64/kernel/
core-y += arch/sparc/mm/ arch/sparc/math-emu/
-libs-$(CONFIG_SPARC32) += arch/sparc/prom/
+libs-y += arch/sparc/prom/
libs-$(CONFIG_SPARC32) += arch/sparc/lib/
-libs-$(CONFIG_SPARC64) += arch/sparc64/prom/
libs-$(CONFIG_SPARC64) += arch/sparc64/lib/
drivers-$(CONFIG_OPROFILE) += arch/sparc/oprofile/
diff --git a/arch/sparc/prom/Makefile b/arch/sparc/prom/Makefile
index 74ca9cceaee..f8e0278b8b4 100644
--- a/arch/sparc/prom/Makefile
+++ b/arch/sparc/prom/Makefile
@@ -1,6 +1,8 @@
# Makefile for the Sun Boot PROM interface library under
# Linux.
#
+asflags := -ansi
+ccflags := -Werror
lib-y := bootstr_$(BITS).o
lib-$(CONFIG_SPARC32) += devmap.o
@@ -15,3 +17,5 @@ lib-$(CONFIG_SPARC32) += segment.o
lib-y += console_$(BITS).o
lib-y += printf_$(BITS).o
lib-y += tree_$(BITS).o
+lib-$(CONFIG_SPARC64) += p1275.o
+lib-$(CONFIG_SPARC64) += cif.o
diff --git a/arch/sparc/prom/bootstr_64.c b/arch/sparc/prom/bootstr_64.c
new file mode 100644
index 00000000000..ab9ccc63b38
--- /dev/null
+++ b/arch/sparc/prom/bootstr_64.c
@@ -0,0 +1,40 @@
+/*
+ * bootstr.c: Boot string/argument acquisition from the PROM.
+ *
+ * Copyright(C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright(C) 1996,1998 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ */
+
+#include <linux/string.h>
+#include <linux/init.h>
+#include <asm/oplib.h>
+
+/* WARNING: The boot loader knows that these next three variables come one right
+ * after another in the .data section. Do not move this stuff into
+ * the .bss section or it will break things.
+ */
+
+#define BARG_LEN 256
+struct {
+ int bootstr_len;
+ int bootstr_valid;
+ char bootstr_buf[BARG_LEN];
+} bootstr_info = {
+ .bootstr_len = BARG_LEN,
+#ifdef CONFIG_CMDLINE
+ .bootstr_valid = 1,
+ .bootstr_buf = CONFIG_CMDLINE,
+#endif
+};
+
+char * __init
+prom_getbootargs(void)
+{
+ /* This check saves us from a panic when bootfd patches args. */
+ if (bootstr_info.bootstr_valid)
+ return bootstr_info.bootstr_buf;
+ prom_getstring(prom_chosen_node, "bootargs",
+ bootstr_info.bootstr_buf, BARG_LEN);
+ bootstr_info.bootstr_valid = 1;
+ return bootstr_info.bootstr_buf;
+}
diff --git a/arch/sparc/prom/cif.S b/arch/sparc/prom/cif.S
new file mode 100644
index 00000000000..5f27ad779c0
--- /dev/null
+++ b/arch/sparc/prom/cif.S
@@ -0,0 +1,48 @@
+/* cif.S: PROM entry/exit assembler trampolines.
+ *
+ * Copyright (C) 1996, 1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz)
+ * Copyright (C) 2005, 2006 David S. Miller <davem@davemloft.net>
+ */
+
+#include <asm/pstate.h>
+#include <asm/cpudata.h>
+#include <asm/thread_info.h>
+
+ .text
+ .globl prom_cif_interface
+prom_cif_interface:
+ sethi %hi(p1275buf), %o0
+ or %o0, %lo(p1275buf), %o0
+ ldx [%o0 + 0x010], %o1 ! prom_cif_stack
+ save %o1, -192, %sp
+ ldx [%i0 + 0x008], %l2 ! prom_cif_handler
+ mov %g4, %l0
+ mov %g5, %l1
+ mov %g6, %l3
+ call %l2
+ add %i0, 0x018, %o0 ! prom_args
+ mov %l0, %g4
+ mov %l1, %g5
+ mov %l3, %g6
+ ret
+ restore
+
+ .globl prom_cif_callback
+prom_cif_callback:
+ sethi %hi(p1275buf), %o1
+ or %o1, %lo(p1275buf), %o1
+ save %sp, -192, %sp
+ TRAP_LOAD_THREAD_REG(%g6, %g1)
+ LOAD_PER_CPU_BASE(%g5, %g6, %g4, %g3, %o0)
+ ldx [%g6 + TI_TASK], %g4
+ call prom_world
+ mov 0, %o0
+ ldx [%i1 + 0x000], %l2
+ call %l2
+ mov %i0, %o0
+ mov %o0, %l1
+ call prom_world
+ mov 1, %o0
+ ret
+ restore %l1, 0, %o0
+
diff --git a/arch/sparc/prom/console_64.c b/arch/sparc/prom/console_64.c
new file mode 100644
index 00000000000..e1c3fc87484
--- /dev/null
+++ b/arch/sparc/prom/console_64.c
@@ -0,0 +1,74 @@
+/* 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@davemloft.net)
+ * 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(const char *s, int len)
+{
+ p1275_cmd("write", P1275_ARG(1,P1275_ARG_IN_BUF)|
+ P1275_INOUT(3,1),
+ prom_stdout, s, P1275_SIZE(len));
+}
diff --git a/arch/sparc/prom/devops_64.c b/arch/sparc/prom/devops_64.c
new file mode 100644
index 00000000000..9dbd803e46e
--- /dev/null
+++ b/arch/sparc/prom/devops_64.c
@@ -0,0 +1,41 @@
+/*
+ * 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 0 on failure.
+ */
+int
+prom_devopen(const 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/sparc/prom/init_64.c b/arch/sparc/prom/init_64.c
new file mode 100644
index 00000000000..7b00f89490a
--- /dev/null
+++ b/arch/sparc/prom/init_64.c
@@ -0,0 +1,57 @@
+/*
+ * 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 <linux/ctype.h>
+
+#include <asm/openprom.h>
+#include <asm/oplib.h>
+
+/* OBP version string. */
+char prom_version[80];
+
+/* The root node of the prom device tree. */
+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_cif_init(void *, void *);
+
+void __init prom_init(void *cif_handler, void *cif_stack)
+{
+ int node;
+
+ prom_cif_init(cif_handler, cif_stack);
+
+ prom_chosen_node = prom_finddevice(prom_chosen_path);
+ 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", prom_version, sizeof(prom_version));
+
+ prom_printf("\n");
+}
+
+void __init prom_init_report(void)
+{
+ printk("PROMLIB: Sun IEEE Boot Prom '%s'\n", prom_version);
+ printk("PROMLIB: Root node compatible: %s\n", prom_root_compatible);
+}
diff --git a/arch/sparc/prom/misc_64.c b/arch/sparc/prom/misc_64.c
new file mode 100644
index 00000000000..9b0c0760901
--- /dev/null
+++ b/arch/sparc/prom/misc_64.c
@@ -0,0 +1,335 @@
+/*
+ * 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 <linux/interrupt.h>
+#include <linux/delay.h>
+#include <asm/openprom.h>
+#include <asm/oplib.h>
+#include <asm/system.h>
+#include <asm/ldc.h>
+
+int prom_service_exists(const char *service_name)
+{
+ int err = p1275_cmd("test", P1275_ARG(0, P1275_ARG_IN_STRING) |
+ P1275_INOUT(1, 1), service_name);
+
+ if (err)
+ return 0;
+ return 1;
+}
+
+void prom_sun4v_guest_soft_state(void)
+{
+ const char *svc = "SUNW,soft-state-supported";
+
+ if (!prom_service_exists(svc))
+ return;
+ p1275_cmd(svc, P1275_INOUT(0, 0));
+}
+
+/* Reset and reboot the machine with the command 'bcommand'. */
+void prom_reboot(const char *bcommand)
+{
+#ifdef CONFIG_SUN_LDOMS
+ if (ldom_domaining_enabled)
+ ldom_reboot(bcommand);
+#endif
+ 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(const char *fstring)
+{
+ if (!fstring || fstring[0] == 0)
+ return;
+ p1275_cmd("interpret", P1275_ARG(0, P1275_ARG_IN_STRING) |
+ P1275_INOUT(1, 1), fstring);
+}
+
+#ifdef CONFIG_SMP
+extern void smp_capture(void);
+extern void smp_release(void);
+#endif
+
+/* Drop into the prom, with the chance to continue with the 'go'
+ * prom command.
+ */
+void prom_cmdline(void)
+{
+ unsigned long flags;
+
+ local_irq_save(flags);
+
+#ifdef CONFIG_SMP
+ smp_capture();
+#endif
+
+ p1275_cmd("enter", P1275_INOUT(0, 0));
+
+#ifdef CONFIG_SMP
+ smp_release();
+#endif
+
+ local_irq_restore(flags);
+}
+
+/* Drop into the prom, but completely terminate the program.
+ * No chance of continuing.
+ */
+void prom_halt(void)
+{
+#ifdef CONFIG_SUN_LDOMS
+ if (ldom_domaining_enabled)
+ ldom_power_off();
+#endif
+again:
+ p1275_cmd("exit", P1275_INOUT(0, 0));
+ goto again; /* PROM is out to get me -DaveM */
+}
+
+void prom_halt_power_off(void)
+{
+#ifdef CONFIG_SUN_LDOMS
+ if (ldom_domaining_enabled)
+ ldom_power_off();
+#endif
+ p1275_cmd("SUNW,power-off", P1275_INOUT(0, 0));
+
+ /* if nothing else helps, we just halt */
+ prom_halt();
+}
+
+/* Set prom sync handler to call function 'funcp'. */
+void prom_setcallback(callback_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;
+}
+
+int prom_get_mmu_ihandle(void)
+{
+ int node, ret;
+
+ if (prom_mmu_ihandle_cache != 0)
+ return prom_mmu_ihandle_cache;
+
+ node = prom_finddevice(prom_chosen_path);
+ ret = prom_getint(node, prom_mmu_name);
+ if (ret == -1 || ret == 0)
+ prom_mmu_ihandle_cache = -1;
+ else
+ prom_mmu_ihandle_cache = ret;
+
+ return ret;
+}
+
+static int prom_get_memory_ihandle(void)
+{
+ static int memory_ihandle_cache;
+ int node, ret;
+
+ if (memory_ihandle_cache != 0)
+ return memory_ihandle_cache;
+
+ node = prom_finddevice("/chosen");
+ ret = prom_getint(node, "memory");
+ if (ret == -1 || ret == 0)
+ memory_ihandle_cache = -1;
+ else
+ memory_ihandle_cache = ret;
+
+ return ret;
+}
+
+/* Load explicit I/D TLB entries. */
+long prom_itlb_load(unsigned long index,
+ unsigned long tte_data,
+ unsigned long vaddr)
+{
+ return p1275_cmd(prom_callmethod_name,
+ (P1275_ARG(0, P1275_ARG_IN_STRING) |
+ P1275_ARG(2, P1275_ARG_IN_64B) |
+ P1275_ARG(3, P1275_ARG_IN_64B) |
+ P1275_INOUT(5, 1)),
+ "SUNW,itlb-load",
+ prom_get_mmu_ihandle(),
+ /* And then our actual args are pushed backwards. */
+ vaddr,
+ tte_data,
+ index);
+}
+
+long prom_dtlb_load(unsigned long index,
+ unsigned long tte_data,
+ unsigned long vaddr)
+{
+ return p1275_cmd(prom_callmethod_name,
+ (P1275_ARG(0, P1275_ARG_IN_STRING) |
+ P1275_ARG(2, P1275_ARG_IN_64B) |
+ P1275_ARG(3, P1275_ARG_IN_64B) |
+ P1275_INOUT(5, 1)),
+ "SUNW,dtlb-load",
+ prom_get_mmu_ihandle(),
+ /* And then our actual args are pushed backwards. */
+ vaddr,
+ tte_data,
+ index);
+}
+
+int prom_map(int mode, unsigned long size,
+ unsigned long vaddr, unsigned long paddr)
+{
+ int ret = p1275_cmd(prom_callmethod_name,
+ (P1275_ARG(0, P1275_ARG_IN_STRING) |
+ P1275_ARG(3, P1275_ARG_IN_64B) |
+ P1275_ARG(4, P1275_ARG_IN_64B) |
+ P1275_ARG(6, P1275_ARG_IN_64B) |
+ P1275_INOUT(7, 1)),
+ prom_map_name,
+ prom_get_mmu_ihandle(),
+ mode,
+ size,
+ vaddr,
+ 0,
+ paddr);
+
+ if (ret == 0)
+ ret = -1;
+ return ret;
+}
+
+void prom_unmap(unsigned long size, unsigned long vaddr)
+{
+ p1275_cmd(prom_callmethod_name,
+ (P1275_ARG(0, P1275_ARG_IN_STRING) |
+ P1275_ARG(2, P1275_ARG_IN_64B) |
+ P1275_ARG(3, P1275_ARG_IN_64B) |
+ P1275_INOUT(4, 0)),
+ prom_unmap_name,
+ prom_get_mmu_ihandle(),
+ size,
+ vaddr);
+}
+
+/* Set aside physical memory which is not touched or modified
+ * across soft resets.
+ */
+unsigned long prom_retain(const char *name,
+ unsigned long pa_low, unsigned long pa_high,
+ long size, long align)
+{
+ /* XXX I don't think we return multiple values correctly.
+ * XXX OBP supposedly returns pa_low/pa_high here, how does
+ * XXX it work?
+ */
+
+ /* If align is zero, the pa_low/pa_high args are passed,
+ * else they are not.
+ */
+ if (align == 0)
+ return p1275_cmd("SUNW,retain",
+ (P1275_ARG(0, P1275_ARG_IN_BUF) | P1275_INOUT(5, 2)),
+ name, pa_low, pa_high, size, align);
+ else
+ return p1275_cmd("SUNW,retain",
+ (P1275_ARG(0, P1275_ARG_IN_BUF) | P1275_INOUT(3, 2)),
+ name, size, align);
+}
+
+/* Get "Unumber" string for the SIMM at the given
+ * memory address. Usually this will be of the form
+ * "Uxxxx" where xxxx is a decimal number which is
+ * etched into the motherboard next to the SIMM slot
+ * in question.
+ */
+int prom_getunumber(int syndrome_code,
+ unsigned long phys_addr,
+ char *buf, int buflen)
+{
+ return p1275_cmd(prom_callmethod_name,
+ (P1275_ARG(0, P1275_ARG_IN_STRING) |
+ P1275_ARG(3, P1275_ARG_OUT_BUF) |
+ P1275_ARG(6, P1275_ARG_IN_64B) |
+ P1275_INOUT(8, 2)),
+ "SUNW,get-unumber", prom_get_memory_ihandle(),
+ buflen, buf, P1275_SIZE(buflen),
+ 0, phys_addr, syndrome_code);
+}
+
+/* Power management extensions. */
+void prom_sleepself(void)
+{
+ p1275_cmd("SUNW,sleep-self", P1275_INOUT(0, 0));
+}
+
+int prom_sleepsystem(void)
+{
+ return p1275_cmd("SUNW,sleep-system", P1275_INOUT(0, 1));
+}
+
+int prom_wakeupsystem(void)
+{
+ return p1275_cmd("SUNW,wakeup-system", P1275_INOUT(0, 1));
+}
+
+#ifdef CONFIG_SMP
+void prom_startcpu(int cpunode, unsigned long pc, unsigned long arg)
+{
+ p1275_cmd("SUNW,start-cpu", P1275_INOUT(3, 0), cpunode, pc, arg);
+}
+
+void prom_startcpu_cpuid(int cpuid, unsigned long pc, unsigned long arg)
+{
+ p1275_cmd("SUNW,start-cpu-by-cpuid", P1275_INOUT(3, 0),
+ cpuid, pc, arg);
+}
+
+void prom_stopcpu_cpuid(int cpuid)
+{
+ p1275_cmd("SUNW,stop-cpu-by-cpuid", P1275_INOUT(1, 0),
+ cpuid);
+}
+
+void prom_stopself(void)
+{
+ p1275_cmd("SUNW,stop-self", P1275_INOUT(0, 0));
+}
+
+void prom_idleself(void)
+{
+ p1275_cmd("SUNW,idle-self", P1275_INOUT(0, 0));
+}
+
+void prom_resumecpu(int cpunode)
+{
+ p1275_cmd("SUNW,resume-cpu", P1275_INOUT(1, 0), cpunode);
+}
+#endif
diff --git a/arch/sparc/prom/p1275.c b/arch/sparc/prom/p1275.c
new file mode 100644
index 00000000000..4b7c937bba6
--- /dev/null
+++ b/arch/sparc/prom/p1275.c
@@ -0,0 +1,151 @@
+/*
+ * 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/sched.h>
+#include <linux/smp.h>
+#include <linux/string.h>
+#include <linux/spinlock.h>
+
+#include <asm/openprom.h>
+#include <asm/oplib.h>
+#include <asm/system.h>
+#include <asm/spitfire.h>
+#include <asm/pstate.h>
+#include <asm/ldc.h>
+
+struct {
+ long prom_callback; /* 0x00 */
+ void (*prom_cif_handler)(long *); /* 0x08 */
+ unsigned long prom_cif_stack; /* 0x10 */
+ unsigned long prom_args [23]; /* 0x18 */
+ char prom_buffer [3000];
+} p1275buf;
+
+extern void prom_world(int);
+
+extern void prom_cif_interface(void);
+extern void prom_cif_callback(void);
+
+/*
+ * This provides SMP safety on the p1275buf. prom_callback() drops this lock
+ * to allow recursuve acquisition.
+ */
+DEFINE_SPINLOCK(prom_entry_lock);
+
+long p1275_cmd(const char *service, long fmt, ...)
+{
+ char *p, *q;
+ unsigned long flags;
+ int nargs, nrets, i;
+ va_list list;
+ long attrs, x;
+
+ p = p1275buf.prom_buffer;
+
+ spin_lock_irqsave(&prom_entry_lock, flags);
+
+ p1275buf.prom_args[0] = (unsigned long)p; /* service */
+ strcpy (p, service);
+ p = (char *)(((long)(strchr (p, 0) + 8)) & ~7);
+ p1275buf.prom_args[1] = nargs = (fmt & 0x0f); /* nargs */
+ p1275buf.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:
+ p1275buf.prom_args[i + 3] =
+ (unsigned)va_arg(list, long);
+ break;
+ case P1275_ARG_IN_64B:
+ p1275buf.prom_args[i + 3] =
+ va_arg(list, unsigned long);
+ break;
+ case P1275_ARG_IN_STRING:
+ strcpy (p, va_arg(list, char *));
+ p1275buf.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 *);
+ p1275buf.prom_args[i + 3] = (unsigned long)p;
+ x = va_arg(list, long);
+ i++; attrs >>= 3;
+ p = (char *)(((long)(p + (int)x + 7)) & ~7);
+ p1275buf.prom_args[i + 3] = x;
+ break;
+ case P1275_ARG_IN_BUF:
+ q = va_arg(list, char *);
+ p1275buf.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);
+ p1275buf.prom_args[i + 3] = x;
+ break;
+ case P1275_ARG_OUT_32B:
+ (void) va_arg(list, char *);
+ p1275buf.prom_args[i + 3] = (unsigned long)p;
+ p += 32;
+ break;
+ case P1275_ARG_IN_FUNCTION:
+ p1275buf.prom_args[i + 3] =
+ (unsigned long)prom_cif_callback;
+ p1275buf.prom_callback = va_arg(list, long);
+ break;
+ }
+ }
+ va_end(list);
+
+ prom_world(1);
+ prom_cif_interface();
+ prom_world(0);
+
+ 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 *)(p1275buf.prom_args[i + 3]), (int)x);
+ i++; attrs >>= 3;
+ break;
+ case P1275_ARG_OUT_32B:
+ p = va_arg(list, char *);
+ memcpy (p, (char *)(p1275buf.prom_args[i + 3]), 32);
+ break;
+ }
+ }
+ va_end(list);
+ x = p1275buf.prom_args [nargs + 3];
+
+ spin_unlock_irqrestore(&prom_entry_lock, flags);
+
+ return x;
+}
+
+void prom_cif_init(void *cif_handler, void *cif_stack)
+{
+ p1275buf.prom_cif_handler = (void (*)(long *))cif_handler;
+ p1275buf.prom_cif_stack = (unsigned long)cif_stack;
+}
diff --git a/arch/sparc/prom/printf_64.c b/arch/sparc/prom/printf_64.c
new file mode 100644
index 00000000000..660943ee4c2
--- /dev/null
+++ b/arch/sparc/prom/printf_64.c
@@ -0,0 +1,47 @@
+/*
+ * 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)
+ * Copyright (c) 2002 Pete Zaitcev (zaitcev@yahoo.com)
+ *
+ * We used to warn all over the code: DO NOT USE prom_printf(),
+ * and yet people do. Anton's banking code was outputting banks
+ * with prom_printf for most of the 2.4 lifetime. Since an effective
+ * stick is not available, we deployed a carrot: an early printk
+ * through PROM by means of -p boot option. This ought to fix it.
+ * USE printk; if you need, deploy -p.
+ */
+
+#include <linux/kernel.h>
+
+#include <asm/openprom.h>
+#include <asm/oplib.h>
+
+static char ppbuf[1024];
+
+void
+prom_write(const char *buf, unsigned int n)
+{
+ char ch;
+
+ while (n != 0) {
+ --n;
+ if ((ch = *buf++) == '\n')
+ prom_putchar('\r');
+ prom_putchar(ch);
+ }
+}
+
+void
+prom_printf(const char *fmt, ...)
+{
+ va_list args;
+ int i;
+
+ va_start(args, fmt);
+ i = vscnprintf(ppbuf, sizeof(ppbuf), fmt, args);
+ va_end(args);
+
+ prom_write(ppbuf, i);
+}
diff --git a/arch/sparc/prom/tree_64.c b/arch/sparc/prom/tree_64.c
new file mode 100644
index 00000000000..281aea44790
--- /dev/null
+++ b/arch/sparc/prom/tree_64.c
@@ -0,0 +1,299 @@
+/*
+ * 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>
+#include <asm/ldc.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)
+{
+ int cnode;
+
+ if(node == -1) return 0;
+ cnode = __prom_getchild(node);
+ if(cnode == -1) return 0;
+ return (int)cnode;
+}
+
+inline int prom_getparent(int node)
+{
+ int 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(prom_peer_name, P1275_INOUT(1, 1), node);
+}
+
+inline int prom_getsibling(int node)
+{
+ int sibnode;
+
+ if (node == -1)
+ return 0;
+ sibnode = __prom_getsibling(node);
+ if (sibnode == -1)
+ return 0;
+
+ return sibnode;
+}
+
+/* Return the length in bytes of property 'prop' at node 'node'.
+ * Return -1 on error.
+ */
+inline int prom_getproplen(int node, const 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, const 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(prom_getprop_name,
+ 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, const 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, const 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, const 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, const 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, const 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, const 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;
+}
+
+/* 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, const 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(const char *name)
+{
+ if (!name)
+ return 0;
+ return p1275_cmd(prom_finddev_name,
+ P1275_ARG(0,P1275_ARG_IN_STRING)|
+ P1275_INOUT(1, 1),
+ name);
+}
+
+int prom_node_has_property(int node, const 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, const char *pname, char *value, int size)
+{
+ if (size == 0)
+ return 0;
+ if ((pname == 0) || (value == 0))
+ return 0;
+
+#ifdef CONFIG_SUN_LDOMS
+ if (ldom_domaining_enabled) {
+ ldom_set_var(pname, value);
+ return 0;
+ }
+#endif
+ 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(const char *path)
+{
+ int node, inst;
+
+ inst = prom_devopen (path);
+ if (inst == 0) return 0;
+ node = prom_inst2pkg (inst);
+ prom_devclose (inst);
+ if (node == -1) return 0;
+ return node;
+}
+
+int prom_ihandle2path(int handle, char *buffer, int bufsize)
+{
+ return p1275_cmd("instance-to-path",
+ P1275_ARG(1,P1275_ARG_OUT_BUF)|
+ P1275_INOUT(3, 1),
+ handle, buffer, P1275_SIZE(bufsize));
+}