1 // SPDX-License-Identifier: GPL-2.0+
3 * Copyright (C) 2017 Bartosz Golaszewski <brgl@bgdev.pl>
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version.
11 #include <linux/slab.h>
12 #include <linux/irq_sim.h>
13 #include <linux/irq.h>
15 struct irq_sim_devres
{
19 static void irq_sim_irqmask(struct irq_data
*data
)
21 struct irq_sim_irq_ctx
*irq_ctx
= irq_data_get_irq_chip_data(data
);
23 irq_ctx
->enabled
= false;
26 static void irq_sim_irqunmask(struct irq_data
*data
)
28 struct irq_sim_irq_ctx
*irq_ctx
= irq_data_get_irq_chip_data(data
);
30 irq_ctx
->enabled
= true;
33 static struct irq_chip irq_sim_irqchip
= {
35 .irq_mask
= irq_sim_irqmask
,
36 .irq_unmask
= irq_sim_irqunmask
,
39 static void irq_sim_handle_irq(struct irq_work
*work
)
41 struct irq_sim_work_ctx
*work_ctx
;
43 work_ctx
= container_of(work
, struct irq_sim_work_ctx
, work
);
44 handle_simple_irq(irq_to_desc(work_ctx
->irq
));
48 * irq_sim_init - Initialize the interrupt simulator: allocate a range of
51 * @sim: The interrupt simulator object to initialize.
52 * @num_irqs: Number of interrupts to allocate
54 * On success: return the base of the allocated interrupt range.
55 * On failure: a negative errno.
57 int irq_sim_init(struct irq_sim
*sim
, unsigned int num_irqs
)
61 sim
->irqs
= kmalloc_array(num_irqs
, sizeof(*sim
->irqs
), GFP_KERNEL
);
65 sim
->irq_base
= irq_alloc_descs(-1, 0, num_irqs
, 0);
66 if (sim
->irq_base
< 0) {
71 for (i
= 0; i
< num_irqs
; i
++) {
72 sim
->irqs
[i
].irqnum
= sim
->irq_base
+ i
;
73 sim
->irqs
[i
].enabled
= false;
74 irq_set_chip(sim
->irq_base
+ i
, &irq_sim_irqchip
);
75 irq_set_chip_data(sim
->irq_base
+ i
, &sim
->irqs
[i
]);
76 irq_set_handler(sim
->irq_base
+ i
, &handle_simple_irq
);
77 irq_modify_status(sim
->irq_base
+ i
,
78 IRQ_NOREQUEST
| IRQ_NOAUTOEN
, IRQ_NOPROBE
);
81 init_irq_work(&sim
->work_ctx
.work
, irq_sim_handle_irq
);
82 sim
->irq_count
= num_irqs
;
86 EXPORT_SYMBOL_GPL(irq_sim_init
);
89 * irq_sim_fini - Deinitialize the interrupt simulator: free the interrupt
90 * descriptors and allocated memory.
92 * @sim: The interrupt simulator to tear down.
94 void irq_sim_fini(struct irq_sim
*sim
)
96 irq_work_sync(&sim
->work_ctx
.work
);
97 irq_free_descs(sim
->irq_base
, sim
->irq_count
);
100 EXPORT_SYMBOL_GPL(irq_sim_fini
);
102 static void devm_irq_sim_release(struct device
*dev
, void *res
)
104 struct irq_sim_devres
*this = res
;
106 irq_sim_fini(this->sim
);
110 * irq_sim_init - Initialize the interrupt simulator for a managed device.
112 * @dev: Device to initialize the simulator object for.
113 * @sim: The interrupt simulator object to initialize.
114 * @num_irqs: Number of interrupts to allocate
116 * On success: return the base of the allocated interrupt range.
117 * On failure: a negative errno.
119 int devm_irq_sim_init(struct device
*dev
, struct irq_sim
*sim
,
120 unsigned int num_irqs
)
122 struct irq_sim_devres
*dr
;
125 dr
= devres_alloc(devm_irq_sim_release
, sizeof(*dr
), GFP_KERNEL
);
129 rv
= irq_sim_init(sim
, num_irqs
);
140 EXPORT_SYMBOL_GPL(devm_irq_sim_init
);
143 * irq_sim_fire - Enqueue an interrupt.
145 * @sim: The interrupt simulator object.
146 * @offset: Offset of the simulated interrupt which should be fired.
148 void irq_sim_fire(struct irq_sim
*sim
, unsigned int offset
)
150 if (sim
->irqs
[offset
].enabled
) {
151 sim
->work_ctx
.irq
= irq_sim_irqnum(sim
, offset
);
152 irq_work_queue(&sim
->work_ctx
.work
);
155 EXPORT_SYMBOL_GPL(irq_sim_fire
);
158 * irq_sim_irqnum - Get the allocated number of a dummy interrupt.
160 * @sim: The interrupt simulator object.
161 * @offset: Offset of the simulated interrupt for which to retrieve
164 int irq_sim_irqnum(struct irq_sim
*sim
, unsigned int offset
)
166 return sim
->irqs
[offset
].irqnum
;
168 EXPORT_SYMBOL_GPL(irq_sim_irqnum
);