Subject: Re: [PATCH 4/6] dt: generalize irq_of_create_mapping()

From: Benjamin Herrenschmidt <[email-masked]>
Date: Tue, 03 May 2011 11:50:22 +1000

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 <[email-masked]>
> ---
>  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
>   */