[2/2] irq/irq_sim: use irq domain
diff mbox series

Message ID 20190812125256.9690-3-brgl@bgdev.pl
State New
Headers show
Series
  • irq/irq_sim: try to improve the API
Related show

Commit Message

Bartosz Golaszewski Aug. 12, 2019, 12:52 p.m. UTC
From: Bartosz Golaszewski <bgolaszewski@baylibre.com>

We currently have a dedicated function to map the interrupt simulator
offsets to global interrupt numbers. This is something that irq_domain
should handle.

Create a linear irq_domain when initializing the interrupt simulator
and modify the irq_sim_fire() function to only take as parameter the
global interrupt number.

Convert both users in the same patch to using the new interface.

Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
---
 drivers/gpio/gpio-mockup.c          |  11 ++-
 drivers/iio/dummy/iio_dummy_evgen.c |  22 ++++--
 include/linux/irq_sim.h             |   5 +-
 kernel/irq/Kconfig                  |   1 +
 kernel/irq/irq_sim.c                | 110 +++++++++++++++++-----------
 5 files changed, 94 insertions(+), 55 deletions(-)

Comments

Jonathan Cameron Aug. 18, 2019, 7:45 p.m. UTC | #1
On Mon, 12 Aug 2019 14:52:56 +0200
Bartosz Golaszewski <brgl@bgdev.pl> wrote:

> From: Bartosz Golaszewski <bgolaszewski@baylibre.com>
> 
> We currently have a dedicated function to map the interrupt simulator
> offsets to global interrupt numbers. This is something that irq_domain
> should handle.
> 
> Create a linear irq_domain when initializing the interrupt simulator
> and modify the irq_sim_fire() function to only take as parameter the
> global interrupt number.
> 
> Convert both users in the same patch to using the new interface.
> 
> Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>

Hmm. Again, in principle looks fine, but what advantages do we get from
using an irq domain?  Pure code wise, it's longer and only a little bit
simpler.

Jonathan

