prom.c

来自「LINUX 2.6.17.4的源码」· C语言 代码 · 共 2,082 行 · 第 1/4 页

C
2,082
字号
/* * 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 <linux/kexec.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/kdump.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...)#endifstatic int __initdata dt_root_addr_cells;static int __initdata dt_root_size_cells;#ifdef CONFIG_PPC64int __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;	int trace = 0;	//#define TRACE(fmt...) do { if (trace) { printk(fmt); mdelay(1000); } } while(0)#define TRACE(fmt...)	if (!strcmp(np->name, "smu-doorbell"))		trace = 1;	TRACE("Finishing SMU doorbell ! num_interrupt_controllers = %d\n",	      num_interrupt_controllers);	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);	TRACE("ints=%p, intlen=%d\n", ints, intlen);	if (ints == NULL)		return 0;	intrcells = prom_n_intr_cells(np);	intlen /= intrcells * sizeof(unsigned int);	TRACE("intrcells=%d, new intlen=%d\n", intrcells, intlen);	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);		TRACE("map, irq=%d, ic=%p, n=%d\n", irq, ic, n);		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]);			TRACE("virq=%d\n", virq);#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;			/* Apple uses bits in there in a different way, let's			 * only keep the real sense bit on macs			 */			if (machine_is(powermac))				sense &= 0x1;			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_is(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") ||					    !strcmp(name, "u4"))))				/* ignore other cascaded controllers, such as				   the k2-sata-root */				break;		}#endif /* CONFIG_PPC64 */		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 finish_node(struct device_node *np,				 unsigned long *mem_start,				 int measure_only){	struct device_node *child;	int rc = 0;	rc = finish_node_interrupts(np, mem_start, measure_only);	if (rc)		goto out;	for (child = np->child; child != NULL; child = child->sibling) {		rc = finish_node(child, mem_start, measure_only);		if (rc)			goto out;	}out:	return rc;}static void __init scan_interrupt_controllers(void){	struct device_node *np;	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;}/** * 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 some of the additional, * fields in each node like {n_}addrs and {n_}intrs, the virt interrupt * mapping is also initialized at this point. */void __init finish_device_tree(void){	unsigned long start, end, size = 0;	DBG(" -> finish_device_tree\n");#ifdef CONFIG_PPC64	/* Initialize virtual IRQ map */	virt_irq_init();#endif	scan_interrupt_controllers();	/*	 * Finish device-tree (pre-parsing some properties etc...)	 * We do this in 2 passes. One with "measure_only" set, which	 * will only measure the amount of memory needed, then we can	 * allocate that memory, and call finish_node again. However,	 * we must be careful as most routines will fail nowadays when	 * prom_alloc() returns 0, so we must make sure our first pass	 * doesn't start at 0. We pre-initialize size to 16 for that	 * reason and then remove those additional 16 bytes	 */	size = 16;	finish_node(allnodes, &size, 1);	size -= 16;	if (0 == size)		end = start = 0;	else		end = start = (unsigned long)__va(lmb_alloc(size, 128));	finish_node(allnodes, &end, 0);	BUG_ON(end != start + size);	DBG(" <- finish_device_tree\n");}static inline char *find_flat_dt_string(u32 offset){	return ((char *)initial_boot_params) +		initial_boot_params->off_dt_strings + offset;}/** * This function is used to scan the flattened device-tree, it is * used to extract the memory informations at boot before we can * unflatten the tree */int __init of_scan_flat_dt(int (*it)(unsigned long node,				     const char *uname, int depth,				     void *data),			   void *data){

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?