diff mbox

[4/6] dt: generalize irq_of_create_mapping()

Message ID 20110428200203.8979.3569.stgit@ponder (mailing list archive)
State Changes Requested
Headers show

Commit Message

Grant Likely April 28, 2011, 8:02 p.m. UTC
This patch creates a common implementation of irq_of_create_mapping()
and factors out the interrupt domain translation code from powerpc to
make it available for all architectures.

It creates a new structure, struct of_irq_domain, which can be
embedded into the private data structure of an interrupt controller.
Any interrupt controller can call of_irq_domain_add() to register a
structure with a .map() hook that can translate irq specifiers from
the device tree into linux irq numbers.

The patch also modifies the powerpc irq_host to embed the
of_irq_domain structure and use the new common infrastructure for
registering domains.  This separates the reverse mapping and irq
allocation infrastructure of irq_host from the domain registration
infrastructure.  I elected to split the functionality this way for
several reasons.  First, with the major irq cleanup done by Thomas
Gleixner, dynamic allocation of irqs can be handled gracefully with
irq_alloc_desc*() and interrupt controllers may not need or want an
irq_host layer to manage it for them.  For example, the new
irq_chip_generic() already has a method for mapping between hwirq and
Linux irq.

Second, the irq_host code currently has a lot of complexity to handle
all the different reverse mapping types from a single structure.  The
radix mapping in particular has a lot of support code, but only one
user.  I didn't want to bring that complexity into the common code
as-is.  Instead, I'd prefer to create a separate encapsulating
structure for each revmap, and each type would have a separate .map
hook.

For now, the mapping code remains powerpc-specific and further
generalization will happen in subsequent patches.

Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
---
 arch/microblaze/kernel/irq.c             |    7 --
 arch/mips/kernel/prom.c                  |   14 ----
 arch/powerpc/include/asm/irq.h           |   21 +++--
 arch/powerpc/include/asm/irqhost.h       |    4 -
 arch/powerpc/kernel/irq.c                |   99 ++++++++++++++-----------
 arch/powerpc/platforms/cell/axon_msi.c   |   12 ++-
 arch/powerpc/platforms/cell/spider-pic.c |    8 +-
 arch/powerpc/sysdev/fsl_msi.c            |    2 -
 arch/powerpc/sysdev/i8259.c              |    2 -
 arch/powerpc/sysdev/ipic.c               |    2 -
 arch/powerpc/sysdev/mpic.c               |    4 +
 arch/powerpc/sysdev/mpic_msi.c           |    2 -
 arch/powerpc/sysdev/mpic_pasemi_msi.c    |    4 +
 arch/powerpc/sysdev/qe_lib/qe_ic.c       |    2 -
 arch/x86/include/asm/irq_controller.h    |   12 ---
 arch/x86/include/asm/prom.h              |    1 
 arch/x86/kernel/devicetree.c             |   77 ++++----------------
 drivers/of/irq.c                         |  118 ++++++++++++++++++++++++++++++
 include/linux/of_irq.h                   |   31 ++++++++
 19 files changed, 248 insertions(+), 174 deletions(-)
 delete mode 100644 arch/x86/include/asm/irq_controller.h

Comments

Benjamin Herrenschmidt May 3, 2011, 1:50 a.m. UTC | #1
On Thu, 2011-04-28 at 14:02 -0600, Grant Likely wrote:
> This patch creates a common implementation of irq_of_create_mapping()
> and factors out the interrupt domain translation code from powerpc to
> make it available for all architectures.

I think you are going the wrong way around.

First thing first, is to make the irq domain / mapping API generic
without the OF bits.

IE. move the IRQ domain generically, get rid of irq_map by putting the
domain ptr & hw numbers in the irq desc/data etc...

Then you can move over the OF specific bits which are optional and
orthogonal to a large extent.

Cheers,
Ben.

> It creates a new structure, struct of_irq_domain, which can be
> embedded into the private data structure of an interrupt controller.
> Any interrupt controller can call of_irq_domain_add() to register a
> structure with a .map() hook that can translate irq specifiers from
> the device tree into linux irq numbers.
> 
> The patch also modifies the powerpc irq_host to embed the
> of_irq_domain structure and use the new common infrastructure for
> registering domains.  This separates the reverse mapping and irq
> allocation infrastructure of irq_host from the domain registration
> infrastructure.  I elected to split the functionality this way for
> several reasons.  First, with the major irq cleanup done by Thomas
> Gleixner, dynamic allocation of irqs can be handled gracefully with
> irq_alloc_desc*() and interrupt controllers may not need or want an
> irq_host layer to manage it for them.  For example, the new
> irq_chip_generic() already has a method for mapping between hwirq and
> Linux irq.
> 
> Second, the irq_host code currently has a lot of complexity to handle
> all the different reverse mapping types from a single structure.  The
> radix mapping in particular has a lot of support code, but only one
> user.  I didn't want to bring that complexity into the common code
> as-is.  Instead, I'd prefer to create a separate encapsulating
> structure for each revmap, and each type would have a separate .map
> hook.
> 
> For now, the mapping code remains powerpc-specific and further
> generalization will happen in subsequent patches.
> 
> Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
> ---
>  arch/microblaze/kernel/irq.c             |    7 --
>  arch/mips/kernel/prom.c                  |   14 ----
>  arch/powerpc/include/asm/irq.h           |   21 +++--
>  arch/powerpc/include/asm/irqhost.h       |    4 -
>  arch/powerpc/kernel/irq.c                |   99 ++++++++++++++-----------
>  arch/powerpc/platforms/cell/axon_msi.c   |   12 ++-
>  arch/powerpc/platforms/cell/spider-pic.c |    8 +-
>  arch/powerpc/sysdev/fsl_msi.c            |    2 -
>  arch/powerpc/sysdev/i8259.c              |    2 -
>  arch/powerpc/sysdev/ipic.c               |    2 -
>  arch/powerpc/sysdev/mpic.c               |    4 +
>  arch/powerpc/sysdev/mpic_msi.c           |    2 -
>  arch/powerpc/sysdev/mpic_pasemi_msi.c    |    4 +
>  arch/powerpc/sysdev/qe_lib/qe_ic.c       |    2 -
>  arch/x86/include/asm/irq_controller.h    |   12 ---
>  arch/x86/include/asm/prom.h              |    1 
>  arch/x86/kernel/devicetree.c             |   77 ++++----------------
>  drivers/of/irq.c                         |  118 ++++++++++++++++++++++++++++++
>  include/linux/of_irq.h                   |   31 ++++++++
>  19 files changed, 248 insertions(+), 174 deletions(-)
>  delete mode 100644 arch/x86/include/asm/irq_controller.h
> 
> diff --git a/arch/microblaze/kernel/irq.c b/arch/microblaze/kernel/irq.c
> index ce7ac84..59bb560 100644
> --- a/arch/microblaze/kernel/irq.c
> +++ b/arch/microblaze/kernel/irq.c
> @@ -54,10 +54,3 @@ unsigned int irq_create_mapping(struct irq_host *host, irq_hw_number_t hwirq)
>  	return hwirq;
>  }
>  EXPORT_SYMBOL_GPL(irq_create_mapping);
> -
> -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/arch/mips/kernel/prom.c b/arch/mips/kernel/prom.c
> index a19811e9..0b82f98 100644
> --- a/arch/mips/kernel/prom.c
> +++ b/arch/mips/kernel/prom.c
> @@ -60,20 +60,6 @@ void __init early_init_dt_setup_initrd_arch(unsigned long start,
>  }
>  #endif
>  
> -/*
> - * 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);
> -
>  void __init early_init_devtree(void *params)
>  {
>  	/* Setup flat device-tree pointer */
> diff --git a/arch/powerpc/include/asm/irq.h b/arch/powerpc/include/asm/irq.h
> index a44be93..ccefc8c 100644
> --- a/arch/powerpc/include/asm/irq.h
> +++ b/arch/powerpc/include/asm/irq.h
> @@ -55,11 +55,18 @@ typedef unsigned long irq_hw_number_t;
>   * model). It's the host callbacks that are responsible for setting the
>   * irq_chip on a given irq_desc after it's been mapped.
>   *
> + * irq_host builds upon the of_irq_domain code in drivers/of/irq.c.
> + * of_irq_domain provides all of the translation hooks for registering irq
> + * controllers. irq_host add mapping infrastructure to and from hardware
> + * irq numbers. IRQ controllers that don't need the mapping infrastructure
> + * can use irq_domain directly.
> + *
>   * The host code and data structures are fairly agnostic to the fact that
>   * we use an open firmware device-tree. We do have references to struct
> - * device_node in two places: in irq_find_host() to find the host matching
> - * a given interrupt controller node, and of course as an argument to its
> - * counterpart host->ops->match() callback. However, those are treated as
> + * device_node in two places: in of_irq_domain_find() to find the host matching
> + * a given interrupt controller node (which is actually common of_irq_domain
> + * code), and of course as an argument to its counterpart host->ops->match()
> + * and host->domain->match() callbacks. However, those are treated as
>   * generic pointers by the core and the fact that it's actually a device-node
>   * pointer is purely a convention between callers and implementation. This
>   * code could thus be used on other architectures by replacing those two
> @@ -137,14 +144,6 @@ extern struct irq_host *irq_alloc_host(struct device_node *of_node,
>  				       struct irq_host_ops *ops,
>  				       irq_hw_number_t inval_irq);
>  
> -
> -/**
> - * irq_find_host - Locates a host for a given device node
> - * @node: device-tree node of the interrupt controller
> - */
> -extern struct irq_host *irq_find_host(struct device_node *node);
> -
> -
>  /**
>   * irq_set_default_host - Set a "default" host
>   * @host: default host pointer
> diff --git a/arch/powerpc/include/asm/irqhost.h b/arch/powerpc/include/asm/irqhost.h
> index 958e6c1..a97a513 100644
> --- a/arch/powerpc/include/asm/irqhost.h
> +++ b/arch/powerpc/include/asm/irqhost.h
> @@ -8,6 +8,7 @@
>  
>  struct irq_host {
>  	struct list_head	link;
> +	struct of_irq_domain	domain;
>  
>  	/* type of reverse mapping technique */
>  	unsigned int		revmap_type;
> @@ -21,9 +22,6 @@ struct irq_host {
>  	struct irq_host_ops	*ops;
>  	void			*host_data;
>  	irq_hw_number_t		inval_irq;
> -
> -	/* Optional device node pointer */
> -	struct device_node	*of_node;
>  };
>  
>  #endif /* _ASM_POWERPC_IRQHOST_H */
> diff --git a/arch/powerpc/kernel/irq.c b/arch/powerpc/kernel/irq.c
> index b961b19..9300e1c 100644
> --- a/arch/powerpc/kernel/irq.c
> +++ b/arch/powerpc/kernel/irq.c
> @@ -514,11 +514,40 @@ struct irq_host *virq_to_host(unsigned int virq)
>  }
>  EXPORT_SYMBOL_GPL(virq_to_host);
>  
> -static int default_irq_host_match(struct irq_host *h, struct device_node *np)
> +/**
> + * irq_host_domain_match() - irq_domain hook to call irq_host match ops.
> + *
> + * This functions gets set as the irq domain match function for irq_host
> + * instances *if* the ->ops->match() hook is populated.  If ->match() is
> + * not populated, then the default irq_domain matching behaviour is used
> + * instead.
> + */
> +static bool irq_host_domain_match(struct of_irq_domain *domain,
> +				  struct device_node *controller)
>  {
> -	return h->of_node != NULL && h->of_node == np;
> +	struct irq_host *host = container_of(domain, struct irq_host, domain);
> +	return host->ops->match(host, controller);
>  }
>  
> +static unsigned int irq_host_domain_map(struct of_irq_domain *domain,
> +					struct device_node *controller,
> +					const u32 *intspec,
> +					unsigned int intsize);
> +
> +/**
> + * irq_alloc_host() - Allocate and register an irq_host
> + * @of_node: Device node of the irq controller; this is used mainly as an
> + *           anonymouns context pointer.
> + * @revmap_type: One of IRQ_HOST_MAP_* defined in arch/powerpc/include/asm/irq.h
> + *               Defines the type of reverse map to be used by the irq_host.
> + * @revmap_arg: Currently only used by the IRQ_HOST_MAP_LINEAR which uses it
> + *              to define the size of the reverse map.
> + * @ops: irq_host ops structure for match/map/unmap/remap/xlate operations.
> + * @inval_irq: Value used by irq controller to indicate an invalid irq.
> + *
> + * irq_host implements mapping between hardware irq numbers and the linux
> + * virq number space.  This function allocates and registers an irq_host.
> + */
>  struct irq_host *irq_alloc_host(struct device_node *of_node,
>  				unsigned int revmap_type,
>  				unsigned int revmap_arg,
> @@ -542,10 +571,10 @@ struct irq_host *irq_alloc_host(struct device_node *of_node,
>  	host->revmap_type = revmap_type;
>  	host->inval_irq = inval_irq;
>  	host->ops = ops;
> -	host->of_node = of_node_get(of_node);
> -
> -	if (host->ops->match == NULL)
> -		host->ops->match = default_irq_host_match;
> +	host->domain.controller = of_node_get(of_node);
> +	host->domain.map = irq_host_domain_map;
> +	if (host->ops->match != NULL)
> +		host->domain.match = irq_host_domain_match;
>  
>  	raw_spin_lock_irqsave(&irq_big_lock, flags);
>  
> @@ -561,7 +590,7 @@ struct irq_host *irq_alloc_host(struct device_node *of_node,
>  			 * instead of the current cruft
>  			 */
>  			if (mem_init_done) {
> -				of_node_put(host->of_node);
> +				of_node_put(host->domain.controller);
>  				kfree(host);
>  			}
>  			return NULL;
> @@ -572,6 +601,8 @@ struct irq_host *irq_alloc_host(struct device_node *of_node,
>  	list_add(&host->link, &irq_hosts);
>  	raw_spin_unlock_irqrestore(&irq_big_lock, flags);
>  
> +	of_irq_domain_add(&host->domain);
> +
>  	/* Additional setups per revmap type */
>  	switch(revmap_type) {
>  	case IRQ_HOST_MAP_LEGACY:
> @@ -611,32 +642,12 @@ struct irq_host *irq_alloc_host(struct device_node *of_node,
>  	return host;
>  }
>  
> -struct irq_host *irq_find_host(struct device_node *node)
> -{
> -	struct irq_host *h, *found = NULL;
> -	unsigned long flags;
> -
> -	/* We might want to match the legacy controller last since
> -	 * it might potentially be set to match all interrupts in
> -	 * the absence of a device node. This isn't a problem so far
> -	 * yet though...
> -	 */
> -	raw_spin_lock_irqsave(&irq_big_lock, flags);
> -	list_for_each_entry(h, &irq_hosts, link)
> -		if (h->ops->match(h, node)) {
> -			found = h;
> -			break;
> -		}
> -	raw_spin_unlock_irqrestore(&irq_big_lock, flags);
> -	return found;
> -}
> -EXPORT_SYMBOL_GPL(irq_find_host);
> -
>  void irq_set_default_host(struct irq_host *host)
>  {
>  	pr_debug("irq: Default host set to @0x%p\n", host);
>  
>  	irq_default_host = host;
> +	of_irq_set_default_domain(&host->domain);
>  }
>  
>  void irq_set_virq_count(unsigned int count)
> @@ -757,30 +768,29 @@ unsigned int irq_create_mapping(struct irq_host *host,
>  		return NO_IRQ;
>  
>  	printk(KERN_DEBUG "irq: irq %lu on host %s mapped to virtual irq %u\n",
> -		hwirq, host->of_node ? host->of_node->full_name : "null", virq);
> +		hwirq, host->domain.controller ? host->domain.controller->full_name : "null", virq);
>  
>  	return virq;
>  }
>  EXPORT_SYMBOL_GPL(irq_create_mapping);
>  
> -unsigned int irq_create_of_mapping(struct device_node *controller,
> -				   const u32 *intspec, unsigned int intsize)
> +/**
> + * irq_host_domain_map() - Map device tree irq to linux irq number
> + * This hook implements all of the powerpc 'irq_host' behaviour, which means
> + * - calling the ->ops->xlate hook to get the hardware irq number,
> + * - calling of_create_mapping to translate/allocate a linux virq number
> + * - calling irq_set_irq_type() if necessary
> + */
> +static unsigned int irq_host_domain_map(struct of_irq_domain *domain,
> +					struct device_node *controller,
> +					const u32 *intspec,
> +					unsigned int intsize)
>  {
> -	struct irq_host *host;
> +	struct irq_host *host = container_of(domain, struct irq_host, domain);
>  	irq_hw_number_t hwirq;
>  	unsigned int type = IRQ_TYPE_NONE;
>  	unsigned int virq;
>  
> -	if (controller == NULL)
> -		host = irq_default_host;
> -	else
> -		host = irq_find_host(controller);
> -	if (host == NULL) {
> -		printk(KERN_WARNING "irq: no irq host found for %s !\n",
> -		       controller->full_name);
> -		return NO_IRQ;
> -	}
> -
>  	/* If host has no translation, then we assume interrupt line */
>  	if (host->ops->xlate == NULL)
>  		hwirq = intspec[0];
> @@ -801,7 +811,6 @@ unsigned int irq_create_of_mapping(struct device_node *controller,
>  		irq_set_irq_type(virq, type);
>  	return virq;
>  }
> -EXPORT_SYMBOL_GPL(irq_create_of_mapping);
>  
>  void irq_dispose_mapping(unsigned int virq)
>  {
> @@ -1128,8 +1137,8 @@ static int virq_debug_show(struct seq_file *m, void *private)
>  				p = none;
>  			seq_printf(m, "%-15s  ", p);
>  
> -			if (irq_map[i].host && irq_map[i].host->of_node)
> -				p = irq_map[i].host->of_node->full_name;
> +			if (irq_map[i].host && irq_map[i].host->domain.controller)
> +				p = irq_map[i].host->domain.controller->full_name;
>  			else
>  				p = none;
>  			seq_printf(m, "%s\n", p);
> diff --git a/arch/powerpc/platforms/cell/axon_msi.c b/arch/powerpc/platforms/cell/axon_msi.c
> index e1469ae..f125673 100644
> --- a/arch/powerpc/platforms/cell/axon_msi.c
> +++ b/arch/powerpc/platforms/cell/axon_msi.c
> @@ -152,7 +152,7 @@ static void axon_msi_cascade(unsigned int irq, struct irq_desc *desc)
>  
>  static struct axon_msic *find_msi_translator(struct pci_dev *dev)
>  {
> -	struct irq_host *irq_host;
> +	struct of_irq_domain *irq_domain;
>  	struct device_node *dn, *tmp;
>  	const phandle *ph;
>  	struct axon_msic *msic = NULL;
> @@ -184,14 +184,14 @@ static struct axon_msic *find_msi_translator(struct pci_dev *dev)
>  		goto out_error;
>  	}
>  
> -	irq_host = irq_find_host(dn);
> -	if (!irq_host) {
> +	irq_domain = of_irq_domain_find(dn);
> +	if (!irq_domain) {
>  		dev_dbg(&dev->dev, "axon_msi: no irq_host found for node %s\n",
>  			dn->full_name);
>  		goto out_error;
>  	}
>  
> -	msic = irq_host->host_data;
> +	msic = irq_domain->priv;
>  
>  out_error:
>  	of_node_put(dn);
> @@ -336,7 +336,7 @@ static void axon_msi_shutdown(struct platform_device *device)
>  	u32 tmp;
>  
>  	pr_devel("axon_msi: disabling %s\n",
> -		  msic->irq_host->of_node->full_name);
> +		  msic->irq_host->domain.controller->full_name);
>  	tmp  = dcr_read(msic->dcr_host, MSIC_CTRL_REG);
>  	tmp &= ~MSIC_CTRL_ENABLE & ~MSIC_CTRL_IRQ_ENABLE;
>  	msic_dcr_write(msic, MSIC_CTRL_REG, tmp);
> @@ -399,7 +399,7 @@ static int axon_msi_probe(struct platform_device *device)
>  		goto out_free_fifo;
>  	}
>  
> -	msic->irq_host->host_data = msic;
> +	msic->irq_host->domain.priv = msic;
>  
>  	irq_set_handler_data(virq, msic);
>  	irq_set_chained_handler(virq, axon_msi_cascade);
> diff --git a/arch/powerpc/platforms/cell/spider-pic.c b/arch/powerpc/platforms/cell/spider-pic.c
> index 73a5494..5bf36ab 100644
> --- a/arch/powerpc/platforms/cell/spider-pic.c
> +++ b/arch/powerpc/platforms/cell/spider-pic.c
> @@ -236,18 +236,20 @@ static unsigned int __init spider_find_cascade_and_node(struct spider_pic *pic)
>  	 * tree in case the device-tree is ever fixed
>  	 */
>  	struct of_irq oirq;
> -	if (of_irq_map_one(pic->host->of_node, 0, &oirq) == 0) {
> +	if (of_irq_map_one(pic->host->domain.controller, 0, &oirq) == 0) {
>  		virq = irq_create_of_mapping(oirq.controller, oirq.specifier,
>  					     oirq.size);
>  		return virq;
>  	}
>  
>  	/* Now do the horrible hacks */
> -	tmp = of_get_property(pic->host->of_node, "#interrupt-cells", NULL);
> +	tmp = of_get_property(pic->host->domain.controller,
> +				"#interrupt-cells", NULL);
>  	if (tmp == NULL)
>  		return NO_IRQ;
>  	intsize = *tmp;
> -	imap = of_get_property(pic->host->of_node, "interrupt-map", &imaplen);
> +	imap = of_get_property(pic->host->domain.controller,
> +				"interrupt-map", &imaplen);
>  	if (imap == NULL || imaplen < (intsize + 1))
>  		return NO_IRQ;
>  	iic = of_find_node_by_phandle(imap[intsize]);
> diff --git a/arch/powerpc/sysdev/fsl_msi.c b/arch/powerpc/sysdev/fsl_msi.c
> index 2c11b3e..b45a25a 100644
> --- a/arch/powerpc/sysdev/fsl_msi.c
> +++ b/arch/powerpc/sysdev/fsl_msi.c
> @@ -82,7 +82,7 @@ static int fsl_msi_init_allocator(struct fsl_msi *msi_data)
>  	int rc;
>  
>  	rc = msi_bitmap_alloc(&msi_data->bitmap, NR_MSI_IRQS,
> -			      msi_data->irqhost->of_node);
> +			      msi_data->irqhost->domain.controller);
>  	if (rc)
>  		return rc;
>  
> diff --git a/arch/powerpc/sysdev/i8259.c b/arch/powerpc/sysdev/i8259.c
> index 30869f0..a6f78fb 100644
> --- a/arch/powerpc/sysdev/i8259.c
> +++ b/arch/powerpc/sysdev/i8259.c
> @@ -166,7 +166,7 @@ static struct resource pic_edgectrl_iores = {
>  
>  static int i8259_host_match(struct irq_host *h, struct device_node *node)
>  {
> -	return h->of_node == NULL || h->of_node == node;
> +	return h->domain.controller == NULL || h->domain.controller == node;
>  }
>  
>  static int i8259_host_map(struct irq_host *h, unsigned int virq,
> diff --git a/arch/powerpc/sysdev/ipic.c b/arch/powerpc/sysdev/ipic.c
> index fc3751f..5805c7b 100644
> --- a/arch/powerpc/sysdev/ipic.c
> +++ b/arch/powerpc/sysdev/ipic.c
> @@ -676,7 +676,7 @@ static struct irq_chip ipic_edge_irq_chip = {
>  static int ipic_host_match(struct irq_host *h, struct device_node *node)
>  {
>  	/* Exact match, unless ipic node is NULL */
> -	return h->of_node == NULL || h->of_node == node;
> +	return h->domain.controller == NULL || h->domain.controller == node;
>  }
>  
>  static int ipic_host_map(struct irq_host *h, unsigned int virq,
> diff --git a/arch/powerpc/sysdev/mpic.c b/arch/powerpc/sysdev/mpic.c
> index 6e9e594..cafc364 100644
> --- a/arch/powerpc/sysdev/mpic.c
> +++ b/arch/powerpc/sysdev/mpic.c
> @@ -956,7 +956,7 @@ static struct irq_chip mpic_irq_ht_chip = {
>  static int mpic_host_match(struct irq_host *h, struct device_node *node)
>  {
>  	/* Exact match, unless mpic node is NULL */
> -	return h->of_node == NULL || h->of_node == node;
> +	return h->domain.controller == NULL || h->domain.controller == node;
>  }
>  
>  static int mpic_host_map(struct irq_host *h, unsigned int virq,
> @@ -1296,7 +1296,7 @@ void __init mpic_assign_isu(struct mpic *mpic, unsigned int isu_num,
>  
>  	BUG_ON(isu_num >= MPIC_MAX_ISU);
>  
> -	mpic_map(mpic, mpic->irqhost->of_node,
> +	mpic_map(mpic, mpic->irqhost->domain.controller,
>  		 paddr, &mpic->isus[isu_num], 0,
>  		 MPIC_INFO(IRQ_STRIDE) * mpic->isu_size);
>  
> diff --git a/arch/powerpc/sysdev/mpic_msi.c b/arch/powerpc/sysdev/mpic_msi.c
> index 50176ed..ddf79c7 100644
> --- a/arch/powerpc/sysdev/mpic_msi.c
> +++ b/arch/powerpc/sysdev/mpic_msi.c
> @@ -85,7 +85,7 @@ int mpic_msi_init_allocator(struct mpic *mpic)
>  	int rc;
>  
>  	rc = msi_bitmap_alloc(&mpic->msi_bitmap, mpic->irq_count,
> -			      mpic->irqhost->of_node);
> +			      mpic->irqhost->domain.controller);
>  	if (rc)
>  		return rc;
>  
> diff --git a/arch/powerpc/sysdev/mpic_pasemi_msi.c b/arch/powerpc/sysdev/mpic_pasemi_msi.c
> index 6b11a89..857be51 100644
> --- a/arch/powerpc/sysdev/mpic_pasemi_msi.c
> +++ b/arch/powerpc/sysdev/mpic_pasemi_msi.c
> @@ -153,8 +153,8 @@ int mpic_pasemi_msi_init(struct mpic *mpic)
>  {
>  	int rc;
>  
> -	if (!mpic->irqhost->of_node ||
> -	    !of_device_is_compatible(mpic->irqhost->of_node,
> +	if (!mpic->irqhost->domain.controller ||
> +	    !of_device_is_compatible(mpic->irqhost->domain.controller,
>  				     "pasemi,pwrficient-openpic"))
>  		return -ENODEV;
>  
> diff --git a/arch/powerpc/sysdev/qe_lib/qe_ic.c b/arch/powerpc/sysdev/qe_lib/qe_ic.c
> index 9dd7746..c2ccafa 100644
> --- a/arch/powerpc/sysdev/qe_lib/qe_ic.c
> +++ b/arch/powerpc/sysdev/qe_lib/qe_ic.c
> @@ -250,7 +250,7 @@ static struct irq_chip qe_ic_irq_chip = {
>  static int qe_ic_host_match(struct irq_host *h, struct device_node *node)
>  {
>  	/* Exact match, unless qe_ic node is NULL */
> -	return h->of_node == NULL || h->of_node == node;
> +	return h->domain.controller == NULL || h->domain.controller == node;
>  }
>  
>  static int qe_ic_host_map(struct irq_host *h, unsigned int virq,
> diff --git a/arch/x86/include/asm/irq_controller.h b/arch/x86/include/asm/irq_controller.h
> deleted file mode 100644
> index 423bbbd..0000000
> --- a/arch/x86/include/asm/irq_controller.h
> +++ /dev/null
> @@ -1,12 +0,0 @@
> -#ifndef __IRQ_CONTROLLER__
> -#define __IRQ_CONTROLLER__
> -
> -struct irq_domain {
> -	int (*xlate)(struct irq_domain *h, const u32 *intspec, u32 intsize,
> -			u32 *out_hwirq, u32 *out_type);
> -	void *priv;
> -	struct device_node *controller;
> -	struct list_head l;
> -};
> -
> -#endif
> diff --git a/arch/x86/include/asm/prom.h b/arch/x86/include/asm/prom.h
> index 971e0b4..eb9d5ab 100644
> --- a/arch/x86/include/asm/prom.h
> +++ b/arch/x86/include/asm/prom.h
> @@ -21,7 +21,6 @@
>  #include <asm/irq.h>
>  #include <asm/atomic.h>
>  #include <asm/setup.h>
> -#include <asm/irq_controller.h>
>  
>  #ifdef CONFIG_OF
>  extern int of_ioapic;
> diff --git a/arch/x86/kernel/devicetree.c b/arch/x86/kernel/devicetree.c
> index 706a9fb..651e724 100644
> --- a/arch/x86/kernel/devicetree.c
> +++ b/arch/x86/kernel/devicetree.c
> @@ -15,64 +15,14 @@
>  #include <linux/of_pci.h>
>  
>  #include <asm/hpet.h>
> -#include <asm/irq_controller.h>
>  #include <asm/apic.h>
>  #include <asm/pci_x86.h>
>  
>  __initdata u64 initial_dtb;
>  char __initdata cmd_line[COMMAND_LINE_SIZE];
> -static LIST_HEAD(irq_domains);
> -static DEFINE_RAW_SPINLOCK(big_irq_lock);
>  
>  int __initdata of_ioapic;
>  
> -#ifdef CONFIG_X86_IO_APIC
> -static void add_interrupt_host(struct irq_domain *ih)
> -{
> -	unsigned long flags;
> -
> -	raw_spin_lock_irqsave(&big_irq_lock, flags);
> -	list_add(&ih->l, &irq_domains);
> -	raw_spin_unlock_irqrestore(&big_irq_lock, flags);
> -}
> -#endif
> -
> -static struct irq_domain *get_ih_from_node(struct device_node *controller)
> -{
> -	struct irq_domain *ih, *found = NULL;
> -	unsigned long flags;
> -
> -	raw_spin_lock_irqsave(&big_irq_lock, flags);
> -	list_for_each_entry(ih, &irq_domains, l) {
> -		if (ih->controller ==  controller) {
> -			found = ih;
> -			break;
> -		}
> -	}
> -	raw_spin_unlock_irqrestore(&big_irq_lock, flags);
> -	return found;
> -}
> -
> -unsigned int irq_create_of_mapping(struct device_node *controller,
> -				   const u32 *intspec, unsigned int intsize)
> -{
> -	struct irq_domain *ih;
> -	u32 virq, type;
> -	int ret;
> -
> -	ih = get_ih_from_node(controller);
> -	if (!ih)
> -		return 0;
> -	ret = ih->xlate(ih, intspec, intsize, &virq, &type);
> -	if (ret)
> -		return 0;
> -	if (type == IRQ_TYPE_NONE)
> -		return virq;
> -	irq_set_irq_type(virq, type);
> -	return virq;
> -}
> -EXPORT_SYMBOL_GPL(irq_create_of_mapping);
> -
>  unsigned long pci_address_to_pio(phys_addr_t address)
>  {
>  	/*
> @@ -366,32 +316,33 @@ static struct of_ioapic_type of_ioapic_type[] =
>  	},
>  };
>  
> -static int ioapic_xlate(struct irq_domain *id, const u32 *intspec, u32 intsize,
> -			u32 *out_hwirq, u32 *out_type)
> +static unsigned int ioapic_of_irq_map(struct of_irq_domain *id,
> +				      struct device_node *np,
> +				      const u32 *intspec, u32 intsize)
>  {
>  	struct io_apic_irq_attr attr;
>  	struct of_ioapic_type *it;
> -	u32 line, idx, type;
> +	u32 line, idx, type, hwirq;
>  
>  	if (intsize < 2)
> -		return -EINVAL;
> +		return 0;
>  
>  	line = *intspec;
>  	idx = (u32) id->priv;
> -	*out_hwirq = line + mp_gsi_routing[idx].gsi_base;
> +	hwirq = line + mp_gsi_routing[idx].gsi_base;
>  
>  	intspec++;
>  	type = *intspec;
>  
>  	if (type >= ARRAY_SIZE(of_ioapic_type))
> -		return -EINVAL;
> +		return 0;
>  
>  	it = of_ioapic_type + type;
> -	*out_type = it->out_type;
> -
>  	set_io_apic_irq_attr(&attr, idx, line, it->trigger, it->polarity);
>  
> -	return io_apic_setup_irq_pin(*out_hwirq, cpu_to_node(0), &attr);
> +	if (io_apic_setup_irq_pin(hwirq, cpu_to_node(0), &attr))
> +		return 0;
> +	return hwirq;
>  }
>  
>  static void __init ioapic_add_ofnode(struct device_node *np)
> @@ -408,14 +359,14 @@ static void __init ioapic_add_ofnode(struct device_node *np)
>  
>  	for (i = 0; i < nr_ioapics; i++) {
>  		if (r.start == mp_ioapics[i].apicaddr) {
> -			struct irq_domain *id;
> +			struct of_irq_domain *id;
>  
>  			id = kzalloc(sizeof(*id), GFP_KERNEL);
>  			BUG_ON(!id);
> -			id->controller = np;
> -			id->xlate = ioapic_xlate;
> +			id->controller = of_node_get(np);
> +			id->map = ioapic_of_irq_map;
>  			id->priv = (void *)i;
> -			add_interrupt_host(id);
> +			of_irq_domain_add(id);
>  			return;
>  		}
>  	}
> diff --git a/drivers/of/irq.c b/drivers/of/irq.c
> index 75b0d3c..6da0964 100644
> --- a/drivers/of/irq.c
> +++ b/drivers/of/irq.c
> @@ -19,6 +19,7 @@
>   */
>  
>  #include <linux/errno.h>
> +#include <linux/list.h>
>  #include <linux/module.h>
>  #include <linux/of.h>
>  #include <linux/of_irq.h>
> @@ -29,6 +30,123 @@
>  #define NO_IRQ 0
>  #endif
>  
> +/*
> + * Device Tree IRQ domains
> + *
> + * IRQ domains provide translation from device tree irq controller nodes to
> + * linux IRQ numbers.  IRQ controllers register an irq_domain with a .map()
> + * hook that performs everything needed to decode and configure a device
> + * tree specified interrupt.
> + */
> +static LIST_HEAD(of_irq_domains);
> +static DEFINE_RAW_SPINLOCK(of_irq_lock);
> +static struct of_irq_domain *of_irq_default_domain;
> +
> +/**
> + * of_irq_domain_default_match() - Return true if the controller pointers match
> + *
> + * Default match behaviour for of_irq_domains.  If the device tree node pointer
> + * matches the value stored in the domain structure, then return true.
> + */
> +static bool of_irq_domain_default_match(struct of_irq_domain *domain,
> +					struct device_node *controller)
> +{
> +	return domain->controller == controller;
> +}
> +
> +/**
> + * of_irq_domain_add() - Register a device tree irq domain
> + * @domain: pointer to domain structure to be registered.
> + *
> + * Adds an of_irq_domain to the global list of domains.
> + */
> +void of_irq_domain_add(struct of_irq_domain *domain)
> +{
> +	unsigned long flags;
> +
> +	if (!domain->match)
> +		domain->match = of_irq_domain_default_match;
> +	if (!domain->map) {
> +		WARN_ON(1);
> +		return;
> +	}
> +
> +	raw_spin_lock_irqsave(&of_irq_lock, flags);
> +	list_add(&domain->list, &of_irq_domains);
> +	raw_spin_unlock_irqrestore(&of_irq_lock, flags);
> +}
> +
> +/**
> + * of_irq_domain_find() - Find the domain that handles a given device tree node
> + *
> + * Returns the pointer to an of_irq_domain capable of translating irq specifiers
> + * for the given irq controller device tree node.  Returns NULL if a suitable
> + * domain could not be found.
> + */
> +struct of_irq_domain *of_irq_domain_find(struct device_node *controller)
> +{
> +	struct of_irq_domain *domain, *found = NULL;
> +	unsigned long flags;
> +
> +	raw_spin_lock_irqsave(&of_irq_lock, flags);
> +	list_for_each_entry(domain, &of_irq_domains, list) {
> +		if (domain->match(domain, controller)) {
> +			found = domain;
> +			break;
> +		}
> +	}
> +	raw_spin_unlock_irqrestore(&of_irq_lock, flags);
> +	return found;
> +}
> +
> +/**
> + * of_irq_set_default_domain() - Set a "default" host
> + * @domain: default domain pointer
> + *
> + * For convenience, it's possible to set a "default" host that will be used
> + * whenever NULL is passed to irq_of_create_mapping(). It makes life easier
> + * for platforms that want to manipulate a few hard coded interrupt numbers
> + * that aren't properly represented in the device-tree.
> + */
> +void of_irq_set_default_domain(struct of_irq_domain *domain)
> +{
> +	pr_debug("irq: Default host set to @0x%p\n", domain);
> +	of_irq_default_domain = domain;
> +}
> +
> +/**
> + * irq_create_of_mapping() - Map a linux irq # from a device tree specifier
> + * @controller - interrupt-controller node in the device tree
> + * @intspec - array of interrupt specifier data.  Points to an array of u32
> + *            values.  Data is *cpu-native* endian u32 values.
> + * @intsize - size of intspec array.
> + *
> + * Given an interrupt controller node pointer and an interrupt specifier, this
> + * function looks up the linux irq number.
> + */
> +unsigned int irq_create_of_mapping(struct device_node *controller,
> +				   const u32 *intspec, unsigned int intsize)
> +{
> +	struct of_irq_domain *domain;
> +
> +	domain = of_irq_domain_find(controller);
> +	if (!domain)
> +		domain = of_irq_default_domain;
> +	if (!domain) {
> +		pr_warn("error: no irq host found for %s !\n",
> +			controller->full_name);
> +#if defined(CONFIG_MIPS) || defined(CONFIG_MICROBLAZE)
> +		/* FIXME: make Microblaze and MIPS register irq domains */
> +		return intspec[0];
> +#else /* defined(CONFIG_MIPS) || defined(CONFIG_MICROBLAZE) */
> +		return NO_IRQ;
> +#endif /* defined(CONFIG_MIPS) || defined(CONFIG_MICROBLAZE) */
> +	}
> +
> +	return domain->map(domain, controller, intspec, intsize);
> +}
> +EXPORT_SYMBOL_GPL(irq_create_of_mapping);
> +
>  /**
>   * irq_of_parse_and_map - Parse and map an interrupt into linux virq space
>   * @device: Device node of the device whose interrupt is to be mapped
> diff --git a/include/linux/of_irq.h b/include/linux/of_irq.h
> index 109e013..511dbc3 100644
> --- a/include/linux/of_irq.h
> +++ b/include/linux/of_irq.h
> @@ -33,6 +33,37 @@ struct of_irq {
>  	u32 specifier[OF_MAX_IRQ_SPEC]; /* Specifier copy */
>  };
>  
> +/**
> + * struct of_irq_domain - Translation domain from device tree to linux irq
> + * @list: Linked list node entry
> + * @match: (optional) Called to determine if the passed device_node
> + *         interrupt-controller can be translated by this irq domain.
> + *         Returns 'true' if it can.
> + * @decode: Translation callback; returns virq, or NO_IRQ if this irq
> + *          domain cannot translate it.
> + * @controller: (optional) pointer to OF node.  By default, if
> + *              'match' is not set, then this of_irq_domain will only
> + *              be used if the device tree node passed in matches the
> + *              controller pointer.
> + * @priv: Private data pointer, not touched by core of_irq_domain code.
> + */
> +struct of_irq_domain {
> +	struct list_head list;
> +	bool (*match)(struct of_irq_domain *d, struct device_node *np);
> +	unsigned int (*map)(struct of_irq_domain *d, struct device_node *np,
> +			    const u32 *intspec, u32 intsize);
> +	struct device_node *controller;
> +	void *priv;
> +};
> +
> +/**
> + * of_irq_domain_add() - Add a device tree interrupt translation domain
> + * @domain: interrupt domain to add.
> + */
> +extern void of_irq_domain_add(struct of_irq_domain *domain);
> +extern void of_irq_set_default_domain(struct of_irq_domain *host);
> +extern struct of_irq_domain *of_irq_domain_find(struct device_node *controller);
> +
>  /*
>   * Workarounds only applied to 32bit powermac machines
>   */
Grant Likely May 4, 2011, 4:05 p.m. UTC | #2
On Tue, May 03, 2011 at 11:50:22AM +1000, Benjamin Herrenschmidt wrote:
> On Thu, 2011-04-28 at 14:02 -0600, Grant Likely wrote:
> > This patch creates a common implementation of irq_of_create_mapping()
> > and factors out the interrupt domain translation code from powerpc to
> > make it available for all architectures.
> 
> I think you are going the wrong way around.
> 
> First thing first, is to make the irq domain / mapping API generic
> without the OF bits.
> 
> IE. move the IRQ domain generically, get rid of irq_map by putting the
> domain ptr & hw numbers in the irq desc/data etc...
> 
> Then you can move over the OF specific bits which are optional and
> orthogonal to a large extent.

As discussed in my other reply, I disagree.  There isn't an immediate
need for the mapping interface in common code.  It would be useful,
sure, for some interrupt controllers, but for many of them
irq_alloc_descs() and an irq_base value is all the functionality that
is needed, and irq_host doesn't gain anything.

The OF translation on the other hand is needed immediately by several
architectures and are very much non-optional in that regard.

g.
Benjamin Herrenschmidt May 5, 2011, 12:43 a.m. UTC | #3
On Wed, 2011-05-04 at 10:05 -0600, Grant Likely wrote:
> > I think you are going the wrong way around.
> > 
> > First thing first, is to make the irq domain / mapping API generic
> > without the OF bits.
> > 
> > IE. move the IRQ domain generically, get rid of irq_map by putting
> the
> > domain ptr & hw numbers in the irq desc/data etc...
> > 
> > Then you can move over the OF specific bits which are optional and
> > orthogonal to a large extent.
> 
> As discussed in my other reply, I disagree.  There isn't an immediate
> need for the mapping interface in common code.  It would be useful,
> sure, for some interrupt controllers, but for many of them
> irq_alloc_descs() and an irq_base value is all the functionality that
> is needed, and irq_host doesn't gain anything.

No but the concept of domain is a pre-requisite. Even if it's an opaque
data structure. And I don't want to have it be some "of" specific thing.

> The OF translation on the other hand is needed immediately by several
> architectures and are very much non-optional in that regard.

But it relies on having an underlying mapping. I don't see how you can
do one without the other, even if your mapping in effect is just an
offset.

And it is -not- related to OF.

Whether you obtain that your interrupt you are interested it is
interrupt 5 of your PIC "foo" via the device-tree or any other way (an
arch quirk for example), you need the mechanism to associate them
together, whether it's a simple offset as you propose (I'm ok with that
for simple things, I just didn't think it was the right approach for
powerpc but it's perfectly valid as an option generically) or a more
complex radix-tree style mapping.

Thus sort out the mapping interfaces first. _Then_ layout the
device-tree bits on top.

The basic parsing for the DT is already there. It will return a parent
node and a __be32* pointer.

Cheers,
Ben.
diff mbox

Patch

diff --git a/arch/microblaze/kernel/irq.c b/arch/microblaze/kernel/irq.c
index ce7ac84..59bb560 100644
--- a/arch/microblaze/kernel/irq.c
+++ b/arch/microblaze/kernel/irq.c
@@ -54,10 +54,3 @@  unsigned int irq_create_mapping(struct irq_host *host, irq_hw_number_t hwirq)
 	return hwirq;
 }
 EXPORT_SYMBOL_GPL(irq_create_mapping);
-
-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/arch/mips/kernel/prom.c b/arch/mips/kernel/prom.c
index a19811e9..0b82f98 100644
--- a/arch/mips/kernel/prom.c
+++ b/arch/mips/kernel/prom.c
@@ -60,20 +60,6 @@  void __init early_init_dt_setup_initrd_arch(unsigned long start,
 }
 #endif
 
-/*
- * 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);
-
 void __init early_init_devtree(void *params)
 {
 	/* Setup flat device-tree pointer */
diff --git a/arch/powerpc/include/asm/irq.h b/arch/powerpc/include/asm/irq.h
index a44be93..ccefc8c 100644
--- a/arch/powerpc/include/asm/irq.h
+++ b/arch/powerpc/include/asm/irq.h
@@ -55,11 +55,18 @@  typedef unsigned long irq_hw_number_t;
  * model). It's the host callbacks that are responsible for setting the
  * irq_chip on a given irq_desc after it's been mapped.
  *
+ * irq_host builds upon the of_irq_domain code in drivers/of/irq.c.
+ * of_irq_domain provides all of the translation hooks for registering irq
+ * controllers. irq_host add mapping infrastructure to and from hardware
+ * irq numbers. IRQ controllers that don't need the mapping infrastructure
+ * can use irq_domain directly.
+ *
  * The host code and data structures are fairly agnostic to the fact that
  * we use an open firmware device-tree. We do have references to struct
- * device_node in two places: in irq_find_host() to find the host matching
- * a given interrupt controller node, and of course as an argument to its
- * counterpart host->ops->match() callback. However, those are treated as
+ * device_node in two places: in of_irq_domain_find() to find the host matching
+ * a given interrupt controller node (which is actually common of_irq_domain
+ * code), and of course as an argument to its counterpart host->ops->match()
+ * and host->domain->match() callbacks. However, those are treated as
  * generic pointers by the core and the fact that it's actually a device-node
  * pointer is purely a convention between callers and implementation. This
  * code could thus be used on other architectures by replacing those two
@@ -137,14 +144,6 @@  extern struct irq_host *irq_alloc_host(struct device_node *of_node,
 				       struct irq_host_ops *ops,
 				       irq_hw_number_t inval_irq);
 
-
-/**
- * irq_find_host - Locates a host for a given device node
- * @node: device-tree node of the interrupt controller
- */
-extern struct irq_host *irq_find_host(struct device_node *node);
-
-
 /**
  * irq_set_default_host - Set a "default" host
  * @host: default host pointer
diff --git a/arch/powerpc/include/asm/irqhost.h b/arch/powerpc/include/asm/irqhost.h
index 958e6c1..a97a513 100644
--- a/arch/powerpc/include/asm/irqhost.h
+++ b/arch/powerpc/include/asm/irqhost.h
@@ -8,6 +8,7 @@ 
 
 struct irq_host {
 	struct list_head	link;
+	struct of_irq_domain	domain;
 
 	/* type of reverse mapping technique */
 	unsigned int		revmap_type;
@@ -21,9 +22,6 @@  struct irq_host {
 	struct irq_host_ops	*ops;
 	void			*host_data;
 	irq_hw_number_t		inval_irq;
-
-	/* Optional device node pointer */
-	struct device_node	*of_node;
 };
 
 #endif /* _ASM_POWERPC_IRQHOST_H */
diff --git a/arch/powerpc/kernel/irq.c b/arch/powerpc/kernel/irq.c
index b961b19..9300e1c 100644
--- a/arch/powerpc/kernel/irq.c
+++ b/arch/powerpc/kernel/irq.c
@@ -514,11 +514,40 @@  struct irq_host *virq_to_host(unsigned int virq)
 }
 EXPORT_SYMBOL_GPL(virq_to_host);
 
-static int default_irq_host_match(struct irq_host *h, struct device_node *np)
+/**
+ * irq_host_domain_match() - irq_domain hook to call irq_host match ops.
+ *
+ * This functions gets set as the irq domain match function for irq_host
+ * instances *if* the ->ops->match() hook is populated.  If ->match() is
+ * not populated, then the default irq_domain matching behaviour is used
+ * instead.
+ */
+static bool irq_host_domain_match(struct of_irq_domain *domain,
+				  struct device_node *controller)
 {
-	return h->of_node != NULL && h->of_node == np;
+	struct irq_host *host = container_of(domain, struct irq_host, domain);
+	return host->ops->match(host, controller);
 }
 
+static unsigned int irq_host_domain_map(struct of_irq_domain *domain,
+					struct device_node *controller,
+					const u32 *intspec,
+					unsigned int intsize);
+
+/**
+ * irq_alloc_host() - Allocate and register an irq_host
+ * @of_node: Device node of the irq controller; this is used mainly as an
+ *           anonymouns context pointer.
+ * @revmap_type: One of IRQ_HOST_MAP_* defined in arch/powerpc/include/asm/irq.h
+ *               Defines the type of reverse map to be used by the irq_host.
+ * @revmap_arg: Currently only used by the IRQ_HOST_MAP_LINEAR which uses it
+ *              to define the size of the reverse map.
+ * @ops: irq_host ops structure for match/map/unmap/remap/xlate operations.
+ * @inval_irq: Value used by irq controller to indicate an invalid irq.
+ *
+ * irq_host implements mapping between hardware irq numbers and the linux
+ * virq number space.  This function allocates and registers an irq_host.
+ */
 struct irq_host *irq_alloc_host(struct device_node *of_node,
 				unsigned int revmap_type,
 				unsigned int revmap_arg,
@@ -542,10 +571,10 @@  struct irq_host *irq_alloc_host(struct device_node *of_node,
 	host->revmap_type = revmap_type;
 	host->inval_irq = inval_irq;
 	host->ops = ops;
-	host->of_node = of_node_get(of_node);
-
-	if (host->ops->match == NULL)
-		host->ops->match = default_irq_host_match;
+	host->domain.controller = of_node_get(of_node);
+	host->domain.map = irq_host_domain_map;
+	if (host->ops->match != NULL)
+		host->domain.match = irq_host_domain_match;
 
 	raw_spin_lock_irqsave(&irq_big_lock, flags);
 
@@ -561,7 +590,7 @@  struct irq_host *irq_alloc_host(struct device_node *of_node,
 			 * instead of the current cruft
 			 */
 			if (mem_init_done) {
-				of_node_put(host->of_node);
+				of_node_put(host->domain.controller);
 				kfree(host);
 			}
 			return NULL;
@@ -572,6 +601,8 @@  struct irq_host *irq_alloc_host(struct device_node *of_node,
 	list_add(&host->link, &irq_hosts);
 	raw_spin_unlock_irqrestore(&irq_big_lock, flags);
 
+	of_irq_domain_add(&host->domain);
+
 	/* Additional setups per revmap type */
 	switch(revmap_type) {
 	case IRQ_HOST_MAP_LEGACY:
@@ -611,32 +642,12 @@  struct irq_host *irq_alloc_host(struct device_node *of_node,
 	return host;
 }
 
-struct irq_host *irq_find_host(struct device_node *node)
-{
-	struct irq_host *h, *found = NULL;
-	unsigned long flags;
-
-	/* We might want to match the legacy controller last since
-	 * it might potentially be set to match all interrupts in
-	 * the absence of a device node. This isn't a problem so far
-	 * yet though...
-	 */
-	raw_spin_lock_irqsave(&irq_big_lock, flags);
-	list_for_each_entry(h, &irq_hosts, link)
-		if (h->ops->match(h, node)) {
-			found = h;
-			break;
-		}
-	raw_spin_unlock_irqrestore(&irq_big_lock, flags);
-	return found;
-}
-EXPORT_SYMBOL_GPL(irq_find_host);
-
 void irq_set_default_host(struct irq_host *host)
 {
 	pr_debug("irq: Default host set to @0x%p\n", host);
 
 	irq_default_host = host;
+	of_irq_set_default_domain(&host->domain);
 }
 
 void irq_set_virq_count(unsigned int count)
@@ -757,30 +768,29 @@  unsigned int irq_create_mapping(struct irq_host *host,
 		return NO_IRQ;
 
 	printk(KERN_DEBUG "irq: irq %lu on host %s mapped to virtual irq %u\n",
-		hwirq, host->of_node ? host->of_node->full_name : "null", virq);
+		hwirq, host->domain.controller ? host->domain.controller->full_name : "null", virq);
 
 	return virq;
 }
 EXPORT_SYMBOL_GPL(irq_create_mapping);
 
-unsigned int irq_create_of_mapping(struct device_node *controller,
-				   const u32 *intspec, unsigned int intsize)
+/**
+ * irq_host_domain_map() - Map device tree irq to linux irq number
+ * This hook implements all of the powerpc 'irq_host' behaviour, which means
+ * - calling the ->ops->xlate hook to get the hardware irq number,
+ * - calling of_create_mapping to translate/allocate a linux virq number
+ * - calling irq_set_irq_type() if necessary
+ */
+static unsigned int irq_host_domain_map(struct of_irq_domain *domain,
+					struct device_node *controller,
+					const u32 *intspec,
+					unsigned int intsize)
 {
-	struct irq_host *host;
+	struct irq_host *host = container_of(domain, struct irq_host, domain);
 	irq_hw_number_t hwirq;
 	unsigned int type = IRQ_TYPE_NONE;
 	unsigned int virq;
 
-	if (controller == NULL)
-		host = irq_default_host;
-	else
-		host = irq_find_host(controller);
-	if (host == NULL) {
-		printk(KERN_WARNING "irq: no irq host found for %s !\n",
-		       controller->full_name);
-		return NO_IRQ;
-	}
-
 	/* If host has no translation, then we assume interrupt line */
 	if (host->ops->xlate == NULL)
 		hwirq = intspec[0];
@@ -801,7 +811,6 @@  unsigned int irq_create_of_mapping(struct device_node *controller,
 		irq_set_irq_type(virq, type);
 	return virq;
 }
-EXPORT_SYMBOL_GPL(irq_create_of_mapping);
 
 void irq_dispose_mapping(unsigned int virq)
 {
@@ -1128,8 +1137,8 @@  static int virq_debug_show(struct seq_file *m, void *private)
 				p = none;
 			seq_printf(m, "%-15s  ", p);
 
-			if (irq_map[i].host && irq_map[i].host->of_node)
-				p = irq_map[i].host->of_node->full_name;
+			if (irq_map[i].host && irq_map[i].host->domain.controller)
+				p = irq_map[i].host->domain.controller->full_name;
 			else
 				p = none;
 			seq_printf(m, "%s\n", p);
diff --git a/arch/powerpc/platforms/cell/axon_msi.c b/arch/powerpc/platforms/cell/axon_msi.c
index e1469ae..f125673 100644
--- a/arch/powerpc/platforms/cell/axon_msi.c
+++ b/arch/powerpc/platforms/cell/axon_msi.c
@@ -152,7 +152,7 @@  static void axon_msi_cascade(unsigned int irq, struct irq_desc *desc)
 
 static struct axon_msic *find_msi_translator(struct pci_dev *dev)
 {
-	struct irq_host *irq_host;
+	struct of_irq_domain *irq_domain;
 	struct device_node *dn, *tmp;
 	const phandle *ph;
 	struct axon_msic *msic = NULL;
@@ -184,14 +184,14 @@  static struct axon_msic *find_msi_translator(struct pci_dev *dev)
 		goto out_error;
 	}
 
-	irq_host = irq_find_host(dn);
-	if (!irq_host) {
+	irq_domain = of_irq_domain_find(dn);
+	if (!irq_domain) {
 		dev_dbg(&dev->dev, "axon_msi: no irq_host found for node %s\n",
 			dn->full_name);
 		goto out_error;
 	}
 
-	msic = irq_host->host_data;
+	msic = irq_domain->priv;
 
 out_error:
 	of_node_put(dn);
@@ -336,7 +336,7 @@  static void axon_msi_shutdown(struct platform_device *device)
 	u32 tmp;
 
 	pr_devel("axon_msi: disabling %s\n",
-		  msic->irq_host->of_node->full_name);
+		  msic->irq_host->domain.controller->full_name);
 	tmp  = dcr_read(msic->dcr_host, MSIC_CTRL_REG);
 	tmp &= ~MSIC_CTRL_ENABLE & ~MSIC_CTRL_IRQ_ENABLE;
 	msic_dcr_write(msic, MSIC_CTRL_REG, tmp);
@@ -399,7 +399,7 @@  static int axon_msi_probe(struct platform_device *device)
 		goto out_free_fifo;
 	}
 
-	msic->irq_host->host_data = msic;
+	msic->irq_host->domain.priv = msic;
 
 	irq_set_handler_data(virq, msic);
 	irq_set_chained_handler(virq, axon_msi_cascade);
diff --git a/arch/powerpc/platforms/cell/spider-pic.c b/arch/powerpc/platforms/cell/spider-pic.c
index 73a5494..5bf36ab 100644
--- a/arch/powerpc/platforms/cell/spider-pic.c
+++ b/arch/powerpc/platforms/cell/spider-pic.c
@@ -236,18 +236,20 @@  static unsigned int __init spider_find_cascade_and_node(struct spider_pic *pic)
 	 * tree in case the device-tree is ever fixed
 	 */
 	struct of_irq oirq;
-	if (of_irq_map_one(pic->host->of_node, 0, &oirq) == 0) {
+	if (of_irq_map_one(pic->host->domain.controller, 0, &oirq) == 0) {
 		virq = irq_create_of_mapping(oirq.controller, oirq.specifier,
 					     oirq.size);
 		return virq;
 	}
 
 	/* Now do the horrible hacks */
-	tmp = of_get_property(pic->host->of_node, "#interrupt-cells", NULL);
+	tmp = of_get_property(pic->host->domain.controller,
+				"#interrupt-cells", NULL);
 	if (tmp == NULL)
 		return NO_IRQ;
 	intsize = *tmp;
-	imap = of_get_property(pic->host->of_node, "interrupt-map", &imaplen);
+	imap = of_get_property(pic->host->domain.controller,
+				"interrupt-map", &imaplen);
 	if (imap == NULL || imaplen < (intsize + 1))
 		return NO_IRQ;
 	iic = of_find_node_by_phandle(imap[intsize]);
diff --git a/arch/powerpc/sysdev/fsl_msi.c b/arch/powerpc/sysdev/fsl_msi.c
index 2c11b3e..b45a25a 100644
--- a/arch/powerpc/sysdev/fsl_msi.c
+++ b/arch/powerpc/sysdev/fsl_msi.c
@@ -82,7 +82,7 @@  static int fsl_msi_init_allocator(struct fsl_msi *msi_data)
 	int rc;
 
 	rc = msi_bitmap_alloc(&msi_data->bitmap, NR_MSI_IRQS,
-			      msi_data->irqhost->of_node);
+			      msi_data->irqhost->domain.controller);
 	if (rc)
 		return rc;
 
diff --git a/arch/powerpc/sysdev/i8259.c b/arch/powerpc/sysdev/i8259.c
index 30869f0..a6f78fb 100644
--- a/arch/powerpc/sysdev/i8259.c
+++ b/arch/powerpc/sysdev/i8259.c
@@ -166,7 +166,7 @@  static struct resource pic_edgectrl_iores = {
 
 static int i8259_host_match(struct irq_host *h, struct device_node *node)
 {
-	return h->of_node == NULL || h->of_node == node;
+	return h->domain.controller == NULL || h->domain.controller == node;
 }
 
 static int i8259_host_map(struct irq_host *h, unsigned int virq,
diff --git a/arch/powerpc/sysdev/ipic.c b/arch/powerpc/sysdev/ipic.c
index fc3751f..5805c7b 100644
--- a/arch/powerpc/sysdev/ipic.c
+++ b/arch/powerpc/sysdev/ipic.c
@@ -676,7 +676,7 @@  static struct irq_chip ipic_edge_irq_chip = {
 static int ipic_host_match(struct irq_host *h, struct device_node *node)
 {
 	/* Exact match, unless ipic node is NULL */
-	return h->of_node == NULL || h->of_node == node;
+	return h->domain.controller == NULL || h->domain.controller == node;
 }
 
 static int ipic_host_map(struct irq_host *h, unsigned int virq,
diff --git a/arch/powerpc/sysdev/mpic.c b/arch/powerpc/sysdev/mpic.c
index 6e9e594..cafc364 100644
--- a/arch/powerpc/sysdev/mpic.c
+++ b/arch/powerpc/sysdev/mpic.c
@@ -956,7 +956,7 @@  static struct irq_chip mpic_irq_ht_chip = {
 static int mpic_host_match(struct irq_host *h, struct device_node *node)
 {
 	/* Exact match, unless mpic node is NULL */
-	return h->of_node == NULL || h->of_node == node;
+	return h->domain.controller == NULL || h->domain.controller == node;
 }
 
 static int mpic_host_map(struct irq_host *h, unsigned int virq,
@@ -1296,7 +1296,7 @@  void __init mpic_assign_isu(struct mpic *mpic, unsigned int isu_num,
 
 	BUG_ON(isu_num >= MPIC_MAX_ISU);
 
-	mpic_map(mpic, mpic->irqhost->of_node,
+	mpic_map(mpic, mpic->irqhost->domain.controller,
 		 paddr, &mpic->isus[isu_num], 0,
 		 MPIC_INFO(IRQ_STRIDE) * mpic->isu_size);
 
diff --git a/arch/powerpc/sysdev/mpic_msi.c b/arch/powerpc/sysdev/mpic_msi.c
index 50176ed..ddf79c7 100644
--- a/arch/powerpc/sysdev/mpic_msi.c
+++ b/arch/powerpc/sysdev/mpic_msi.c
@@ -85,7 +85,7 @@  int mpic_msi_init_allocator(struct mpic *mpic)
 	int rc;
 
 	rc = msi_bitmap_alloc(&mpic->msi_bitmap, mpic->irq_count,
-			      mpic->irqhost->of_node);
+			      mpic->irqhost->domain.controller);
 	if (rc)
 		return rc;
 
diff --git a/arch/powerpc/sysdev/mpic_pasemi_msi.c b/arch/powerpc/sysdev/mpic_pasemi_msi.c
index 6b11a89..857be51 100644
--- a/arch/powerpc/sysdev/mpic_pasemi_msi.c
+++ b/arch/powerpc/sysdev/mpic_pasemi_msi.c
@@ -153,8 +153,8 @@  int mpic_pasemi_msi_init(struct mpic *mpic)
 {
 	int rc;
 
-	if (!mpic->irqhost->of_node ||
-	    !of_device_is_compatible(mpic->irqhost->of_node,
+	if (!mpic->irqhost->domain.controller ||
+	    !of_device_is_compatible(mpic->irqhost->domain.controller,
 				     "pasemi,pwrficient-openpic"))
 		return -ENODEV;
 
diff --git a/arch/powerpc/sysdev/qe_lib/qe_ic.c b/arch/powerpc/sysdev/qe_lib/qe_ic.c
index 9dd7746..c2ccafa 100644
--- a/arch/powerpc/sysdev/qe_lib/qe_ic.c
+++ b/arch/powerpc/sysdev/qe_lib/qe_ic.c
@@ -250,7 +250,7 @@  static struct irq_chip qe_ic_irq_chip = {
 static int qe_ic_host_match(struct irq_host *h, struct device_node *node)
 {
 	/* Exact match, unless qe_ic node is NULL */
-	return h->of_node == NULL || h->of_node == node;
+	return h->domain.controller == NULL || h->domain.controller == node;
 }
 
 static int qe_ic_host_map(struct irq_host *h, unsigned int virq,
diff --git a/arch/x86/include/asm/irq_controller.h b/arch/x86/include/asm/irq_controller.h
deleted file mode 100644
index 423bbbd..0000000
--- a/arch/x86/include/asm/irq_controller.h
+++ /dev/null
@@ -1,12 +0,0 @@ 
-#ifndef __IRQ_CONTROLLER__
-#define __IRQ_CONTROLLER__
-
-struct irq_domain {
-	int (*xlate)(struct irq_domain *h, const u32 *intspec, u32 intsize,
-			u32 *out_hwirq, u32 *out_type);
-	void *priv;
-	struct device_node *controller;
-	struct list_head l;
-};
-
-#endif
diff --git a/arch/x86/include/asm/prom.h b/arch/x86/include/asm/prom.h
index 971e0b4..eb9d5ab 100644
--- a/arch/x86/include/asm/prom.h
+++ b/arch/x86/include/asm/prom.h
@@ -21,7 +21,6 @@ 
 #include <asm/irq.h>
 #include <asm/atomic.h>
 #include <asm/setup.h>
-#include <asm/irq_controller.h>
 
 #ifdef CONFIG_OF
 extern int of_ioapic;
diff --git a/arch/x86/kernel/devicetree.c b/arch/x86/kernel/devicetree.c
index 706a9fb..651e724 100644
--- a/arch/x86/kernel/devicetree.c
+++ b/arch/x86/kernel/devicetree.c
@@ -15,64 +15,14 @@ 
 #include <linux/of_pci.h>
 
 #include <asm/hpet.h>
-#include <asm/irq_controller.h>
 #include <asm/apic.h>
 #include <asm/pci_x86.h>
 
 __initdata u64 initial_dtb;
 char __initdata cmd_line[COMMAND_LINE_SIZE];
-static LIST_HEAD(irq_domains);
-static DEFINE_RAW_SPINLOCK(big_irq_lock);
 
 int __initdata of_ioapic;
 
-#ifdef CONFIG_X86_IO_APIC
-static void add_interrupt_host(struct irq_domain *ih)
-{
-	unsigned long flags;
-
-	raw_spin_lock_irqsave(&big_irq_lock, flags);
-	list_add(&ih->l, &irq_domains);
-	raw_spin_unlock_irqrestore(&big_irq_lock, flags);
-}
-#endif
-
-static struct irq_domain *get_ih_from_node(struct device_node *controller)
-{
-	struct irq_domain *ih, *found = NULL;
-	unsigned long flags;
-
-	raw_spin_lock_irqsave(&big_irq_lock, flags);
-	list_for_each_entry(ih, &irq_domains, l) {
-		if (ih->controller ==  controller) {
-			found = ih;
-			break;
-		}
-	}
-	raw_spin_unlock_irqrestore(&big_irq_lock, flags);
-	return found;
-}
-
-unsigned int irq_create_of_mapping(struct device_node *controller,
-				   const u32 *intspec, unsigned int intsize)
-{
-	struct irq_domain *ih;
-	u32 virq, type;
-	int ret;
-
-	ih = get_ih_from_node(controller);
-	if (!ih)
-		return 0;
-	ret = ih->xlate(ih, intspec, intsize, &virq, &type);
-	if (ret)
-		return 0;
-	if (type == IRQ_TYPE_NONE)
-		return virq;
-	irq_set_irq_type(virq, type);
-	return virq;
-}
-EXPORT_SYMBOL_GPL(irq_create_of_mapping);
-
 unsigned long pci_address_to_pio(phys_addr_t address)
 {
 	/*
@@ -366,32 +316,33 @@  static struct of_ioapic_type of_ioapic_type[] =
 	},
 };
 
-static int ioapic_xlate(struct irq_domain *id, const u32 *intspec, u32 intsize,
-			u32 *out_hwirq, u32 *out_type)
+static unsigned int ioapic_of_irq_map(struct of_irq_domain *id,
+				      struct device_node *np,
+				      const u32 *intspec, u32 intsize)
 {
 	struct io_apic_irq_attr attr;
 	struct of_ioapic_type *it;
-	u32 line, idx, type;
+	u32 line, idx, type, hwirq;
 
 	if (intsize < 2)
-		return -EINVAL;
+		return 0;
 
 	line = *intspec;
 	idx = (u32) id->priv;
-	*out_hwirq = line + mp_gsi_routing[idx].gsi_base;
+	hwirq = line + mp_gsi_routing[idx].gsi_base;
 
 	intspec++;
 	type = *intspec;
 
 	if (type >= ARRAY_SIZE(of_ioapic_type))
-		return -EINVAL;
+		return 0;
 
 	it = of_ioapic_type + type;
-	*out_type = it->out_type;
-
 	set_io_apic_irq_attr(&attr, idx, line, it->trigger, it->polarity);
 
-	return io_apic_setup_irq_pin(*out_hwirq, cpu_to_node(0), &attr);
+	if (io_apic_setup_irq_pin(hwirq, cpu_to_node(0), &attr))
+		return 0;
+	return hwirq;
 }
 
 static void __init ioapic_add_ofnode(struct device_node *np)
@@ -408,14 +359,14 @@  static void __init ioapic_add_ofnode(struct device_node *np)
 
 	for (i = 0; i < nr_ioapics; i++) {
 		if (r.start == mp_ioapics[i].apicaddr) {
-			struct irq_domain *id;
+			struct of_irq_domain *id;
 
 			id = kzalloc(sizeof(*id), GFP_KERNEL);
 			BUG_ON(!id);
-			id->controller = np;
-			id->xlate = ioapic_xlate;
+			id->controller = of_node_get(np);
+			id->map = ioapic_of_irq_map;
 			id->priv = (void *)i;
-			add_interrupt_host(id);
+			of_irq_domain_add(id);
 			return;
 		}
 	}
diff --git a/drivers/of/irq.c b/drivers/of/irq.c
index 75b0d3c..6da0964 100644
--- a/drivers/of/irq.c
+++ b/drivers/of/irq.c
@@ -19,6 +19,7 @@ 
  */
 
 #include <linux/errno.h>
+#include <linux/list.h>
 #include <linux/module.h>
 #include <linux/of.h>
 #include <linux/of_irq.h>
@@ -29,6 +30,123 @@ 
 #define NO_IRQ 0
 #endif
 
+/*
+ * Device Tree IRQ domains
+ *
+ * IRQ domains provide translation from device tree irq controller nodes to
+ * linux IRQ numbers.  IRQ controllers register an irq_domain with a .map()
+ * hook that performs everything needed to decode and configure a device
+ * tree specified interrupt.
+ */
+static LIST_HEAD(of_irq_domains);
+static DEFINE_RAW_SPINLOCK(of_irq_lock);
+static struct of_irq_domain *of_irq_default_domain;
+
+/**
+ * of_irq_domain_default_match() - Return true if the controller pointers match
+ *
+ * Default match behaviour for of_irq_domains.  If the device tree node pointer
+ * matches the value stored in the domain structure, then return true.
+ */
+static bool of_irq_domain_default_match(struct of_irq_domain *domain,
+					struct device_node *controller)
+{
+	return domain->controller == controller;
+}
+
+/**
+ * of_irq_domain_add() - Register a device tree irq domain
+ * @domain: pointer to domain structure to be registered.
+ *
+ * Adds an of_irq_domain to the global list of domains.
+ */
+void of_irq_domain_add(struct of_irq_domain *domain)
+{
+	unsigned long flags;
+
+	if (!domain->match)
+		domain->match = of_irq_domain_default_match;
+	if (!domain->map) {
+		WARN_ON(1);
+		return;
+	}
+
+	raw_spin_lock_irqsave(&of_irq_lock, flags);
+	list_add(&domain->list, &of_irq_domains);
+	raw_spin_unlock_irqrestore(&of_irq_lock, flags);
+}
+
+/**
+ * of_irq_domain_find() - Find the domain that handles a given device tree node
+ *
+ * Returns the pointer to an of_irq_domain capable of translating irq specifiers
+ * for the given irq controller device tree node.  Returns NULL if a suitable
+ * domain could not be found.
+ */
+struct of_irq_domain *of_irq_domain_find(struct device_node *controller)
+{
+	struct of_irq_domain *domain, *found = NULL;
+	unsigned long flags;
+
+	raw_spin_lock_irqsave(&of_irq_lock, flags);
+	list_for_each_entry(domain, &of_irq_domains, list) {
+		if (domain->match(domain, controller)) {
+			found = domain;
+			break;
+		}
+	}
+	raw_spin_unlock_irqrestore(&of_irq_lock, flags);
+	return found;
+}
+
+/**
+ * of_irq_set_default_domain() - Set a "default" host
+ * @domain: default domain pointer
+ *
+ * For convenience, it's possible to set a "default" host that will be used
+ * whenever NULL is passed to irq_of_create_mapping(). It makes life easier
+ * for platforms that want to manipulate a few hard coded interrupt numbers
+ * that aren't properly represented in the device-tree.
+ */
+void of_irq_set_default_domain(struct of_irq_domain *domain)
+{
+	pr_debug("irq: Default host set to @0x%p\n", domain);
+	of_irq_default_domain = domain;
+}
+
+/**
+ * irq_create_of_mapping() - Map a linux irq # from a device tree specifier
+ * @controller - interrupt-controller node in the device tree
+ * @intspec - array of interrupt specifier data.  Points to an array of u32
+ *            values.  Data is *cpu-native* endian u32 values.
+ * @intsize - size of intspec array.
+ *
+ * Given an interrupt controller node pointer and an interrupt specifier, this
+ * function looks up the linux irq number.
+ */
+unsigned int irq_create_of_mapping(struct device_node *controller,
+				   const u32 *intspec, unsigned int intsize)
+{
+	struct of_irq_domain *domain;
+
+	domain = of_irq_domain_find(controller);
+	if (!domain)
+		domain = of_irq_default_domain;
+	if (!domain) {
+		pr_warn("error: no irq host found for %s !\n",
+			controller->full_name);
+#if defined(CONFIG_MIPS) || defined(CONFIG_MICROBLAZE)
+		/* FIXME: make Microblaze and MIPS register irq domains */
+		return intspec[0];
+#else /* defined(CONFIG_MIPS) || defined(CONFIG_MICROBLAZE) */
+		return NO_IRQ;
+#endif /* defined(CONFIG_MIPS) || defined(CONFIG_MICROBLAZE) */
+	}
+
+	return domain->map(domain, controller, intspec, intsize);
+}
+EXPORT_SYMBOL_GPL(irq_create_of_mapping);
+
 /**
  * irq_of_parse_and_map - Parse and map an interrupt into linux virq space
  * @device: Device node of the device whose interrupt is to be mapped
diff --git a/include/linux/of_irq.h b/include/linux/of_irq.h
index 109e013..511dbc3 100644
--- a/include/linux/of_irq.h
+++ b/include/linux/of_irq.h
@@ -33,6 +33,37 @@  struct of_irq {
 	u32 specifier[OF_MAX_IRQ_SPEC]; /* Specifier copy */
 };
 
+/**
+ * struct of_irq_domain - Translation domain from device tree to linux irq
+ * @list: Linked list node entry
+ * @match: (optional) Called to determine if the passed device_node
+ *         interrupt-controller can be translated by this irq domain.
+ *         Returns 'true' if it can.
+ * @decode: Translation callback; returns virq, or NO_IRQ if this irq
+ *          domain cannot translate it.
+ * @controller: (optional) pointer to OF node.  By default, if
+ *              'match' is not set, then this of_irq_domain will only
+ *              be used if the device tree node passed in matches the
+ *              controller pointer.
+ * @priv: Private data pointer, not touched by core of_irq_domain code.
+ */
+struct of_irq_domain {
+	struct list_head list;
+	bool (*match)(struct of_irq_domain *d, struct device_node *np);
+	unsigned int (*map)(struct of_irq_domain *d, struct device_node *np,
+			    const u32 *intspec, u32 intsize);
+	struct device_node *controller;
+	void *priv;
+};
+
+/**
+ * of_irq_domain_add() - Add a device tree interrupt translation domain
+ * @domain: interrupt domain to add.
+ */
+extern void of_irq_domain_add(struct of_irq_domain *domain);
+extern void of_irq_set_default_domain(struct of_irq_domain *host);
+extern struct of_irq_domain *of_irq_domain_find(struct device_node *controller);
+
 /*
  * Workarounds only applied to 32bit powermac machines
  */