linux/drivers/of/of_pci_irq.c
<<
>>
Prefs
   1#include <linux/kernel.h>
   2#include <linux/of_pci.h>
   3#include <linux/of_irq.h>
   4#include <linux/export.h>
   5#include <asm/prom.h>
   6
   7/**
   8 * of_irq_map_pci - Resolve the interrupt for a PCI device
   9 * @pdev:       the device whose interrupt is to be resolved
  10 * @out_irq:    structure of_irq filled by this function
  11 *
  12 * This function resolves the PCI interrupt for a given PCI device. If a
  13 * device-node exists for a given pci_dev, it will use normal OF tree
  14 * walking. If not, it will implement standard swizzling and walk up the
  15 * PCI tree until an device-node is found, at which point it will finish
  16 * resolving using the OF tree walking.
  17 */
  18int of_irq_map_pci(const struct pci_dev *pdev, struct of_irq *out_irq)
  19{
  20        struct device_node *dn, *ppnode;
  21        struct pci_dev *ppdev;
  22        u32 lspec;
  23        __be32 lspec_be;
  24        __be32 laddr[3];
  25        u8 pin;
  26        int rc;
  27
  28        /* Check if we have a device node, if yes, fallback to standard
  29         * device tree parsing
  30         */
  31        dn = pci_device_to_OF_node(pdev);
  32        if (dn) {
  33                rc = of_irq_map_one(dn, 0, out_irq);
  34                if (!rc)
  35                        return rc;
  36        }
  37
  38        /* Ok, we don't, time to have fun. Let's start by building up an
  39         * interrupt spec.  we assume #interrupt-cells is 1, which is standard
  40         * for PCI. If you do different, then don't use that routine.
  41         */
  42        rc = pci_read_config_byte(pdev, PCI_INTERRUPT_PIN, &pin);
  43        if (rc != 0)
  44                return rc;
  45        /* No pin, exit */
  46        if (pin == 0)
  47                return -ENODEV;
  48
  49        /* Now we walk up the PCI tree */
  50        lspec = pin;
  51        for (;;) {
  52                /* Get the pci_dev of our parent */
  53                ppdev = pdev->bus->self;
  54
  55                /* Ouch, it's a host bridge... */
  56                if (ppdev == NULL) {
  57                        ppnode = pci_bus_to_OF_node(pdev->bus);
  58
  59                        /* No node for host bridge ? give up */
  60                        if (ppnode == NULL)
  61                                return -EINVAL;
  62                } else {
  63                        /* We found a P2P bridge, check if it has a node */
  64                        ppnode = pci_device_to_OF_node(ppdev);
  65                }
  66
  67                /* Ok, we have found a parent with a device-node, hand over to
  68                 * the OF parsing code.
  69                 * We build a unit address from the linux device to be used for
  70                 * resolution. Note that we use the linux bus number which may
  71                 * not match your firmware bus numbering.
  72                 * Fortunately, in most cases, interrupt-map-mask doesn't
  73                 * include the bus number as part of the matching.
  74                 * You should still be careful about that though if you intend
  75                 * to rely on this function (you ship  a firmware that doesn't
  76                 * create device nodes for all PCI devices).
  77                 */
  78                if (ppnode)
  79                        break;
  80
  81                /* We can only get here if we hit a P2P bridge with no node,
  82                 * let's do standard swizzling and try again
  83                 */
  84                lspec = pci_swizzle_interrupt_pin(pdev, lspec);
  85                pdev = ppdev;
  86        }
  87
  88        lspec_be = cpu_to_be32(lspec);
  89        laddr[0] = cpu_to_be32((pdev->bus->number << 16) | (pdev->devfn << 8));
  90        laddr[1]  = laddr[2] = cpu_to_be32(0);
  91        return of_irq_map_raw(ppnode, &lspec_be, 1, laddr, out_irq);
  92}
  93EXPORT_SYMBOL_GPL(of_irq_map_pci);
  94
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.