diff options
Diffstat (limited to 'arch/powerpc/kernel/prom.c')
| -rw-r--r-- | arch/powerpc/kernel/prom.c | 454 | 
1 files changed, 3 insertions, 451 deletions
| diff --git a/arch/powerpc/kernel/prom.c b/arch/powerpc/kernel/prom.c index 4c524cb52184..a1787ffb6319 100644 --- a/arch/powerpc/kernel/prom.c +++ b/arch/powerpc/kernel/prom.c @@ -30,6 +30,7 @@  #include <linux/module.h>  #include <linux/kexec.h>  #include <linux/debugfs.h> +#include <linux/irq.h>  #include <asm/prom.h>  #include <asm/rtas.h> @@ -86,424 +87,6 @@ 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) + @@ -1389,27 +972,6 @@ prom_n_size_cells(struct device_node* np)  EXPORT_SYMBOL(prom_n_size_cells);  /** - * Work out the sense (active-low level / active-high edge) - * of each interrupt from the device tree. - */ -void __init prom_get_irq_senses(unsigned char *senses, int off, int max) -{ -	struct device_node *np; -	int i, j; - -	/* default to level-triggered */ -	memset(senses, IRQ_SENSE_LEVEL | IRQ_POLARITY_NEGATIVE, max - off); - -	for (np = allnodes; np != 0; np = np->allnext) { -		for (j = 0; j < np->n_intrs; j++) { -			i = np->intrs[j].line; -			if (i >= off && i < max) -				senses[i-off] = np->intrs[j].sense; -		} -	} -} - -/**   * Construct and return a list of the device_nodes with a given name.   */  struct device_node *find_devices(const char *name) @@ -1808,7 +1370,6 @@ static void of_node_release(struct kref *kref)  			node->deadprops = NULL;  		}  	} -	kfree(node->intrs);  	kfree(node->full_name);  	kfree(node->data);  	kfree(node); @@ -1881,13 +1442,7 @@ void of_detach_node(const struct device_node *np)  #ifdef CONFIG_PPC_PSERIES  /*   * Fix up the uninitialized fields in a new device node: - * name, type, n_addrs, addrs, n_intrs, intrs, and pci-specific fields - * - * A lot of boot-time code is duplicated here, because functions such - * as finish_node_interrupts, interpret_pci_props, etc. cannot use the - * slab allocator. - * - * This should probably be split up into smaller chunks. + * name, type and pci-specific fields   */  static int of_finish_dynamic_node(struct device_node *node) @@ -1928,8 +1483,6 @@ static int prom_reconfig_notifier(struct notifier_block *nb,  	switch (action) {  	case PSERIES_RECONFIG_ADD:  		err = of_finish_dynamic_node(node); -		if (!err) -			finish_node(node, NULL, 0);  		if (err < 0) {  			printk(KERN_ERR "finish_node returned %d\n", err);  			err = NOTIFY_BAD; @@ -1975,8 +1528,7 @@ struct property *of_find_property(struct device_node *np, const char *name,   * 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) +void *get_property(struct device_node *np, const char *name, int *lenp)  {  	struct property *pp = of_find_property(np,name,lenp);  	return pp ? pp->value : NULL; | 