> ---
>  drivers/gpio/gpio-mockup.c          |  11 ++-
>  drivers/iio/dummy/iio_dummy_evgen.c |  22 ++++--
>  include/linux/irq_sim.h             |   5 +-
>  kernel/irq/Kconfig                  |   1 +
>  kernel/irq/irq_sim.c                | 110 +++++++++++++++++-----------
>  5 files changed, 94 insertions(+), 55 deletions(-)
> 
> diff --git a/drivers/gpio/gpio-mockup.c b/drivers/gpio/gpio-mockup.c
> index 9b28ffec5826..4cf594f0e7cd 100644
> --- a/drivers/gpio/gpio-mockup.c
> +++ b/drivers/gpio/gpio-mockup.c
> @@ -186,7 +186,7 @@ static int gpio_mockup_to_irq(struct gpio_chip *gc, unsigned int offset)
>  {
>  	struct gpio_mockup_chip *chip = gpiochip_get_data(gc);
>  
> -	return irq_sim_irqnum(chip->irqsim, offset);
> +	return irq_create_mapping(irq_sim_get_domain(chip->irqsim), offset);
>  }
>  
>  static void gpio_mockup_free(struct gpio_chip *gc, unsigned int offset)
> @@ -228,6 +228,7 @@ static ssize_t gpio_mockup_debugfs_write(struct file *file,
>  	struct gpio_mockup_dbgfs_private *priv;
>  	int rv, val, curr, irq, irq_type;
>  	struct gpio_mockup_chip *chip;
> +	struct irq_domain *domain;
>  	struct seq_file *sfile;
>  	struct gpio_desc *desc;
>  	struct gpio_chip *gc;
> @@ -248,6 +249,7 @@ static ssize_t gpio_mockup_debugfs_write(struct file *file,
>  	gc = &chip->gc;
>  	desc = &gc->gpiodev->descs[priv->offset];
>  	sim = chip->irqsim;
> +	domain = irq_sim_get_domain(sim);
>  
>  	mutex_lock(&chip->lock);
>  
> @@ -257,12 +259,15 @@ static ssize_t gpio_mockup_debugfs_write(struct file *file,
>  		if (curr == val)
>  			goto out;
>  
> -		irq = irq_sim_irqnum(sim, priv->offset);
> +		irq = irq_find_mapping(domain, priv->offset);
> +		if (!irq)
> +			return -ENOENT;
> +
>  		irq_type = irq_get_trigger_type(irq);
>  
>  		if ((val == 1 && (irq_type & IRQ_TYPE_EDGE_RISING)) ||
>  		    (val == 0 && (irq_type & IRQ_TYPE_EDGE_FALLING)))
> -			irq_sim_fire(sim, priv->offset);
> +			irq_sim_fire(irq);
>  	}
>  
>  	/* Change the value unless we're actively driving the line. */
> diff --git a/drivers/iio/dummy/iio_dummy_evgen.c b/drivers/iio/dummy/iio_dummy_evgen.c
> index efbcd4a5609e..cc827f60a535 100644
> --- a/drivers/iio/dummy/iio_dummy_evgen.c
> +++ b/drivers/iio/dummy/iio_dummy_evgen.c
> @@ -31,14 +31,13 @@
>   * @lock: protect the evgen state
>   * @inuse: mask of which irqs are connected
>   * @irq_sim: interrupt simulator
> - * @base: base of irq range
>   */
>  struct iio_dummy_eventgen {
>  	struct iio_dummy_regs regs[IIO_EVENTGEN_NO];
>  	struct mutex lock;
>  	bool inuse[IIO_EVENTGEN_NO];
>  	struct irq_sim *irq_sim;
> -	int base;
> +	struct irq_domain *domain;
>  };
>  
>  /* We can only ever have one instance of this 'device' */
> @@ -56,7 +55,7 @@ static int iio_dummy_evgen_create(void)
>  		return PTR_ERR(iio_evgen->irq_sim);
>  	}
>  
> -	iio_evgen->base = irq_sim_irqnum(iio_evgen->irq_sim, 0);
> +	iio_evgen->domain = irq_sim_get_domain(iio_evgen->irq_sim);
>  	mutex_init(&iio_evgen->lock);
>  
>  	return 0;
> @@ -78,7 +77,7 @@ int iio_dummy_evgen_get_irq(void)
>  	mutex_lock(&iio_evgen->lock);
>  	for (i = 0; i < IIO_EVENTGEN_NO; i++) {
>  		if (!iio_evgen->inuse[i]) {
> -			ret = irq_sim_irqnum(iio_evgen->irq_sim, i);
> +			ret = irq_create_mapping(iio_evgen->domain, i);
>  			iio_evgen->inuse[i] = true;
>  			break;
>  		}
> @@ -99,15 +98,21 @@ EXPORT_SYMBOL_GPL(iio_dummy_evgen_get_irq);
>   */
>  void iio_dummy_evgen_release_irq(int irq)
>  {
> +	struct irq_data *irqd;
> +
> +	irqd = irq_get_irq_data(irq);
> +
>  	mutex_lock(&iio_evgen->lock);
> -	iio_evgen->inuse[irq - iio_evgen->base] = false;
> +	iio_evgen->inuse[irqd_to_hwirq(irqd)] = false;
>  	mutex_unlock(&iio_evgen->lock);
>  }
>  EXPORT_SYMBOL_GPL(iio_dummy_evgen_release_irq);
>  
>  struct iio_dummy_regs *iio_dummy_evgen_get_regs(int irq)
>  {
> -	return &iio_evgen->regs[irq - iio_evgen->base];
> +	struct irq_data *irqd = irq_get_irq_data(irq);
> +
> +	return &iio_evgen->regs[irqd_to_hwirq(irqd)];
>  }
>  EXPORT_SYMBOL_GPL(iio_dummy_evgen_get_regs);
>  
> @@ -129,7 +134,7 @@ static ssize_t iio_evgen_poke(struct device *dev,
>  {
>  	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
>  	unsigned long event;
> -	int ret;
> +	int ret, irq;
>  
>  	ret = kstrtoul(buf, 10, &event);
>  	if (ret)
> @@ -138,7 +143,8 @@ static ssize_t iio_evgen_poke(struct device *dev,
>  	iio_evgen->regs[this_attr->address].reg_id   = this_attr->address;
>  	iio_evgen->regs[this_attr->address].reg_data = event;
>  
> -	irq_sim_fire(iio_evgen->irq_sim, this_attr->address);
> +	irq = irq_create_mapping(iio_evgen->domain, this_attr->address);
> +	irq_sim_fire(irq);
>  
>  	return len;
>  }
> diff --git a/include/linux/irq_sim.h b/include/linux/irq_sim.h
> index 4bbf036145e2..4056d0e7f0b4 100644
> --- a/include/linux/irq_sim.h
> +++ b/include/linux/irq_sim.h
> @@ -7,6 +7,7 @@
>  #define _LINUX_IRQ_SIM_H
>  
>  #include <linux/irq_work.h>
> +#include <linux/irqdomain.h>
>  #include <linux/device.h>
>  
>  /*
> @@ -19,7 +20,7 @@ struct irq_sim;
>  struct irq_sim *irq_sim_new(unsigned int num_irqs);
>  struct irq_sim *devm_irq_sim_new(struct device *dev, unsigned int num_irqs);
>  void irq_sim_free(struct irq_sim *sim);
> -void irq_sim_fire(struct irq_sim *sim, unsigned int offset);
> -int irq_sim_irqnum(struct irq_sim *sim, unsigned int offset);
> +void irq_sim_fire(int virq);
> +struct irq_domain *irq_sim_get_domain(struct irq_sim *sim);
>  
>  #endif /* _LINUX_IRQ_SIM_H */
> diff --git a/kernel/irq/Kconfig b/kernel/irq/Kconfig
> index f92d9a687372..d0890f7729d4 100644
> --- a/kernel/irq/Kconfig
> +++ b/kernel/irq/Kconfig
> @@ -68,6 +68,7 @@ config IRQ_DOMAIN
>  config IRQ_SIM
>  	bool
>  	select IRQ_WORK
> +	select IRQ_DOMAIN
>  
>  # Support for hierarchical irq domains
>  config IRQ_DOMAIN_HIERARCHY
> diff --git a/kernel/irq/irq_sim.c b/kernel/irq/irq_sim.c
> index 79f0a6494b6c..a1c91aefb6cd 100644
> --- a/kernel/irq/irq_sim.c
> +++ b/kernel/irq/irq_sim.c
> @@ -15,13 +15,14 @@ struct irq_sim_work_ctx {
>  struct irq_sim_irq_ctx {
>  	int			irqnum;
>  	bool			enabled;
> +	struct irq_sim_work_ctx	*work_ctx;
>  };
>  
>  struct irq_sim {
>  	struct irq_sim_work_ctx	work_ctx;
>  	int			irq_base;
>  	unsigned int		irq_count;
> -	struct irq_sim_irq_ctx	*irqs;
> +	struct irq_domain	*domain;
>  };
>  
>  struct irq_sim_devres {
> @@ -74,11 +75,46 @@ static void irq_sim_handle_irq(struct irq_work *work)
>  		offset = find_next_bit(work_ctx->pending,
>  				       sim->irq_count, offset);
>  		clear_bit(offset, work_ctx->pending);
> -		irqnum = irq_sim_irqnum(sim, offset);
> +		irqnum = irq_find_mapping(sim->domain, offset);
>  		handle_simple_irq(irq_to_desc(irqnum));
>  	}
>  }
>  
> +static int irq_sim_domain_map(struct irq_domain *domain,
> +			      unsigned int virq, irq_hw_number_t hw)
> +{
> +	struct irq_sim *sim = domain->host_data;
> +	struct irq_sim_irq_ctx *ctx;
> +
> +	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
> +	if (!ctx)
> +		return -ENOMEM;
> +
> +	irq_set_chip(virq, &irq_sim_irqchip);
> +	irq_set_chip_data(virq, ctx);
> +	irq_set_handler(virq, handle_simple_irq);
> +	irq_modify_status(virq, IRQ_NOREQUEST | IRQ_NOAUTOEN, IRQ_NOPROBE);
> +	ctx->work_ctx = &sim->work_ctx;
> +
> +	return 0;
> +}
> +
> +static void irq_sim_domain_unmap(struct irq_domain *domain, unsigned int virq)
> +{
> +	struct irq_sim_irq_ctx *ctx;
> +	struct irq_data *irqd;
> +
> +	irqd = irq_domain_get_irq_data(domain, virq);
> +	ctx = irq_data_get_irq_chip_data(irqd);
> +
> +	kfree(ctx);
> +}
> +
> +static const struct irq_domain_ops irq_sim_domain_ops = {
> +	.map		= irq_sim_domain_map,
> +	.unmap		= irq_sim_domain_unmap,
> +};
> +
>  /**
>   * irq_sim_new - Create a new interrupt simulator: allocate a range of
>   *               dummy interrupts.
> @@ -91,42 +127,21 @@ static void irq_sim_handle_irq(struct irq_work *work)
>  struct irq_sim *irq_sim_new(unsigned int num_irqs)
>  {
>  	struct irq_sim *sim;
> -	int i;
>  
>  	sim = kmalloc(sizeof(*sim), GFP_KERNEL);
>  	if (!sim)
>  		return ERR_PTR(-ENOMEM);
>  
> -	sim->irqs = kmalloc_array(num_irqs, sizeof(*sim->irqs), GFP_KERNEL);
> -	if (!sim->irqs) {
> -		kfree(sim);
> -		return ERR_PTR(-ENOMEM);
> -	}
> -
> -	sim->irq_base = irq_alloc_descs(-1, 0, num_irqs, 0);
> -	if (sim->irq_base < 0) {
> -		kfree(sim->irqs);
> -		kfree(sim);
> -		return ERR_PTR(sim->irq_base);
> -	}
> -
>  	sim->work_ctx.pending = bitmap_zalloc(num_irqs, GFP_KERNEL);
>  	if (!sim->work_ctx.pending) {
> -		kfree(sim->irqs);
>  		kfree(sim);
> -		irq_free_descs(sim->irq_base, num_irqs);
>  		return ERR_PTR(-ENOMEM);
>  	}
>  
> -	for (i = 0; i < num_irqs; i++) {
> -		sim->irqs[i].irqnum = sim->irq_base + i;
> -		sim->irqs[i].enabled = false;
> -		irq_set_chip(sim->irq_base + i, &irq_sim_irqchip);
> -		irq_set_chip_data(sim->irq_base + i, &sim->irqs[i]);
> -		irq_set_handler(sim->irq_base + i, &handle_simple_irq);
> -		irq_modify_status(sim->irq_base + i,
> -				  IRQ_NOREQUEST | IRQ_NOAUTOEN, IRQ_NOPROBE);
> -	}
> +	sim->domain = irq_domain_create_linear(NULL, num_irqs,
> +					       &irq_sim_domain_ops, sim);
> +	if (!sim->domain)
> +		return ERR_PTR(-ENOMEM);
>  
>  	init_irq_work(&sim->work_ctx.work, irq_sim_handle_irq);
>  	sim->irq_count = num_irqs;
> @@ -143,10 +158,17 @@ EXPORT_SYMBOL_GPL(irq_sim_new);
>   */
>  void irq_sim_free(struct irq_sim *sim)
>  {
> +	int i, irq;
> +
> +	for (i = 0; i < sim->irq_count; i++) {
> +		irq = irq_find_mapping(sim->domain, i);
> +		if (irq)
> +			irq_dispose_mapping(irq);
> +	}
> +
> +	irq_domain_remove(sim->domain);
>  	irq_work_sync(&sim->work_ctx.work);
>  	bitmap_free(sim->work_ctx.pending);
> -	irq_free_descs(sim->irq_base, sim->irq_count);
> -	kfree(sim->irqs);
>  	kfree(sim);
>  }
>  EXPORT_SYMBOL_GPL(irq_sim_free);
> @@ -189,27 +211,31 @@ EXPORT_SYMBOL_GPL(devm_irq_sim_new);
>  /**
>   * irq_sim_fire - Enqueue an interrupt.
>   *
> - * @sim:        The interrupt simulator object.
> - * @offset:     Offset of the simulated interrupt which should be fired.
> + * @virq:        Virtual interrupt number to fire. It must be associated with
> + *               an existing interrupt simulator.
>   */
> -void irq_sim_fire(struct irq_sim *sim, unsigned int offset)
> +void irq_sim_fire(int virq)
>  {
> -	if (sim->irqs[offset].enabled) {
> -		set_bit(offset, sim->work_ctx.pending);
> -		irq_work_queue(&sim->work_ctx.work);
> +	struct irq_sim_irq_ctx *ctx;
> +	struct irq_data *irqd;
> +
> +	irqd = irq_get_irq_data(virq);
> +	ctx = irq_data_get_irq_chip_data(irqd);
> +
> +	if (ctx->enabled) {
> +		set_bit(irqd_to_hwirq(irqd), ctx->work_ctx->pending);
> +		irq_work_queue(&ctx->work_ctx->work);
>  	}
>  }
>  EXPORT_SYMBOL_GPL(irq_sim_fire);
>  
>  /**
> - * irq_sim_irqnum - Get the allocated number of a dummy interrupt.
> + * irq_sim_get_domain - Retrieve the interrupt domain for this simulator.
>   *
> - * @sim:        The interrupt simulator object.
> - * @offset:     Offset of the simulated interrupt for which to retrieve
> - *              the number.
> + * @sim:         The interrupt simulator the domain of which we retrieve.
>   */
> -int irq_sim_irqnum(struct irq_sim *sim, unsigned int offset)
> +struct irq_domain *irq_sim_get_domain(struct irq_sim *sim)
>  {
> -	return sim->irqs[offset].irqnum;
> +	return sim->domain;
>  }
> -EXPORT_SYMBOL_GPL(irq_sim_irqnum);
> +EXPORT_SYMBOL(irq_sim_get_domain);
Marc Zyngier Aug. 19, 2019, 8:18 a.m. UTC | #2
On Mon, 12 Aug 2019 13:52:56 +0100,
Bartosz Golaszewski <brgl@bgdev.pl> wrote:
> 
> From: Bartosz Golaszewski <bgolaszewski@baylibre.com>
> 
> We currently have a dedicated function to map the interrupt simulator
> offsets to global interrupt numbers. This is something that irq_domain
> should handle.
> 
> Create a linear irq_domain when initializing the interrupt simulator
> and modify the irq_sim_fire() function to only take as parameter the
> global interrupt number.
> 
> Convert both users in the same patch to using the new interface.
> 
> Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
> ---
>  drivers/gpio/gpio-mockup.c          |  11 ++-
>  drivers/iio/dummy/iio_dummy_evgen.c |  22 ++++--
>  include/linux/irq_sim.h             |   5 +-
>  kernel/irq/Kconfig                  |   1 +
>  kernel/irq/irq_sim.c                | 110 +++++++++++++++++-----------
>  5 files changed, 94 insertions(+), 55 deletions(-)
> 
> diff --git a/drivers/gpio/gpio-mockup.c b/drivers/gpio/gpio-mockup.c
> index 9b28ffec5826..4cf594f0e7cd 100644
> --- a/drivers/gpio/gpio-mockup.c
> +++ b/drivers/gpio/gpio-mockup.c
> @@ -186,7 +186,7 @@ static int gpio_mockup_to_irq(struct gpio_chip *gc, unsigned int offset)
>  {
>  	struct gpio_mockup_chip *chip = gpiochip_get_data(gc);
>  
> -	return irq_sim_irqnum(chip->irqsim, offset);
> +	return irq_create_mapping(irq_sim_get_domain(chip->irqsim), offset);
>  }
>  
>  static void gpio_mockup_free(struct gpio_chip *gc, unsigned int offset)
> @@ -228,6 +228,7 @@ static ssize_t gpio_mockup_debugfs_write(struct file *file,
>  	struct gpio_mockup_dbgfs_private *priv;
>  	int rv, val, curr, irq, irq_type;
>  	struct gpio_mockup_chip *chip;
> +	struct irq_domain *domain;
>  	struct seq_file *sfile;
>  	struct gpio_desc *desc;
>  	struct gpio_chip *gc;
> @@ -248,6 +249,7 @@ static ssize_t gpio_mockup_debugfs_write(struct file *file,
>  	gc = &chip->gc;
>  	desc = &gc->gpiodev->descs[priv->offset];
>  	sim = chip->irqsim;
> +	domain = irq_sim_get_domain(sim);
>  
>  	mutex_lock(&chip->lock);
>  
> @@ -257,12 +259,15 @@ static ssize_t gpio_mockup_debugfs_write(struct file *file,
>  		if (curr == val)
>  			goto out;
>  
> -		irq = irq_sim_irqnum(sim, priv->offset);
> +		irq = irq_find_mapping(domain, priv->offset);
> +		if (!irq)
> +			return -ENOENT;
> +
>  		irq_type = irq_get_trigger_type(irq);
>  
>  		if ((val == 1 && (irq_type & IRQ_TYPE_EDGE_RISING)) ||
>  		    (val == 0 && (irq_type & IRQ_TYPE_EDGE_FALLING)))
> -			irq_sim_fire(sim, priv->offset);
> +			irq_sim_fire(irq);
>  	}
>  
>  	/* Change the value unless we're actively driving the line. */
> diff --git a/drivers/iio/dummy/iio_dummy_evgen.c b/drivers/iio/dummy/iio_dummy_evgen.c
> index efbcd4a5609e..cc827f60a535 100644
> --- a/drivers/iio/dummy/iio_dummy_evgen.c
> +++ b/drivers/iio/dummy/iio_dummy_evgen.c
> @@ -31,14 +31,13 @@
>   * @lock: protect the evgen state
>   * @inuse: mask of which irqs are connected
>   * @irq_sim: interrupt simulator
> - * @base: base of irq range
>   */
>  struct iio_dummy_eventgen {
>  	struct iio_dummy_regs regs[IIO_EVENTGEN_NO];
>  	struct mutex lock;
>  	bool inuse[IIO_EVENTGEN_NO];
>  	struct irq_sim *irq_sim;
> -	int base;
> +	struct irq_domain *domain;
>  };
>  
>  /* We can only ever have one instance of this 'device' */
> @@ -56,7 +55,7 @@ static int iio_dummy_evgen_create(void)
>  		return PTR_ERR(iio_evgen->irq_sim);
>  	}
>  
> -	iio_evgen->base = irq_sim_irqnum(iio_evgen->irq_sim, 0);
> +	iio_evgen->domain = irq_sim_get_domain(iio_evgen->irq_sim);
>  	mutex_init(&iio_evgen->lock);
>  
>  	return 0;
> @@ -78,7 +77,7 @@ int iio_dummy_evgen_get_irq(void)
>  	mutex_lock(&iio_evgen->lock);
>  	for (i = 0; i < IIO_EVENTGEN_NO; i++) {
>  		if (!iio_evgen->inuse[i]) {
> -			ret = irq_sim_irqnum(iio_evgen->irq_sim, i);
> +			ret = irq_create_mapping(iio_evgen->domain, i);
>  			iio_evgen->inuse[i] = true;
>  			break;
>  		}
> @@ -99,15 +98,21 @@ EXPORT_SYMBOL_GPL(iio_dummy_evgen_get_irq);
>   */
>  void iio_dummy_evgen_release_irq(int irq)
>  {
> +	struct irq_data *irqd;
> +
> +	irqd = irq_get_irq_data(irq);
> +
>  	mutex_lock(&iio_evgen->lock);
> -	iio_evgen->inuse[irq - iio_evgen->base] = false;
> +	iio_evgen->inuse[irqd_to_hwirq(irqd)] = false;
>  	mutex_unlock(&iio_evgen->lock);
>  }
>  EXPORT_SYMBOL_GPL(iio_dummy_evgen_release_irq);
>  
>  struct iio_dummy_regs *iio_dummy_evgen_get_regs(int irq)
>  {
> -	return &iio_evgen->regs[irq - iio_evgen->base];
> +	struct irq_data *irqd = irq_get_irq_data(irq);
> +
> +	return &iio_evgen->regs[irqd_to_hwirq(irqd)];
>  }
>  EXPORT_SYMBOL_GPL(iio_dummy_evgen_get_regs);
>  
> @@ -129,7 +134,7 @@ static ssize_t iio_evgen_poke(struct device *dev,
>  {
>  	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
>  	unsigned long event;
> -	int ret;
> +	int ret, irq;
>  
>  	ret = kstrtoul(buf, 10, &event);
>  	if (ret)
> @@ -138,7 +143,8 @@ static ssize_t iio_evgen_poke(struct device *dev,
>  	iio_evgen->regs[this_attr->address].reg_id   = this_attr->address;
>  	iio_evgen->regs[this_attr->address].reg_data = event;
>  
> -	irq_sim_fire(iio_evgen->irq_sim, this_attr->address);
> +	irq = irq_create_mapping(iio_evgen->domain, this_attr->address);
> +	irq_sim_fire(irq);

That's not the way the API is intended to be used. irq_create_mapping() is
something that should only happen once, or at least as rarely as possible.
At interrupt handling time, you should call irq_find_mapping() instead.

You need to rethink the way this driver is using interrupts.

>  
>  	return len;
>  }
> diff --git a/include/linux/irq_sim.h b/include/linux/irq_sim.h
> index 4bbf036145e2..4056d0e7f0b4 100644
> --- a/include/linux/irq_sim.h
> +++ b/include/linux/irq_sim.h
> @@ -7,6 +7,7 @@
>  #define _LINUX_IRQ_SIM_H
>  
>  #include <linux/irq_work.h>
> +#include <linux/irqdomain.h>
>  #include <linux/device.h>
>  
>  /*
> @@ -19,7 +20,7 @@ struct irq_sim;
>  struct irq_sim *irq_sim_new(unsigned int num_irqs);
>  struct irq_sim *devm_irq_sim_new(struct device *dev, unsigned int num_irqs);
>  void irq_sim_free(struct irq_sim *sim);
> -void irq_sim_fire(struct irq_sim *sim, unsigned int offset);
> -int irq_sim_irqnum(struct irq_sim *sim, unsigned int offset);
> +void irq_sim_fire(int virq);
> +struct irq_domain *irq_sim_get_domain(struct irq_sim *sim);
>  
>  #endif /* _LINUX_IRQ_SIM_H */
> diff --git a/kernel/irq/Kconfig b/kernel/irq/Kconfig
> index f92d9a687372..d0890f7729d4 100644
> --- a/kernel/irq/Kconfig
> +++ b/kernel/irq/Kconfig
> @@ -68,6 +68,7 @@ config IRQ_DOMAIN
>  config IRQ_SIM
>  	bool
>  	select IRQ_WORK
> +	select IRQ_DOMAIN
>  
>  # Support for hierarchical irq domains
>  config IRQ_DOMAIN_HIERARCHY
> diff --git a/kernel/irq/irq_sim.c b/kernel/irq/irq_sim.c
> index 79f0a6494b6c..a1c91aefb6cd 100644
> --- a/kernel/irq/irq_sim.c
> +++ b/kernel/irq/irq_sim.c
> @@ -15,13 +15,14 @@ struct irq_sim_work_ctx {
>  struct irq_sim_irq_ctx {
>  	int			irqnum;
>  	bool			enabled;
> +	struct irq_sim_work_ctx	*work_ctx;
>  };
>  
>  struct irq_sim {
>  	struct irq_sim_work_ctx	work_ctx;
>  	int			irq_base;
>  	unsigned int		irq_count;
> -	struct irq_sim_irq_ctx	*irqs;
> +	struct irq_domain	*domain;
>  };
>  
>  struct irq_sim_devres {
> @@ -74,11 +75,46 @@ static void irq_sim_handle_irq(struct irq_work *work)
>  		offset = find_next_bit(work_ctx->pending,
>  				       sim->irq_count, offset);
>  		clear_bit(offset, work_ctx->pending);
> -		irqnum = irq_sim_irqnum(sim, offset);
> +		irqnum = irq_find_mapping(sim->domain, offset);
>  		handle_simple_irq(irq_to_desc(irqnum));
>  	}
>  }
>  
> +static int irq_sim_domain_map(struct irq_domain *domain,
> +			      unsigned int virq, irq_hw_number_t hw)
> +{
> +	struct irq_sim *sim = domain->host_data;
> +	struct irq_sim_irq_ctx *ctx;
> +
> +	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
> +	if (!ctx)
> +		return -ENOMEM;
> +
> +	irq_set_chip(virq, &irq_sim_irqchip);
> +	irq_set_chip_data(virq, ctx);
> +	irq_set_handler(virq, handle_simple_irq);
> +	irq_modify_status(virq, IRQ_NOREQUEST | IRQ_NOAUTOEN, IRQ_NOPROBE);
> +	ctx->work_ctx = &sim->work_ctx;
> +
> +	return 0;
> +}
> +
> +static void irq_sim_domain_unmap(struct irq_domain *domain, unsigned int virq)
> +{
> +	struct irq_sim_irq_ctx *ctx;
> +	struct irq_data *irqd;
> +
> +	irqd = irq_domain_get_irq_data(domain, virq);
> +	ctx = irq_data_get_irq_chip_data(irqd);
> +
> +	kfree(ctx);

I'd expect something like:

        irq_set_handler(virq, NULL);
        irq_domain_reset_irq_data(irqd);

in addition to the above.

> +}
> +
> +static const struct irq_domain_ops irq_sim_domain_ops = {
> +	.map		= irq_sim_domain_map,
> +	.unmap		= irq_sim_domain_unmap,
> +};
> +
>  /**
>   * irq_sim_new - Create a new interrupt simulator: allocate a range of
>   *               dummy interrupts.
> @@ -91,42 +127,21 @@ static void irq_sim_handle_irq(struct irq_work *work)
>  struct irq_sim *irq_sim_new(unsigned int num_irqs)
>  {
>  	struct irq_sim *sim;
> -	int i;
>  
>  	sim = kmalloc(sizeof(*sim), GFP_KERNEL);
>  	if (!sim)
>  		return ERR_PTR(-ENOMEM);
>  
> -	sim->irqs = kmalloc_array(num_irqs, sizeof(*sim->irqs), GFP_KERNEL);
> -	if (!sim->irqs) {
> -		kfree(sim);
> -		return ERR_PTR(-ENOMEM);
> -	}
> -
> -	sim->irq_base = irq_alloc_descs(-1, 0, num_irqs, 0);
> -	if (sim->irq_base < 0) {
> -		kfree(sim->irqs);
> -		kfree(sim);
> -		return ERR_PTR(sim->irq_base);
> -	}
> -
>  	sim->work_ctx.pending = bitmap_zalloc(num_irqs, GFP_KERNEL);
>  	if (!sim->work_ctx.pending) {
> -		kfree(sim->irqs);
>  		kfree(sim);
> -		irq_free_descs(sim->irq_base, num_irqs);
>  		return ERR_PTR(-ENOMEM);
>  	}
>  
> -	for (i = 0; i < num_irqs; i++) {
> -		sim->irqs[i].irqnum = sim->irq_base + i;
> -		sim->irqs[i].enabled = false;
> -		irq_set_chip(sim->irq_base + i, &irq_sim_irqchip);
> -		irq_set_chip_data(sim->irq_base + i, &sim->irqs[i]);
> -		irq_set_handler(sim->irq_base + i, &handle_simple_irq);
> -		irq_modify_status(sim->irq_base + i,
> -				  IRQ_NOREQUEST | IRQ_NOAUTOEN, IRQ_NOPROBE);
> -	}
> +	sim->domain = irq_domain_create_linear(NULL, num_irqs,
> +					       &irq_sim_domain_ops, sim);
> +	if (!sim->domain)
> +		return ERR_PTR(-ENOMEM);

Aren't you now leaking memory from the sim structure?

>  
>  	init_irq_work(&sim->work_ctx.work, irq_sim_handle_irq);
>  	sim->irq_count = num_irqs;
> @@ -143,10 +158,17 @@ EXPORT_SYMBOL_GPL(irq_sim_new);
>   */
>  void irq_sim_free(struct irq_sim *sim)
>  {
> +	int i, irq;
> +
> +	for (i = 0; i < sim->irq_count; i++) {
> +		irq = irq_find_mapping(sim->domain, i);
> +		if (irq)
> +			irq_dispose_mapping(irq);
> +	}
> +
> +	irq_domain_remove(sim->domain);
>  	irq_work_sync(&sim->work_ctx.work);
>  	bitmap_free(sim->work_ctx.pending);
> -	irq_free_descs(sim->irq_base, sim->irq_count);
> -	kfree(sim->irqs);
>  	kfree(sim);
>  }
>  EXPORT_SYMBOL_GPL(irq_sim_free);
> @@ -189,27 +211,31 @@ EXPORT_SYMBOL_GPL(devm_irq_sim_new);
>  /**
>   * irq_sim_fire - Enqueue an interrupt.
>   *
> - * @sim:        The interrupt simulator object.
> - * @offset:     Offset of the simulated interrupt which should be fired.
> + * @virq:        Virtual interrupt number to fire. It must be associated with
> + *               an existing interrupt simulator.
>   */
> -void irq_sim_fire(struct irq_sim *sim, unsigned int offset)
> +void irq_sim_fire(int virq)
>  {
> -	if (sim->irqs[offset].enabled) {
> -		set_bit(offset, sim->work_ctx.pending);
> -		irq_work_queue(&sim->work_ctx.work);
> +	struct irq_sim_irq_ctx *ctx;
> +	struct irq_data *irqd;
> +
> +	irqd = irq_get_irq_data(virq);
> +	ctx = irq_data_get_irq_chip_data(irqd);
> +
> +	if (ctx->enabled) {
> +		set_bit(irqd_to_hwirq(irqd), ctx->work_ctx->pending);
> +		irq_work_queue(&ctx->work_ctx->work);
>  	}
>  }
>  EXPORT_SYMBOL_GPL(irq_sim_fire);
>  
>  /**
> - * irq_sim_irqnum - Get the allocated number of a dummy interrupt.
> + * irq_sim_get_domain - Retrieve the interrupt domain for this simulator.
>   *
> - * @sim:        The interrupt simulator object.
> - * @offset:     Offset of the simulated interrupt for which to retrieve
> - *              the number.
> + * @sim:         The interrupt simulator the domain of which we retrieve.
>   */
> -int irq_sim_irqnum(struct irq_sim *sim, unsigned int offset)
> +struct irq_domain *irq_sim_get_domain(struct irq_sim *sim)
>  {
> -	return sim->irqs[offset].irqnum;
> +	return sim->domain;
>  }
> -EXPORT_SYMBOL_GPL(irq_sim_irqnum);
> +EXPORT_SYMBOL(irq_sim_get_domain);

This seems to be reinventing irq_find_matching_fwnode(). Please consider
allocating a fwnode as part of the irq_sim structure, and use this fwnode
to lookup the domain instead.

	M.

Patch
diff mbox series

diff --git a/drivers/gpio/gpio-mockup.c b/drivers/gpio/gpio-mockup.c
index 9b28ffec5826..4cf594f0e7cd 100644
--- a/drivers/gpio/gpio-mockup.c
+++ b/drivers/gpio/gpio-mockup.c
@@ -186,7 +186,7 @@  static int gpio_mockup_to_irq(struct gpio_chip *gc, unsigned int offset)
 {
 	struct gpio_mockup_chip *chip = gpiochip_get_data(gc);
 
-	return irq_sim_irqnum(chip->irqsim, offset);
+	return irq_create_mapping(irq_sim_get_domain(chip->irqsim), offset);
 }
 
 static void gpio_mockup_free(struct gpio_chip *gc, unsigned int offset)
@@ -228,6 +228,7 @@  static ssize_t gpio_mockup_debugfs_write(struct file *file,
 	struct gpio_mockup_dbgfs_private *priv;
 	int rv, val, curr, irq, irq_type;
 	struct gpio_mockup_chip *chip;
+	struct irq_domain *domain;
 	struct seq_file *sfile;
 	struct gpio_desc *desc;
 	struct gpio_chip *gc;
@@ -248,6 +249,7 @@  static ssize_t gpio_mockup_debugfs_write(struct file *file,
 	gc = &chip->gc;
 	desc = &gc->gpiodev->descs[priv->offset];
 	sim = chip->irqsim;
+	domain = irq_sim_get_domain(sim);
 
 	mutex_lock(&chip->lock);
 
@@ -257,12 +259,15 @@  static ssize_t gpio_mockup_debugfs_write(struct file *file,
 		if (curr == val)
 			goto out;
 
-		irq = irq_sim_irqnum(sim, priv->offset);
+		irq = irq_find_mapping(domain, priv->offset);
+		if (!irq)
+			return -ENOENT;
+
 		irq_type = irq_get_trigger_type(irq);
 
 		if ((val == 1 && (irq_type & IRQ_TYPE_EDGE_RISING)) ||
 		    (val == 0 && (irq_type & IRQ_TYPE_EDGE_FALLING)))
-			irq_sim_fire(sim, priv->offset);
+			irq_sim_fire(irq);
 	}
 
 	/* Change the value unless we're actively driving the line. */
diff --git a/drivers/iio/dummy/iio_dummy_evgen.c b/drivers/iio/dummy/iio_dummy_evgen.c
index efbcd4a5609e..cc827f60a535 100644
--- a/drivers/iio/dummy/iio_dummy_evgen.c
+++ b/drivers/iio/dummy/iio_dummy_evgen.c
@@ -31,14 +31,13 @@ 
  * @lock: protect the evgen state
  * @inuse: mask of which irqs are connected
  * @irq_sim: interrupt simulator
- * @base: base of irq range
  */
 struct iio_dummy_eventgen {
 	struct iio_dummy_regs regs[IIO_EVENTGEN_NO];
 	struct mutex lock;
 	bool inuse[IIO_EVENTGEN_NO];
 	struct irq_sim *irq_sim;
-	int base;
+	struct irq_domain *domain;
 };
 
 /* We can only ever have one instance of this 'device' */
@@ -56,7 +55,7 @@  static int iio_dummy_evgen_create(void)
 		return PTR_ERR(iio_evgen->irq_sim);
 	}
 
-	iio_evgen->base = irq_sim_irqnum(iio_evgen->irq_sim, 0);
+	iio_evgen->domain = irq_sim_get_domain(iio_evgen->irq_sim);
 	mutex_init(&iio_evgen->lock);
 
 	return 0;
@@ -78,7 +77,7 @@  int iio_dummy_evgen_get_irq(void)
 	mutex_lock(&iio_evgen->lock);
 	for (i = 0; i < IIO_EVENTGEN_NO; i++) {
 		if (!iio_evgen->inuse[i]) {
-			ret = irq_sim_irqnum(iio_evgen->irq_sim, i);
+			ret = irq_create_mapping(iio_evgen->domain, i);
 			iio_evgen->inuse[i] = true;
 			break;
 		}
@@ -99,15 +98,21 @@  EXPORT_SYMBOL_GPL(iio_dummy_evgen_get_irq);
  */
 void iio_dummy_evgen_release_irq(int irq)
 {
+	struct irq_data *irqd;
+
+	irqd = irq_get_irq_data(irq);
+
 	mutex_lock(&iio_evgen->lock);
-	iio_evgen->inuse[irq - iio_evgen->base] = false;
+	iio_evgen->inuse[irqd_to_hwirq(irqd)] = false;
 	mutex_unlock(&iio_evgen->lock);
 }
 EXPORT_SYMBOL_GPL(iio_dummy_evgen_release_irq);
 
 struct iio_dummy_regs *iio_dummy_evgen_get_regs(int irq)
 {
-	return &iio_evgen->regs[irq - iio_evgen->base];
+	struct irq_data *irqd = irq_get_irq_data(irq);
+
+	return &iio_evgen->regs[irqd_to_hwirq(irqd)];
 }
 EXPORT_SYMBOL_GPL(iio_dummy_evgen_get_regs);
 
@@ -129,7 +134,7 @@  static ssize_t iio_evgen_poke(struct device *dev,
 {
 	struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
 	unsigned long event;
-	int ret;
+	int ret, irq;
 
 	ret = kstrtoul(buf, 10, &event);
 	if (ret)
@@ -138,7 +143,8 @@  static ssize_t iio_evgen_poke(struct device *dev,
 	iio_evgen->regs[this_attr->address].reg_id   = this_attr->address;
 	iio_evgen->regs[this_attr->address].reg_data = event;
 
-	irq_sim_fire(iio_evgen->irq_sim, this_attr->address);
+	irq = irq_create_mapping(iio_evgen->domain, this_attr->address);
+	irq_sim_fire(irq);
 
 	return len;
 }
diff --git a/include/linux/irq_sim.h b/include/linux/irq_sim.h
index 4bbf036145e2..4056d0e7f0b4 100644
--- a/include/linux/irq_sim.h
+++ b/include/linux/irq_sim.h
@@ -7,6 +7,7 @@ 
 #define _LINUX_IRQ_SIM_H
 
 #include <linux/irq_work.h>
+#include <linux/irqdomain.h>
 #include <linux/device.h>
 
 /*
@@ -19,7 +20,7 @@  struct irq_sim;
 struct irq_sim *irq_sim_new(unsigned int num_irqs);
 struct irq_sim *devm_irq_sim_new(struct device *dev, unsigned int num_irqs);
 void irq_sim_free(struct irq_sim *sim);
-void irq_sim_fire(struct irq_sim *sim, unsigned int offset);
-int irq_sim_irqnum(struct irq_sim *sim, unsigned int offset);
+void irq_sim_fire(int virq);
+struct irq_domain *irq_sim_get_domain(struct irq_sim *sim);
 
 #endif /* _LINUX_IRQ_SIM_H */
diff --git a/kernel/irq/Kconfig b/kernel/irq/Kconfig
index f92d9a687372..d0890f7729d4 100644
--- a/kernel/irq/Kconfig
+++ b/kernel/irq/Kconfig
@@ -68,6 +68,7 @@  config IRQ_DOMAIN
 config IRQ_SIM
 	bool
 	select IRQ_WORK
+	select IRQ_DOMAIN
 
 # Support for hierarchical irq domains
 config IRQ_DOMAIN_HIERARCHY
diff --git a/kernel/irq/irq_sim.c b/kernel/irq/irq_sim.c
index 79f0a6494b6c..a1c91aefb6cd 100644
--- a/kernel/irq/irq_sim.c
+++ b/kernel/irq/irq_sim.c
@@ -15,13 +15,14 @@  struct irq_sim_work_ctx {
 struct irq_sim_irq_ctx {
 	int			irqnum;
 	bool			enabled;
+	struct irq_sim_work_ctx	*work_ctx;
 };
 
 struct irq_sim {
 	struct irq_sim_work_ctx	work_ctx;
 	int			irq_base;
 	unsigned int		irq_count;
-	struct irq_sim_irq_ctx	*irqs;
+	struct irq_domain	*domain;
 };
 
 struct irq_sim_devres {
@@ -74,11 +75,46 @@  static void irq_sim_handle_irq(struct irq_work *work)
 		offset = find_next_bit(work_ctx->pending,
 				       sim->irq_count, offset);
 		clear_bit(offset, work_ctx->pending);
-		irqnum = irq_sim_irqnum(sim, offset);
+		irqnum = irq_find_mapping(sim->domain, offset);
 		handle_simple_irq(irq_to_desc(irqnum));
 	}
 }
 
+static int irq_sim_domain_map(struct irq_domain *domain,
+			      unsigned int virq, irq_hw_number_t hw)
+{
+	struct irq_sim *sim = domain->host_data;
+	struct irq_sim_irq_ctx *ctx;
+
+	ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+	if (!ctx)
+		return -ENOMEM;
+
+	irq_set_chip(virq, &irq_sim_irqchip);
+	irq_set_chip_data(virq, ctx);
+	irq_set_handler(virq, handle_simple_irq);
+	irq_modify_status(virq, IRQ_NOREQUEST | IRQ_NOAUTOEN, IRQ_NOPROBE);
+	ctx->work_ctx = &sim->work_ctx;
+
+	return 0;
+}
+
+static void irq_sim_domain_unmap(struct irq_domain *domain, unsigned int virq)
+{
+	struct irq_sim_irq_ctx *ctx;
+	struct irq_data *irqd;
+
+	irqd = irq_domain_get_irq_data(domain, virq);
+	ctx = irq_data_get_irq_chip_data(irqd);
+
+	kfree(ctx);
+}
+
+static const struct irq_domain_ops irq_sim_domain_ops = {
+	.map		= irq_sim_domain_map,
+	.unmap		= irq_sim_domain_unmap,
+};
+
 /**
  * irq_sim_new - Create a new interrupt simulator: allocate a range of
  *               dummy interrupts.
@@ -91,42 +127,21 @@  static void irq_sim_handle_irq(struct irq_work *work)
 struct irq_sim *irq_sim_new(unsigned int num_irqs)
 {
 	struct irq_sim *sim;
-	int i;
 
 	sim = kmalloc(sizeof(*sim), GFP_KERNEL);
 	if (!sim)
 		return ERR_PTR(-ENOMEM);
 
-	sim->irqs = kmalloc_array(num_irqs, sizeof(*sim->irqs), GFP_KERNEL);
-	if (!sim->irqs) {
-		kfree(sim);
-		return ERR_PTR(-ENOMEM);
-	}
-
-	sim->irq_base = irq_alloc_descs(-1, 0, num_irqs, 0);
-	if (sim->irq_base < 0) {
-		kfree(sim->irqs);
-		kfree(sim);
-		return ERR_PTR(sim->irq_base);
-	}
-
 	sim->work_ctx.pending = bitmap_zalloc(num_irqs, GFP_KERNEL);
 	if (!sim->work_ctx.pending) {
-		kfree(sim->irqs);
 		kfree(sim);
-		irq_free_descs(sim->irq_base, num_irqs);
 		return ERR_PTR(-ENOMEM);
 	}
 
-	for (i = 0; i < num_irqs; i++) {
-		sim->irqs[i].irqnum = sim->irq_base + i;
-		sim->irqs[i].enabled = false;
-		irq_set_chip(sim->irq_base + i, &irq_sim_irqchip);
-		irq_set_chip_data(sim->irq_base + i, &sim->irqs[i]);
-		irq_set_handler(sim->irq_base + i, &handle_simple_irq);
-		irq_modify_status(sim->irq_base + i,
-				  IRQ_NOREQUEST | IRQ_NOAUTOEN, IRQ_NOPROBE);
-	}
+	sim->domain = irq_domain_create_linear(NULL, num_irqs,
+					       &irq_sim_domain_ops, sim);
+	if (!sim->domain)
+		return ERR_PTR(-ENOMEM);
 
 	init_irq_work(&sim->work_ctx.work, irq_sim_handle_irq);
 	sim->irq_count = num_irqs;
@@ -143,10 +158,17 @@  EXPORT_SYMBOL_GPL(irq_sim_new);
  */
 void irq_sim_free(struct irq_sim *sim)
 {
+	int i, irq;
+
+	for (i = 0; i < sim->irq_count; i++) {
+		irq = irq_find_mapping(sim->domain, i);
+		if (irq)
+			irq_dispose_mapping(irq);
+	}
+
+	irq_domain_remove(sim->domain);
 	irq_work_sync(&sim->work_ctx.work);
 	bitmap_free(sim->work_ctx.pending);
-	irq_free_descs(sim->irq_base, sim->irq_count);
-	kfree(sim->irqs);
 	kfree(sim);
 }
 EXPORT_SYMBOL_GPL(irq_sim_free);
@@ -189,27 +211,31 @@  EXPORT_SYMBOL_GPL(devm_irq_sim_new);
 /**
  * irq_sim_fire - Enqueue an interrupt.
  *
- * @sim:        The interrupt simulator object.
- * @offset:     Offset of the simulated interrupt which should be fired.
+ * @virq:        Virtual interrupt number to fire. It must be associated with
+ *               an existing interrupt simulator.
  */
-void irq_sim_fire(struct irq_sim *sim, unsigned int offset)
+void irq_sim_fire(int virq)
 {
-	if (sim->irqs[offset].enabled) {
-		set_bit(offset, sim->work_ctx.pending);
-		irq_work_queue(&sim->work_ctx.work);
+	struct irq_sim_irq_ctx *ctx;
+	struct irq_data *irqd;
+
+	irqd = irq_get_irq_data(virq);
+	ctx = irq_data_get_irq_chip_data(irqd);
+
+	if (ctx->enabled) {
+		set_bit(irqd_to_hwirq(irqd), ctx->work_ctx->pending);
+		irq_work_queue(&ctx->work_ctx->work);
 	}
 }
 EXPORT_SYMBOL_GPL(irq_sim_fire);
 
 /**
- * irq_sim_irqnum - Get the allocated number of a dummy interrupt.
+ * irq_sim_get_domain - Retrieve the interrupt domain for this simulator.
  *
- * @sim:        The interrupt simulator object.
- * @offset:     Offset of the simulated interrupt for which to retrieve
- *              the number.
+ * @sim:         The interrupt simulator the domain of which we retrieve.
  */
-int irq_sim_irqnum(struct irq_sim *sim, unsigned int offset)
+struct irq_domain *irq_sim_get_domain(struct irq_sim *sim)
 {
-	return sim->irqs[offset].irqnum;
+	return sim->domain;
 }
-EXPORT_SYMBOL_GPL(irq_sim_irqnum);
+EXPORT_SYMBOL(irq_sim_get_domain);