📄 prom.c
字号:
/* * BK Id: %F% %I% %G% %U% %#% *//* * 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 <linux/config.h>#include <linux/kernel.h>#include <linux/string.h>#include <linux/init.h>#include <linux/version.h>#include <linux/threads.h>#include <linux/spinlock.h>#include <linux/pci.h>#include <linux/slab.h>#include <asm/sections.h>#include <asm/prom.h>#include <asm/page.h>#include <asm/processor.h>#include <asm/irq.h>#include <asm/open_pic.h>#include <asm/system.h>#include <asm/btext.h>#include <asm/pci-bridge.h>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 isa_reg_property { unsigned space; unsigned address; unsigned size;};typedef unsigned long interpret_func(struct device_node *, unsigned long, int, int);static interpret_func interpret_pci_props;static interpret_func interpret_dbdma_props;static interpret_func interpret_isa_props;static interpret_func interpret_macio_props;static interpret_func interpret_root_props;extern char *klimit;/* Set for a newworld or CHRP machine */int use_of_interrupt_tree;struct device_node *dflt_interrupt_controller;int num_interrupt_controllers;int pmac_newworld;extern unsigned int rtas_entry; /* physical pointer */extern struct device_node *allnodes;static unsigned long finish_node(struct device_node *, unsigned long, interpret_func *, int, int);static unsigned long finish_node_interrupts(struct device_node *, unsigned long);extern void enter_rtas(void *);void phys_call_rtas(int, int, int, ...);extern char cmd_line[512]; /* XXX */extern boot_infos_t *boot_infos;unsigned long dev_tree_size;void __openfirmwarephys_call_rtas(int service, int nargs, int nret, ...){ va_list list; union { unsigned long words[16]; double align; } u; void (*rtas)(void *, unsigned long); int i; u.words[0] = service; u.words[1] = nargs; u.words[2] = nret; va_start(list, nret); for (i = 0; i < nargs; ++i) u.words[i+3] = va_arg(list, unsigned long); va_end(list); rtas = (void (*)(void *, unsigned long)) rtas_entry; rtas(&u, rtas_data);}/* * finish_device_tree is called once things are running normally * (i.e. with text and data mapped to the address they were linked at). * It traverses the device tree and fills in the name, type, * {n_}addrs and {n_}intrs fields of each node. */void __initfinish_device_tree(void){ unsigned long mem = (unsigned long) klimit; struct device_node *np; /* All newworld pmac machines and CHRPs now use the interrupt tree */ for (np = allnodes; np != NULL; np = np->allnext) { if (get_property(np, "interrupt-parent", 0)) { use_of_interrupt_tree = 1; break; } } if (_machine == _MACH_Pmac && use_of_interrupt_tree) pmac_newworld = 1;#ifdef CONFIG_BOOTX_TEXT if (boot_infos && pmac_newworld) { prom_print("WARNING ! BootX/miBoot booting is not supported on this machine\n"); prom_print(" You should use an Open Firmware bootloader\n"); }#endif /* CONFIG_BOOTX_TEXT */ if (use_of_interrupt_tree) { /* * We want to find out here how many interrupt-controller * nodes there are, and if we are booted from BootX, * we need a pointer to the first (and hopefully only) * such node. But we can't use find_devices here since * np->name has not been set yet. -- paulus */ int n = 0; char *name, *ic; int iclen; for (np = allnodes; np != NULL; np = np->allnext) { ic = get_property(np, "interrupt-controller", &iclen); name = get_property(np, "name", NULL); /* checking iclen makes sure we don't get a false match on /chosen.interrupt_controller */ if ((name != NULL && strcmp(name, "interrupt-controller") == 0) || (ic != NULL && iclen == 0 && strcmp(name, "AppleKiwi"))) { if (n == 0) dflt_interrupt_controller = np; ++n; } } num_interrupt_controllers = n; } mem = finish_node(allnodes, mem, NULL, 1, 1); dev_tree_size = mem - (unsigned long) allnodes; klimit = (char *) mem;}static unsigned long __initfinish_node(struct device_node *np, unsigned long mem_start, interpret_func *ifunc, int naddrc, int nsizec){ struct device_node *child; int *ip; np->name = get_property(np, "name", 0); np->type = get_property(np, "device_type", 0); if (!np->name) np->name = "<NULL>"; if (!np->type) np->type = "<NULL>"; /* get the device addresses and interrupts */ if (ifunc != NULL) mem_start = ifunc(np, mem_start, naddrc, nsizec); if (use_of_interrupt_tree) mem_start = finish_node_interrupts(np, mem_start); /* Look for #address-cells and #size-cells properties. */ ip = (int *) get_property(np, "#address-cells", 0); if (ip != NULL) naddrc = *ip; ip = (int *) get_property(np, "#size-cells", 0); if (ip != NULL) nsizec = *ip; if (np->parent == NULL) ifunc = interpret_root_props; else if (np->type == 0) ifunc = NULL; else if (!strcmp(np->type, "pci") || !strcmp(np->type, "vci")) ifunc = interpret_pci_props; else if (!strcmp(np->type, "dbdma")) ifunc = interpret_dbdma_props; else if (!strcmp(np->type, "mac-io") || ifunc == interpret_macio_props) ifunc = interpret_macio_props; else if (!strcmp(np->type, "isa")) ifunc = interpret_isa_props; else if (!strcmp(np->name, "uni-n")) ifunc = interpret_root_props; else if (!((ifunc == interpret_dbdma_props || ifunc == interpret_macio_props) && (!strcmp(np->type, "escc") || !strcmp(np->type, "media-bay")))) ifunc = NULL; /* if we were booted from BootX, convert the full name */ if (boot_infos && strncmp(np->full_name, "Devices:device-tree", 19) == 0) { if (np->full_name[19] == 0) { strcpy(np->full_name, "/"); } else if (np->full_name[19] == ':') { char *p = np->full_name + 19; np->full_name = p; for (; *p; ++p) if (*p == ':') *p = '/'; } } for (child = np->child; child != NULL; child = child->sibling) mem_start = finish_node(child, mem_start, ifunc, naddrc, nsizec); return mem_start;}/* * Find the interrupt parent of a node. */static struct device_node * __initintr_parent(struct device_node *p){ phandle *parp; parp = (phandle *) get_property(p, "interrupt-parent", NULL); if (parp == NULL) return p->parent; p = find_phandle(*parp); if (p != NULL) return p; /* * On a powermac booted with BootX, we don't get to know the * phandles for any nodes, so find_phandle will return NULL. * Fortunately these machines only have one interrupt controller * so there isn't in fact any ambiguity. -- paulus */ if (num_interrupt_controllers == 1) p = dflt_interrupt_controller; return p;}/* * Find out the size of each entry of the interrupts property * for a node. */static int __initprom_n_intr_cells(struct device_node *np){ struct device_node *p; unsigned int *icp; for (p = np; (p = intr_parent(p)) != NULL; ) { icp = (unsigned int *) get_property(p, "#interrupt-cells", NULL); if (icp != NULL) return *icp; if (get_property(p, "interrupt-controller", NULL) != NULL || get_property(p, "interrupt-map", NULL) != NULL) { printk("oops, node %s doesn't have #interrupt-cells\n", p->full_name); return 1; } } printk("prom_n_intr_cells failed for %s\n", np->full_name); return 1;}/* * Map an interrupt from a device up to the platform interrupt * descriptor. */static int __initmap_interrupt(unsigned int **irq, struct device_node **ictrler, struct device_node *np, unsigned int *ints, int nintrc){ struct device_node *p, *ipar; unsigned int *imap, *imask, *ip; int i, imaplen, match; int newintrc, newaddrc; unsigned int *reg; int naddrc; reg = (unsigned int *) get_property(np, "reg", NULL); naddrc = prom_n_addr_cells(np); p = intr_parent(np); while (p != NULL) { if (get_property(p, "interrupt-controller", NULL) != NULL) /* this node is an interrupt controller, stop here */ break; imap = (unsigned int *) get_property(p, "interrupt-map", &imaplen); if (imap == NULL) { p = intr_parent(p); continue; } imask = (unsigned int *) get_property(p, "interrupt-map-mask", NULL); if (imask == NULL) { printk("oops, %s has interrupt-map but no mask\n", p->full_name); return 0; } imaplen /= sizeof(unsigned int); match = 0; ipar = NULL; while (imaplen > 0 && !match) { /* check the child-interrupt field */ match = 1; for (i = 0; i < naddrc && match; ++i) match = ((reg[i] ^ imap[i]) & imask[i]) == 0; for (; i < naddrc + nintrc && match; ++i) match = ((ints[i-naddrc] ^ imap[i]) & imask[i]) == 0; imap += naddrc + nintrc; imaplen -= naddrc + nintrc; /* grab the interrupt parent */ ipar = find_phandle((phandle) *imap++); --imaplen; if (ipar == NULL && num_interrupt_controllers == 1) /* cope with BootX not giving us phandles */ ipar = dflt_interrupt_controller; if (ipar == NULL) { printk("oops, no int parent %x in map of %s\n", imap[-1], p->full_name); return 0; } /* find the parent's # addr and intr cells */ ip = (unsigned int *) get_property(ipar, "#interrupt-cells", NULL); if (ip == NULL) { printk("oops, no #interrupt-cells on %s\n", ipar->full_name); return 0; } newintrc = *ip; ip = (unsigned int *) get_property(ipar, "#address-cells", NULL); newaddrc = (ip == NULL)? 0: *ip; imap += newaddrc + newintrc; imaplen -= newaddrc + newintrc; } if (imaplen < 0) { printk("oops, error decoding int-map on %s, len=%d\n", p->full_name, imaplen); return 0; } if (!match) { printk("oops, no match in %s int-map for %s\n", p->full_name, np->full_name); return 0; } p = ipar; naddrc = newaddrc; nintrc = newintrc; ints = imap - nintrc; reg = ints - naddrc; } if (p == NULL) printk("hmmm, int tree for %s doesn't have ctrler\n", np->full_name); *irq = ints; *ictrler = p; return nintrc;}/* * New version of finish_node_interrupts. */static unsigned long __initfinish_node_interrupts(struct device_node *np, unsigned long mem_start){ unsigned int *ints; int intlen, intrcells; int i, j, n, offset; unsigned int *irq; struct device_node *ic; ints = (unsigned int *) get_property(np, "interrupts", &intlen); if (ints == NULL) return mem_start; intrcells = prom_n_intr_cells(np); intlen /= intrcells * sizeof(unsigned int); np->n_intrs = intlen; np->intrs = (struct interrupt_info *) mem_start; mem_start += intlen * sizeof(struct interrupt_info); for (i = 0; i < intlen; ++i) { np->intrs[i].line = 0; np->intrs[i].sense = 1; n = map_interrupt(&irq, &ic, np, ints, intrcells); if (n <= 0) continue; offset = 0; /*
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -