Messages in this thread | | | From | Grant Likely <> | Date | Tue, 26 Jul 2011 03:40:01 -0600 | Subject | Re: [PATCH v3 1/2] irq: add irq_domain translation infrastructure |
| |
On Tue, Jul 26, 2011 at 3:36 AM, Grant Likely <grant.likely@secretlab.ca> wrote: > 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.
Oops, I obviously forgot to update the commit text. This patch only affects ARM, I've split the MIPS, Microblaze and x86 bits out into a separate patch. I'll craft new commit text before reposting.
g.
> > 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> > --- > 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 | 123 +++++++++++++++++++++++++++++++++++++++++++ > 9 files changed, 220 insertions(+), 19 deletions(-) > create mode 100644 include/linux/irqdomain.h > create mode 100644 kernel/irq/irqdomain.c > > diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig > index 9cb1f4b..b2695aa 100644 > --- a/arch/arm/Kconfig > +++ b/arch/arm/Kconfig > @@ -1680,6 +1680,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. > > diff --git a/arch/arm/include/asm/prom.h b/arch/arm/include/asm/prom.h > index 11b8708..6f65ca8 100644 > --- 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); > > diff --git a/arch/arm/kernel/devtree.c b/arch/arm/kernel/devtree.c > index 0cdd7b4..1a33e9d 100644 > --- a/arch/arm/kernel/devtree.c > +++ b/arch/arm/kernel/devtree.c > @@ -132,17 +132,3 @@ struct machine_desc * __init setup_machine_fdt(unsigned int dt_phys) > > 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); > diff --git a/include/linux/irq.h b/include/linux/irq.h > index 5f69504..87a06f3 100644 > --- a/include/linux/irq.h > +++ b/include/linux/irq.h > @@ -108,14 +108,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 > @@ -128,9 +132,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; > diff --git a/include/linux/irqdomain.h b/include/linux/irqdomain.h > new file mode 100644 > index 0000000..8f2c10a > --- /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 */ > diff --git a/include/linux/of_irq.h b/include/linux/of_irq.h > index e6955f5..cd2e61c 100644 > --- a/include/linux/of_irq.h > +++ b/include/linux/of_irq.h > @@ -63,6 +63,9 @@ extern int of_irq_map_one(struct device_node *device, int index, > 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(struct device_node *dev, > 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 */ > diff --git a/kernel/irq/Kconfig b/kernel/irq/Kconfig > index d1d051b3..5a38bf4 100644 > --- 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 > diff --git a/kernel/irq/Makefile b/kernel/irq/Makefile > index 7329005..fff1738 100644 > --- 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 > diff --git a/kernel/irq/irqdomain.c b/kernel/irq/irqdomain.c > new file mode 100644 > index 0000000..171b754 > --- /dev/null > +++ b/kernel/irq/irqdomain.c > @@ -0,0 +1,123 @@ > + > +#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 */ > >
-- Grant Likely, B.Sc., P.Eng. Secret Lab Technologies Ltd. -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
| |