Commit c5760066 authored by Anton Blanchard's avatar Anton Blanchard

ppc64: merge Paul's interrupt-map parsing code

parent a7872552
...@@ -1538,137 +1538,185 @@ finish_node(struct device_node *np, unsigned long mem_start, ...@@ -1538,137 +1538,185 @@ finish_node(struct device_node *np, unsigned long mem_start,
return mem_start; 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 * Find the interrupt parent of a node.
* for CHRP. The current version was only tested on Apple "Core99" machines
* and may not handle cascaded controllers correctly.
*/ */
__init static struct device_node * __init
static unsigned long intr_parent(struct device_node *p)
finish_node_interrupts(struct device_node *np, unsigned long mem_start)
{ {
/* Finish this node */ phandle *parp;
unsigned int *isizep, *asizep, *interrupts, *map, *map_mask, *reg;
phandle *parent, map_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); parp = (phandle *) get_property(p, "interrupt-parent", NULL);
if (interrupts == NULL) if (parp == NULL)
return mem_start; return p->parent;
ipsize = l>>2; return find_phandle(*parp);
}
reg = (unsigned int *)get_property(np, "reg", &l); /*
regpsize = l>>2; * Find out the size of each entry of the interrupts property
* for a node.
*/
static int __init
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;
}
/* We assume default interrupt cell size is 1 (bugus ?) */ /*
isize = 1; * Map an interrupt from a device up to the platform interrupt
node = np; * descriptor.
*/
do { static int __init
/* We adjust the cell size if the current parent contains an #interrupt-cells map_interrupt(unsigned int **irq, struct device_node **ictrler,
* property */ struct device_node *np, unsigned int *ints, int nintrc)
isizep = (unsigned int *)get_property(node, "#interrupt-cells", &l); {
if (isizep) struct device_node *p, *ipar;
isize = *isizep; unsigned int *imap, *imask, *ip;
int i, imaplen, match;
/* We don't do interrupt cascade (ISA) for now, we stop on the first int newintrc, newaddrc;
* controller found unsigned int *reg;
*/ int naddrc;
if (get_property(node, "interrupt-controller", &l)) {
int i,j; reg = (unsigned int *) get_property(np, "reg", NULL);
naddrc = prom_n_addr_cells(np);
np->intrs = (struct interrupt_info *) mem_start; p = intr_parent(np);
np->n_intrs = ipsize / isize; while (p != NULL) {
mem_start += np->n_intrs * sizeof(struct interrupt_info); if (get_property(p, "interrupt-controller", NULL) != NULL)
for (i = 0; i < np->n_intrs; ++i) { /* this node is an interrupt controller, stop here */
np->intrs[i].line = openpic_to_irq(virt_irq_create_mapping(*interrupts++)); break;
np->intrs[i].sense = 1; imap = (unsigned int *)
if (isize > 1) get_property(p, "interrupt-map", &imaplen);
np->intrs[i].sense = *interrupts++; if (imap == NULL) {
for (j=2; j<isize; j++) p = intr_parent(p);
interrupts++; 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) {
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;
}
#ifdef DEBUG_IRQ
if (p == NULL)
printk("hmmm, int tree for %s doesn't have ctrler\n",
np->full_name);
#endif
*irq = ints;
*ictrler = p;
return nintrc;
}
/*
* New version of finish_node_interrupts.
*/
static unsigned long __init
finish_node_interrupts(struct device_node *np, unsigned long mem_start)
{
unsigned int *ints;
int intlen, intrcells;
int i, j, n;
unsigned int *irq;
struct device_node *ic;
ints = (unsigned int *) get_property(np, "interrupts", &intlen);
if (ints == NULL)
return mem_start; return mem_start;
} intrcells = prom_n_intr_cells(np);
/* We lookup for an interrupt-map. This code can only handle one interrupt intlen /= intrcells * sizeof(unsigned int);
* per device in the map. We also don't handle #address-cells in the parent np->n_intrs = intlen;
* I skip the pci node itself here, may not be necessary but I don't like it's np->intrs = (struct interrupt_info *) mem_start;
* reg property. mem_start += intlen * sizeof(struct interrupt_info);
*/
if (np != node) for (i = 0; i < intlen; ++i) {
map = (unsigned int *)get_property(node, "interrupt-map", &l); np->intrs[i].line = 0;
else np->intrs[i].sense = 1;
map = NULL; n = map_interrupt(&irq, &ic, np, ints, intrcells);
if (map && l) { if (n <= 0)
int i, found, temp_isize, temp_asize; continue;
map_size = l>>2; np->intrs[i].line = openpic_to_irq(virt_irq_create_mapping(irq[0]));
map_mask = (unsigned int *)get_property(node, "interrupt-map-mask", &l); if (n > 1)
asizep = (unsigned int *)get_property(node, "#address-cells", &l); np->intrs[i].sense = irq[1];
if (asizep && l == sizeof(unsigned int)) if (n > 2) {
asize = *asizep; printk("hmmm, got %d intr cells for %s:", n,
else np->full_name);
asize = 0; for (j = 0; j < n; ++j)
found = 0; printk(" %d", irq[j]);
while (map_size>0 && !found) { printk("\n");
found = 1; }
for (i=0; i<asize; i++) { ints += intrcells;
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--;
}
map_parent = *((phandle *)map);
map+=1; map_size-=1;
parent_node = find_phandle(map_parent);
temp_isize = isize;
temp_asize = 0;
if (parent_node) {
isizep = (unsigned int *)get_property(parent_node, "#interrupt-cells", &l);
if (isizep)
temp_isize = *isizep;
asizep = (unsigned int *)get_property(parent_node, "#address-cells", &l);
if (asizep && l == sizeof(unsigned int))
temp_asize = *asizep;
}
if (!found) {
map += temp_isize + temp_asize;
map_size -= temp_isize + temp_asize;
}
}
if (found) {
/* Mapped to a new parent. Use the reg and interrupts specified in
* the map as the new search parameters. Then search from the parent.
*/
node = parent_node;
reg = map;
regpsize = temp_asize;
interrupts = map + temp_asize;
ipsize = temp_isize;
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);
return mem_start; return mem_start;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment