| From 9a23bed05c4e337589f8e76bed25c843979b77d5 Mon Sep 17 00:00:00 2001 |
| From: Grant Likely <grant.likely@secretlab.ca> |
| Date: Tue, 26 Jul 2011 03:19:06 -0600 |
| Subject: 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> |
| (cherry picked from commit 08a543ad33fc188650801bd36eed4ffe272643e1) |
| |
| Signed-off-by: Simon Horman <horms@verge.net.au> |
| --- |
| arch/arm/Kconfig | 1 |
| arch/arm/include/asm/prom.h | 5 - |
| arch/arm/kernel/devtree.c | 14 ----- |
| include/linux/irq.h | 6 ++ |
| include/linux/irqdomain.h | 81 +++++++++++++++++++++++++++++ |
| include/linux/of_irq.h | 4 + |
| kernel/irq/Kconfig | 4 + |
| kernel/irq/Makefile | 1 |
| kernel/irq/irqdomain.c | 122 ++++++++++++++++++++++++++++++++++++++++++++ |
| 9 files changed, 219 insertions(+), 19 deletions(-) |
| create mode 100644 include/linux/irqdomain.h |
| create mode 100644 kernel/irq/irqdomain.c |
| |
| --- a/arch/arm/Kconfig |
| +++ b/arch/arm/Kconfig |
| @@ -1715,6 +1715,7 @@ config USE_OF |
| bool "Flattened Device Tree support" |
| select OF |
| select OF_EARLY_FLATTREE |
| + select IRQ_DOMAIN |
| help |
| Include support for flattened device tree machine descriptions. |
| |
| --- a/arch/arm/include/asm/prom.h |
| +++ b/arch/arm/include/asm/prom.h |
| @@ -16,11 +16,6 @@ |
| #include <asm/setup.h> |
| #include <asm/irq.h> |
| |
| -static inline void irq_dispose_mapping(unsigned int virq) |
| -{ |
| - return; |
| -} |
| - |
| extern struct machine_desc *setup_machine_fdt(unsigned int dt_phys); |
| extern void arm_dt_memblock_reserve(void); |
| |
| --- a/arch/arm/kernel/devtree.c |
| +++ b/arch/arm/kernel/devtree.c |
| @@ -132,17 +132,3 @@ struct machine_desc * __init setup_machi |
| |
| return mdesc_best; |
| } |
| - |
| -/** |
| - * irq_create_of_mapping - Hook to resolve OF irq specifier into a Linux irq# |
| - * |
| - * Currently the mapping mechanism is trivial; simple flat hwirq numbers are |
| - * mapped 1:1 onto Linux irq numbers. Cascaded irq controllers are not |
| - * supported. |
| - */ |
| -unsigned int irq_create_of_mapping(struct device_node *controller, |
| - const u32 *intspec, unsigned int intsize) |
| -{ |
| - return intspec[0]; |
| -} |
| -EXPORT_SYMBOL_GPL(irq_create_of_mapping); |
| --- a/include/linux/irq.h |
| +++ b/include/linux/irq.h |
| @@ -114,14 +114,18 @@ enum { |
| }; |
| |
| struct msi_desc; |
| +struct irq_domain; |
| |
| /** |
| * struct irq_data - per irq and irq chip data passed down to chip functions |
| * @irq: interrupt number |
| + * @hwirq: hardware interrupt number, local to the interrupt domain |
| * @node: node index useful for balancing |
| * @state_use_accessors: status information for irq chip functions. |
| * Use accessor functions to deal with it |
| * @chip: low level interrupt hardware access |
| + * @domain: Interrupt translation domain; responsible for mapping |
| + * between hwirq number and linux irq number. |
| * @handler_data: per-IRQ data for the irq_chip methods |
| * @chip_data: platform-specific per-chip private data for the chip |
| * methods, to allow shared chip implementations |
| @@ -134,9 +138,11 @@ struct msi_desc; |
| */ |
| struct irq_data { |
| unsigned int irq; |
| + unsigned long hwirq; |
| unsigned int node; |
| unsigned int state_use_accessors; |
| struct irq_chip *chip; |
| + struct irq_domain *domain; |
| void *handler_data; |
| void *chip_data; |
| struct msi_desc *msi_desc; |
| --- /dev/null |
| +++ b/include/linux/irqdomain.h |
| @@ -0,0 +1,81 @@ |
| +/* |
| + * irq_domain - IRQ translation domains |
| + * |
| + * Translation infrastructure between hw and linux irq numbers. This is |
| + * helpful for interrupt controllers to implement mapping between hardware |
| + * irq numbers and the Linux irq number space. |
| + * |
| + * irq_domains also have a hook for translating device tree interrupt |
| + * representation into a hardware irq number that can be mapped back to a |
| + * Linux irq number without any extra platform support code. |
| + * |
| + * irq_domain is expected to be embedded in an interrupt controller's private |
| + * data structure. |
| + */ |
| +#ifndef _LINUX_IRQDOMAIN_H |
| +#define _LINUX_IRQDOMAIN_H |
| + |
| +#include <linux/irq.h> |
| + |
| +#ifdef CONFIG_IRQ_DOMAIN |
| +struct device_node; |
| +struct irq_domain; |
| + |
| +/** |
| + * struct irq_domain_ops - Methods for irq_domain objects |
| + * @to_irq: (optional) given a local hardware irq number, return the linux |
| + * irq number. If to_irq is not implemented, then the irq_domain |
| + * will use this translation: irq = (domain->irq_base + hwirq) |
| + * @dt_translate: Given a device tree node and interrupt specifier, decode |
| + * the hardware irq number and linux irq type value. |
| + */ |
| +struct irq_domain_ops { |
| + unsigned int (*to_irq)(struct irq_domain *d, unsigned long hwirq); |
| + |
| +#ifdef CONFIG_OF |
| + int (*dt_translate)(struct irq_domain *d, struct device_node *node, |
| + const u32 *intspec, unsigned int intsize, |
| + unsigned long *out_hwirq, unsigned int *out_type); |
| +#endif /* CONFIG_OF */ |
| +}; |
| + |
| +/** |
| + * struct irq_domain - Hardware interrupt number translation object |
| + * @list: Element in global irq_domain list. |
| + * @irq_base: Start of irq_desc range assigned to the irq_domain. The creator |
| + * of the irq_domain is responsible for allocating the array of |
| + * irq_desc structures. |
| + * @nr_irq: Number of irqs managed by the irq domain |
| + * @ops: pointer to irq_domain methods |
| + * @priv: private data pointer for use by owner. Not touched by irq_domain |
| + * core code. |
| + * @of_node: (optional) Pointer to device tree nodes associated with the |
| + * irq_domain. Used when decoding device tree interrupt specifiers. |
| + */ |
| +struct irq_domain { |
| + struct list_head list; |
| + unsigned int irq_base; |
| + unsigned int nr_irq; |
| + const struct irq_domain_ops *ops; |
| + void *priv; |
| + struct device_node *of_node; |
| +}; |
| + |
| +/** |
| + * irq_domain_to_irq() - Translate from a hardware irq to a linux irq number |
| + * |
| + * Returns the linux irq number associated with a hardware irq. By default, |
| + * the mapping is irq == domain->irq_base + hwirq, but this mapping can |
| + * be overridden if the irq_domain implements a .to_irq() hook. |
| + */ |
| +static inline unsigned int irq_domain_to_irq(struct irq_domain *d, |
| + unsigned long hwirq) |
| +{ |
| + return d->ops->to_irq ? d->ops->to_irq(d, hwirq) : d->irq_base + hwirq; |
| +} |
| + |
| +extern void irq_domain_add(struct irq_domain *domain); |
| +extern void irq_domain_del(struct irq_domain *domain); |
| +#endif /* CONFIG_IRQ_DOMAIN */ |
| + |
| +#endif /* _LINUX_IRQDOMAIN_H */ |
| --- a/include/linux/of_irq.h |
| +++ b/include/linux/of_irq.h |
| @@ -63,6 +63,9 @@ extern int of_irq_map_one(struct device_ |
| extern unsigned int irq_create_of_mapping(struct device_node *controller, |
| const u32 *intspec, |
| unsigned int intsize); |
| +#ifdef CONFIG_IRQ_DOMAIN |
| +extern void irq_dispose_mapping(unsigned int irq); |
| +#endif |
| extern int of_irq_to_resource(struct device_node *dev, int index, |
| struct resource *r); |
| extern int of_irq_count(struct device_node *dev); |
| @@ -70,6 +73,7 @@ extern int of_irq_to_resource_table(stru |
| struct resource *res, int nr_irqs); |
| extern struct device_node *of_irq_find_parent(struct device_node *child); |
| |
| + |
| #endif /* CONFIG_OF_IRQ */ |
| #endif /* CONFIG_OF */ |
| #endif /* __OF_IRQ_H */ |
| --- a/kernel/irq/Kconfig |
| +++ b/kernel/irq/Kconfig |
| @@ -52,6 +52,10 @@ config IRQ_EDGE_EOI_HANDLER |
| config GENERIC_IRQ_CHIP |
| bool |
| |
| +# Generic irq_domain hw <--> linux irq number translation |
| +config IRQ_DOMAIN |
| + bool |
| + |
| # Support forced irq threading |
| config IRQ_FORCED_THREADING |
| bool |
| --- a/kernel/irq/Makefile |
| +++ b/kernel/irq/Makefile |
| @@ -2,6 +2,7 @@ |
| obj-y := irqdesc.o handle.o manage.o spurious.o resend.o chip.o dummychip.o devres.o |
| obj-$(CONFIG_GENERIC_IRQ_CHIP) += generic-chip.o |
| obj-$(CONFIG_GENERIC_IRQ_PROBE) += autoprobe.o |
| +obj-$(CONFIG_IRQ_DOMAIN) += irqdomain.o |
| obj-$(CONFIG_PROC_FS) += proc.o |
| obj-$(CONFIG_GENERIC_PENDING_IRQ) += migration.o |
| obj-$(CONFIG_PM_SLEEP) += pm.o |
| --- /dev/null |
| +++ b/kernel/irq/irqdomain.c |
| @@ -0,0 +1,122 @@ |
| +#include <linux/irq.h> |
| +#include <linux/irqdomain.h> |
| +#include <linux/module.h> |
| +#include <linux/mutex.h> |
| +#include <linux/of.h> |
| + |
| +static LIST_HEAD(irq_domain_list); |
| +static DEFINE_MUTEX(irq_domain_mutex); |
| + |
| +/** |
| + * irq_domain_add() - Register an irq_domain |
| + * @domain: ptr to initialized irq_domain structure |
| + * |
| + * Registers an irq_domain structure. The irq_domain must at a minimum be |
| + * initialized with an ops structure pointer, and either a ->to_irq hook or |
| + * a valid irq_base value. Everything else is optional. |
| + */ |
| +void irq_domain_add(struct irq_domain *domain) |
| +{ |
| + struct irq_data *d; |
| + int hwirq; |
| + |
| + /* |
| + * This assumes that the irq_domain owner has already allocated |
| + * the irq_descs. This block will be removed when support for dynamic |
| + * allocation of irq_descs is added to irq_domain. |
| + */ |
| + for (hwirq = 0; hwirq < domain->nr_irq; hwirq++) { |
| + d = irq_get_irq_data(irq_domain_to_irq(domain, hwirq)); |
| + if (d || d->domain) { |
| + /* things are broken; just report, don't clean up */ |
| + WARN(1, "error: irq_desc already assigned to a domain"); |
| + return; |
| + } |
| + d->domain = domain; |
| + d->hwirq = hwirq; |
| + } |
| + |
| + mutex_lock(&irq_domain_mutex); |
| + list_add(&domain->list, &irq_domain_list); |
| + mutex_unlock(&irq_domain_mutex); |
| +} |
| + |
| +/** |
| + * irq_domain_del() - Unregister an irq_domain |
| + * @domain: ptr to registered irq_domain. |
| + */ |
| +void irq_domain_del(struct irq_domain *domain) |
| +{ |
| + struct irq_data *d; |
| + int hwirq; |
| + |
| + mutex_lock(&irq_domain_mutex); |
| + list_del(&domain->list); |
| + mutex_unlock(&irq_domain_mutex); |
| + |
| + /* Clear the irq_domain assignments */ |
| + for (hwirq = 0; hwirq < domain->nr_irq; hwirq++) { |
| + d = irq_get_irq_data(irq_domain_to_irq(domain, hwirq)); |
| + d->domain = NULL; |
| + } |
| +} |
| + |
| +#if defined(CONFIG_OF_IRQ) |
| +/** |
| + * irq_create_of_mapping() - Map a linux irq number from a DT interrupt spec |
| + * |
| + * Used by the device tree interrupt mapping code to translate a device tree |
| + * interrupt specifier to a valid linux irq number. Returns either a valid |
| + * linux IRQ number or 0. |
| + * |
| + * When the caller no longer need the irq number returned by this function it |
| + * should arrange to call irq_dispose_mapping(). |
| + */ |
| +unsigned int irq_create_of_mapping(struct device_node *controller, |
| + const u32 *intspec, unsigned int intsize) |
| +{ |
| + struct irq_domain *domain; |
| + unsigned long hwirq; |
| + unsigned int irq, type; |
| + int rc = -EINVAL; |
| + |
| + /* Find a domain which can translate the irq spec */ |
| + mutex_lock(&irq_domain_mutex); |
| + list_for_each_entry(domain, &irq_domain_list, list) { |
| + if (!domain->ops->dt_translate) |
| + continue; |
| + rc = domain->ops->dt_translate(domain, controller, |
| + intspec, intsize, &hwirq, &type); |
| + if (rc == 0) |
| + break; |
| + } |
| + mutex_unlock(&irq_domain_mutex); |
| + |
| + if (rc != 0) |
| + return 0; |
| + |
| + irq = irq_domain_to_irq(domain, hwirq); |
| + if (type != IRQ_TYPE_NONE) |
| + irq_set_irq_type(irq, type); |
| + pr_debug("%s: mapped hwirq=%i to irq=%i, flags=%x\n", |
| + controller->full_name, (int)hwirq, irq, type); |
| + return irq; |
| +} |
| +EXPORT_SYMBOL_GPL(irq_create_of_mapping); |
| + |
| +/** |
| + * irq_dispose_mapping() - Discard a mapping created by irq_create_of_mapping() |
| + * @irq: linux irq number to be discarded |
| + * |
| + * Calling this function indicates the caller no longer needs a reference to |
| + * the linux irq number returned by a prior call to irq_create_of_mapping(). |
| + */ |
| +void irq_dispose_mapping(unsigned int irq) |
| +{ |
| + /* |
| + * nothing yet; will be filled when support for dynamic allocation of |
| + * irq_descs is added to irq_domain |
| + */ |
| +} |
| +EXPORT_SYMBOL_GPL(irq_dispose_mapping); |
| +#endif /* CONFIG_OF_IRQ */ |