of/irq: Refactor interrupt-map parsing

All the users of of_irq_parse_raw pass in a raw interrupt specifier from
the device tree and expect it to be returned (possibly modified) in an
of_phandle_args structure. However, the primary function of
of_irq_parse_raw() is to check for translations due to the presence of
one or more interrupt-map properties. The actual placing of the data
into an of_phandle_args structure is trivial. If it is refactored to
accept an of_phandle_args structure directly, then it becomes possible
to consume of_phandle_args from other sources. This is important for an
upcoming patch that allows a device to be connected to more than one
interrupt parent. It also simplifies the code a bit.

The biggest complication with this patch is that the old version works
on the interrupt specifiers in __be32 form, but the of_phandle_args
structure is intended to carry it in the cpu-native version. A bit of
churn was required to make this work. In the end it results in tighter
code, so the churn is worth it.

Signed-off-by: Grant Likely <grant.likely@linaro.org>
Acked-by: Tony Lindgren <tony@atomide.com>
Cc: Benjamin Herrenschmidt <benh@kernel.crashing.org>
This commit is contained in:
Grant Likely 2013-09-15 22:32:39 +01:00
parent e6d30ab1e7
commit 2361613206
4 changed files with 67 additions and 59 deletions

View file

@ -322,7 +322,6 @@ static void hpcd_final_uli5288(struct pci_dev *dev)
struct pci_controller *hose = pci_bus_to_host(dev->bus); struct pci_controller *hose = pci_bus_to_host(dev->bus);
struct device_node *hosenode = hose ? hose->dn : NULL; struct device_node *hosenode = hose ? hose->dn : NULL;
struct of_phandle_args oirq; struct of_phandle_args oirq;
int pin = 2;
u32 laddr[3]; u32 laddr[3];
if (!machine_is(mpc86xx_hpcd)) if (!machine_is(mpc86xx_hpcd))
@ -331,9 +330,12 @@ static void hpcd_final_uli5288(struct pci_dev *dev)
if (!hosenode) if (!hosenode)
return; return;
oirq.np = hosenode;
oirq.args[0] = 2;
oirq.args_count = 1;
laddr[0] = (hose->first_busno << 16) | (PCI_DEVFN(31, 0) << 8); laddr[0] = (hose->first_busno << 16) | (PCI_DEVFN(31, 0) << 8);
laddr[1] = laddr[2] = 0; laddr[1] = laddr[2] = 0;
of_irq_parse_raw(hosenode, &pin, 1, laddr, &oirq); of_irq_parse_raw(laddr, &oirq);
dev->irq = irq_create_of_mapping(&oirq); dev->irq = irq_create_of_mapping(&oirq);
} }

View file

