Skip to content

Commit

Permalink
genirq/irq_sim: Simplify the API
Browse files Browse the repository at this point in the history
The interrupt simulator API exposes a lot of custom data structures and
functions and doesn't reuse the interfaces already exposed by the irq
subsystem. This patch tries to address it.

We hide all the simulator-related data structures from users and instead
rely on the well-known irq domain. When creating the interrupt simulator
the user receives a pointer to a newly created irq_domain and can use it
to create mappings for simulated interrupts.

It is also possible to pass a handle to fwnode when creating the simulator
domain and retrieve it using irq_find_matching_fwnode().

The irq_sim_fire() function is dropped as well. Instead we implement the
irq_get/set_irqchip_state interface.

We modify the two modules that use the simulator at the same time as
adding these changes in order to reduce the intermediate bloat that would
result when trying to migrate the drivers in separate patches.

Signed-off-by: Bartosz Golaszewski <bgolaszewski@baylibre.com>
Signed-off-by: Marc Zyngier <maz@kernel.org>
Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
Acked-by: Jonathan Cameron <Jonathan.Cameron@huawei.com> #for IIO
Link: https://lore.kernel.org/r/20200514083901.23445-3-brgl@bgdev.pl
  • Loading branch information
brgl authored and Marc Zyngier committed May 18, 2020
1 parent 5c8f77a commit 337cbeb
Show file tree
Hide file tree
Showing 5 changed files with 237 additions and 151 deletions.
53 changes: 42 additions & 11 deletions drivers/gpio/gpio-mockup.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include <linux/interrupt.h>
#include <linux/irq.h>
#include <linux/irq_sim.h>
#include <linux/irqdomain.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/property.h>
Expand Down Expand Up @@ -48,7 +49,7 @@ struct gpio_mockup_line_status {
struct gpio_mockup_chip {
struct gpio_chip gc;
struct gpio_mockup_line_status *lines;
struct irq_sim irqsim;
struct irq_domain *irq_sim_domain;
struct dentry *dbg_dir;
struct mutex lock;
};
Expand Down Expand Up @@ -144,14 +145,12 @@ static void gpio_mockup_set_multiple(struct gpio_chip *gc,
static int gpio_mockup_apply_pull(struct gpio_mockup_chip *chip,
unsigned int offset, int value)
{
int curr, irq, irq_type, ret = 0;
struct gpio_desc *desc;
struct gpio_chip *gc;
struct irq_sim *sim;
int curr, irq, irq_type;

gc = &chip->gc;
desc = &gc->gpiodev->descs[offset];
sim = &chip->irqsim;

mutex_lock(&chip->lock);

Expand All @@ -161,14 +160,28 @@ static int gpio_mockup_apply_pull(struct gpio_mockup_chip *chip,
if (curr == value)
goto out;

irq = irq_sim_irqnum(sim, offset);
irq = irq_find_mapping(chip->irq_sim_domain, offset);
if (!irq)
/*
* This is fine - it just means, nobody is listening
* for interrupts on this line, otherwise
* irq_create_mapping() would have been called from
* the to_irq() callback.
*/
goto set_value;

irq_type = irq_get_trigger_type(irq);

if ((value == 1 && (irq_type & IRQ_TYPE_EDGE_RISING)) ||
(value == 0 && (irq_type & IRQ_TYPE_EDGE_FALLING)))
irq_sim_fire(sim, offset);
(value == 0 && (irq_type & IRQ_TYPE_EDGE_FALLING))) {
ret = irq_set_irqchip_state(irq, IRQCHIP_STATE_PENDING,
true);
if (ret)
goto out;
}
}

set_value:
/* Change the value unless we're actively driving the line. */
if (!test_bit(FLAG_REQUESTED, &desc->flags) ||
!test_bit(FLAG_IS_OUT, &desc->flags))
Expand All @@ -177,7 +190,7 @@ static int gpio_mockup_apply_pull(struct gpio_mockup_chip *chip,
out:
chip->lines[offset].pull = value;
mutex_unlock(&chip->lock);
return 0;
return ret;
}

static int gpio_mockup_set_config(struct gpio_chip *gc,
Expand Down Expand Up @@ -236,7 +249,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(chip->irq_sim_domain, offset);
}

static void gpio_mockup_free(struct gpio_chip *gc, unsigned int offset)
Expand Down Expand Up @@ -389,6 +402,19 @@ static int gpio_mockup_name_lines(struct device *dev,
return 0;
}

static void gpio_mockup_dispose_mappings(void *data)
{
struct gpio_mockup_chip *chip = data;
struct gpio_chip *gc = &chip->gc;
int i, irq;

for (i = 0; i < gc->ngpio; i++) {
irq = irq_find_mapping(chip->irq_sim_domain, i);
if (irq)
irq_dispose_mapping(irq);
}
}

static int gpio_mockup_probe(struct platform_device *pdev)
{
struct gpio_mockup_chip *chip;
Expand Down Expand Up @@ -456,8 +482,13 @@ static int gpio_mockup_probe(struct platform_device *pdev)
return rv;
}

rv = devm_irq_sim_init(dev, &chip->irqsim, gc->ngpio);
if (rv < 0)
chip->irq_sim_domain = devm_irq_domain_create_sim(dev, NULL,
gc->ngpio);
if (IS_ERR(chip->irq_sim_domain))
return PTR_ERR(chip->irq_sim_domain);

rv = devm_add_action_or_reset(dev, gpio_mockup_dispose_mappings, chip);
if (rv)
return rv;

rv = devm_gpiochip_add_data(dev, &chip->gc, chip);
Expand Down
34 changes: 20 additions & 14 deletions drivers/iio/dummy/iio_dummy_evgen.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,28 +37,25 @@ 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 *irq_sim_domain;
};

/* We can only ever have one instance of this 'device' */
static struct iio_dummy_eventgen *iio_evgen;

static int iio_dummy_evgen_create(void)
{
int ret;

iio_evgen = kzalloc(sizeof(*iio_evgen), GFP_KERNEL);
if (!iio_evgen)
return -ENOMEM;

ret = irq_sim_init(&iio_evgen->irq_sim, IIO_EVENTGEN_NO);
if (ret < 0) {
iio_evgen->irq_sim_domain = irq_domain_create_sim(NULL,
IIO_EVENTGEN_NO);
if (IS_ERR(iio_evgen->irq_sim_domain)) {
kfree(iio_evgen);
return ret;
return PTR_ERR(iio_evgen->irq_sim_domain);
}

iio_evgen->base = irq_sim_irqnum(&iio_evgen->irq_sim, 0);
mutex_init(&iio_evgen->lock);

return 0;
Expand All @@ -80,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->irq_sim_domain, i);
iio_evgen->inuse[i] = true;
break;
}
Expand All @@ -101,21 +98,27 @@ EXPORT_SYMBOL_GPL(iio_dummy_evgen_get_irq);
*/
void iio_dummy_evgen_release_irq(int irq)
{
struct irq_data *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;
irq_dispose_mapping(irq);
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);

static void iio_dummy_evgen_free(void)
{
irq_sim_fini(&iio_evgen->irq_sim);
irq_domain_remove_sim(iio_evgen->irq_sim_domain);
kfree(iio_evgen);
}

Expand All @@ -131,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)
Expand All @@ -140,7 +143,10 @@ 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_find_mapping(iio_evgen->irq_sim_domain, this_attr->address);
ret = irq_set_irqchip_state(irq, IRQCHIP_STATE_PENDING, true);
if (ret)
return ret;

