📄 prom.c
字号:
/* * Procedures for creating, accessing and interpreting the device tree. * * Paul Mackerras August 1996. * Copyright (C) 1996-2005 Paul Mackerras. * * Adapted for 64bit PowerPC by Dave Engebretsen and Peter Bergner. * {engebret|bergner}@us.ibm.com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version * 2 of the License, or (at your option) any later version. */#undef DEBUG#include <stdarg.h>#include <linux/config.h>#include <linux/kernel.h>#include <linux/string.h>#include <linux/init.h>#include <linux/threads.h>#include <linux/spinlock.h>#include <linux/types.h>#include <linux/pci.h>#include <linux/stringify.h>#include <linux/delay.h>#include <linux/initrd.h>#include <linux/bitops.h>#include <linux/module.h>#include <asm/prom.h>#include <asm/rtas.h>#include <asm/lmb.h>#include <asm/page.h>#include <asm/processor.h>#include <asm/irq.h>#include <asm/io.h>#include <asm/smp.h>#include <asm/system.h>#include <asm/mmu.h>#include <asm/pgtable.h>#include <asm/pci.h>#include <asm/iommu.h>#include <asm/btext.h>#include <asm/sections.h>#include <asm/machdep.h>#include <asm/pSeries_reconfig.h>#include <asm/pci-bridge.h>#ifdef DEBUG#define DBG(fmt...) printk(KERN_ERR fmt)#else#define DBG(fmt...)#endifstruct pci_reg_property { struct pci_address addr; u32 size_hi; u32 size_lo;};struct isa_reg_property { u32 space; u32 address; u32 size;};typedef int interpret_func(struct device_node *, unsigned long *, int, int, int);static int __initdata dt_root_addr_cells;static int __initdata dt_root_size_cells;#ifdef CONFIG_PPC64static int __initdata iommu_is_off;int __initdata iommu_force_on;unsigned long tce_alloc_start, tce_alloc_end;#endiftypedef u32 cell_t;#if 0static struct boot_param_header *initial_boot_params __initdata;#elsestruct boot_param_header *initial_boot_params;#endifstatic struct device_node *allnodes = NULL;/* use when traversing tree through the allnext, child, sibling, * or parent members of struct device_node. */static DEFINE_RWLOCK(devtree_lock);/* export that to outside world */struct device_node *of_chosen;struct device_node *dflt_interrupt_controller;int num_interrupt_controllers;/* * Wrapper for allocating memory for various data that needs to be * attached to device nodes as they are processed at boot or when * added to the device tree later (e.g. DLPAR). At boot there is * already a region reserved so we just increment *mem_start by size; * otherwise we call kmalloc. */static void * prom_alloc(unsigned long size, unsigned long *mem_start){ unsigned long tmp; if (!mem_start) return kmalloc(size, GFP_KERNEL); tmp = *mem_start; *mem_start += size; return (void *)tmp;}/* * Find the device_node with a given phandle. */static struct device_node * find_phandle(phandle ph){ struct device_node *np; for (np = allnodes; np != 0; np = np->allnext) if (np->linux_phandle == ph) return np; return NULL;}/* * Find the interrupt parent of a node. */static struct device_node * __devinit intr_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. */int __devinit prom_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; } }#ifdef DEBUG_IRQ printk("prom_n_intr_cells failed for %s\n", np->full_name);#endif return 1;}/* * Map an interrupt from a device up to the platform interrupt * descriptor. */static int __devinit map_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 = 0, newaddrc = 0; 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) {#ifdef DEBUG_IRQ printk("oops, no match in %s int-map for %s\n", p->full_name, np->full_name);#endif return 0; } p = ipar; naddrc = newaddrc; nintrc = newintrc; ints = imap - nintrc; reg = ints - naddrc; } if (p == NULL) {#ifdef DEBUG_IRQ printk("hmmm, int tree for %s doesn't have ctrler\n", np->full_name);#endif return 0; } *irq = ints; *ictrler = p; return nintrc;}static unsigned char map_isa_senses[4] = { IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE, IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE, IRQ_SENSE_EDGE | IRQ_POLARITY_NEGATIVE, IRQ_SENSE_EDGE | IRQ_POLARITY_POSITIVE};static unsigned char map_mpic_senses[4] = { IRQ_SENSE_EDGE | IRQ_POLARITY_POSITIVE, IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE, /* 2 seems to be used for the 8259 cascade... */ IRQ_SENSE_LEVEL | IRQ_POLARITY_POSITIVE, IRQ_SENSE_EDGE | IRQ_POLARITY_NEGATIVE,};static int __devinit finish_node_interrupts(struct device_node *np, unsigned long *mem_start, int measure_only){ unsigned int *ints; int intlen, intrcells, intrcount; int i, j, n, sense; unsigned int *irq, virq; struct device_node *ic; if (num_interrupt_controllers == 0) { /* * Old machines just have a list of interrupt numbers * and no interrupt-controller nodes. */ ints = (unsigned int *) get_property(np, "AAPL,interrupts", &intlen); /* XXX old interpret_pci_props looked in parent too */ /* XXX old interpret_macio_props looked for interrupts before AAPL,interrupts */ if (ints == NULL) ints = (unsigned int *) get_property(np, "interrupts", &intlen); if (ints == NULL) return 0; np->n_intrs = intlen / sizeof(unsigned int); np->intrs = prom_alloc(np->n_intrs * sizeof(np->intrs[0]), mem_start); if (!np->intrs) return -ENOMEM; if (measure_only) return 0; for (i = 0; i < np->n_intrs; ++i) { np->intrs[i].line = *ints++; np->intrs[i].sense = IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE; } return 0; } ints = (unsigned int *) get_property(np, "interrupts", &intlen); if (ints == NULL) return 0; intrcells = prom_n_intr_cells(np); intlen /= intrcells * sizeof(unsigned int); np->intrs = prom_alloc(intlen * sizeof(*(np->intrs)), mem_start); if (!np->intrs) return -ENOMEM; if (measure_only) return 0; intrcount = 0; for (i = 0; i < intlen; ++i, ints += intrcells) { n = map_interrupt(&irq, &ic, np, ints, intrcells); if (n <= 0) continue; /* don't map IRQ numbers under a cascaded 8259 controller */ if (ic && device_is_compatible(ic, "chrp,iic")) { np->intrs[intrcount].line = irq[0]; sense = (n > 1)? (irq[1] & 3): 3; np->intrs[intrcount].sense = map_isa_senses[sense]; } else { virq = virt_irq_create_mapping(irq[0]);#ifdef CONFIG_PPC64 if (virq == NO_IRQ) { printk(KERN_CRIT "Could not allocate interrupt" " number for %s\n", np->full_name); continue; }#endif np->intrs[intrcount].line = irq_offset_up(virq); sense = (n > 1)? (irq[1] & 3): 1; np->intrs[intrcount].sense = map_mpic_senses[sense]; }#ifdef CONFIG_PPC64 /* We offset irq numbers for the u3 MPIC by 128 in PowerMac */ if (_machine == PLATFORM_POWERMAC && ic && ic->parent) { char *name = get_property(ic->parent, "name", NULL); if (name && !strcmp(name, "u3")) np->intrs[intrcount].line += 128; else if (!(name && !strcmp(name, "mac-io"))) /* ignore other cascaded controllers, such as the k2-sata-root */ break; }#endif if (n > 2) { printk("hmmm, got %d intr cells for %s:", n, np->full_name); for (j = 0; j < n; ++j) printk(" %d", irq[j]); printk("\n"); } ++intrcount; } np->n_intrs = intrcount; return 0;}static int __devinit interpret_pci_props(struct device_node *np, unsigned long *mem_start, int naddrc, int nsizec, int measure_only){ struct address_range *adr; struct pci_reg_property *pci_addrs; int i, l, n_addrs; pci_addrs = (struct pci_reg_property *) get_property(np, "assigned-addresses", &l); if (!pci_addrs) return 0; n_addrs = l / sizeof(*pci_addrs); adr = prom_alloc(n_addrs * sizeof(*adr), mem_start); if (!adr) return -ENOMEM; if (measure_only) return 0; np->addrs = adr; np->n_addrs = n_addrs; for (i = 0; i < n_addrs; i++) { adr[i].space = pci_addrs[i].addr.a_hi; adr[i].address = pci_addrs[i].addr.a_lo | ((u64)pci_addrs[i].addr.a_mid << 32); adr[i].size = pci_addrs[i].size_lo; } return 0;}static int __init interpret_dbdma_props(struct device_node *np, unsigned long *mem_start, int naddrc, int nsizec, int measure_only){ struct reg_property32 *rp; struct address_range *adr; unsigned long base_address; int i, l; struct device_node *db; base_address = 0; if (!measure_only) { for (db = np->parent; db != NULL; db = db->parent) { if (!strcmp(db->type, "dbdma") && db->n_addrs != 0) { base_address = db->addrs[0].address; break; } } } rp = (struct reg_property32 *) get_property(np, "reg", &l); if (rp != 0 && l >= sizeof(struct reg_property32)) { i = 0; adr = (struct address_range *) (*mem_start); while ((l -= sizeof(struct reg_property32)) >= 0) { if (!measure_only) { adr[i].space = 2; adr[i].address = rp[i].address + base_address; adr[i].size = rp[i].size; } ++i; } np->addrs = adr; np->n_addrs = i; (*mem_start) += i * sizeof(struct address_range); } return 0;}static int __init interpret_macio_props(struct device_node *np, unsigned long *mem_start, int naddrc, int nsizec, int measure_only){ struct reg_property32 *rp; struct address_range *adr; unsigned long base_address; int i, l; struct device_node *db; base_address = 0; if (!measure_only) { for (db = np->parent; db != NULL; db = db->parent) { if (!strcmp(db->type, "mac-io") && db->n_addrs != 0) { base_address = db->addrs[0].address; break; } } } rp = (struct reg_property32 *) get_property(np, "reg", &l); if (rp != 0 && l >= sizeof(struct reg_property32)) { i = 0; adr = (struct address_range *) (*mem_start); while ((l -= sizeof(struct reg_property32)) >= 0) { if (!measure_only) { adr[i].space = 2; adr[i].address = rp[i].address + base_address; adr[i].size = rp[i].size; } ++i; } np->addrs = adr; np->n_addrs = i; (*mem_start) += i * sizeof(struct address_range); } return 0;}static int __init interpret_isa_props(struct device_node *np, unsigned long *mem_start, int naddrc, int nsizec, int measure_only){ struct isa_reg_property *rp; struct address_range *adr; int i, l; rp = (struct isa_reg_property *) get_property(np, "reg", &l); if (rp != 0 && l >= sizeof(struct isa_reg_property)) { i = 0; adr = (struct address_range *) (*mem_start); while ((l -= sizeof(struct isa_reg_property)) >= 0) { if (!measure_only) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -