History

genirq/irq_sim: Simplify the API

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.

Bartosz Golaszewski committed 6 years ago

Commit 337cbeb1 parent 5c8f77a5 files · +237 −151Browse files

Unified · Split

5 files
  1. drivers/gpio/gpio-mockup.c +42 −11
  2. drivers/iio/dummy/iio_dummy_evgen.c +20 −14
  3. include/linux/irq_sim.h +9 −24
  4. kernel/irq/Kconfig +1 −0
  5. kernel/irq/irq_sim.c +165 −102
drivers/gpio/gpio-mockup.c+42−11
@@ -14,6 +14,7 @@ · 13 unmodified lines
14#include <linux/interrupt.h>14#include <linux/interrupt.h>
15#include <linux/irq.h>15#include <linux/irq.h>
16#include <linux/irq_sim.h>16#include <linux/irq_sim.h>
17#include <linux/irqdomain.h>
17#include <linux/module.h>18#include <linux/module.h>
18#include <linux/platform_device.h>19#include <linux/platform_device.h>
19#include <linux/property.h>20#include <linux/property.h>
@@ -48,7 +49,7 @@ struct gpio_mockup_line_status { · 28 unmodified lines
48struct gpio_mockup_chip {49struct gpio_mockup_chip {
49 struct gpio_chip gc;50 struct gpio_chip gc;
50 struct gpio_mockup_line_status *lines;51 struct gpio_mockup_line_status *lines;
51 struct irq_sim irqsim;52 struct irq_domain *irq_sim_domain;
52 struct dentry *dbg_dir;53 struct dentry *dbg_dir;
53 struct mutex lock;54 struct mutex lock;
54};55};
@@ -144,14 +145,12 @@ static void gpio_mockup_set_multiple(struct gpio_chip *gc, · 89 unmodified lines
144static int gpio_mockup_apply_pull(struct gpio_mockup_chip *chip,145static int gpio_mockup_apply_pull(struct gpio_mockup_chip *chip,
145 unsigned int offset, int value)146 unsigned int offset, int value)
146{147{
148 int curr, irq, irq_type, ret = 0;
147 struct gpio_desc *desc;149 struct gpio_desc *desc;
148 struct gpio_chip *gc;150 struct gpio_chip *gc;
149 struct irq_sim *sim;
150 int curr, irq, irq_type;
151 151
152 gc = &chip->gc;152 gc = &chip->gc;
153 desc = &gc->gpiodev->descs[offset];153 desc = &gc->gpiodev->descs[offset];
154 sim = &chip->irqsim;
155 154
156 mutex_lock(&chip->lock);155 mutex_lock(&chip->lock);
157 156
@@ -161,14 +160,28 @@ static int gpio_mockup_apply_pull(struct gpio_mockup_chip *chip, · 3 unmodified lines
161 if (curr == value)160 if (curr == value)
162 goto out;161 goto out;
163 162
164 irq = irq_sim_irqnum(sim, offset);163 irq = irq_find_mapping(chip->irq_sim_domain, offset);
164 if (!irq)
165 /*
166 * This is fine - it just means, nobody is listening
167 * for interrupts on this line, otherwise
168 * irq_create_mapping() would have been called from
169 * the to_irq() callback.
170 */
171 goto set_value;
172
165 irq_type = irq_get_trigger_type(irq);173 irq_type = irq_get_trigger_type(irq);
166 174
167 if ((value == 1 && (irq_type & IRQ_TYPE_EDGE_RISING)) ||175 if ((value == 1 && (irq_type & IRQ_TYPE_EDGE_RISING)) ||
168 (value == 0 && (irq_type & IRQ_TYPE_EDGE_FALLING)))176 (value == 0 && (irq_type & IRQ_TYPE_EDGE_FALLING))) {
169 irq_sim_fire(sim, offset);177 ret = irq_set_irqchip_state(irq, IRQCHIP_STATE_PENDING,
178 true);
179 if (ret)
180 goto out;
181 }
170 }182 }
171 183
184set_value:
172 /* Change the value unless we're actively driving the line. */185 /* Change the value unless we're actively driving the line. */
173 if (!test_bit(FLAG_REQUESTED, &desc->flags) ||186 if (!test_bit(FLAG_REQUESTED, &desc->flags) ||
174 !test_bit(FLAG_IS_OUT, &desc->flags))187 !test_bit(FLAG_IS_OUT, &desc->flags))
@@ -177,7 +190,7 @@ static int gpio_mockup_apply_pull(struct gpio_mockup_chip *chip, · 2 unmodified lines
177out:190out:
178 chip->lines[offset].pull = value;191 chip->lines[offset].pull = value;
179 mutex_unlock(&chip->lock);192 mutex_unlock(&chip->lock);
180 return 0;193 return ret;
181}194}
182 195
183static int gpio_mockup_set_config(struct gpio_chip *gc,196static int gpio_mockup_set_config(struct gpio_chip *gc,
@@ -236,7 +249,7 @@ static int gpio_mockup_to_irq(struct gpio_chip *gc, unsigned int offset) · 52 unmodified lines
236{249{
237 struct gpio_mockup_chip *chip = gpiochip_get_data(gc);250 struct gpio_mockup_chip *chip = gpiochip_get_data(gc);
238 251
239 return irq_sim_irqnum(&chip->irqsim, offset);252 return irq_create_mapping(chip->irq_sim_domain, offset);
240}253}
241 254
242static void gpio_mockup_free(struct gpio_chip *gc, unsigned int offset)255static void gpio_mockup_free(struct gpio_chip *gc, unsigned int offset)
@@ -389,6 +402,19 @@ static int gpio_mockup_name_lines(struct device *dev, · 146 unmodified lines
389 return 0;402 return 0;
390}403}
391 404
405static void gpio_mockup_dispose_mappings(void *data)
406{
407 struct gpio_mockup_chip *chip = data;
408 struct gpio_chip *gc = &chip->gc;
409 int i, irq;
410
411 for (i = 0; i < gc->ngpio; i++) {
412 irq = irq_find_mapping(chip->irq_sim_domain, i);
413 if (irq)
414 irq_dispose_mapping(irq);
415 }
416}
417
392static int gpio_mockup_probe(struct platform_device *pdev)418static int gpio_mockup_probe(struct platform_device *pdev)
393{419{
394 struct gpio_mockup_chip *chip;420 struct gpio_mockup_chip *chip;
@@ -456,8 +482,13 @@ static int gpio_mockup_probe(struct platform_device *pdev) · 61 unmodified lines
456 return rv;482 return rv;
457 }483 }
458 484
459 rv = devm_irq_sim_init(dev, &chip->irqsim, gc->ngpio);485 chip->irq_sim_domain = devm_irq_domain_create_sim(dev, NULL,
460 if (rv < 0)486 gc->ngpio);
487 if (IS_ERR(chip->irq_sim_domain))
488 return PTR_ERR(chip->irq_sim_domain);
489
490 rv = devm_add_action_or_reset(dev, gpio_mockup_dispose_mappings, chip);
491 if (rv)
461 return rv;492 return rv;
462 493
463 rv = devm_gpiochip_add_data(dev, &chip->gc, chip);494 rv = devm_gpiochip_add_data(dev, &chip->gc, chip);
drivers/iio/dummy/iio_dummy_evgen.c+20−14
@@ -37,8 +37,7 @@ struct iio_dummy_eventgen { · 36 unmodified lines
37 struct iio_dummy_regs regs[IIO_EVENTGEN_NO];37 struct iio_dummy_regs regs[IIO_EVENTGEN_NO];
38 struct mutex lock;38 struct mutex lock;
39 bool inuse[IIO_EVENTGEN_NO];39 bool inuse[IIO_EVENTGEN_NO];
40 struct irq_sim irq_sim;40 struct irq_domain *irq_sim_domain;
41 int base;
42};41};
43 42
44/* We can only ever have one instance of this 'device' */43/* We can only ever have one instance of this 'device' */
@@ -46,19 +45,17 @@ static struct iio_dummy_eventgen *iio_evgen; · 1 unmodified line
46 45
47static int iio_dummy_evgen_create(void)46static int iio_dummy_evgen_create(void)
48{47{
49 int ret;
50
51 iio_evgen = kzalloc(sizeof(*iio_evgen), GFP_KERNEL);48 iio_evgen = kzalloc(sizeof(*iio_evgen), GFP_KERNEL);
52 if (!iio_evgen)49 if (!iio_evgen)
53 return -ENOMEM;50 return -ENOMEM;
54 51
55 ret = irq_sim_init(&iio_evgen->irq_sim, IIO_EVENTGEN_NO);52 iio_evgen->irq_sim_domain = irq_domain_create_sim(NULL,
56 if (ret < 0) {53 IIO_EVENTGEN_NO);
54 if (IS_ERR(iio_evgen->irq_sim_domain)) {
57 kfree(iio_evgen);55 kfree(iio_evgen);
58 return ret;56 return PTR_ERR(iio_evgen->irq_sim_domain);
59 }57 }
60 58
61 iio_evgen->base = irq_sim_irqnum(&iio_evgen->irq_sim, 0);
62 mutex_init(&iio_evgen->lock);59 mutex_init(&iio_evgen->lock);
63 60
64 return 0;61 return 0;
@@ -80,7 +77,7 @@ int iio_dummy_evgen_get_irq(void) · 15 unmodified lines
80 mutex_lock(&iio_evgen->lock);77 mutex_lock(&iio_evgen->lock);
81 for (i = 0; i < IIO_EVENTGEN_NO; i++) {78 for (i = 0; i < IIO_EVENTGEN_NO; i++) {
82 if (!iio_evgen->inuse[i]) {79 if (!iio_evgen->inuse[i]) {
83 ret = irq_sim_irqnum(&iio_evgen->irq_sim, i);80 ret = irq_create_mapping(iio_evgen->irq_sim_domain, i);
84 iio_evgen->inuse[i] = true;81 iio_evgen->inuse[i] = true;
85 break;82 break;
86 }83 }
@@ -101,21 +98,27 @@ EXPORT_SYMBOL_GPL(iio_dummy_evgen_get_irq); · 14 unmodified lines
101 */98 */
102void iio_dummy_evgen_release_irq(int irq)99void iio_dummy_evgen_release_irq(int irq)
103{100{
101 struct irq_data *irqd = irq_get_irq_data(irq);
102
104 mutex_lock(&iio_evgen->lock);103 mutex_lock(&iio_evgen->lock);
105 iio_evgen->inuse[irq - iio_evgen->base] = false;104 iio_evgen->inuse[irqd_to_hwirq(irqd)] = false;
105 irq_dispose_mapping(irq);
106 mutex_unlock(&iio_evgen->lock);106 mutex_unlock(&iio_evgen->lock);
107}107}
108EXPORT_SYMBOL_GPL(iio_dummy_evgen_release_irq);108EXPORT_SYMBOL_GPL(iio_dummy_evgen_release_irq);
109 109
110struct iio_dummy_regs *iio_dummy_evgen_get_regs(int irq)110struct iio_dummy_regs *iio_dummy_evgen_get_regs(int irq)
111{111{
112 return &iio_evgen->regs[irq - iio_evgen->base];112 struct irq_data *irqd = irq_get_irq_data(irq);
113
114 return &iio_evgen->regs[irqd_to_hwirq(irqd)];
115
113}116}
114EXPORT_SYMBOL_GPL(iio_dummy_evgen_get_regs);117EXPORT_SYMBOL_GPL(iio_dummy_evgen_get_regs);
115 118
116static void iio_dummy_evgen_free(void)119static void iio_dummy_evgen_free(void)
117{120{
118 irq_sim_fini(&iio_evgen->irq_sim);121 irq_domain_remove_sim(iio_evgen->irq_sim_domain);
119 kfree(iio_evgen);122 kfree(iio_evgen);
120}123}
121 124
@@ -131,7 +134,7 @@ static ssize_t iio_evgen_poke(struct device *dev, · 9 unmodified lines
131{134{
132 struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);135 struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
133 unsigned long event;136 unsigned long event;
134 int ret;137 int ret, irq;
135 138
136 ret = kstrtoul(buf, 10, &event);139 ret = kstrtoul(buf, 10, &event);
137 if (ret)140 if (ret)
@@ -140,7 +143,10 @@ static ssize_t iio_evgen_poke(struct device *dev, · 2 unmodified lines
140 iio_evgen->regs[this_attr->address].reg_id = this_attr->address;143 iio_evgen->regs[this_attr->address].reg_id = this_attr->address;
141 iio_evgen->regs[this_attr->address].reg_data = event;144 iio_evgen->regs[this_attr->address].reg_data = event;
142 145
143 irq_sim_fire(&iio_evgen->irq_sim, this_attr->address);146 irq = irq_find_mapping(iio_evgen->irq_sim_domain, this_attr->address);
147 ret = irq_set_irqchip_state(irq, IRQCHIP_STATE_PENDING, true);
148 if (ret)
149 return ret;
144 150
145 return len;151 return len;
146}152}
include/linux/irq_sim.h+9−24
@@ -1,41 +1,26 @@
1/* SPDX-License-Identifier: GPL-2.0+ */1/* SPDX-License-Identifier: GPL-2.0+ */
2/*2/*
3 * Copyright (C) 2017-2018 Bartosz Golaszewski <brgl@bgdev.pl>3 * Copyright (C) 2017-2018 Bartosz Golaszewski <brgl@bgdev.pl>
4 * Copyright (C) 2020 Bartosz Golaszewski <bgolaszewski@baylibre.com>
4 */5 */
5 6
6#ifndef _LINUX_IRQ_SIM_H7#ifndef _LINUX_IRQ_SIM_H
7#define _LINUX_IRQ_SIM_H8#define _LINUX_IRQ_SIM_H
8 9
9#include <linux/irq_work.h>
10#include <linux/device.h>10#include <linux/device.h>
11#include <linux/fwnode.h>
12#include <linux/irqdomain.h>
11 13
12/*14/*
13 * Provides a framework for allocating simulated interrupts which can be15 * Provides a framework for allocating simulated interrupts which can be
14 * requested like normal irqs and enqueued from process context.16 * requested like normal irqs and enqueued from process context.
15 */17 */
16 18
17struct irq_sim_work_ctx {19struct irq_domain *irq_domain_create_sim(struct fwnode_handle *fwnode,
18 struct irq_work work;20 unsigned int num_irqs);
19 unsigned long *pending;21struct irq_domain *devm_irq_domain_create_sim(struct device *dev,
20};22 struct fwnode_handle *fwnode,
21 23 unsigned int num_irqs);
22struct irq_sim_irq_ctx {24void irq_domain_remove_sim(struct irq_domain *domain);
23 int irqnum;
24 bool enabled;
25};
26
27struct irq_sim {
28 struct irq_sim_work_ctx work_ctx;
29 int irq_base;
30 unsigned int irq_count;
31 struct irq_sim_irq_ctx *irqs;
32};
33
34int irq_sim_init(struct irq_sim *sim, unsigned int num_irqs);
35int devm_irq_sim_init(struct device *dev, struct irq_sim *sim,
36 unsigned int num_irqs);
37void irq_sim_fini(struct irq_sim *sim);
38void irq_sim_fire(struct irq_sim *sim, unsigned int offset);
39int irq_sim_irqnum(struct irq_sim *sim, unsigned int offset);
40 25
41#endif /* _LINUX_IRQ_SIM_H */26#endif /* _LINUX_IRQ_SIM_H */
kernel/irq/Kconfig+1−0
@@ -72,6 +72,7 @@ config IRQ_DOMAIN · 71 unmodified lines
72config IRQ_SIM72config IRQ_SIM
73 bool73 bool
74 select IRQ_WORK74 select IRQ_WORK
75 select IRQ_DOMAIN
75 76
76# Support for hierarchical irq domains77# Support for hierarchical irq domains
77config IRQ_DOMAIN_HIERARCHY78config IRQ_DOMAIN_HIERARCHY
kernel/irq/irq_sim.c+165−102
@@ -1,14 +1,31 @@
1// SPDX-License-Identifier: GPL-2.0+1// SPDX-License-Identifier: GPL-2.0+
2/*2/*
3 * Copyright (C) 2017-2018 Bartosz Golaszewski <brgl@bgdev.pl>3 * Copyright (C) 2017-2018 Bartosz Golaszewski <brgl@bgdev.pl>
4 * Copyright (C) 2020 Bartosz Golaszewski <bgolaszewski@baylibre.com>
4 */5 */
5 6
6#include <linux/slab.h>
7#include <linux/irq_sim.h>
8#include <linux/irq.h>7#include <linux/irq.h>
8#include <linux/irq_sim.h>
9#include <linux/irq_work.h>
10#include <linux/interrupt.h>
11#include <linux/slab.h>
12
13struct irq_sim_work_ctx {
14 struct irq_work work;
15 int irq_base;
16 unsigned int irq_count;
17 unsigned long *pending;
18 struct irq_domain *domain;
19};
20
21struct irq_sim_irq_ctx {
22 int irqnum;
23 bool enabled;
24 struct irq_sim_work_ctx *work_ctx;
25};
9 26
10struct irq_sim_devres {27struct irq_sim_devres {
11 struct irq_sim *sim;28 struct irq_domain *domain;
12};29};
13 30
14static void irq_sim_irqmask(struct irq_data *data)31static void irq_sim_irqmask(struct irq_data *data)
@@ -36,159 +53,205 @@ static int irq_sim_set_type(struct irq_data *data, unsigned int type) · 21 unmodified lines
36 return 0;53 return 0;
37}54}
38 55
56static int irq_sim_get_irqchip_state(struct irq_data *data,
57 enum irqchip_irq_state which, bool *state)
58{
59 struct irq_sim_irq_ctx *irq_ctx = irq_data_get_irq_chip_data(data);
60 irq_hw_number_t hwirq = irqd_to_hwirq(data);
61
62 switch (which) {
63 case IRQCHIP_STATE_PENDING:
64 if (irq_ctx->enabled)
65 *state = test_bit(hwirq, irq_ctx->work_ctx->pending);
66 break;
67 default:
68 return -EINVAL;
69 }
70
71 return 0;
72}
73
74static int irq_sim_set_irqchip_state(struct irq_data *data,
75 enum irqchip_irq_state which, bool state)
76{
77 struct irq_sim_irq_ctx *irq_ctx = irq_data_get_irq_chip_data(data);
78 irq_hw_number_t hwirq = irqd_to_hwirq(data);
79
80 switch (which) {
81 case IRQCHIP_STATE_PENDING:
82 if (irq_ctx->enabled) {
83 assign_bit(hwirq, irq_ctx->work_ctx->pending, state);
84 if (state)
85 irq_work_queue(&irq_ctx->work_ctx->work);
86 }
87 break;
88 default:
89 return -EINVAL;
90 }
91
92 return 0;
93}
94
39static struct irq_chip irq_sim_irqchip = {95static struct irq_chip irq_sim_irqchip = {
40 .name = "irq_sim",96 .name = "irq_sim",
41 .irq_mask = irq_sim_irqmask,97 .irq_mask = irq_sim_irqmask,
42 .irq_unmask = irq_sim_irqunmask,98 .irq_unmask = irq_sim_irqunmask,
43 .irq_set_type = irq_sim_set_type,99 .irq_set_type = irq_sim_set_type,
100 .irq_get_irqchip_state = irq_sim_get_irqchip_state,
101 .irq_set_irqchip_state = irq_sim_set_irqchip_state,
44};102};
45 103
46static void irq_sim_handle_irq(struct irq_work *work)104static void irq_sim_handle_irq(struct irq_work *work)
47{105{
48 struct irq_sim_work_ctx *work_ctx;106 struct irq_sim_work_ctx *work_ctx;
49 unsigned int offset = 0;107 unsigned int offset = 0;
50 struct irq_sim *sim;
51 int irqnum;108 int irqnum;
52 109
53 work_ctx = container_of(work, struct irq_sim_work_ctx, work);110 work_ctx = container_of(work, struct irq_sim_work_ctx, work);
54 sim = container_of(work_ctx, struct irq_sim, work_ctx);
55 111
56 while (!bitmap_empty(work_ctx->pending, sim->irq_count)) {112 while (!bitmap_empty(work_ctx->pending, work_ctx->irq_count)) {
57 offset = find_next_bit(work_ctx->pending,113 offset = find_next_bit(work_ctx->pending,
58 sim->irq_count, offset);114 work_ctx->irq_count, offset);
59 clear_bit(offset, work_ctx->pending);115 clear_bit(offset, work_ctx->pending);
60 irqnum = irq_sim_irqnum(sim, offset);116 irqnum = irq_find_mapping(work_ctx->domain, offset);
61 handle_simple_irq(irq_to_desc(irqnum));117 handle_simple_irq(irq_to_desc(irqnum));
62 }118 }
63}119}
64 120
121static int irq_sim_domain_map(struct irq_domain *domain,
122 unsigned int virq, irq_hw_number_t hw)
123{
124 struct irq_sim_work_ctx *work_ctx = domain->host_data;
125 struct irq_sim_irq_ctx *irq_ctx;
126
127 irq_ctx = kzalloc(sizeof(*irq_ctx), GFP_KERNEL);
128 if (!irq_ctx)
129 return -ENOMEM;
130
131 irq_set_chip(virq, &irq_sim_irqchip);
132 irq_set_chip_data(virq, irq_ctx);
133 irq_set_handler(virq, handle_simple_irq);
134 irq_modify_status(virq, IRQ_NOREQUEST | IRQ_NOAUTOEN, IRQ_NOPROBE);
135 irq_ctx->work_ctx = work_ctx;
136
137 return 0;
138}
139
140static void irq_sim_domain_unmap(struct irq_domain *domain, unsigned int virq)
141{
142 struct irq_sim_irq_ctx *irq_ctx;
143 struct irq_data *irqd;
144
145 irqd = irq_domain_get_irq_data(domain, virq);
146 irq_ctx = irq_data_get_irq_chip_data(irqd);
147
148 irq_set_handler(virq, NULL);
149 irq_domain_reset_irq_data(irqd);
150 kfree(irq_ctx);
151}
152
153static const struct irq_domain_ops irq_sim_domain_ops = {
154 .map = irq_sim_domain_map,
155 .unmap = irq_sim_domain_unmap,
156};
157
65/**158/**
66 * irq_sim_init - Initialize the interrupt simulator: allocate a range of159 * irq_domain_create_sim - Create a new interrupt simulator irq_domain and
67 * dummy interrupts.160 * allocate a range of dummy interrupts.
68 *161 *
69 * @sim: The interrupt simulator object to initialize.162 * @fnode: struct fwnode_handle to be associated with this domain.
70 * @num_irqs: Number of interrupts to allocate163 * @num_irqs: Number of interrupts to allocate.
71 *164 *
72 * On success: return the base of the allocated interrupt range.165 * On success: return a new irq_domain object.
73 * On failure: a negative errno.166 * On failure: a negative errno wrapped with ERR_PTR().
74 */167 */
75int irq_sim_init(struct irq_sim *sim, unsigned int num_irqs)168struct irq_domain *irq_domain_create_sim(struct fwnode_handle *fwnode,
169 unsigned int num_irqs)
76{170{
77 int i;171 struct irq_sim_work_ctx *work_ctx;
78 172
79 sim->irqs = kmalloc_array(num_irqs, sizeof(*sim->irqs), GFP_KERNEL);173 work_ctx = kmalloc(sizeof(*work_ctx), GFP_KERNEL);
80 if (!sim->irqs)174 if (!work_ctx)
81 return -ENOMEM;175 goto err_out;
82 176
83 sim->irq_base = irq_alloc_descs(-1, 0, num_irqs, 0);177 work_ctx->pending = bitmap_zalloc(num_irqs, GFP_KERNEL);
84 if (sim->irq_base < 0) {178 if (!work_ctx->pending)
85 kfree(sim->irqs);179 goto err_free_work_ctx;
86 return sim->irq_base;
87 }
88 180
89 sim->work_ctx.pending = bitmap_zalloc(num_irqs, GFP_KERNEL);181 work_ctx->domain = irq_domain_create_linear(fwnode, num_irqs,
90 if (!sim->work_ctx.pending) {182 &irq_sim_domain_ops,
91 kfree(sim->irqs);183 work_ctx);
92 irq_free_descs(sim->irq_base, num_irqs);184 if (!work_ctx->domain)
93 return -ENOMEM;185 goto err_free_bitmap;
94 }
95 186
96 for (i = 0; i < num_irqs; i++) {187 work_ctx->irq_count = num_irqs;
97 sim->irqs[i].irqnum = sim->irq_base + i;188 init_irq_work(&work_ctx->work, irq_sim_handle_irq);
98 sim->irqs[i].enabled = false;
99 irq_set_chip(sim->irq_base + i, &irq_sim_irqchip);
100 irq_set_chip_data(sim->irq_base + i, &sim->irqs[i]);
101 irq_set_handler(sim->irq_base + i, &handle_simple_irq);
102 irq_modify_status(sim->irq_base + i,
103 IRQ_NOREQUEST | IRQ_NOAUTOEN, IRQ_NOPROBE);
104 }
105 189
106 init_irq_work(&sim->work_ctx.work, irq_sim_handle_irq);190 return work_ctx->domain;
107 sim->irq_count = num_irqs;
108 191
109 return sim->irq_base;192err_free_bitmap:
193 bitmap_free(work_ctx->pending);
194err_free_work_ctx:
195 kfree(work_ctx);
196err_out:
197 return ERR_PTR(-ENOMEM);
110}198}
111EXPORT_SYMBOL_GPL(irq_sim_init);199EXPORT_SYMBOL_GPL(irq_domain_create_sim);
112 200
113/**201/**
114 * irq_sim_fini - Deinitialize the interrupt simulator: free the interrupt202 * irq_domain_remove_sim - Deinitialize the interrupt simulator domain: free
115 * descriptors and allocated memory.203 * the interrupt descriptors and allocated memory.
116 *204 *
117 * @sim: The interrupt simulator to tear down.205 * @domain: The interrupt simulator domain to tear down.
118 */206 */
119void irq_sim_fini(struct irq_sim *sim)207void irq_domain_remove_sim(struct irq_domain *domain)
120{208{
121 irq_work_sync(&sim->work_ctx.work);209 struct irq_sim_work_ctx *work_ctx = domain->host_data;
122 bitmap_free(sim->work_ctx.pending);210
123 irq_free_descs(sim->irq_base, sim->irq_count);211 irq_work_sync(&work_ctx->work);
124 kfree(sim->irqs);212 bitmap_free(work_ctx->pending);
213 kfree(work_ctx);
214
215 irq_domain_remove(domain);
125}216}
126EXPORT_SYMBOL_GPL(irq_sim_fini);217EXPORT_SYMBOL_GPL(irq_domain_remove_sim);
127 218
128static void devm_irq_sim_release(struct device *dev, void *res)219static void devm_irq_domain_release_sim(struct device *dev, void *res)
129{220{
130 struct irq_sim_devres *this = res;221 struct irq_sim_devres *this = res;
131 222
132 irq_sim_fini(this->sim);223 irq_domain_remove_sim(this->domain);
133}224}
134 225
135/**226/**
136 * irq_sim_init - Initialize the interrupt simulator for a managed device.227 * devm_irq_domain_create_sim - Create a new interrupt simulator for
228 * a managed device.
137 *229 *
138 * @dev: Device to initialize the simulator object for.230 * @dev: Device to initialize the simulator object for.
139 * @sim: The interrupt simulator object to initialize.231 * @fnode: struct fwnode_handle to be associated with this domain.
140 * @num_irqs: Number of interrupts to allocate232 * @num_irqs: Number of interrupts to allocate
141 *233 *
142 * On success: return the base of the allocated interrupt range.234 * On success: return a new irq_domain object.
143 * On failure: a negative errno.235 * On failure: a negative errno wrapped with ERR_PTR().
144 */236 */
145int devm_irq_sim_init(struct device *dev, struct irq_sim *sim,237struct irq_domain *devm_irq_domain_create_sim(struct device *dev,
146 unsigned int num_irqs)238 struct fwnode_handle *fwnode,
239 unsigned int num_irqs)
147{240{
148 struct irq_sim_devres *dr;241 struct irq_sim_devres *dr;
149 int rv;
150 242
151 dr = devres_alloc(devm_irq_sim_release, sizeof(*dr), GFP_KERNEL);243 dr = devres_alloc(devm_irq_domain_release_sim,
244 sizeof(*dr), GFP_KERNEL);
152 if (!dr)245 if (!dr)
153 return -ENOMEM;246 return ERR_PTR(-ENOMEM);
154 247
155 rv = irq_sim_init(sim, num_irqs);248 dr->domain = irq_domain_create_sim(fwnode, num_irqs);
156 if (rv < 0) {249 if (IS_ERR(dr->domain)) {
157 devres_free(dr);250 devres_free(dr);
158 return rv;251 return dr->domain;
159 }252 }
160 253
161 dr->sim = sim;
162 devres_add(dev, dr);254 devres_add(dev, dr);
163 255 return dr->domain;
164 return rv;
165}
166EXPORT_SYMBOL_GPL(devm_irq_sim_init);
167
168/**
169 * irq_sim_fire - Enqueue an interrupt.
170 *
171 * @sim: The interrupt simulator object.
172 * @offset: Offset of the simulated interrupt which should be fired.
173 */
174void irq_sim_fire(struct irq_sim *sim, unsigned int offset)
175{
176 if (sim->irqs[offset].enabled) {
177 set_bit(offset, sim->work_ctx.pending);
178 irq_work_queue(&sim->work_ctx.work);
179 }
180}
181EXPORT_SYMBOL_GPL(irq_sim_fire);
182
183/**
184 * irq_sim_irqnum - Get the allocated number of a dummy interrupt.
185 *
186 * @sim: The interrupt simulator object.
187 * @offset: Offset of the simulated interrupt for which to retrieve
188 * the number.
189 */
190int irq_sim_irqnum(struct irq_sim *sim, unsigned int offset)
191{
192 return sim->irqs[offset].irqnum;
193}256}
194EXPORT_SYMBOL_GPL(irq_sim_irqnum);257EXPORT_SYMBOL_GPL(devm_irq_domain_create_sim);

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t