linux/drivers/of/irq.c
<<
>>
Prefs
   1/*
   2 *  Derived from arch/i386/kernel/irq.c
   3 *    Copyright (C) 1992 Linus Torvalds
   4 *  Adapted from arch/i386 by Gary Thomas
   5 *    Copyright (C) 1995-1996 Gary Thomas (gdt@linuxppc.org)
   6 *  Updated and modified by Cort Dougan <cort@fsmlabs.com>
   7 *    Copyright (C) 1996-2001 Cort Dougan
   8 *  Adapted for Power Macintosh by Paul Mackerras
   9 *    Copyright (C) 1996 Paul Mackerras (paulus@cs.anu.edu.au)
  10 *
  11 * This program is free software; you can redistribute it and/or
  12 * modify it under the terms of the GNU General Public License
  13 * as published by the Free Software Foundation; either version
  14 * 2 of the License, or (at your option) any later version.
  15 *
  16 * This file contains the code used to make IRQ descriptions in the
  17 * device tree to actual irq numbers on an interrupt controller
  18 * driver.
  19 */
  20
  21#include <linux/errno.h>
  22#include <linux/module.h>
  23#include <linux/of.h>
  24#include <linux/of_irq.h>
  25#include <linux/string.h>
  26
  27/* For archs that don't support NO_IRQ (such as x86), provide a dummy value */
  28#ifndef NO_IRQ
  29#define NO_IRQ 0
  30#endif
  31
  32/**
  33 * irq_of_parse_and_map - Parse and map an interrupt into linux virq space
  34 * @device: Device node of the device whose interrupt is to be mapped
  35 * @index: Index of the interrupt to map
  36 *
  37 * This function is a wrapper that chains of_irq_map_one() and
  38 * irq_create_of_mapping() to make things easier to callers
  39 */
  40unsigned int irq_of_parse_and_map(struct device_node *dev, int index)
  41{
  42        struct of_irq oirq;
  43
  44        if (of_irq_map_one(dev, index, &oirq))
  45                return NO_IRQ;
  46
  47        return irq_create_of_mapping(oirq.controller, oirq.specifier,
  48                                     oirq.size);
  49}
  50EXPORT_SYMBOL_GPL(irq_of_parse_and_map);
  51
  52/**
  53 * of_irq_find_parent - Given a device node, find its interrupt parent node
  54 * @child: pointer to device node
  55 *
  56 * Returns a pointer to the interrupt parent node, or NULL if the interrupt
  57 * parent could not be determined.
  58 */
  59struct device_node *of_irq_find_parent(struct device_node *child)
  60{
  61        struct device_node *p;
  62        const __be32 *parp;
  63
  64        if (!of_node_get(child))
  65                return NULL;
  66
  67        do {
  68                parp = of_get_property(child, "interrupt-parent", NULL);
  69                if (parp == NULL)
  70                        p = of_get_parent(child);
  71                else {
  72                        if (of_irq_workarounds & OF_IMAP_NO_PHANDLE)
  73                                p = of_node_get(of_irq_dflt_pic);
  74                        else
  75                                p = of_find_node_by_phandle(be32_to_cpup(parp));
  76                }
  77                of_node_put(child);
  78                child = p;
  79        } while (p && of_get_property(p, "#interrupt-cells", NULL) == NULL);
  80
  81        return p;
  82}
  83
  84/**
  85 * of_irq_map_raw - Low level interrupt tree parsing
  86 * @parent:     the device interrupt parent
  87 * @intspec:    interrupt specifier ("interrupts" property of the device)
  88 * @ointsize:   size of the passed in interrupt specifier
  89 * @addr:       address specifier (start of "reg" property of the device)
  90 * @out_irq:    structure of_irq filled by this function
  91 *
  92 * Returns 0 on success and a negative number on error
  93 *
  94 * This function is a low-level interrupt tree walking function. It
  95 * can be used to do a partial walk with synthetized reg and interrupts
  96 * properties, for example when resolving PCI interrupts when no device
  97 * node exist for the parent.
  98 */
  99int of_irq_map_raw(struct device_node *parent, const __be32 *intspec,
 100                   u32 ointsize, const __be32 *addr, struct of_irq *out_irq)
 101{
 102        struct device_node *ipar, *tnode, *old = NULL, *newpar = NULL;
 103        const __be32 *tmp, *imap, *imask;
 104        u32 intsize = 1, addrsize, newintsize = 0, newaddrsize = 0;
 105        int imaplen, match, i;
 106
 107        pr_debug("of_irq_map_raw: par=%s,intspec=[0x%08x 0x%08x...],ointsize=%d\n",
 108                 parent->full_name, be32_to_cpup(intspec),
 109                 be32_to_cpup(intspec + 1), ointsize);
 110
 111        ipar = of_node_get(parent);
 112
 113        /* First get the #interrupt-cells property of the current cursor
 114         * that tells us how to interpret the passed-in intspec. If there
 115         * is none, we are nice and just walk up the tree
 116         */
 117        do {
 118                tmp = of_get_property(ipar, "#interrupt-cells", NULL);
 119                if (tmp != NULL) {
 120                        intsize = be32_to_cpu(*tmp);
 121                        break;
 122                }
 123                tnode = ipar;
 124                ipar = of_irq_find_parent(ipar);
 125                of_node_put(tnode);
 126        } while (ipar);
 127        if (ipar == NULL) {
 128                pr_debug(" -> no parent found !\n");
 129                goto fail;
 130        }
 131
 132        pr_debug("of_irq_map_raw: ipar=%s, size=%d\n", ipar->full_name, intsize);
 133
 134        if (ointsize != intsize)
 135                return -EINVAL;
 136
 137        /* Look for this #address-cells. We have to implement the old linux
 138         * trick of looking for the parent here as some device-trees rely on it
 139         */
 140        old = of_node_get(ipar);
 141        do {
 142                tmp = of_get_property(old, "#address-cells", NULL);
 143                tnode = of_get_parent(old);
 144                of_node_put(old);
 145                old = tnode;
 146        } while (old && tmp == NULL);
 147        of_node_put(old);
 148        old = NULL;
 149        addrsize = (tmp == NULL) ? 2 : be32_to_cpu(*tmp);
 150
 151        pr_debug(" -> addrsize=%d\n", addrsize);
 152
 153        /* Now start the actual "proper" walk of the interrupt tree */
 154        while (ipar != NULL) {
 155                /* Now check if cursor is an interrupt-controller and if it is
 156                 * then we are done
 157                 */
 158                if (of_get_property(ipar, "interrupt-controller", NULL) !=
 159                                NULL) {
 160                        pr_debug(" -> got it !\n");
 161                        for (i = 0; i < intsize; i++)
 162                                out_irq->specifier[i] =
 163                                                of_read_number(intspec +i, 1);
 164                        out_irq->size = intsize;
 165                        out_irq->controller = ipar;
 166                        of_node_put(old);
 167                        return 0;
 168                }
 169
 170                /* Now look for an interrupt-map */
 171                imap = of_get_property(ipar, "interrupt-map", &imaplen);
 172                /* No interrupt map, check for an interrupt parent */
 173                if (imap == NULL) {
 174                        pr_debug(" -> no map, getting parent\n");
 175                        newpar = of_irq_find_parent(ipar);
 176                        goto skiplevel;
 177                }
 178                imaplen /= sizeof(u32);
 179
 180                /* Look for a mask */
 181                imask = of_get_property(ipar, "interrupt-map-mask", NULL);
 182
 183                /* If we were passed no "reg" property and we attempt to parse
 184                 * an interrupt-map, then #address-cells must be 0.
 185                 * Fail if it's not.
 186                 */
 187                if (addr == NULL && addrsize != 0) {
 188                        pr_debug(" -> no reg passed in when needed !\n");
 189                        goto fail;
 190                }
 191
 192                /* Parse interrupt-map */
 193                match = 0;
 194                while (imaplen > (addrsize + intsize + 1) && !match) {
 195                        /* Compare specifiers */
 196                        match = 1;
 197                        for (i = 0; i < addrsize && match; ++i) {
 198                                u32 mask = imask ? imask[i] : 0xffffffffu;
 199                                match = ((addr[i] ^ imap[i]) & mask) == 0;
 200                        }
 201                        for (; i < (addrsize + intsize) && match; ++i) {
 202                                u32 mask = imask ? imask[i] : 0xffffffffu;
 203                                match =
 204                                   ((intspec[i-addrsize] ^ imap[i]) & mask) == 0;
 205                        }
 206                        imap += addrsize + intsize;
 207                        imaplen -= addrsize + intsize;
 208
 209                        pr_debug(" -> match=%d (imaplen=%d)\n", match, imaplen);
 210
 211                        /* Get the interrupt parent */
 212                        if (of_irq_workarounds & OF_IMAP_NO_PHANDLE)
 213                                newpar = of_node_get(of_irq_dflt_pic);
 214                        else
 215                                newpar = of_find_node_by_phandle(be32_to_cpup(imap));
 216                        imap++;
 217                        --imaplen;
 218
 219                        /* Check if not found */
 220                        if (newpar == NULL) {
 221                                pr_debug(" -> imap parent not found !\n");
 222                                goto fail;
 223                        }
 224
 225                        /* Get #interrupt-cells and #address-cells of new
 226                         * parent
 227                         */
 228                        tmp = of_get_property(newpar, "#interrupt-cells", NULL);
 229                        if (tmp == NULL) {
 230                                pr_debug(" -> parent lacks #interrupt-cells!\n");
 231                                goto fail;
 232                        }
 233                        newintsize = be32_to_cpu(*tmp);
 234                        tmp = of_get_property(newpar, "#address-cells", NULL);
 235                        newaddrsize = (tmp == NULL) ? 0 : be32_to_cpu(*tmp);
 236
 237                        pr_debug(" -> newintsize=%d, newaddrsize=%d\n",
 238                            newintsize, newaddrsize);
 239
 240                        /* Check for malformed properties */
 241                        if (imaplen < (newaddrsize + newintsize))
 242                                goto fail;
 243
 244                        imap += newaddrsize + newintsize;
 245                        imaplen -= newaddrsize + newintsize;
 246
 247                        pr_debug(" -> imaplen=%d\n", imaplen);
 248                }
 249                if (!match)
 250                        goto fail;
 251
 252                of_node_put(old);
 253                old = of_node_get(newpar);
 254                addrsize = newaddrsize;
 255                intsize = newintsize;
 256                intspec = imap - intsize;
 257                addr = intspec - addrsize;
 258
 259        skiplevel:
 260                /* Iterate again with new parent */
 261                pr_debug(" -> new parent: %s\n", newpar ? newpar->full_name : "<>");
 262                of_node_put(ipar);
 263                ipar = newpar;
 264                newpar = NULL;
 265        }
 266 fail:
 267        of_node_put(ipar);
 268        of_node_put(old);
 269        of_node_put(newpar);
 270
 271        return -EINVAL;
 272}
 273EXPORT_SYMBOL_GPL(of_irq_map_raw);
 274
 275/**
 276 * of_irq_map_one - Resolve an interrupt for a device
 277 * @device: the device whose interrupt is to be resolved
 278 * @index: index of the interrupt to resolve
 279 * @out_irq: structure of_irq filled by this function
 280 *
 281 * This function resolves an interrupt, walking the tree, for a given
 282 * device-tree node. It's the high level pendant to of_irq_map_raw().
 283 */
 284int of_irq_map_one(struct device_node *device, int index, struct of_irq *out_irq)
 285{
 286        struct device_node *p;
 287        const __be32 *intspec, *tmp, *addr;
 288        u32 intsize, intlen;
 289        int res = -EINVAL;
 290
 291        pr_debug("of_irq_map_one: dev=%s, index=%d\n", device->full_name, index);
 292
 293        /* OldWorld mac stuff is "special", handle out of line */
 294        if (of_irq_workarounds & OF_IMAP_OLDWORLD_MAC)
 295                return of_irq_map_oldworld(device, index, out_irq);
 296
 297        /* Get the interrupts property */
 298        intspec = of_get_property(device, "interrupts", &intlen);
 299        if (intspec == NULL)
 300                return -EINVAL;
 301        intlen /= sizeof(*intspec);
 302
 303        pr_debug(" intspec=%d intlen=%d\n", be32_to_cpup(intspec), intlen);
 304
 305        /* Get the reg property (if any) */
 306        addr = of_get_property(device, "reg", NULL);
 307
 308        /* Look for the interrupt parent. */
 309        p = of_irq_find_parent(device);
 310        if (p == NULL)
 311                return -EINVAL;
 312
 313        /* Get size of interrupt specifier */
 314        tmp = of_get_property(p, "#interrupt-cells", NULL);
 315        if (tmp == NULL)
 316                goto out;
 317        intsize = be32_to_cpu(*tmp);
 318
 319        pr_debug(" intsize=%d intlen=%d\n", intsize, intlen);
 320
 321        /* Check index */
 322        if ((index + 1) * intsize > intlen)
 323                goto out;
 324
 325        /* Get new specifier and map it */
 326        res = of_irq_map_raw(p, intspec + index * intsize, intsize,
 327                             addr, out_irq);
 328 out:
 329        of_node_put(p);
 330        return res;
 331}
 332EXPORT_SYMBOL_GPL(of_irq_map_one);
 333
 334/**
 335 * of_irq_to_resource - Decode a node's IRQ and return it as a resource
 336 * @dev: pointer to device tree node
 337 * @index: zero-based index of the irq
 338 * @r: pointer to resource structure to return result into.
 339 */
 340int of_irq_to_resource(struct device_node *dev, int index, struct resource *r)
 341{
 342        int irq = irq_of_parse_and_map(dev, index);
 343
 344        /* Only dereference the resource if both the
 345         * resource and the irq are valid. */
 346        if (r && irq != NO_IRQ) {
 347                r->start = r->end = irq;
 348                r->flags = IORESOURCE_IRQ;
 349                r->name = dev->full_name;
 350        }
 351
 352        return irq;
 353}
 354EXPORT_SYMBOL_GPL(of_irq_to_resource);
 355
 356/**
 357 * of_irq_count - Count the number of IRQs a node uses
 358 * @dev: pointer to device tree node
 359 */
 360int of_irq_count(struct device_node *dev)
 361{
 362        int nr = 0;
 363
 364        while (of_irq_to_resource(dev, nr, NULL) != NO_IRQ)
 365                nr++;
 366
 367        return nr;
 368}
 369
 370/**
 371 * of_irq_to_resource_table - Fill in resource table with node's IRQ info
 372 * @dev: pointer to device tree node
 373 * @res: array of resources to fill in
 374 * @nr_irqs: the number of IRQs (and upper bound for num of @res elements)
 375 *
 376 * Returns the size of the filled in table (up to @nr_irqs).
 377 */
 378int of_irq_to_resource_table(struct device_node *dev, struct resource *res,
 379                int nr_irqs)
 380{
 381        int i;
 382
 383        for (i = 0; i < nr_irqs; i++, res++)
 384                if (of_irq_to_resource(dev, i, res) == NO_IRQ)
 385                        break;
 386
 387        return i;
 388}
 389