patch-2.4.7 linux/arch/ppc/kernel/prom.c

Next file: linux/arch/ppc/kernel/ptrace.c
Previous file: linux/arch/ppc/kernel/process.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.4.6/linux/arch/ppc/kernel/prom.c linux/arch/ppc/kernel/prom.c
@@ -116,8 +116,11 @@
 unsigned int rtas_size;
 unsigned int old_rtas;
 
-/* Set for a newworld machine */
+/* 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;
 
 static struct device_node *allnodes;
@@ -1153,7 +1156,19 @@
 		*prev_propp = PTRUNRELOC(pp);
 		prev_propp = &pp->next;
 	}
-	*prev_propp = 0;
+	if (np->node != NULL) {
+		/* Add a "linux,phandle" property" */
+		pp = (struct property *) mem_start;
+		*prev_propp = PTRUNRELOC(pp);
+		prev_propp = &pp->next;
+		namep = (char *) (pp + 1);
+		pp->name = PTRUNRELOC(namep);
+		strcpy(namep, RELOC("linux,phandle"));
+		mem_start = ALIGN((unsigned long)namep + strlen(namep) + 1);
+		pp->value = (unsigned char *) PTRUNRELOC(&np->node);
+		pp->length = sizeof(np->node);
+	}
+	*prev_propp = NULL;
 
 	/* get the node's full name */
 	l = (int) call_prom(RELOC("package-to-path"), 3, 1, node,
@@ -1186,19 +1201,46 @@
 finish_device_tree(void)
 {
 	unsigned long mem = (unsigned long) klimit;
+	struct device_node *np;
 
-	/* All newworld machines now use the interrupt tree */
-	struct device_node *np = allnodes;
-
-	while(np && (_machine == _MACH_Pmac)) {
+	/* 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)) {
-			pmac_newworld = 1;
+			use_of_interrupt_tree = 1;
 			break;
 		}
-		np = np->allnext;
 	}
-	if ((_machine == _MACH_chrp) || (boot_infos == 0 && pmac_newworld))
-		use_of_interrupt_tree = 1;
+	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;
+
+		for (np = allnodes; np != NULL; np = np->allnext) {
+			if ((name = get_property(np, "name", NULL)) == NULL
+			    || strcmp(name, "interrupt-controller") != 0)
+				continue;
+			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;
@@ -1240,9 +1282,8 @@
 	if (ifunc != NULL) {
 		mem_start = ifunc(np, mem_start, naddrc, nsizec);
 	}
-	if (use_of_interrupt_tree) {
+	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);
@@ -1298,141 +1339,210 @@
 	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.
+/*
+ * Find the interrupt parent of a node.
+ */
+static struct device_node *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.
  */
-__init
-static unsigned long
-finish_node_interrupts(struct device_node *np, unsigned long mem_start)
+static int
+prom_n_intr_cells(struct device_node *np)
 {
-	/* Finish this node */
-	unsigned int *isizep, *asizep, *interrupts, *map, *map_mask, *reg;
-	phandle *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;
+	struct device_node *p;
+	unsigned int *icp;
 
-	reg = (unsigned int *)get_property(np, "reg", &l);
-	regpsize = l>>2;
+	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;
+}
 
-	/* 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;
-		int cvt_irq;
-
-		/* XXX on chrp, offset interrupt numbers for the
-		   8259 by 0, those for the openpic by 16 */
-		cvt_irq = _machine == _MACH_chrp
-			&& get_property(node, "interrupt-parent", NULL) == 0;
-	    	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 = *interrupts++;
-		    if (cvt_irq)
-			np->intrs[i].line = openpic_to_irq(np->intrs[i].line);
-		    np->intrs[i].sense = 1;
-		    if (isize > 1)
-		        np->intrs[i].sense = *interrupts++;
-		    for (j=2; j<isize; j++)
-		    	interrupts++;
+/*
+ * Map an interrupt from a device up to the platform interrupt
+ * descriptor.
+ */
+static int
+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, 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
+finish_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;
-	    }
-	    /* 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;
-	        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--;
-	            }
-	            parent = *((phandle *)(map));
-	            map+=1; map_size-=1;
-	            parent_node = find_phandle(parent);
-	            temp_isize = isize;
-	            if (parent_node) {
-			isizep = (unsigned int *)get_property(parent_node, "#interrupt-cells", &l);
-	    		if (isizep)
-	    		    temp_isize = *isizep;
-	            }
-	            if (!found) {
-	            	map += temp_isize;
-	            	map_size-=temp_isize;
-	            }
-	        }
-	        if (found) {
-	            node = parent_node;
-	            reg = NULL;
-	            regpsize = 0;
-	            interrupts = (unsigned int *)map;
-	            ipsize = temp_isize*1;
-		    continue;
-	        }
-	    }
-	    /* We look for an explicit interrupt-parent.
-	     */
-	    parent = (phandle *)get_property(node, "interrupt-parent", &l);
-	    if (parent && (l == sizeof(phandle)) &&
-	    	(parent_node = find_phandle(*parent))) {
-	    	node = parent_node;
-	    	continue;
-	    }
-	    /* Default, get real parent */
-	    node = node->parent;
-	} while(node);
+	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;
+		/*
+		 * On a CHRP we have an 8259 which is subordinate to
+		 * the openpic in the interrupt tree, but we want the
+		 * openpic's interrupt numbers offsetted, not the 8259's.
+		 * So we apply the offset if the controller is at the
+		 * root of the interrupt tree, i.e. has no interrupt-parent.
+		 * This doesn't cope with the general case of multiple
+		 * cascaded interrupt controllers, but then neither will
+		 * irq.c at the moment either.  -- paulus
+		 */
+		if (num_interrupt_controllers > 1 && ic != NULL
+		    && get_property(ic, "interrupt-parent", NULL) == NULL)
+			offset = 16;
+		np->intrs[i].line = irq[0] + offset;
+		if (n > 1)
+			np->intrs[i].sense = irq[1];
+		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");
+		}
+		ints += intrcells;
+	}
 
 	return mem_start;
 }
 
