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
- Lines: 555
- Date:
Sun Jul 8 13:17:30 2001
- Orig file:
v2.4.6/linux/arch/ppc/kernel/prom.c
- Orig date:
Tue Jul 3 17:08:18 2001
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)