Commit 08a543ad33fc188650801bd36eed4ffe272643e1

Authored by Grant Likely
1 parent 5fd1a2ed0e

irq: add irq_domain translation infrastructure

This patch adds irq_domain infrastructure for translating from
hardware irq numbers to linux irqs.  This is particularly important
for architectures adding device tree support because the current
implementation (excluding PowerPC and SPARC) cannot handle
translation for more than a single interrupt controller.  irq_domain
supports device tree translation for any number of interrupt
controllers.

This patch converts x86, Microblaze, ARM and MIPS to use irq_domain
for device tree irq translation.  x86 is untested beyond compiling it,
irq_domain is enabled for MIPS and Microblaze, but the old behaviour is
preserved until the core code is modified to actually register an
irq_domain yet.  On ARM it works and is required for much of the new
ARM device tree board support.

PowerPC has /not/ been converted to use this new infrastructure.  It
is still missing some features before it can replace the virq
infrastructure already in powerpc (see documentation on
irq_domain_map/unmap for details).  Followup patches will add the
missing pieces and migrate PowerPC to use irq_domain.

SPARC has its own method of managing interrupts from the device tree
and is unaffected by this change.

Acked-by: Ralf Baechle <ralf@linux-mips.org>
Signed-off-by: Grant Likely <grant.likely@secretlab.ca>

Showing 9 changed files with 219 additions and 19 deletions Side-by-side Diff

... ... @@ -1682,6 +1682,7 @@
1682 1682 bool "Flattened Device Tree support"
1683 1683 select OF
1684 1684 select OF_EARLY_FLATTREE
  1685 + select IRQ_DOMAIN
1685 1686 help
1686 1687 Include support for flattened device tree machine descriptions.
1687 1688  
arch/arm/include/asm/prom.h
... ... @@ -16,11 +16,6 @@
16 16 #include <asm/setup.h>
17 17 #include <asm/irq.h>
18 18  
19   -static inline void irq_dispose_mapping(unsigned int virq)
20   -{
21   - return;
22   -}
23   -
24 19 extern struct machine_desc *setup_machine_fdt(unsigned int dt_phys);
25 20 extern void arm_dt_memblock_reserve(void);
26 21  
arch/arm/kernel/devtree.c
... ... @@ -132,18 +132,4 @@
132 132  
133 133 return mdesc_best;
134 134 }
135   -
136   -/**
137   - * irq_create_of_mapping - Hook to resolve OF irq specifier into a Linux irq#
138   - *
139   - * Currently the mapping mechanism is trivial; simple flat hwirq numbers are
140   - * mapped 1:1 onto Linux irq numbers. Cascaded irq controllers are not
141   - * supported.
142   - */
143   -unsigned int irq_create_of_mapping(struct device_node *controller,
144   - const u32 *intspec, unsigned int intsize)
145   -{
146   - return intspec[0];
147   -}
148   -EXPORT_SYMBOL_GPL(irq_create_of_mapping);
... ... @@ -108,14 +108,18 @@
108 108 };
109 109  
110 110 struct msi_desc;
  111 +struct irq_domain;