@ -80,31 +80,32 @@ struct device_node *of_irq_find_parent(struct device_node *child)
/** /**
* of_irq_parse_raw - Low level interrupt tree parsing * of_irq_parse_raw - Low level interrupt tree parsing
* @parent: the device interrupt parent * @parent: the device interrupt parent
* @intspec: interrupt specifier ("interrupts" property of the device) * @addr: address specifier (start of "reg" property of the device) in be32 format
* @ointsize: size of the passed in interrupt specifier * @out_irq: structure of_irq updated by this function
* @addr: address specifier (start of "reg" property of the device)
* @out_irq: structure of_irq filled by this function
* *
* Returns 0 on success and a negative number on error * Returns 0 on success and a negative number on error
* *
* This function is a low-level interrupt tree walking function. It * This function is a low-level interrupt tree walking function. It
* can be used to do a partial walk with synthetized reg and interrupts * can be used to do a partial walk with synthetized reg and interrupts
* properties, for example when resolving PCI interrupts when no device * properties, for example when resolving PCI interrupts when no device
* node exist for the parent. * node exist for the parent. It takes an interrupt specifier structure as
* input, walks the tree looking for any interrupt-map properties, translates
* the specifier for each map, and then returns the translated map.
*/ */
int of_irq_parse_raw(struct device_node *parent, const __be32 *intspec, int of_irq_parse_raw(const __be32 *addr, struct of_phandle_args *out_irq)
u32 ointsize, const __be32 *addr, struct of_phandle_args *out_irq)
{ {
struct device_node *ipar, *tnode, *old = NULL, *newpar = NULL; struct device_node *ipar, *tnode, *old = NULL, *newpar = NULL;
const __be32 *tmp, *imap, *imask; __be32 initial_match_array[8];
const __be32 *match_array = initial_match_array;
const __be32 *tmp, *imap, *imask, dummy_imask[] = { ~0, ~0, ~0, ~0, ~0 };
u32 intsize = 1, addrsize, newintsize = 0, newaddrsize = 0; u32 intsize = 1, addrsize, newintsize = 0, newaddrsize = 0;
int imaplen, match, i; int imaplen, match, i;
pr_debug("of_irq_parse_raw: par=%s,intspec=[0x%08x 0x%08x...],ointsize=%d\n", pr_debug("of_irq_parse_raw: par=%s,intspec=[0x%08x 0x%08x...],ointsize=%d\n",
of_node_full_name(parent), be32_to_cpup(intspec), of_node_full_name(out_irq->np), out_irq->args[0], out_irq->args[1],
be32_to_cpup(intspec + 1), ointsize); out_irq->args_count);
ipar = of_node_get(parent); ipar = of_node_get(out_irq->np);
/* First get the #interrupt-cells property of the current cursor /* First get the #interrupt-cells property of the current cursor
* that tells us how to interpret the passed-in intspec. If there * that tells us how to interpret the passed-in intspec. If there
@ -127,7 +128,7 @@ int of_irq_parse_raw(struct device_node *parent, const __be32 *intspec,
pr_debug("of_irq_parse_raw: ipar=%s, size=%d\n", of_node_full_name(ipar), intsize); pr_debug("of_irq_parse_raw: ipar=%s, size=%d\n", of_node_full_name(ipar), intsize);
if (ointsize != intsize) if (out_irq->args_count != intsize)
return -EINVAL; return -EINVAL;
/* Look for this #address-cells. We have to implement the old linux /* Look for this #address-cells. We have to implement the old linux
@ -146,6 +147,21 @@ int of_irq_parse_raw(struct device_node *parent, const __be32 *intspec,
pr_debug(" -> addrsize=%d\n", addrsize); pr_debug(" -> addrsize=%d\n", addrsize);
/* If we were passed no "reg" property and we attempt to parse
* an interrupt-map, then #address-cells must be 0.
* Fail if it's not.
*/
if (addr == NULL && addrsize != 0) {
pr_debug(" -> no reg passed in when needed !\n");
return -EINVAL;
}
/* Precalculate the match array - this simplifies match loop */
for (i = 0; i < addrsize; i++)
initial_match_array[i] = addr[i];
for (i = 0; i < intsize; i++)
initial_match_array[addrsize + i] = cpu_to_be32(out_irq->args[i]);
/* Now start the actual "proper" walk of the interrupt tree */ /* Now start the actual "proper" walk of the interrupt tree */
while (ipar != NULL) { while (ipar != NULL) {
/* Now check if cursor is an interrupt-controller and if it is /* Now check if cursor is an interrupt-controller and if it is
@ -154,11 +170,6 @@ int of_irq_parse_raw(struct device_node *parent, const __be32 *intspec,
if (of_get_property(ipar, "interrupt-controller", NULL) != if (of_get_property(ipar, "interrupt-controller", NULL) !=
NULL) { NULL) {
pr_debug(" -> got it !\n"); pr_debug(" -> got it !\n");
for (i = 0; i < intsize; i++)
out_irq->args[i] =
of_read_number(intspec +i, 1);
out_irq->args_count = intsize;
out_irq->np = ipar;
of_node_put(old); of_node_put(old);
return 0; return 0;
} }
@ -175,34 +186,16 @@ int of_irq_parse_raw(struct device_node *parent, const __be32 *intspec,
/* Look for a mask */ /* Look for a mask */
imask = of_get_property(ipar, "interrupt-map-mask", NULL); imask = of_get_property(ipar, "interrupt-map-mask", NULL);
if (!imask)
/* If we were passed no "reg" property and we attempt to parse imask = dummy_imask;
* an interrupt-map, then #address-cells must be 0.
* Fail if it's not.
*/
if (addr == NULL && addrsize != 0) {
pr_debug(" -> no reg passed in when needed !\n");
goto fail;
}
/* Parse interrupt-map */ /* Parse interrupt-map */
match = 0; match = 0;
while (imaplen > (addrsize + intsize + 1) && !match) { while (imaplen > (addrsize + intsize + 1) && !match) {
/* Compare specifiers */ /* Compare specifiers */
match = 1; match = 1;
for (i = 0; i < addrsize && match; ++i) { for (i = 0; i < (addrsize + intsize); i++, imaplen--)
__be32 mask = imask ? imask[i] match = !((match_array[i] ^ *imap++) & imask[i]);
: cpu_to_be32(0xffffffffu);
match = ((addr[i] ^ imap[i]) & mask) == 0;
}
for (; i < (addrsize + intsize) && match; ++i) {
__be32 mask = imask ? imask[i]
: cpu_to_be32(0xffffffffu);
match =
((intspec[i-addrsize] ^ imap[i]) & mask) == 0;
}
imap += addrsize + intsize;
imaplen -= addrsize + intsize;
pr_debug(" -> match=%d (imaplen=%d)\n", match, imaplen); pr_debug(" -> match=%d (imaplen=%d)\n", match, imaplen);
@ -247,12 +240,18 @@ int of_irq_parse_raw(struct device_node *parent, const __be32 *intspec,
if (!match) if (!match)
goto fail; goto fail;
of_node_put(old); /*
old = of_node_get(newpar); * Successfully parsed an interrrupt-map translation; copy new
* interrupt specifier into the out_irq structure
*/
of_node_put(out_irq->np);
out_irq->np = of_node_get(newpar);
match_array = imap - newaddrsize - newintsize;
for (i = 0; i < newintsize; i++)
out_irq->args[i] = be32_to_cpup(imap - newintsize + i);
out_irq->args_count = intsize = newintsize;
addrsize = newaddrsize; addrsize = newaddrsize;
intsize = newintsize;
intspec = imap - intsize;
addr = intspec - addrsize;
skiplevel: skiplevel:
/* Iterate again with new parent */ /* Iterate again with new parent */
@ -263,7 +262,7 @@ int of_irq_parse_raw(struct device_node *parent, const __be32 *intspec,
} }
fail: fail:
of_node_put(ipar); of_node_put(ipar);
of_node_put(old); of_node_put(out_irq->np);
of_node_put(newpar); of_node_put(newpar);
return -EINVAL; return -EINVAL;
@ -276,15 +275,16 @@ EXPORT_SYMBOL_GPL(of_irq_parse_raw);
* @index: index of the interrupt to resolve * @index: index of the interrupt to resolve
* @out_irq: structure of_irq filled by this function * @out_irq: structure of_irq filled by this function
* *
* This function resolves an interrupt, walking the tree, for a given * This function resolves an interrupt for a node by walking the interrupt tree,
* device-tree node. It's the high level pendant to of_irq_parse_raw(). * finding which interrupt controller node it is attached to, and returning the
* interrupt specifier that can be used to retrieve a Linux IRQ number.
*/ */
int of_irq_parse_one(struct device_node *device, int index, struct of_phandle_args *out_irq) int of_irq_parse_one(struct device_node *device, int index, struct of_phandle_args *out_irq)
{ {
struct device_node *p; struct device_node *p;
const __be32 *intspec, *tmp, *addr; const __be32 *intspec, *tmp, *addr;
u32 intsize, intlen; u32 intsize, intlen;
int res = -EINVAL; int i, res = -EINVAL;
pr_debug("of_irq_parse_one: dev=%s, index=%d\n", of_node_full_name(device), index); pr_debug("of_irq_parse_one: dev=%s, index=%d\n", of_node_full_name(device), index);
@ -320,9 +320,15 @@ int of_irq_parse_one(struct device_node *device, int index, struct of_phandle_ar
if ((index + 1) * intsize > intlen) if ((index + 1) * intsize > intlen)
goto out; goto out;
/* Get new specifier and map it */ /* Copy intspec into irq structure */
res = of_irq_parse_raw(p, intspec + index * intsize, intsize, intspec += index * intsize;
addr, out_irq); out_irq->np = p;
out_irq->args_count = intsize;
for (i = 0; i < intsize; i++)
out_irq->args[i] = be32_to_cpup(intspec++);
/* Check if there are any interrupt-map translations to process */
res = of_irq_parse_raw(addr, out_irq);
out: out:
of_node_put(p); of_node_put(p);
return res; return res;

View file

@ -85,9 +85,12 @@ int of_irq_parse_pci(const struct pci_dev *pdev, struct of_phandle_args *out_irq
pdev = ppdev; pdev = ppdev;
} }
out_irq->np = ppnode;
out_irq->args_count = 1;
out_irq->args[0] = lspec;
lspec_be = cpu_to_be32(lspec); lspec_be = cpu_to_be32(lspec);
laddr[0] = cpu_to_be32((pdev->bus->number << 16) | (pdev->devfn << 8)); laddr[0] = cpu_to_be32((pdev->bus->number << 16) | (pdev->devfn << 8));
laddr[1] = laddr[2] = cpu_to_be32(0); laddr[1] = laddr[2] = cpu_to_be32(0);
return of_irq_parse_raw(ppnode, &lspec_be, 1, laddr, out_irq); return of_irq_parse_raw(laddr, out_irq);
} }
EXPORT_SYMBOL_GPL(of_irq_parse_pci); EXPORT_SYMBOL_GPL(of_irq_parse_pci);

View file

@ -31,10 +31,7 @@ static inline int of_irq_parse_oldworld(struct device_node *device, int index,
} }
#endif /* CONFIG_PPC32 && CONFIG_PPC_PMAC */ #endif /* CONFIG_PPC32 && CONFIG_PPC_PMAC */
extern int of_irq_parse_raw(const __be32 *addr, struct of_phandle_args *out_irq);
extern int of_irq_parse_raw(struct device_node *parent, const __be32 *intspec,
u32 ointsize, const __be32 *addr,
struct of_phandle_args *out_irq);
extern int of_irq_parse_one(struct device_node *device, int index, extern int of_irq_parse_one(struct device_node *device, int index,
struct of_phandle_args *out_irq); struct of_phandle_args *out_irq);
extern unsigned int irq_create_of_mapping(struct of_phandle_args *irq_data); extern unsigned int irq_create_of_mapping(struct of_phandle_args *irq_data);