summaryrefslogtreecommitdiff
path: root/drivers/of
diff options
context:
space:
mode:
authorMinghuan Lian <Minghuan.Lian@freescale.com>2014-09-25 04:53:49 (GMT)
committerMatthew Weigel <Matthew.Weigel@freescale.com>2014-12-11 18:36:46 (GMT)
commitcde55c2fe38a7162e97db271c53988b7c5e5657d (patch)
tree2ddcfc503a915533fb0718859a15f44e3a88b946 /drivers/of
parentbcb5f7f7f44f3df88f9703bc3caed9805175464a (diff)
downloadlinux-fsl-qoriq-cde55c2fe38a7162e97db271c53988b7c5e5657d.tar.xz
of/irq: Add of_irq_parse_and_map_pci() and related function
The patch addes of_irq_parse_and_map_pci() and related function which will be used by Layerscape PCIe driver. of_irq_parse_raw() and of_irq_parse_one() are copied from Linux 3.17 drivers/of/irq.c. The related commit ID include: 7dc2e1134a22dc242175d5321c0c9e97d16eb87b 2361613206e66ce59cc0e08efa8d98ec15b84ed1 624cfca534f9b1ffb1326617b4e973a3d5ecff4a a9ecdc0fdc54aa499604dbd43132988effcac9b4 355e62f5ad12b005c862838156262eb2df2f8dff a7c194b007ec40a130207e9ace9cecf598fc6ac5 0c02c8007ea5554d028f99fd3e29fc201fdeeab3 530210c7814e83564c7ca7bca8192515042c0b63 of_irq_parse_pci() and of_irq_parse_and_map_pci() are copied from Linux 3.17 drivers/of/of_pci_irq.c The related commit ID include: 98d9f30c820d509145757e6ecbc36013aa02f7bc 2361613206e66ce59cc0e08efa8d98ec15b84ed1 16b84e5a505c790538e534ad8dfda9c288691e40 0c02c8007ea5554d028f99fd3e29fc201fdeeab3 530210c7814e83564c7ca7bca8192515042c0b63 irq_create_of_mapping_new() is copied from Linux 3.17 kernel/irq/irqdomain.c irq_create_of_mapping(). The related commit ID include: e6d30ab1e7d1281784672c0fc2ffa385cfb7279e The copyright is owned by the author of the related commit ID You can find out the detailed copyright information based on the commit ID in the upstream repository. Signed-off-by: Minghuan Lian <Minghuan.Lian@freescale.com> Change-Id: I24bcaaea4c6cbe43229bccaceb80e74f57a9ef93 Reviewed-on: http://git.am.freescale.net:8181/19677 Tested-by: Review Code-CDREVIEW <CDREVIEW@freescale.com> Reviewed-by: Yang Li <LeoLi@freescale.com> Reviewed-by: Zhengxiong Jin <Jason.Jin@freescale.com>
Diffstat (limited to 'drivers/of')
-rw-r--r--drivers/of/irq.c276
-rw-r--r--drivers/of/of_pci_irq.c113
2 files changed, 389 insertions, 0 deletions
diff --git a/drivers/of/irq.c b/drivers/of/irq.c
index 1752988..526cde6 100644
--- a/drivers/of/irq.c
+++ b/drivers/of/irq.c
@@ -79,6 +79,211 @@ struct device_node *of_irq_find_parent(struct device_node *child)
}
/**
+ * of_irq_parse_raw - Low level interrupt tree parsing
+ * @parent: the device interrupt parent
+ * @addr: address specifier (start of "reg" property of the device)
+ * in be32 format
+ * @out_irq: structure of_irq updated by this function
+ *
+ * Returns 0 on success and a negative number on error
+ *
+ * This function is a low-level interrupt tree walking function. It
+ * can be used to do a partial walk with synthetized reg and interrupts
+ * properties, for example when resolving PCI interrupts when no device
+ * 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(const __be32 *addr, struct of_phandle_args *out_irq)
+{
+ struct device_node *ipar, *tnode, *old = NULL, *newpar = NULL;
+ __be32 initial_match_array[MAX_PHANDLE_ARGS];
+ const __be32 *match_array = initial_match_array;
+ const __be32 *tmp, *imap, *imask;
+ const __be32 dummy_imask[] = { [0 ... MAX_PHANDLE_ARGS] = ~0 };
+ u32 intsize = 1, addrsize, newintsize = 0, newaddrsize = 0;
+ int imaplen, match, i;
+
+#ifdef DEBUG
+ of_print_phandle_args("of_irq_parse_raw: ", out_irq);
+#endif
+
+ ipar = of_node_get(out_irq->np);
+
+ /* First get the #interrupt-cells property of the current cursor
+ * that tells us how to interpret the passed-in intspec. If there
+ * is none, we are nice and just walk up the tree
+ */
+ do {
+ tmp = of_get_property(ipar, "#interrupt-cells", NULL);
+ if (tmp != NULL) {
+ intsize = be32_to_cpu(*tmp);
+ break;
+ }
+ tnode = ipar;
+ ipar = of_irq_find_parent(ipar);
+ of_node_put(tnode);
+ } while (ipar);
+ if (ipar == NULL) {
+ pr_debug(" -> no parent found !\n");
+ goto fail;
+ }
+
+ pr_debug("of_irq_parse_raw: ipar=%s, size=%d\n",
+ of_node_full_name(ipar), intsize);
+
+ if (out_irq->args_count != intsize)
+ return -EINVAL;
+
+ /* Look for this #address-cells. We have to implement the old linux
+ * trick of looking for the parent here as some device-trees rely on it
+ */
+ old = of_node_get(ipar);
+ do {
+ tmp = of_get_property(old, "#address-cells", NULL);
+ tnode = of_get_parent(old);
+ of_node_put(old);
+ old = tnode;
+ } while (old && tmp == NULL);
+ of_node_put(old);
+ old = NULL;
+ addrsize = (tmp == NULL) ? 2 : be32_to_cpu(*tmp);
+
+ pr_debug(" -> addrsize=%d\n", addrsize);
+
+ /* Range check so that the temporary buffer doesn't overflow */
+ if (WARN_ON(addrsize + intsize > MAX_PHANDLE_ARGS))
+ goto fail;
+
+ /* Precalculate the match array - this simplifies match loop */
+ for (i = 0; i < addrsize; i++)
+ initial_match_array[i] = addr ? addr[i] : 0;
+ 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 */
+ while (ipar != NULL) {
+ /* Now check if cursor is an interrupt-controller and if it is
+ * then we are done
+ */
+ if (of_get_property(ipar, "interrupt-controller", NULL) !=
+ NULL) {
+ pr_debug(" -> got it !\n");
+ return 0;
+ }
+
+ /*
+ * interrupt-map parsing does not work without a reg
+ * property when #address-cells != 0
+ */
+ if (addrsize && !addr) {
+ pr_debug(" -> no reg passed in when needed !\n");
+ goto fail;
+ }
+
+ /* Now look for an interrupt-map */
+ imap = of_get_property(ipar, "interrupt-map", &imaplen);
+ /* No interrupt map, check for an interrupt parent */
+ if (imap == NULL) {
+ pr_debug(" -> no map, getting parent\n");
+ newpar = of_irq_find_parent(ipar);
+ goto skiplevel;
+ }
+ imaplen /= sizeof(u32);
+
+ /* Look for a mask */
+ imask = of_get_property(ipar, "interrupt-map-mask", NULL);
+ if (!imask)
+ imask = dummy_imask;
+
+ /* Parse interrupt-map */
+ match = 0;
+ while (imaplen > (addrsize + intsize + 1) && !match) {
+ /* Compare specifiers */
+ match = 1;
+ for (i = 0; i < (addrsize + intsize); i++, imaplen--)
+ match &= !((match_array[i] ^ *imap++) &
+ imask[i]);
+
+ pr_debug(" -> match=%d (imaplen=%d)\n", match, imaplen);
+
+ /* Get the interrupt parent */
+ if (of_irq_workarounds & OF_IMAP_NO_PHANDLE)
+ newpar = of_node_get(of_irq_dflt_pic);
+ else
+ newpar = of_find_node_by_phandle(
+ be32_to_cpup(imap));
+ imap++;
+ --imaplen;
+
+ /* Check if not found */
+ if (newpar == NULL) {
+ pr_debug(" -> imap parent not found !\n");
+ goto fail;
+ }
+
+ if (!of_device_is_available(newpar))
+ match = 0;
+
+ /* Get #interrupt-cells and #address-cells of new
+ * parent
+ */
+ tmp = of_get_property(newpar, "#interrupt-cells", NULL);
+ if (tmp == NULL) {
+ pr_debug(" -> parent lacks #interrupt-cells!\n");
+ goto fail;
+ }
+ newintsize = be32_to_cpu(*tmp);
+ tmp = of_get_property(newpar, "#address-cells", NULL);
+ newaddrsize = (tmp == NULL) ? 0 : be32_to_cpu(*tmp);
+
+ pr_debug(" -> newintsize=%d, newaddrsize=%d\n",
+ newintsize, newaddrsize);
+
+ /* Check for malformed properties */
+ if (WARN_ON(newaddrsize + newintsize >
+ MAX_PHANDLE_ARGS))
+ goto fail;
+ if (imaplen < (newaddrsize + newintsize))
+ goto fail;
+
+ imap += newaddrsize + newintsize;
+ imaplen -= newaddrsize + newintsize;
+
+ pr_debug(" -> imaplen=%d\n", imaplen);
+ }
+ if (!match)
+ goto fail;
+
+ /*
+ * Successfully parsed an interrrupt-map translation; copy new
+ * interrupt specifier into the out_irq structure
+ */
+ out_irq->np = 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;
+
+skiplevel:
+ /* Iterate again with new parent */
+ pr_debug(" -> new parent: %s\n", of_node_full_name(newpar));
+ of_node_put(ipar);
+ ipar = newpar;
+ newpar = NULL;
+ }
+ fail:
+ of_node_put(ipar);
+ of_node_put(newpar);
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(of_irq_parse_raw);
+
+/**
* of_irq_map_raw - Low level interrupt tree parsing
* @parent: the device interrupt parent
* @intspec: interrupt specifier ("interrupts" property of the device)
@@ -272,6 +477,77 @@ int of_irq_map_raw(struct device_node *parent, const __be32 *intspec,
EXPORT_SYMBOL_GPL(of_irq_map_raw);
/**
+ * of_irq_parse_one - Resolve an interrupt for a device
+ * @device: the device whose interrupt is to be resolved
+ * @index: index of the interrupt to resolve
+ * @out_irq: structure of_irq filled by this function
+ *
+ * This function resolves an interrupt for a node by walking the interrupt tree,
+ * 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)
+{
+ struct device_node *p;
+ const __be32 *intspec, *tmp, *addr;
+ u32 intsize, intlen;
+ int i, res = -EINVAL;
+
+ pr_debug("of_irq_parse_one: dev=%s, index=%d\n",
+ of_node_full_name(device), index);
+
+ /* Get the reg property (if any) */
+ addr = of_get_property(device, "reg", NULL);
+
+ /* Try the new-style interrupts-extended first */
+ res = of_parse_phandle_with_args(device, "interrupts-extended",
+ "#interrupt-cells", index, out_irq);
+ if (!res)
+ return of_irq_parse_raw(addr, out_irq);
+
+ /* Get the interrupts property */
+ intspec = of_get_property(device, "interrupts", &intlen);
+ if (intspec == NULL)
+ return -EINVAL;
+
+ intlen /= sizeof(*intspec);
+
+ pr_debug(" intspec=%d intlen=%d\n", be32_to_cpup(intspec), intlen);
+
+ /* Look for the interrupt parent. */
+ p = of_irq_find_parent(device);
+ if (p == NULL)
+ return -EINVAL;
+
+ /* Get size of interrupt specifier */
+ tmp = of_get_property(p, "#interrupt-cells", NULL);
+ if (tmp == NULL)
+ goto out;
+ intsize = be32_to_cpu(*tmp);
+
+ pr_debug(" intsize=%d intlen=%d\n", intsize, intlen);
+
+ /* Check index */
+ if ((index + 1) * intsize > intlen)
+ goto out;
+
+ /* Copy intspec into irq structure */
+ intspec += index * intsize;
+ 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:
+ of_node_put(p);
+ return res;
+}
+EXPORT_SYMBOL_GPL(of_irq_parse_one);
+
+/**
* of_irq_map_one - Resolve an interrupt for a device
* @device: the device whose interrupt is to be resolved
* @index: index of the interrupt to resolve
diff --git a/drivers/of/of_pci_irq.c b/drivers/of/of_pci_irq.c
index 6770538..3843b46 100644
--- a/drivers/of/of_pci_irq.c
+++ b/drivers/of/of_pci_irq.c
@@ -91,3 +91,116 @@ int of_irq_map_pci(const struct pci_dev *pdev, struct of_irq *out_irq)
return of_irq_map_raw(ppnode, &lspec_be, 1, laddr, out_irq);
}
EXPORT_SYMBOL_GPL(of_irq_map_pci);
+
+/**
+ * of_irq_parse_pci - Resolve the interrupt for a PCI device
+ * @pdev: the device whose interrupt is to be resolved
+ * @out_irq: structure of_irq filled by this function
+ *
+ * This function resolves the PCI interrupt for a given PCI device. If a
+ * device-node exists for a given pci_dev, it will use normal OF tree
+ * walking. If not, it will implement standard swizzling and walk up the
+ * PCI tree until an device-node is found, at which point it will finish
+ * resolving using the OF tree walking.
+ */
+int of_irq_parse_pci(const struct pci_dev *pdev,
+ struct of_phandle_args *out_irq)
+{
+ struct device_node *dn, *ppnode;
+ struct pci_dev *ppdev;
+ __be32 laddr[3];
+ u8 pin;
+ int rc;
+
+ /* Check if we have a device node, if yes, fallback to standard
+ * device tree parsing
+ */
+ dn = pci_device_to_OF_node(pdev);
+ if (dn) {
+ rc = of_irq_parse_one(dn, 0, out_irq);
+ if (!rc)
+ return rc;
+ }
+
+ /* Ok, we don't, time to have fun. Let's start by building up an
+ * interrupt spec. we assume #interrupt-cells is 1, which is standard
+ * for PCI. If you do different, then don't use that routine.
+ */
+ rc = pci_read_config_byte(pdev, PCI_INTERRUPT_PIN, &pin);
+ if (rc != 0)
+ return rc;
+ /* No pin, exit */
+ if (pin == 0)
+ return -ENODEV;
+
+ /* Now we walk up the PCI tree */
+ for (;;) {
+ /* Get the pci_dev of our parent */
+ ppdev = pdev->bus->self;
+
+ /* Ouch, it's a host bridge... */
+ if (ppdev == NULL) {
+ ppnode = pci_bus_to_OF_node(pdev->bus);
+
+ /* No node for host bridge ? give up */
+ if (ppnode == NULL)
+ return -EINVAL;
+ } else {
+ /* We found a P2P bridge, check if it has a node */
+ ppnode = pci_device_to_OF_node(ppdev);
+ }
+
+ /* Ok, we have found a parent with a device-node, hand over to
+ * the OF parsing code.
+ * We build a unit address from the linux device to be used for
+ * resolution. Note that we use the linux bus number which may
+ * not match your firmware bus numbering.
+ * Fortunately, in most cases, interrupt-map-mask doesn't
+ * include the bus number as part of the matching.
+ * You should still be careful about that though if you intend
+ * to rely on this function (you ship a firmware that doesn't
+ * create device nodes for all PCI devices).
+ */
+ if (ppnode)
+ break;
+
+ /* We can only get here if we hit a P2P bridge with no node,
+ * let's do standard swizzling and try again
+ */
+ pin = pci_swizzle_interrupt_pin(pdev, pin);
+ pdev = ppdev;
+ }
+
+ out_irq->np = ppnode;
+ out_irq->args_count = 1;
+ out_irq->args[0] = pin;
+ laddr[0] = cpu_to_be32((pdev->bus->number << 16) | (pdev->devfn << 8));
+ laddr[1] = laddr[2] = cpu_to_be32(0);
+ return of_irq_parse_raw(laddr, out_irq);
+}
+EXPORT_SYMBOL_GPL(of_irq_parse_pci);
+
+/**
+ * of_irq_parse_and_map_pci() - Decode a PCI irq from the device tree and map to a virq
+ * @dev: The pci device needing an irq
+ * @slot: PCI slot number; passed when used as map_irq callback. Unused
+ * @pin: PCI irq pin number; passed when used as map_irq callback. Unused
+ *
+ * @slot and @pin are unused, but included in the function so that this
+ * function can be used directly as the map_irq callback to pci_fixup_irqs().
+ */
+int of_irq_parse_and_map_pci(const struct pci_dev *dev, u8 slot, u8 pin)
+{
+ struct of_phandle_args oirq;
+ int ret;
+
+ ret = of_irq_parse_pci(dev, &oirq);
+ if (ret) {
+ dev_err(&dev->dev,
+ "of_irq_parse_pci() failed with rc=%d\n", ret);
+ return 0; /* Proper return code 0 == NO_IRQ */
+ }
+
+ return irq_create_of_mapping_new(&oirq);
+}
+EXPORT_SYMBOL_GPL(of_irq_parse_and_map_pci);