111 112  
112 113 /**
113 114 * struct irq_data - per irq and irq chip data passed down to chip functions
114 115 * @irq: interrupt number
  116 + * @hwirq: hardware interrupt number, local to the interrupt domain
115 117 * @node: node index useful for balancing
116 118 * @state_use_accessors: status information for irq chip functions.
117 119 * Use accessor functions to deal with it
118 120 * @chip: low level interrupt hardware access
  121 + * @domain: Interrupt translation domain; responsible for mapping
  122 + * between hwirq number and linux irq number.
119 123 * @handler_data: per-IRQ data for the irq_chip methods
120 124 * @chip_data: platform-specific per-chip private data for the chip
121 125 * methods, to allow shared chip implementations
122 126  
... ... @@ -128,9 +132,11 @@
128 132 */
129 133 struct irq_data {
130 134 unsigned int irq;
  135 + unsigned long hwirq;
131 136 unsigned int node;
132 137 unsigned int state_use_accessors;
133 138 struct irq_chip *chip;
  139 + struct irq_domain *domain;
134 140 void *handler_data;
135 141 void *chip_data;
136 142 struct msi_desc *msi_desc;
include/linux/irqdomain.h
  1 +/*
  2 + * irq_domain - IRQ translation domains
  3 + *
  4 + * Translation infrastructure between hw and linux irq numbers. This is
  5 + * helpful for interrupt controllers to implement mapping between hardware
  6 + * irq numbers and the Linux irq number space.
  7 + *
  8 + * irq_domains also have a hook for translating device tree interrupt
  9 + * representation into a hardware irq number that can be mapped back to a
  10 + * Linux irq number without any extra platform support code.
  11 + *
  12 + * irq_domain is expected to be embedded in an interrupt controller's private
  13 + * data structure.
  14 + */
  15 +#ifndef _LINUX_IRQDOMAIN_H
  16 +#define _LINUX_IRQDOMAIN_H
  17 +
  18 +#include <linux/irq.h>
  19 +
  20 +#ifdef CONFIG_IRQ_DOMAIN
  21 +struct device_node;
  22 +struct irq_domain;
  23 +
  24 +/**
  25 + * struct irq_domain_ops - Methods for irq_domain objects
  26 + * @to_irq: (optional) given a local hardware irq number, return the linux
  27 + * irq number. If to_irq is not implemented, then the irq_domain
  28 + * will use this translation: irq = (domain->irq_base + hwirq)
  29 + * @dt_translate: Given a device tree node and interrupt specifier, decode
  30 + * the hardware irq number and linux irq type value.
  31 + */
  32 +struct irq_domain_ops {
  33 + unsigned int (*to_irq)(struct irq_domain *d, unsigned long hwirq);
  34 +
  35 +#ifdef CONFIG_OF
  36 + int (*dt_translate)(struct irq_domain *d, struct device_node *node,
  37 + const u32 *intspec, unsigned int intsize,
  38 + unsigned long *out_hwirq, unsigned int *out_type);
  39 +#endif /* CONFIG_OF */
  40 +};
  41 +
  42 +/**
  43 + * struct irq_domain - Hardware interrupt number translation object
  44 + * @list: Element in global irq_domain list.
  45 + * @irq_base: Start of irq_desc range assigned to the irq_domain. The creator
  46 + * of the irq_domain is responsible for allocating the array of
  47 + * irq_desc structures.
  48 + * @nr_irq: Number of irqs managed by the irq domain
  49 + * @ops: pointer to irq_domain methods
  50 + * @priv: private data pointer for use by owner. Not touched by irq_domain
  51 + * core code.
  52 + * @of_node: (optional) Pointer to device tree nodes associated with the
  53 + * irq_domain. Used when decoding device tree interrupt specifiers.
  54 + */
  55 +struct irq_domain {
  56 + struct list_head list;
  57 + unsigned int irq_base;
  58 + unsigned int nr_irq;
  59 + const struct irq_domain_ops *ops;
  60 + void *priv;
  61 + struct device_node *of_node;
  62 +};
  63 +
  64 +/**
  65 + * irq_domain_to_irq() - Translate from a hardware irq to a linux irq number
  66 + *
  67 + * Returns the linux irq number associated with a hardware irq. By default,
  68 + * the mapping is irq == domain->irq_base + hwirq, but this mapping can
  69 + * be overridden if the irq_domain implements a .to_irq() hook.
  70 + */
  71 +static inline unsigned int irq_domain_to_irq(struct irq_domain *d,
  72 + unsigned long hwirq)
  73 +{
  74 + return d->ops->to_irq ? d->ops->to_irq(d, hwirq) : d->irq_base + hwirq;
  75 +}
  76 +
  77 +extern void irq_domain_add(struct irq_domain *domain);
  78 +extern void irq_domain_del(struct irq_domain *domain);
  79 +#endif /* CONFIG_IRQ_DOMAIN */
  80 +
  81 +#endif /* _LINUX_IRQDOMAIN_H */
include/linux/of_irq.h
... ... @@ -63,12 +63,16 @@
63 63 extern unsigned int irq_create_of_mapping(struct device_node *controller,
64 64 const u32 *intspec,
65 65 unsigned int intsize);
  66 +#ifdef CONFIG_IRQ_DOMAIN
  67 +extern void irq_dispose_mapping(unsigned int irq);
  68 +#endif
66 69 extern int of_irq_to_resource(struct device_node *dev, int index,
67 70 struct resource *r);
68 71 extern int of_irq_count(struct device_node *dev);
69 72 extern int of_irq_to_resource_table(struct device_node *dev,
70 73 struct resource *res, int nr_irqs);
71 74 extern struct device_node *of_irq_find_parent(struct device_node *child);
  75 +
72 76  
73 77 #endif /* CONFIG_OF_IRQ */
74 78 #endif /* CONFIG_OF */
... ... @@ -52,6 +52,10 @@
52 52 config GENERIC_IRQ_CHIP
53 53 bool
54 54  
  55 +# Generic irq_domain hw <--> linux irq number translation
  56 +config IRQ_DOMAIN
  57 + bool
  58 +
55 59 # Support forced irq threading
56 60 config IRQ_FORCED_THREADING
57 61 bool
... ... @@ -2,6 +2,7 @@
2 2 obj-y := irqdesc.o handle.o manage.o spurious.o resend.o chip.o dummychip.o devres.o
3 3 obj-$(CONFIG_GENERIC_IRQ_CHIP) += generic-chip.o
4 4 obj-$(CONFIG_GENERIC_IRQ_PROBE) += autoprobe.o
  5 +obj-$(CONFIG_IRQ_DOMAIN) += irqdomain.o
5 6 obj-$(CONFIG_PROC_FS) += proc.o
6 7 obj-$(CONFIG_GENERIC_PENDING_IRQ) += migration.o
7 8 obj-$(CONFIG_PM_SLEEP) += pm.o
kernel/irq/irqdomain.c
  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 +
  7 +static LIST_HEAD(irq_domain_list);
  8 +static DEFINE_MUTEX(irq_domain_mutex);
  9 +
  10 +/**
  11 + * irq_domain_add() - Register an irq_domain
  12 + * @domain: ptr to initialized irq_domain structure
  13 + *
  14 + * Registers an irq_domain structure. The irq_domain must at a minimum be
  15 + * initialized with an ops structure pointer, and either a ->to_irq hook or
  16 + * a valid irq_base value. Everything else is optional.
  17 + */
  18 +void irq_domain_add(struct irq_domain *domain)
  19 +{
  20 + struct irq_data *d;
  21 + int hwirq;
  22 +
  23 + /*
  24 + * This assumes that the irq_domain owner has already allocated
  25 + * the irq_descs. This block will be removed when support for dynamic
  26 + * allocation of irq_descs is added to irq_domain.
  27 + */
  28 + for (hwirq = 0; hwirq < domain->nr_irq; hwirq++) {
  29 + d = irq_get_irq_data(irq_domain_to_irq(domain, hwirq));
  30 + if (d || d->domain) {
  31 + /* things are broken; just report, don't clean up */
  32 + WARN(1, "error: irq_desc already assigned to a domain");
  33 + return;
  34 + }
  35 + d->domain = domain;
  36 + d->hwirq = hwirq;
  37 + }
  38 +
  39 + mutex_lock(&irq_domain_mutex);
  40 + list_add(&domain->list, &irq_domain_list);
  41 + mutex_unlock(&irq_domain_mutex);
  42 +}
  43 +
  44 +/**
  45 + * irq_domain_del() - Unregister an irq_domain
  46 + * @domain: ptr to registered irq_domain.
  47 + */
  48 +void irq_domain_del(struct irq_domain *domain)
  49 +{
  50 + struct irq_data *d;
  51 + int hwirq;
  52 +
  53 + mutex_lock(&irq_domain_mutex);
  54 + list_del(&domain->list);
  55 + mutex_unlock(&irq_domain_mutex);
  56 +
  57 + /* Clear the irq_domain assignments */
  58 + for (hwirq = 0; hwirq < domain->nr_irq; hwirq++) {
  59 + d = irq_get_irq_data(irq_domain_to_irq(domain, hwirq));
  60 + d->domain = NULL;
  61 + }
  62 +}
  63 +
  64 +#if defined(CONFIG_OF_IRQ)
  65 +/**
  66 + * irq_create_of_mapping() - Map a linux irq number from a DT interrupt spec
  67 + *
  68 + * Used by the device tree interrupt mapping code to translate a device tree
  69 + * interrupt specifier to a valid linux irq number. Returns either a valid
  70 + * linux IRQ number or 0.
  71 + *
  72 + * When the caller no longer need the irq number returned by this function it
  73 + * should arrange to call irq_dispose_mapping().
  74 + */
  75 +unsigned int irq_create_of_mapping(struct device_node *controller,
  76 + const u32 *intspec, unsigned int intsize)
  77 +{
  78 + struct irq_domain *domain;
  79 + unsigned long hwirq;
  80 + unsigned int irq, type;
  81 + int rc = -EINVAL;
  82 +
  83 + /* Find a domain which can translate the irq spec */
  84 + mutex_lock(&irq_domain_mutex);
  85 + list_for_each_entry(domain, &irq_domain_list, list) {
  86 + if (!domain->ops->dt_translate)
  87 + continue;
  88 + rc = domain->ops->dt_translate(domain, controller,
  89 + intspec, intsize, &hwirq, &type);
  90 + if (rc == 0)
  91 + break;
  92 + }
  93 + mutex_unlock(&irq_domain_mutex);
  94 +
  95 + if (rc != 0)
  96 + return 0;
  97 +
  98 + irq = irq_domain_to_irq(domain, hwirq);
  99 + if (type != IRQ_TYPE_NONE)
  100 + irq_set_irq_type(irq, type);
  101 + pr_debug("%s: mapped hwirq=%i to irq=%i, flags=%x\n",
  102 + controller->full_name, (int)hwirq, irq, type);
  103 + return irq;
  104 +}
  105 +EXPORT_SYMBOL_GPL(irq_create_of_mapping);
  106 +
  107 +/**
  108 + * irq_dispose_mapping() - Discard a mapping created by irq_create_of_mapping()
  109 + * @irq: linux irq number to be discarded
  110 + *
  111 + * Calling this function indicates the caller no longer needs a reference to
  112 + * the linux irq number returned by a prior call to irq_create_of_mapping().
  113 + */
  114 +void irq_dispose_mapping(unsigned int irq)
  115 +{
  116 + /*
  117 + * nothing yet; will be filled when support for dynamic allocation of
  118 + * irq_descs is added to irq_domain
  119 + */
  120 +}
  121 +EXPORT_SYMBOL_GPL(irq_dispose_mapping);
  122 +#endif /* CONFIG_OF_IRQ */