patch-2.1.51 linux/arch/ppc/kernel/prom.c
Next file: linux/arch/ppc/kernel/ptrace.c
Previous file: linux/arch/ppc/kernel/process.c
Back to the patch index
Back to the overall index
- Lines: 524
- Date:
Sat Aug 16 09:51:08 1997
- Orig file:
v2.1.50/linux/arch/ppc/kernel/prom.c
- Orig date:
Wed Dec 31 16:00:00 1969
diff -u --recursive --new-file v2.1.50/linux/arch/ppc/kernel/prom.c linux/arch/ppc/kernel/prom.c
@@ -0,0 +1,523 @@
+/*
+ * Procedures for interfacing to the Open Firmware PROM on
+ * Power Macintosh computers.
+ *
+ * In particular, we are interested in the device tree
+ * and in using some of its services (exit, write to stdout).
+ *
+ * Paul Mackerras August 1996.
+ * Copyright (C) 1996 Paul Mackerras.
+ */
+
+#include <stdarg.h>
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/blk.h>
+#include <asm/prom.h>
+#include <asm/page.h>
+
+#define getpromprop(node, name, buf, len) \
+ ((int)call_prom("getprop", 4, 1, (node), (name), (buf), (len)))
+
+ihandle prom_stdout;
+ihandle prom_chosen;
+
+char command_line[256];
+int screen_initialized = 0;
+
+char prom_display_path[128];
+
+struct prom_args {
+ const char *service;
+ int nargs;
+ int nret;
+ void *args[10];
+} prom_args;
+
+struct pci_address {
+ unsigned a_hi;
+ unsigned a_mid;
+ unsigned a_lo;
+};
+
+struct pci_reg_property {
+ struct pci_address addr;
+ unsigned size_hi;
+ unsigned size_lo;
+};
+
+struct pci_range {
+ struct pci_address addr;
+ unsigned phys;
+ unsigned size_hi;
+ unsigned size_lo;
+};
+
+void (*prom_entry)(void *);
+extern int prom_trashed;
+
+static int prom_callback(struct prom_args *);
+static unsigned long inspect_node(phandle, struct device_node *, unsigned long,
+ unsigned long, unsigned long);
+static void check_display(void);
+static int prom_next_node(phandle *);
+
+extern int pmac_display_supported(const char *);
+extern void enter_prom(void *);
+
+void
+prom_exit()
+{
+ struct prom_args args;
+
+ args.service = "exit";
+ args.nargs = 0;
+ args.nret = 0;
+ enter_prom(&args);
+ for (;;) /* should never get here */
+ ;
+}
+
+void *
+call_prom(const char *service, int nargs, int nret, ...)
+{
+ va_list list;
+ int i;
+
+ if (prom_trashed)
+ panic("prom called after its memory was reclaimed");
+ prom_args.service = service;
+ prom_args.nargs = nargs;
+ prom_args.nret = nret;
+ va_start(list, nret);
+ for (i = 0; i < nargs; ++i)
+ prom_args.args[i] = va_arg(list, void *);
+ va_end(list);
+ for (i = 0; i < nret; ++i)
+ prom_args.args[i + nargs] = 0;
+ enter_prom(&prom_args);
+ return prom_args.args[nargs];
+}
+
+void
+prom_print(const char *msg)
+{
+ const char *p, *q;
+ const char *crlf = "\r\n";
+
+ if (screen_initialized)
+ return;
+ for (p = msg; *p != 0; p = q) {
+ for (q = p; *q != 0 && *q != '\n'; ++q)
+ ;
+ if (q > p)
+ call_prom("write", 3, 1, prom_stdout, p, q - p);
+ if (*q != 0) {
+ ++q;
+ call_prom("write", 3, 1, prom_stdout, crlf, 2);
+ }
+ }
+}
+
+/*
+ * We enter here early on, when the Open Firmware prom is still
+ * handling exceptions and the MMU hash table for us.
+ */
+void
+prom_init(char *params, int unused, void (*pp)(void *))
+{
+ /* First get a handle for the stdout device */
+ if ( _machine != _MACH_Pmac ) /* prep */
+ return;
+ prom_entry = pp;
+ prom_chosen = call_prom("finddevice", 1, 1, "/chosen");
+ if (prom_chosen == (void *)-1)
+ prom_exit();
+ call_prom("getprop", 4, 1, prom_chosen, "stdout", &prom_stdout,
+ (void *) sizeof(prom_stdout));
+
+ /*
+ * If we were booted via quik, params points to the physical address
+ * of the command-line parameters.
+ * If we were booted from an xcoff image (i.e. netbooted or
+ * booted from floppy), we get the command line from the bootargs
+ * property of the /chosen node. If an initial ramdisk is present,
+ * params and unused are used for initrd_start and initrd_size,
+ * otherwise they contain 0xdeadbeef.
+ */
+ command_line[0] = 0;
+ if ((unsigned long) params >= 0x4000
+ && (unsigned long) params < 0x800000
+ && unused == 0) {
+ strncpy(command_line, params+KERNELBASE, sizeof(command_line));
+ } else {
+#ifdef CONFIG_BLK_DEV_INITRD
+ if ((unsigned long) params - KERNELBASE < 0x800000
+ && unused != 0 && unused != 0xdeadbeef) {
+ initrd_start = (unsigned long) params;
+ initrd_end = initrd_start + unused;
+ ROOT_DEV = MKDEV(RAMDISK_MAJOR, 0);
+ }
+#endif
+ call_prom("getprop", 4, 1, prom_chosen, "bootargs",
+ command_line, sizeof(command_line));
+ }
+ command_line[sizeof(command_line) - 1] = 0;
+
+ check_display();
+}
+
+/*
+ * If we have a display that we don't know how to drive,
+ * we will want to try to execute OF's open method for it
+ * later. However, OF may fall over if we do that after
+ * we've taken over the MMU and done set_prom_callback.
+ * So we check whether we will need to open the display,
+ * and if so, open it now.
+ */
+static void
+check_display()
+{
+ phandle node;
+ ihandle ih;
+ char type[16], name[64], path[128];
+
+ for (node = 0; prom_next_node(&node); ) {
+ type[0] = 0;
+ getpromprop(node, "device_type", type, sizeof(type));
+ if (strcmp(type, "display") != 0)
+ continue;
+ name[0] = 0;
+ getpromprop(node, "name", name, sizeof(name));
+ if (pmac_display_supported(name))
+ /* we have a supported display */
+ return;
+ }
+ printk(KERN_INFO "No supported display found\n");
+ for (node = 0; prom_next_node(&node); ) {
+ type[0] = 0;
+ getpromprop(node, "device_type", type, sizeof(type));
+ if (strcmp(type, "display") != 0)
+ continue;
+ /* It seems OF doesn't null-terminate the path :-( */
+ memset(path, 0, sizeof(path));
+ if ((int) call_prom("package-to-path", 3, 1,
+ node, path, sizeof(path) - 1) < 0) {
+ printk(KERN_WARNING "can't get path for display %p\n",
+ node);
+ continue;
+ }
+ ih = call_prom("open", 1, 1, path);
+ if (ih == 0 || ih == (ihandle) -1) {
+ printk(KERN_WARNING "couldn't open display %s\n",
+ path);
+ continue;
+ }
+ printk(KERN_INFO "Opened display device %s using "
+ "Open Firmware\n", path);
+ strcpy(prom_display_path, path);
+ break;
+ }
+}
+
+static int
+prom_next_node(phandle *nodep)
+{
+ phandle node;
+
+ if ((node = *nodep) != 0
+ && (*nodep = call_prom("child", 1, 1, node)) != 0)
+ return 1;
+ if ((*nodep = call_prom("peer", 1, 1, node)) != 0)
+ return 1;
+ for (;;) {
+ if ((node = call_prom("parent", 1, 1, node)) == 0)
+ return 0;
+ if ((*nodep = call_prom("peer", 1, 1, node)) != 0)
+ return 1;
+ }
+}
+
+/*
+ * Callback routine for the PROM to call us.
+ * No services are implemented yet :-)
+ */
+static int
+prom_callback(struct prom_args *argv)
+{
+ printk("uh oh, prom callback '%s' (%d/%d)\n", argv->service,
+ argv->nargs, argv->nret);
+ return -1;
+}
+
+/*
+ * Register a callback with the Open Firmware PROM so it can ask
+ * us to map/unmap memory, etc.
+ */
+void
+set_prom_callback()
+{
+ call_prom("set-callback", 1, 1, prom_callback);
+}
+
+void
+abort()
+{
+#ifdef CONFIG_XMON
+ xmon(0);
+#endif
+ prom_exit();
+}
+
+/*
+ * Make a copy of the device tree from the PROM.
+ */
+
+static struct device_node *allnodes;
+static struct device_node **allnextp;
+
+#define ALIGN(x) (((x) + sizeof(unsigned long)-1) & -sizeof(unsigned long))
+
+unsigned long
+copy_device_tree(unsigned long mem_start, unsigned long mem_end)
+{
+ phandle root;
+
+ root = call_prom("peer", 1, 1, (phandle)0);
+ if (root == (phandle)0)
+ panic("couldn't get device tree root\n");
+ allnextp = &allnodes;
+ mem_start = inspect_node(root, 0, 0, mem_start, mem_end);
+ *allnextp = 0;
+ return mem_start;
+}
+
+static unsigned long
+inspect_node(phandle node, struct device_node *dad, unsigned long base_address,
+ unsigned long mem_start, unsigned long mem_end)
+{
+ struct reg_property *reg, *rp;
+ struct pci_reg_property *pci_addrs;
+ int l, i;
+ phandle child;
+ struct device_node *np;
+ struct property *pp, **prev_propp;
+ char *prev_name;
+
+ np = (struct device_node *) mem_start;
+ mem_start += sizeof(struct device_node);
+ memset(np, 0, sizeof(*np));
+ np->node = node;
+ *allnextp = np;
+ allnextp = &np->allnext;
+ np->parent = dad;
+ if (dad != 0) {
+ /* we temporarily use the `next' field as `last_child'. */
+ if (dad->next == 0)
+ dad->child = np;
+ else
+ dad->next->sibling = np;
+ dad->next = np;
+ }
+
+ /* get and store all properties */
+ prev_propp = &np->properties;
+ prev_name = 0;
+ for (;;) {
+ pp = (struct property *) mem_start;
+ pp->name = (char *) (pp + 1);
+ if ((int) call_prom("nextprop", 3, 1, node, prev_name,
+ pp->name) <= 0)
+ break;
+ mem_start = ALIGN((unsigned long)pp->name
+ + strlen(pp->name) + 1);
+ pp->value = (unsigned char *) mem_start;
+ pp->length = (int)
+ call_prom("getprop", 4, 1, node, pp->name, pp->value,
+ mem_end - mem_start);
+ if (pp->length < 0)
+ panic("hey, where did property %s go?", pp->name);
+ mem_start = ALIGN(mem_start + pp->length);
+ prev_name = pp->name;
+ *prev_propp = pp;
+ prev_propp = &pp->next;
+ }
+ *prev_propp = 0;
+
+ np->name = get_property(np, "name", 0);
+ np->type = get_property(np, "device_type", 0);
+
+ /* get all the device addresses and interrupts */
+ reg = (struct reg_property *) mem_start;
+ pci_addrs = (struct pci_reg_property *)
+ get_property(np, "assigned-addresses", &l);
+ i = 0;
+ if (pci_addrs != 0) {
+ while ((l -= sizeof(struct pci_reg_property)) >= 0) {
+ /* XXX assumes PCI addresses mapped 1-1 to physical */
+ reg[i].address = pci_addrs[i].addr.a_lo;
+ reg[i].size = pci_addrs[i].size_lo;
+ ++i;
+ }
+ } else {
+ rp = (struct reg_property *) get_property(np, "reg", &l);
+ if (rp != 0) {
+ while ((l -= sizeof(struct reg_property)) >= 0) {
+ reg[i].address = rp[i].address + base_address;
+ reg[i].size = rp[i].size;
+ ++i;
+ }
+ }
+ }
+ if (i > 0) {
+ np->addrs = reg;
+ np->n_addrs = i;
+ mem_start += i * sizeof(struct reg_property);
+ }
+
+ np->intrs = (int *) get_property(np, "AAPL,interrupts", &l);
+ if (np->intrs != 0)
+ np->n_intrs = l / sizeof(int);
+
+ /* get the node's full name */
+ l = (int) call_prom("package-to-path", 3, 1, node,
+ (char *) mem_start, mem_end - mem_start);
+ if (l >= 0) {
+ np->full_name = (char *) mem_start;
+ np->full_name[l] = 0;
+ mem_start = ALIGN(mem_start + l + 1);
+ }
+
+ if (np->type != 0 && strcmp(np->type, "dbdma") == 0 && np->n_addrs > 0)
+ base_address = np->addrs[0].address;
+
+ child = call_prom("child", 1, 1, node);
+ while (child != (void *)0) {
+ mem_start = inspect_node(child, np, base_address,
+ mem_start, mem_end);
+ child = call_prom("peer", 1, 1, child);
+ }
+
+ return mem_start;
+}
+
+/*
+ * Construct a return a list of the device_nodes with a given name.
+ */
+struct device_node *
+find_devices(const char *name)
+{
+ struct device_node *head, **prevp, *np;
+
+ prevp = &head;
+ for (np = allnodes; np != 0; np = np->allnext) {
+ if (np->name != 0 && strcasecmp(np->name, name) == 0) {
+ *prevp = np;
+ prevp = &np->next;
+ }
+ }
+ *prevp = 0;
+ return head;
+}
+
+/*
+ * Construct a return a list of the device_nodes with a given type.
+ */
+struct device_node *
+find_type_devices(const char *type)
+{
+ struct device_node *head, **prevp, *np;
+
+ prevp = &head;
+ for (np = allnodes; np != 0; np = np->allnext) {
+ if (np->type != 0 && strcasecmp(np->type, type) == 0) {
+ *prevp = np;
+ prevp = &np->next;
+ }
+ }
+ *prevp = 0;
+ return head;
+}
+
+/*
+ * Find the device_node with a given full_name.
+ */
+struct device_node *
+find_path_device(const char *path)
+{
+ struct device_node *np;
+
+ for (np = allnodes; np != 0; np = np->allnext)
+ if (np->full_name != 0 && strcasecmp(np->full_name, path) == 0)
+ return np;
+ return NULL;
+}
+
+/*
+ * Find a property with a given name for a given node
+ * and return the value.
+ */
+unsigned char *
+get_property(struct device_node *np, const char *name, int *lenp)
+{
+ struct property *pp;
+
+ for (pp = np->properties; pp != 0; pp = pp->next)
+ if (strcmp(pp->name, name) == 0) {
+ if (lenp != 0)
+ *lenp = pp->length;
+ return pp->value;
+ }
+ return 0;
+}
+
+void
+print_properties(struct device_node *np)
+{
+ struct property *pp;
+ char *cp;
+ int i, n;
+
+ for (pp = np->properties; pp != 0; pp = pp->next) {
+ printk(KERN_INFO "%s", pp->name);
+ for (i = strlen(pp->name); i < 16; ++i)
+ printk(" ");
+ cp = (char *) pp->value;
+ for (i = pp->length; i > 0; --i, ++cp)
+ if ((i > 1 && (*cp < 0x20 || *cp > 0x7e))
+ || (i == 1 && *cp != 0))
+ break;
+ if (i == 0 && pp->length > 1) {
+ /* looks like a string */
+ printk(" %s\n", (char *) pp->value);
+ } else {
+ /* dump it in hex */
+ n = pp->length;
+ if (n > 64)
+ n = 64;
+ if (pp->length % 4 == 0) {
+ unsigned int *p = (unsigned int *) pp->value;
+
+ n /= 4;
+ for (i = 0; i < n; ++i) {
+ if (i != 0 && (i % 4) == 0)
+ printk("\n ");
+ printk(" %08x", *p++);
+ }
+ } else {
+ unsigned char *bp = pp->value;
+
+ for (i = 0; i < n; ++i) {
+ if (i != 0 && (i % 16) == 0)
+ printk("\n ");
+ printk(" %02x", *bp++);
+ }
+ }
+ printk("\n");
+ if (pp->length > 64)
+ printk(" ... (length = %d)\n",
+ pp->length);
+ }
+ }
+}
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov