diff options
Diffstat (limited to 'arch/sparc64/prom')
-rw-r--r-- | arch/sparc64/prom/Makefile | 10 | ||||
-rw-r--r-- | arch/sparc64/prom/bootstr.c | 40 | ||||
-rw-r--r-- | arch/sparc64/prom/cif.S | 225 | ||||
-rw-r--r-- | arch/sparc64/prom/console.c | 146 | ||||
-rw-r--r-- | arch/sparc64/prom/devops.c | 41 | ||||
-rw-r--r-- | arch/sparc64/prom/init.c | 101 | ||||
-rw-r--r-- | arch/sparc64/prom/map.S | 72 | ||||
-rw-r--r-- | arch/sparc64/prom/memory.c | 152 | ||||
-rw-r--r-- | arch/sparc64/prom/misc.c | 339 | ||||
-rw-r--r-- | arch/sparc64/prom/p1275.c | 161 | ||||
-rw-r--r-- | arch/sparc64/prom/printf.c | 47 | ||||
-rw-r--r-- | arch/sparc64/prom/tree.c | 377 |
12 files changed, 1711 insertions, 0 deletions
diff --git a/arch/sparc64/prom/Makefile b/arch/sparc64/prom/Makefile new file mode 100644 index 0000000..8f2420d --- /dev/null +++ b/arch/sparc64/prom/Makefile @@ -0,0 +1,10 @@ +# $Id: Makefile,v 1.7 2000/12/14 22:57:25 davem Exp $ +# Makefile for the Sun Boot PROM interface library under +# Linux. +# + +EXTRA_AFLAGS := -ansi +EXTRA_CFLAGS := -Werror + +lib-y := bootstr.o devops.o init.o memory.o misc.o \ + tree.o console.o printf.o p1275.o map.o cif.o diff --git a/arch/sparc64/prom/bootstr.c b/arch/sparc64/prom/bootstr.c new file mode 100644 index 0000000..a727861 --- /dev/null +++ b/arch/sparc64/prom/bootstr.c @@ -0,0 +1,40 @@ +/* $Id: bootstr.c,v 1.6 1999/08/31 06:55:01 davem Exp $ + * 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/sparc64/prom/cif.S b/arch/sparc64/prom/cif.S new file mode 100644 index 0000000..29d0ae7 --- /dev/null +++ b/arch/sparc64/prom/cif.S @@ -0,0 +1,225 @@ +/* cif.S: PROM entry/exit assembler trampolines. + * + * Copyright (C) 1996,1997 Jakub Jelinek (jj@sunsite.mff.cuni.cz) + * Copyright (C) 2005 David S. Miller <davem@davemloft.net> + */ + +#include <asm/pstate.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, -0x190, %sp + ldx [%i0 + 0x008], %l2 ! prom_cif_handler + rdpr %pstate, %l4 + wrpr %g0, 0x15, %pstate ! save alternate globals + stx %g1, [%sp + 2047 + 0x0b0] + stx %g2, [%sp + 2047 + 0x0b8] + stx %g3, [%sp + 2047 + 0x0c0] + stx %g4, [%sp + 2047 + 0x0c8] + stx %g5, [%sp + 2047 + 0x0d0] + stx %g6, [%sp + 2047 + 0x0d8] + stx %g7, [%sp + 2047 + 0x0e0] + wrpr %g0, 0x814, %pstate ! save interrupt globals + stx %g1, [%sp + 2047 + 0x0e8] + stx %g2, [%sp + 2047 + 0x0f0] + stx %g3, [%sp + 2047 + 0x0f8] + stx %g4, [%sp + 2047 + 0x100] + stx %g5, [%sp + 2047 + 0x108] + stx %g6, [%sp + 2047 + 0x110] + stx %g7, [%sp + 2047 + 0x118] + wrpr %g0, 0x14, %pstate ! save normal globals + stx %g1, [%sp + 2047 + 0x120] + stx %g2, [%sp + 2047 + 0x128] + stx %g3, [%sp + 2047 + 0x130] + stx %g4, [%sp + 2047 + 0x138] + stx %g5, [%sp + 2047 + 0x140] + stx %g6, [%sp + 2047 + 0x148] + stx %g7, [%sp + 2047 + 0x150] + wrpr %g0, 0x414, %pstate ! save mmu globals + stx %g1, [%sp + 2047 + 0x158] + stx %g2, [%sp + 2047 + 0x160] + stx %g3, [%sp + 2047 + 0x168] + stx %g4, [%sp + 2047 + 0x170] + stx %g5, [%sp + 2047 + 0x178] + stx %g6, [%sp + 2047 + 0x180] + stx %g7, [%sp + 2047 + 0x188] + mov %g1, %l0 ! also save to locals, so we can handle + mov %g2, %l1 ! tlb faults later on, when accessing + mov %g3, %l3 ! the stack. + mov %g7, %l5 + wrpr %l4, PSTATE_IE, %pstate ! turn off interrupts + call %l2 + add %i0, 0x018, %o0 ! prom_args + wrpr %g0, 0x414, %pstate ! restore mmu globals + mov %l0, %g1 + mov %l1, %g2 + mov %l3, %g3 + mov %l5, %g7 + wrpr %g0, 0x14, %pstate ! restore normal globals + ldx [%sp + 2047 + 0x120], %g1 + ldx [%sp + 2047 + 0x128], %g2 + ldx [%sp + 2047 + 0x130], %g3 + ldx [%sp + 2047 + 0x138], %g4 + ldx [%sp + 2047 + 0x140], %g5 + ldx [%sp + 2047 + 0x148], %g6 + ldx [%sp + 2047 + 0x150], %g7 + wrpr %g0, 0x814, %pstate ! restore interrupt globals + ldx [%sp + 2047 + 0x0e8], %g1 + ldx [%sp + 2047 + 0x0f0], %g2 + ldx [%sp + 2047 + 0x0f8], %g3 + ldx [%sp + 2047 + 0x100], %g4 + ldx [%sp + 2047 + 0x108], %g5 + ldx [%sp + 2047 + 0x110], %g6 + ldx [%sp + 2047 + 0x118], %g7 + wrpr %g0, 0x15, %pstate ! restore alternate globals + ldx [%sp + 2047 + 0x0b0], %g1 + ldx [%sp + 2047 + 0x0b8], %g2 + ldx [%sp + 2047 + 0x0c0], %g3 + ldx [%sp + 2047 + 0x0c8], %g4 + ldx [%sp + 2047 + 0x0d0], %g5 + ldx [%sp + 2047 + 0x0d8], %g6 + ldx [%sp + 2047 + 0x0e0], %g7 + wrpr %l4, 0, %pstate ! restore original pstate + ret + restore + + .globl prom_cif_callback +prom_cif_callback: + sethi %hi(p1275buf), %o1 + or %o1, %lo(p1275buf), %o1 + save %sp, -0x270, %sp + rdpr %pstate, %l4 + wrpr %g0, 0x15, %pstate ! save PROM alternate globals + stx %g1, [%sp + 2047 + 0x0b0] + stx %g2, [%sp + 2047 + 0x0b8] + stx %g3, [%sp + 2047 + 0x0c0] + stx %g4, [%sp + 2047 + 0x0c8] + stx %g5, [%sp + 2047 + 0x0d0] + stx %g6, [%sp + 2047 + 0x0d8] + stx %g7, [%sp + 2047 + 0x0e0] + ! restore Linux alternate globals + ldx [%sp + 2047 + 0x190], %g1 + ldx [%sp + 2047 + 0x198], %g2 + ldx [%sp + 2047 + 0x1a0], %g3 + ldx [%sp + 2047 + 0x1a8], %g4 + ldx [%sp + 2047 + 0x1b0], %g5 + ldx [%sp + 2047 + 0x1b8], %g6 + ldx [%sp + 2047 + 0x1c0], %g7 + wrpr %g0, 0x814, %pstate ! save PROM interrupt globals + stx %g1, [%sp + 2047 + 0x0e8] + stx %g2, [%sp + 2047 + 0x0f0] + stx %g3, [%sp + 2047 + 0x0f8] + stx %g4, [%sp + 2047 + 0x100] + stx %g5, [%sp + 2047 + 0x108] + stx %g6, [%sp + 2047 + 0x110] + stx %g7, [%sp + 2047 + 0x118] + ! restore Linux interrupt globals + ldx [%sp + 2047 + 0x1c8], %g1 + ldx [%sp + 2047 + 0x1d0], %g2 + ldx [%sp + 2047 + 0x1d8], %g3 + ldx [%sp + 2047 + 0x1e0], %g4 + ldx [%sp + 2047 + 0x1e8], %g5 + ldx [%sp + 2047 + 0x1f0], %g6 + ldx [%sp + 2047 + 0x1f8], %g7 + wrpr %g0, 0x14, %pstate ! save PROM normal globals + stx %g1, [%sp + 2047 + 0x120] + stx %g2, [%sp + 2047 + 0x128] + stx %g3, [%sp + 2047 + 0x130] + stx %g4, [%sp + 2047 + 0x138] + stx %g5, [%sp + 2047 + 0x140] + stx %g6, [%sp + 2047 + 0x148] + stx %g7, [%sp + 2047 + 0x150] + ! restore Linux normal globals + ldx [%sp + 2047 + 0x200], %g1 + ldx [%sp + 2047 + 0x208], %g2 + ldx [%sp + 2047 + 0x210], %g3 + ldx [%sp + 2047 + 0x218], %g4 + ldx [%sp + 2047 + 0x220], %g5 + ldx [%sp + 2047 + 0x228], %g6 + ldx [%sp + 2047 + 0x230], %g7 + wrpr %g0, 0x414, %pstate ! save PROM mmu globals + stx %g1, [%sp + 2047 + 0x158] + stx %g2, [%sp + 2047 + 0x160] + stx %g3, [%sp + 2047 + 0x168] + stx %g4, [%sp + 2047 + 0x170] + stx %g5, [%sp + 2047 + 0x178] + stx %g6, [%sp + 2047 + 0x180] + stx %g7, [%sp + 2047 + 0x188] + ! restore Linux mmu globals + ldx [%sp + 2047 + 0x238], %o0 + ldx [%sp + 2047 + 0x240], %o1 + ldx [%sp + 2047 + 0x248], %l2 + ldx [%sp + 2047 + 0x250], %l3 + ldx [%sp + 2047 + 0x258], %l5 + ldx [%sp + 2047 + 0x260], %l6 + ldx [%sp + 2047 + 0x268], %l7 + ! switch to Linux tba + sethi %hi(sparc64_ttable_tl0), %l1 + rdpr %tba, %l0 ! save PROM tba + mov %o0, %g1 + mov %o1, %g2 + mov %l2, %g3 + mov %l3, %g4 + mov %l5, %g5 + mov %l6, %g6 + mov %l7, %g7 + wrpr %l1, %tba ! install Linux tba + wrpr %l4, 0, %pstate ! restore PSTATE + call prom_world + mov %g0, %o0 + ldx [%i1 + 0x000], %l2 + call %l2 + mov %i0, %o0 + mov %o0, %l1 + call prom_world + or %g0, 1, %o0 + wrpr %g0, 0x14, %pstate ! interrupts off + ! restore PROM mmu globals + ldx [%sp + 2047 + 0x158], %o0 + ldx [%sp + 2047 + 0x160], %o1 + ldx [%sp + 2047 + 0x168], %l2 + ldx [%sp + 2047 + 0x170], %l3 + ldx [%sp + 2047 + 0x178], %l5 + ldx [%sp + 2047 + 0x180], %l6 + ldx [%sp + 2047 + 0x188], %l7 + wrpr %g0, 0x414, %pstate ! restore PROM mmu globals + mov %o0, %g1 + mov %o1, %g2 + mov %l2, %g3 + mov %l3, %g4 + mov %l5, %g5 + mov %l6, %g6 + mov %l7, %g7 + wrpr %l0, %tba ! restore PROM tba + wrpr %g0, 0x14, %pstate ! restore PROM normal globals + ldx [%sp + 2047 + 0x120], %g1 + ldx [%sp + 2047 + 0x128], %g2 + ldx [%sp + 2047 + 0x130], %g3 + ldx [%sp + 2047 + 0x138], %g4 + ldx [%sp + 2047 + 0x140], %g5 + ldx [%sp + 2047 + 0x148], %g6 + ldx [%sp + 2047 + 0x150], %g7 + wrpr %g0, 0x814, %pstate ! restore PROM interrupt globals + ldx [%sp + 2047 + 0x0e8], %g1 + ldx [%sp + 2047 + 0x0f0], %g2 + ldx [%sp + 2047 + 0x0f8], %g3 + ldx [%sp + 2047 + 0x100], %g4 + ldx [%sp + 2047 + 0x108], %g5 + ldx [%sp + 2047 + 0x110], %g6 + ldx [%sp + 2047 + 0x118], %g7 + wrpr %g0, 0x15, %pstate ! restore PROM alternate globals + ldx [%sp + 2047 + 0x0b0], %g1 + ldx [%sp + 2047 + 0x0b8], %g2 + ldx [%sp + 2047 + 0x0c0], %g3 + ldx [%sp + 2047 + 0x0c8], %g4 + ldx [%sp + 2047 + 0x0d0], %g5 + ldx [%sp + 2047 + 0x0d8], %g6 + ldx [%sp + 2047 + 0x0e0], %g7 + wrpr %l4, 0, %pstate + ret + restore %l1, 0, %o0 + diff --git a/arch/sparc64/prom/console.c b/arch/sparc64/prom/console.c new file mode 100644 index 0000000..028a53f --- /dev/null +++ b/arch/sparc64/prom/console.c @@ -0,0 +1,146 @@ +/* $Id: console.c,v 1.9 1997/10/29 07:41:43 ecd 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(void) +{ + 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", 6)) + return PROMDEV_I_UNK; + /* FIXME: Is there any better way how to find out? */ + memset(propb, 0, sizeof(propb)); + st_p = prom_finddevice ("/options"); + prom_getproperty(st_p, "input-device", propb, sizeof(propb)); + + /* + * If we get here with propb == 'keyboard', we are on ttya, as + * the PROM defaulted to this due to 'no input device'. + */ + if (!strncmp(propb, "keyboard", 8)) + return PROMDEV_ITTYA; + + if (strncmp (propb, "tty", 3) || !propb[3]) + 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(void) +{ + 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, 6)) + return PROMDEV_O_UNK; + /* FIXME: Is there any better way how to find out? */ + memset(propb, 0, sizeof(propb)); + st_p = prom_finddevice ("/options"); + prom_getproperty(st_p, "output-device", propb, sizeof(propb)); + + /* + * If we get here with propb == 'screen', we are on ttya, as + * the PROM defaulted to this due to 'no input device'. + */ + if (!strncmp(propb, "screen", 6)) + return PROMDEV_OTTYA; + + if (strncmp (propb, "tty", 3) || !propb[3]) + 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 0000000..2c99b21 --- /dev/null +++ b/arch/sparc64/prom/devops.c @@ -0,0 +1,41 @@ +/* $Id: devops.c,v 1.3 1997/10/29 07:43:28 ecd 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 0 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 0000000..817faae --- /dev/null +++ b/arch/sparc64/prom/init.c @@ -0,0 +1,101 @@ +/* $Id: init.c,v 1.10 1999/09/21 14:35:59 davem 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 <linux/ctype.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_cif_init(void *, void *); + +void __init prom_init(void *cif_handler, void *cif_stack) +{ + char buffer[80], *p; + int ints[3]; + int node; + int i = 0; + int bufadjust; + + 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)) + goto strange_version; + + /* + * Version field is expected to be 'OBP xx.yy.zz date...' + * However, Sun can't stick to this format very well, so + * we need to check for 'OBP xx.yy.zz date...' and adjust + * accordingly. -spot + */ + + if (strncmp (buffer, "OBP ", 5)) + bufadjust = 4; + else + bufadjust = 5; + + p = buffer + bufadjust; + while (p && isdigit(*p) && i < 3) { + ints[i++] = simple_strtoul(p, NULL, 0); + if ((p = strchr(p, '.')) != NULL) + p++; + } + if (i != 3) + goto strange_version; + + prom_rev = ints[1]; + prom_prev = (ints[0] << 16) | (ints[1] << 8) | ints[2]; + + printk ("PROMLIB: Sun IEEE Boot Prom %s\n", buffer + bufadjust); + + prom_meminit(); + + /* Initialization successful. */ + return; + +strange_version: + prom_printf ("Strange OBP version `%s'.\n", buffer); + prom_halt (); +} diff --git a/arch/sparc64/prom/map.S b/arch/sparc64/prom/map.S new file mode 100644 index 0000000..21b3f9c --- /dev/null +++ b/arch/sparc64/prom/map.S @@ -0,0 +1,72 @@ +/* $Id: map.S,v 1.2 1999/11/19 05:53:02 davem Exp $ + * map.S: Tricky coding required to fixup the kernel OBP maps + * properly. + * + * Copyright (C) 1999 David S. Miller (davem@redhat.com) + */ + + .text + .align 8192 + .globl prom_boot_page +prom_boot_page: +call_method: + .asciz "call-method" + .align 8 +map: + .asciz "map" + .align 8 + + /* When we are invoked, our caller has remapped us to + * page zero, therefore we must use PC relative addressing + * for everything after we begin performing the unmap/map + * calls. + */ + .globl prom_remap +prom_remap: /* %o0 = physpage, %o1 = virtpage, %o2 = mmu_ihandle */ + rd %pc, %g1 + srl %o2, 0, %o2 ! kill sign extension + sethi %hi(p1275buf), %g2 + or %g2, %lo(p1275buf), %g2 + ldx [%g2 + 0x10], %g3 ! prom_cif_stack + save %g3, -(192 + 128), %sp + ldx [%g2 + 0x08], %l0 ! prom_cif_handler + mov %g6, %i3 + mov %g4, %i4 + mov %g5, %i5 + flushw + + sethi %hi(prom_remap - call_method), %g7 + or %g7, %lo(prom_remap - call_method), %g7 + sub %g1, %g7, %l2 ! call-method string + sethi %hi(prom_remap - map), %g7 + or %g7, %lo(prom_remap - map), %g7 + sub %g1, %g7, %l4 ! map string + + /* OK, map the 4MB region we really live at. */ + stx %l2, [%sp + 2047 + 128 + 0x00] ! call-method + mov 7, %l5 + stx %l5, [%sp + 2047 + 128 + 0x08] ! num_args + mov 1, %l5 + stx %l5, [%sp + 2047 + 128 + 0x10] ! num_rets + stx %l4, [%sp + 2047 + 128 + 0x18] ! map + stx %i2, [%sp + 2047 + 128 + 0x20] ! mmu_ihandle + mov -1, %l5 + stx %l5, [%sp + 2047 + 128 + 0x28] ! mode == default + sethi %hi(4 * 1024 * 1024), %l5 + stx %l5, [%sp + 2047 + 128 + 0x30] ! size + stx %i1, [%sp + 2047 + 128 + 0x38] ! vaddr + stx %g0, [%sp + 2047 + 128 + 0x40] ! filler + stx %i0, [%sp + 2047 + 128 + 0x48] ! paddr + call %l0 + add %sp, (2047 + 128), %o0 ! argument array + + /* Restore hard-coded globals. */ + mov %i3, %g6 + mov %i4, %g4 + mov %i5, %g5 + + /* Wheee.... we are done. */ + ret + restore + + .align 8192 diff --git a/arch/sparc64/prom/memory.c b/arch/sparc64/prom/memory.c new file mode 100644 index 0000000..f4a8143 --- /dev/null +++ b/arch/sparc64/prom/memory.c @@ -0,0 +1,152 @@ +/* $Id: memory.c,v 1.5 1999/08/31 06:55:04 davem 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. + */ +static void __init +prom_sortmemlist(struct linux_mlist_p1275 *thislist) +{ + int swapi = 0; + int i, mitr; + unsigned long tmpaddr, tmpsize; + unsigned long lowest; + + for(i=0; thislist[i].theres_more; i++) { + lowest = thislist[i].start_adr; + for(mitr = i+1; thislist[mitr-1].theres_more; 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. */ +void __init 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 = + prom_reg_memlist[iter].reg_size; + prom_phys_avail[iter].theres_more = + &prom_phys_avail[iter+1]; + } + prom_phys_avail[iter-1].theres_more = NULL; + + 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 = + prom_reg_memlist[iter].reg_size; + prom_phys_total[iter].theres_more = + &prom_phys_total[iter+1]; + } + prom_phys_total[iter-1].theres_more = NULL; + + 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 = + prom_reg_memlist[iter].reg_size; + prom_prom_taken[iter].theres_more = + &prom_prom_taken[iter+1]; + } + prom_prom_taken[iter-1].theres_more = NULL; + + 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 = + -1UL - 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 0000000..19c44e9 --- /dev/null +++ b/arch/sparc64/prom/misc.c @@ -0,0 +1,339 @@ +/* $Id: misc.c,v 1.20 2001/09/21 03:17:07 kanoj 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/config.h> +#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> + +/* 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. */ +extern void (*prom_palette)(int); + +#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); + + if (!serial_console && prom_palette) + prom_palette(1); + +#ifdef CONFIG_SMP + smp_capture(); +#endif + + p1275_cmd("enter", P1275_INOUT(0, 0)); + +#ifdef CONFIG_SMP + smp_release(); +#endif + + if (!serial_console && prom_palette) + prom_palette(0); + + local_irq_restore(flags); +} + +#ifdef CONFIG_SMP +extern void smp_promstop_others(void); +#endif + +/* Drop into the prom, but completely terminate the program. + * No chance of continuing. + */ +void prom_halt(void) +{ +#ifdef CONFIG_SMP + smp_promstop_others(); + udelay(8000); +#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_SMP + smp_promstop_others(); + udelay(8000); +#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; +} + +/* 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 its own. */ +void prom_set_trap_table(unsigned long tba) +{ + p1275_cmd("SUNW,set-trap-table", P1275_INOUT(1, 0), tba); +} + +int mmu_ihandle_cache = 0; + +int prom_get_mmu_ihandle(void) +{ + int node, ret; + + if (mmu_ihandle_cache != 0) + return mmu_ihandle_cache; + + node = prom_finddevice("/chosen"); + ret = prom_getint(node, "mmu"); + if (ret == -1 || ret == 0) + mmu_ihandle_cache = -1; + else + 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("call-method", + (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("call-method", + (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("call-method", + (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)), + "map", + 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("call-method", + (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)), + "unmap", + prom_get_mmu_ihandle(), + size, + vaddr); +} + +/* Set aside physical memory which is not touched or modified + * across soft resets. + */ +unsigned long prom_retain(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("call-method", + (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 o0) +{ + p1275_cmd("SUNW,start-cpu", P1275_INOUT(3, 0), cpunode, pc, o0); +} + +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/sparc64/prom/p1275.c b/arch/sparc64/prom/p1275.c new file mode 100644 index 0000000..59fe38b --- /dev/null +++ b/arch/sparc64/prom/p1275.c @@ -0,0 +1,161 @@ +/* $Id: p1275.c,v 1.22 2001/10/18 09:40:00 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/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> + +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); + +static inline unsigned long spitfire_get_primary_context(void) +{ + unsigned long ctx; + + __asm__ __volatile__("ldxa [%1] %2, %0" + : "=r" (ctx) + : "r" (PRIMARY_CONTEXT), "i" (ASI_DMMU)); + return ctx; +} + +/* + * This provides SMP safety on the p1275buf. prom_callback() drops this lock + * to allow recursuve acquisition. + */ +DEFINE_SPINLOCK(prom_entry_lock); + +long p1275_cmd (char *service, long fmt, ...) +{ + char *p, *q; + unsigned long flags; + int nargs, nrets, i; + va_list list; + long attrs, x; + + p = p1275buf.prom_buffer; + BUG_ON((spitfire_get_primary_context() & CTX_NR_MASK) != 0); + + 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/sparc64/prom/printf.c b/arch/sparc64/prom/printf.c new file mode 100644 index 0000000..a6df82c --- /dev/null +++ b/arch/sparc64/prom/printf.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(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/sparc64/prom/tree.c b/arch/sparc64/prom/tree.c new file mode 100644 index 0000000..ccf7325 --- /dev/null +++ b/arch/sparc64/prom/tree.c @@ -0,0 +1,377 @@ +/* $Id: tree.c,v 1.10 1998/01/10 22:39:00 ecd 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) +{ + 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 ("peer", 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, 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; + int pci = 0, ebus = 0, ide = 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")) + goto getit; + } + } + if ((pci = prom_getparent (node))) { + i = prom_getproperty (pci, "name", buffer, len); + if (i > 0) { + buffer [i] = 0; + if (!strcmp (buffer, "pci")) + goto getit; + } + pci = 0; + } + if ((ebus = prom_getparent (node))) { + i = prom_getproperty (ebus, "name", buffer, len); + if (i > 0) { + buffer[i] = 0; + if (!strcmp (buffer, "ebus")) + goto getit; + } + ebus = 0; + } + if ((ide = prom_getparent (node))) { + i = prom_getproperty (ide, "name", buffer, len); + if (i > 0) { + buffer [i] = 0; + if (!strcmp (buffer, "ide")) + goto getit; + } + ide = 0; + } +getit: + 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 if (pci) { + int dev, fn; + reg = (struct linux_prom_registers *)reg64; + fn = (reg[0].which_io >> 8) & 0x07; + dev = (reg[0].which_io >> 11) & 0x1f; + if (fn) + sprintf (buffer, "@%x,%x", dev, fn); + else + sprintf (buffer, "@%x", dev); + } else if (ebus) { + reg = (struct linux_prom_registers *)reg64; + sprintf (buffer, "@%x,%x", reg[0].which_io, reg[0].phys_addr); + } else if (ide) { + reg = (struct linux_prom_registers *)reg64; + sprintf (buffer, "@%x,%x", reg[0].which_io, reg[0].phys_addr); + } else if (i == 4) { /* Happens on 8042's children on Ultra/PCI. */ + reg = (struct linux_prom_registers *)reg64; + sprintf (buffer, "@%x", reg[0].which_io); + } 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 == 0) return 0; + node = prom_inst2pkg (inst); + prom_devclose (inst); + if (node == -1) return 0; + return node; +} |