return len;
}
Expand Down
33 changes: 9 additions & 24 deletions include/linux/irq_sim.h
Original file line number Diff line number Diff line change
@@ -1,41 +1,26 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* Copyright (C) 2017-2018 Bartosz Golaszewski <brgl@bgdev.pl>
* Copyright (C) 2020 Bartosz Golaszewski <bgolaszewski@baylibre.com>
*/

#ifndef _LINUX_IRQ_SIM_H
#define _LINUX_IRQ_SIM_H

#include <linux/irq_work.h>
#include <linux/device.h>
#include <linux/fwnode.h>
#include <linux/irqdomain.h>

/*
* Provides a framework for allocating simulated interrupts which can be
* requested like normal irqs and enqueued from process context.
*/

struct irq_sim_work_ctx {
struct irq_work work;
unsigned long *pending;
};

struct irq_sim_irq_ctx {
int irqnum;
bool enabled;
};

struct irq_sim {
struct irq_sim_work_ctx work_ctx;
int irq_base;
unsigned int irq_count;
struct irq_sim_irq_ctx *irqs;
};

int irq_sim_init(struct irq_sim *sim, unsigned int num_irqs);
int devm_irq_sim_init(struct device *dev, struct irq_sim *sim,
unsigned int num_irqs);
void irq_sim_fini(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);
struct irq_domain *irq_domain_create_sim(struct fwnode_handle *fwnode,
unsigned int num_irqs);
struct irq_domain *devm_irq_domain_create_sim(struct device *dev,
struct fwnode_handle *fwnode,
unsigned int num_irqs);
void irq_domain_remove_sim(struct irq_domain *domain);

#endif /* _LINUX_IRQ_SIM_H */
1 change: 1 addition & 0 deletions kernel/irq/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ config IRQ_DOMAIN
config IRQ_SIM
bool
select IRQ_WORK
select IRQ_DOMAIN

# Support for hierarchical irq domains
config IRQ_DOMAIN_HIERARCHY
Expand Down
Loading

0 comments on commit 337cbeb

Please sign in to comment.