📄 prom.c
字号:
lmb_reserve(0, __pa(RELOC(klimit))); if (RELOC(_machine) == _MACH_pSeries) prom_initialize_tce_table(); if ((long) call_prom(RELOC("getprop"), 4, 1, _prom->chosen, RELOC("mmu"), &getprop_rval, sizeof(getprop_rval)) <= 0) { prom_print(RELOC(" no MMU found\n")); prom_exit(); } /* We assume the phys. address size is 3 cells */ RELOC(prom_mmu) = (ihandle)(unsigned long)getprop_rval; if ((long)call_prom(RELOC("call-method"), 4, 4, RELOC("translate"), prom_mmu, (void *)(KERNELBASE - offset), (void *)1) != 0) { prom_print(RELOC(" (translate failed) ")); } else { prom_print(RELOC(" (translate ok) ")); phys = (unsigned long)_prom->args.rets[3]; } /* If OpenFirmware version >= 3, then use quiesce call */ if (_prom->version >= 3) { prom_print(RELOC("Calling quiesce ...\n")); call_prom(RELOC("quiesce"), 0, 0); phys = KERNELBASE - offset; } prom_print(RELOC("returning from prom_init\n")); return phys;}static intprom_set_color(ihandle ih, int i, int r, int g, int b){ unsigned long offset = reloc_offset(); return (int)(long)call_prom(RELOC("call-method"), 6, 1, RELOC("color!"), ih, (void *)(long) i, (void *)(long) b, (void *)(long) g, (void *)(long) r );}/* * 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 will probably fall over if we do that * we've taken over the MMU. * So we check whether we will need to open the display, * and if so, open it now. */static unsigned long __initcheck_display(unsigned long mem){ phandle node; ihandle ih; int i; unsigned long offset = reloc_offset(); struct prom_t *_prom = PTRRELOC(&prom); char type[64], *path; static unsigned char default_colors[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, 0x00, 0xaa, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0x00, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x00, 0xaa, 0xaa, 0xaa, 0x55, 0x55, 0x55, 0x55, 0x55, 0xff, 0x55, 0xff, 0x55, 0x55, 0xff, 0xff, 0xff, 0x55, 0x55, 0xff, 0x55, 0xff, 0xff, 0xff, 0x55, 0xff, 0xff, 0xff }; _prom->disp_node = 0; for (node = 0; prom_next_node(&node); ) { type[0] = 0; call_prom(RELOC("getprop"), 4, 1, node, RELOC("device_type"), type, sizeof(type)); if (strcmp(type, RELOC("display")) != 0) continue; /* It seems OF doesn't null-terminate the path :-( */ path = (char *) mem; memset(path, 0, 256); if ((long) call_prom(RELOC("package-to-path"), 3, 1, node, path, 255) < 0) continue; prom_print(RELOC("opening display ")); prom_print(path); ih = (ihandle)call_prom(RELOC("open"), 1, 1, path); if (ih == (ihandle)0 || ih == (ihandle)-1) { prom_print(RELOC("... failed\n")); continue; } prom_print(RELOC("... ok\n")); if (_prom->disp_node == 0) _prom->disp_node = (ihandle)(unsigned long)node; /* Setup a useable color table when the appropriate * method is available. Should update this to set-colors */ for (i = 0; i < 32; i++) if (prom_set_color(ih, i, RELOC(default_colors)[i*3], RELOC(default_colors)[i*3+1], RELOC(default_colors)[i*3+2]) != 0) break;#ifdef CONFIG_FB for (i = 0; i < LINUX_LOGO_COLORS; i++) if (prom_set_color(ih, i + 32, RELOC(linux_logo_red)[i], RELOC(linux_logo_green)[i], RELOC(linux_logo_blue)[i]) != 0) break;#endif /* CONFIG_FB */ /* * If this display is the device that OF is using for stdout, * move it to the front of the list. */ mem += strlen(path) + 1; i = RELOC(prom_num_displays)++; if (RELOC(of_stdout_device) != 0 && i > 0 && strcmp(PTRRELOC(RELOC(of_stdout_device)), path) == 0) { for (; i > 0; --i) RELOC(prom_display_paths[i]) = RELOC(prom_display_paths[i-1]); } RELOC(prom_display_paths[i]) = PTRUNRELOC(path); if (RELOC(prom_num_displays) >= FB_MAX) break; } return DOUBLEWORD_ALIGN(mem);}voidvirt_irq_init(void){ int i; for (i = 0; i < NR_IRQS; i++) virt_irq_to_real_map[i] = UNDEFINED_IRQ; for (i = 0; i < NR_HW_IRQS; i++) real_irq_to_virt_map[i] = UNDEFINED_IRQ;}/* Create a mapping for a real_irq if it doesn't already exist. * Return the virtual irq as a convenience. */unsigned longvirt_irq_create_mapping(unsigned long real_irq){ unsigned long virq; if (naca->interrupt_controller == IC_OPEN_PIC) return real_irq; /* no mapping for openpic (for now) */ virq = real_irq_to_virt(real_irq); if (virq == UNDEFINED_IRQ) { /* Assign a virtual IRQ number */ if (real_irq < NR_IRQS && virt_irq_to_real(real_irq) == UNDEFINED_IRQ) { /* A 1-1 mapping will work. */ virq = real_irq; } else { while (last_virt_irq < NR_IRQS && virt_irq_to_real(++last_virt_irq) != UNDEFINED_IRQ) /* skip irq's in use */; if (last_virt_irq >= NR_IRQS) panic("Too many IRQs are required on this system. NR_IRQS=%d\n", NR_IRQS); virq = last_virt_irq; } virt_irq_to_real_map[virq] = real_irq; real_irq_to_virt_map[real_irq] = virq; } return virq;}static int __initprom_next_node(phandle *nodep){ phandle node; unsigned long offset = reloc_offset(); if ((node = *nodep) != 0 && (*nodep = call_prom(RELOC("child"), 1, 1, node)) != 0) return 1; if ((*nodep = call_prom(RELOC("peer"), 1, 1, node)) != 0) return 1; for (;;) { if ((node = call_prom(RELOC("parent"), 1, 1, node)) == 0) return 0; if ((*nodep = call_prom(RELOC("peer"), 1, 1, node)) != 0) return 1; }}/* * Make a copy of the device tree from the PROM. */static unsigned long __initcopy_device_tree(unsigned long mem_start){ phandle root; unsigned long new_start; struct device_node **allnextp; unsigned long offset = reloc_offset(); unsigned long mem_end = mem_start + (8<<20); root = call_prom(RELOC("peer"), 1, 1, (phandle)0); if (root == (phandle)0) { prom_print(RELOC("couldn't get device tree root\n")); prom_exit(); } allnextp = &RELOC(allnodes); mem_start = DOUBLEWORD_ALIGN(mem_start); new_start = inspect_node(root, 0, mem_start, mem_end, &allnextp); *allnextp = 0; return new_start;}__initstatic unsigned longinspect_node(phandle node, struct device_node *dad, unsigned long mem_start, unsigned long mem_end, struct device_node ***allnextpp){ int l; phandle child; struct device_node *np; struct property *pp, **prev_propp; char *prev_name, *namep; unsigned char *valp; unsigned long offset = reloc_offset(); np = (struct device_node *) mem_start; mem_start += sizeof(struct device_node); memset(np, 0, sizeof(*np)); np->node = node; **allnextpp = PTRUNRELOC(np); *allnextpp = &np->allnext; if (dad != 0) { np->parent = PTRUNRELOC(dad); /* we temporarily use the `next' field as `last_child'. */ if (dad->next == 0) dad->child = PTRUNRELOC(np); else dad->next->sibling = PTRUNRELOC(np); dad->next = np; } /* get and store all properties */ prev_propp = &np->properties; prev_name = RELOC(""); for (;;) { pp = (struct property *) mem_start; namep = (char *) (pp + 1); pp->name = PTRUNRELOC(namep); if ((long) call_prom(RELOC("nextprop"), 3, 1, node, prev_name, namep) <= 0) break; mem_start = DOUBLEWORD_ALIGN((unsigned long)namep + strlen(namep) + 1); prev_name = namep; valp = (unsigned char *) mem_start; pp->value = PTRUNRELOC(valp); pp->length = (int)(long) call_prom(RELOC("getprop"), 4, 1, node, namep, valp, mem_end - mem_start); if (pp->length < 0) continue; mem_start = DOUBLEWORD_ALIGN(mem_start + pp->length); *prev_propp = PTRUNRELOC(pp); prev_propp = &pp->next; } *prev_propp = 0; /* get the node's full name */ l = (long) call_prom(RELOC("package-to-path"), 3, 1, node, (char *) mem_start, mem_end - mem_start); if (l >= 0) { np->full_name = PTRUNRELOC((char *) mem_start); *(char *)(mem_start + l) = 0; mem_start = DOUBLEWORD_ALIGN(mem_start + l + 1); } /* do all our children */ child = call_prom(RELOC("child"), 1, 1, node); while (child != (phandle)0) { mem_start = inspect_node(child, np, mem_start, mem_end, allnextpp); child = call_prom(RELOC("peer"), 1, 1, child); } return mem_start;}/* * 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 = klimit; virt_irq_init(); mem = finish_node(allnodes, mem, NULL, 0, 0); dev_tree_size = mem - (unsigned long) allnodes; mem = _ALIGN(mem, PAGE_SIZE); lmb_reserve(__pa(klimit), mem-klimit); klimit = mem; rtas.dev = find_devices("rtas");}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); /* get the device addresses and interrupts */ if (ifunc != NULL) { mem_start = ifunc(np, mem_start, naddrc, nsizec); } 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; /* the f50 sets the name to 'display' and 'compatible' to what we * expect for the name -- Cort */ ifunc = NULL; if (!strcmp(np->name, "display")) np->name = get_property(np, "compatible", 0); if (!strcmp(np->name, "device-tree") || 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, "isa")) ifunc = interpret_isa_props; for (child = np->child; child != NULL; child = child->sibling) mem_start = finish_node(child, mem_start, ifunc, naddrc, nsizec); return mem_start;}/* This routine walks the interrupt tree for a given device node and gather * all necessary informations according to the draft interrupt mapping * for CHRP. The current version was only tested on Apple "Core99" machines * and may not handle cascaded controllers correctly. */__initstatic unsigned longfinish_node_interrupts(struct device_node *np, unsigned long mem_start){ /* Finish this node */ unsigned int *isizep, *asizep, *interrupts, *map, *map_mask, *reg; phandle *parent, map_parent; struct device_node *node, *parent_node; int l, isize, ipsize, asize, map_size, regpsize; /* Currently, we don't look at all nodes with no "interrupts" property */ interrupts = (unsigned int *)get_property(np, "interrupts", &l); if (interrupts == NULL) return mem_start; ipsize = l>>2; reg = (unsigned int *)get_property(np, "reg", &l); regpsize = l>>2; /* We assume default interrupt cell size is 1 (bugus ?) */ isize = 1; node = np; do { /* We adjust the cell size if the current parent contains an #interrupt-cells * property */ isizep = (unsigned int *)get_property(node, "#interrupt-cells", &l); if (isizep) isize = *isizep; /* We don't do interrupt cascade (ISA) for now, we stop on the first * controller found */ if (get_property(node, "interrupt-controller", &l)) { int i,j; np->intrs = (struct interrupt_info *) mem_start; np->n_intrs = ipsize / isize; mem_start += np->n_intrs * sizeof(struct interrupt_info); for (i = 0; i < np->n_intrs; ++i) { np->intrs[i].line = openpic_to_irq(virt_irq_create_mapping(*interrupts++)); np->intrs[i].sense = 1; if (isize > 1) np->intrs[i].sense = *interrupts++; for (j=2; j<isize; j++) interrupts++; } return mem_start; } /* We lookup for an interrupt-map. This code can only handle one interrupt * per device in the map. We also don't handle #address-cells in the parent * I skip the pci node itself here, may not be necessary but I don't like it's * reg property. */ if (np != node) map = (unsigned int *)get_property(node, "interrupt-map", &l); else map = NULL; if (map && l) { int i, found, temp_isize, temp_asize; map_size = l>>2; map_mask = (unsigned int *)get_property(node, "interrupt-map-mask", &l); asizep = (unsigned int *)get_property(node, "#address-cells", &l); if (asizep && l == sizeof(unsigned int)) asize = *asizep; else asize = 0; found = 0; while (map_size>0 && !found) { found = 1; for (i=0; i<asize; i++) { unsigned int mask = map_mask ? map_mask[i] : 0xffffffff; if (!reg || (i>=regpsize) || ((mask & *map) != (mask & reg[i]))) found = 0; map++; map_size--; } for (i=0; i<isize; i++) { unsigned int mask = map_mask ? map_mask[i+asize] : 0xffffffff; if ((mask & *map) != (mask & interrupts[i])) found = 0; map++; map_size--; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -