linux/kernel/irq/irqdomain.c
<<
>>
Prefs
   1#include <linux/irq.h>
   2#include <linux/irqdomain.h>
   3#include <linux/module.h>
   4#include <linux/mutex.h>
   5#include <linux/of.h>
   6#include <linux/of_address.h>
   7#include <linux/slab.h>
   8
   9static LIST_HEAD(irq_domain_list);
  10static DEFINE_MUTEX(irq_domain_mutex);
  11
  12/**
  13 * irq_domain_add() - Register an irq_domain
  14 * @domain: ptr to initialized irq_domain structure
  15 *
  16 * Registers an irq_domain structure.  The irq_domain must at a minimum be
  17 * initialized with an ops structure pointer, and either a ->to_irq hook or
  18 * a valid irq_base value.  Everything else is optional.
  19 */
  20void irq_domain_add(struct irq_domain *domain)
  21{
  22        struct irq_data *d;
  23        int hwirq;
  24
  25        /*
  26         * This assumes that the irq_domain owner has already allocated
  27         * the irq_descs.  This block will be removed when support for dynamic
  28         * allocation of irq_descs is added to irq_domain.
  29         */
  30        for (hwirq = 0; hwirq < domain->nr_irq; hwirq++) {
  31                d = irq_get_irq_data(irq_domain_to_irq(domain, hwirq));
  32                if (!d) {
  33                        WARN(1, "error: assigning domain to non existant irq_desc");
  34                        return;
  35                }
  36                if (d->domain) {
  37                        /* things are broken; just report, don't clean up */
  38                        WARN(1, "error: irq_desc already assigned to a domain");
  39                        return;
  40                }
  41                d->domain = domain;
  42                d->hwirq = hwirq;
  43        }
  44
  45        mutex_lock(&irq_domain_mutex);
  46        list_add(&domain->list, &irq_domain_list);
  47        mutex_unlock(&irq_domain_mutex);
  48}
  49
  50/**
  51 * irq_domain_del() - Unregister an irq_domain
  52 * @domain: ptr to registered irq_domain.
  53 */
  54void irq_domain_del(struct irq_domain *domain)
  55{
  56        struct irq_data *d;
  57        int hwirq;
  58
  59        mutex_lock(&irq_domain_mutex);
  60        list_del(&domain->list);
  61        mutex_unlock(&irq_domain_mutex);
  62
  63        /* Clear the irq_domain assignments */
  64        for (hwirq = 0; hwirq < domain->nr_irq; hwirq++) {
  65                d = irq_get_irq_data(irq_domain_to_irq(domain, hwirq));
  66                d->domain = NULL;
  67        }
  68}
  69
  70#if defined(CONFIG_OF_IRQ)
  71/**
  72 * irq_create_of_mapping() - Map a linux irq number from a DT interrupt spec
  73 *
  74 * Used by the device tree interrupt mapping code to translate a device tree
  75 * interrupt specifier to a valid linux irq number.  Returns either a valid
  76 * linux IRQ number or 0.
  77 *
  78 * When the caller no longer need the irq number returned by this function it
  79 * should arrange to call irq_dispose_mapping().
  80 */
  81unsigned int irq_create_of_mapping(struct device_node *controller,
  82                                   const u32 *intspec, unsigned int intsize)
  83{
  84        struct irq_domain *domain;
  85        unsigned long hwirq;
  86        unsigned int irq, type;
  87        int rc = -EINVAL;
  88
  89        /* Find a domain which can translate the irq spec */
  90        mutex_lock(&irq_domain_mutex);
  91        list_for_each_entry(domain, &irq_domain_list, list) {
  92                if (!domain->ops->dt_translate)
  93                        continue;
  94                rc = domain->ops->dt_translate(domain, controller,
  95                                        intspec, intsize, &hwirq, &type);
  96                if (rc == 0)
  97                        break;
  98        }
  99        mutex_unlock(&irq_domain_mutex);
 100
 101        if (rc != 0)
 102                return 0;
 103
 104        irq = irq_domain_to_irq(domain, hwirq);
 105        if (type != IRQ_TYPE_NONE)
 106                irq_set_irq_type(irq, type);
 107        pr_debug("%s: mapped hwirq=%i to irq=%i, flags=%x\n",
 108                 controller->full_name, (int)hwirq, irq, type);
 109        return irq;
 110}
 111EXPORT_SYMBOL_GPL(irq_create_of_mapping);
 112
 113/**
 114 * irq_dispose_mapping() - Discard a mapping created by irq_create_of_mapping()
 115 * @irq: linux irq number to be discarded
 116 *
 117 * Calling this function indicates the caller no longer needs a reference to
 118 * the linux irq number returned by a prior call to irq_create_of_mapping().
 119 */
 120void irq_dispose_mapping(unsigned int irq)
 121{
 122        /*
 123         * nothing yet; will be filled when support for dynamic allocation of
 124         * irq_descs is added to irq_domain
 125         */
 126}
 127EXPORT_SYMBOL_GPL(irq_dispose_mapping);
 128
 129int irq_domain_simple_dt_translate(struct irq_domain *d,
 130                            struct device_node *controller,
 131                            const u32 *intspec, unsigned int intsize,
 132                            unsigned long *out_hwirq, unsigned int *out_type)
 133{
 134        if (d->of_node != controller)
 135                return -EINVAL;
 136        if (intsize < 1)
 137                return -EINVAL;
 138
 139        *out_hwirq = intspec[0];
 140        *out_type = IRQ_TYPE_NONE;
 141        if (intsize > 1)
 142                *out_type = intspec[1] & IRQ_TYPE_SENSE_MASK;
 143        return 0;
 144}
 145
 146struct irq_domain_ops irq_domain_simple_ops = {
 147        .dt_translate = irq_domain_simple_dt_translate,
 148};
 149EXPORT_SYMBOL_GPL(irq_domain_simple_ops);
 150
 151/**
 152 * irq_domain_create_simple() - Set up a 'simple' translation range
 153 */
 154void irq_domain_add_simple(struct device_node *controller, int irq_base)
 155{
 156        struct irq_domain *domain;
 157
 158        domain = kzalloc(sizeof(*domain), GFP_KERNEL);
 159        if (!domain) {
 160                WARN_ON(1);
 161                return;
 162        }
 163
 164        domain->irq_base = irq_base;
 165        domain->of_node = of_node_get(controller);
 166        domain->ops = &irq_domain_simple_ops;
 167        irq_domain_add(domain);
 168}
 169EXPORT_SYMBOL_GPL(irq_domain_add_simple);
 170
 171void irq_domain_generate_simple(const struct of_device_id *match,
 172                                u64 phys_base, unsigned int irq_start)
 173{
 174        struct device_node *node;
 175        pr_info("looking for phys_base=%llx, irq_start=%i\n",
 176                (unsigned long long) phys_base, (int) irq_start);
 177        node = of_find_matching_node_by_address(NULL, match, phys_base);
 178        if (node)
 179                irq_domain_add_simple(node, irq_start);
 180        else
 181                pr_info("no node found\n");
 182}
 183EXPORT_SYMBOL_GPL(irq_domain_generate_simple);
 184#endif /* CONFIG_OF_IRQ */
 185