-
 /*
  * When BootX makes a copy of the device tree from the MacOS
  * Name Registry, it is in the format we use but all of the pointers
@@ -1475,7 +1585,7 @@
 		ip = (int *) get_property(np, "#address-cells", 0);
 		if (ip != NULL)
 			return *ip;
-	} while(np->parent);
+	} while (np->parent);
 	/* No #address-cells property for the root node, default to 1 */
 	return 1;
 }
@@ -1490,7 +1600,7 @@
 		ip = (int *) get_property(np, "#size-cells", 0);
 		if (ip != NULL)
 			return *ip;
-	} while(np->parent);
+	} while (np->parent);
 	/* No #size-cells property for the root node, default to 1 */
 	return 1;
 }
@@ -1502,8 +1612,7 @@
 {
 	struct address_range *adr;
 	struct pci_reg_property *pci_addrs;
-	int i, l, *ip, ml;
-	struct pci_intr_map *imp;
+	int i, l, *ip;
 
 	pci_addrs = (struct pci_reg_property *)
 		get_property(np, "assigned-addresses", &l);
@@ -1525,44 +1634,6 @@
 	if (use_of_interrupt_tree)
 		return mem_start;
 
-	/*
-	 * If the pci host bridge has an interrupt-map property,
-	 * look for our node in it.
-	 */
-	if (np->parent != 0 && pci_addrs != 0
-	    && (imp = (struct pci_intr_map *)
-		get_property(np->parent, "interrupt-map", &ml)) != 0
-	    && (ip = (int *) get_property(np, "interrupts", &l)) != 0) {
-		unsigned int devfn = pci_addrs[0].addr.a_hi & 0xff00;
-		unsigned int cell_size;
-		struct device_node* np2;
-		/* This is hackish, but is only used for BootX booting */
-		cell_size = sizeof(struct pci_intr_map);
-		np2 = np->parent;
-		while(np2) {
-			if (device_is_compatible(np2, "uni-north")) {
-				cell_size += 4;
-				break;
-			}
-			np2 = np2->parent;
-		}
-		np->n_intrs = 0;
-		np->intrs = (struct interrupt_info *) mem_start;
-		for (i = 0; (ml -= cell_size) >= 0; ++i) {
-			if (imp->addr.a_hi == devfn) {
-				np->intrs[np->n_intrs].line = imp->intr;
-				np->intrs[np->n_intrs].sense = 1; /* FIXME */
-				++np->n_intrs;
-			}
-			imp = (struct pci_intr_map *)(((unsigned int)imp)
-				+ cell_size);
-		}
-		if (np->n_intrs == 0)
-			np->intrs = 0;
-		mem_start += np->n_intrs * sizeof(struct interrupt_info);
-		return mem_start;
-	}
-
 	ip = (int *) get_property(np, "AAPL,interrupts", &l);
 	if (ip == 0 && np->parent)
 		ip = (int *) get_property(np->parent, "AAPL,interrupts", &l);
@@ -1677,26 +1748,10 @@
 		ip = (int *) get_property(np, "AAPL,interrupts", &l);
 	if (ip != 0) {
 		np->intrs = (struct interrupt_info *) mem_start;
-		if (_machine == _MACH_Pmac) {
-			/* for the iMac */
-			np->n_intrs = l / sizeof(int);
-			/* Hack for BootX on Core99 */
-			if (keylargo)
-				np->n_intrs = np->n_intrs/2;
-			for (i = 0; i < np->n_intrs; ++i) {
-				np->intrs[i].line = *ip++;
-				if (keylargo)
-					np->intrs[i].sense = *ip++;
-				else
-					np->intrs[i].sense = 1;
-			}
-		} else {
-			/* CHRP machines */
-			np->n_intrs = l / (2 * sizeof(int));
-			for (i = 0; i < np->n_intrs; ++i) {
-				np->intrs[i].line = openpic_to_irq(*ip++);
-				np->intrs[i].sense = *ip++;
-			}
+		np->n_intrs = l / sizeof(int);
+		for (i = 0; i < np->n_intrs; ++i) {
+			np->intrs[i].line = *ip++;
+			np->intrs[i].sense = 1;
 		}
 		mem_start += np->n_intrs * sizeof(struct interrupt_info);
 	}
@@ -1978,13 +2033,12 @@
 {
 	struct property *pp;
 
-	for (pp = np->properties; pp != 0; pp = pp->next) {
+	for (pp = np->properties; pp != 0; pp = pp->next)
 		if (pp->name != NULL && strcmp(pp->name, name) == 0) {
 			if (lenp != 0)
 				*lenp = pp->length;
 			return pp->value;
 		}
-	}
 	return 0;
 }
 

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen (who was at: slshen@lbl.gov)