2 * Copyright (C) 2017 Bartosz Golaszewski <brgl@bgdev.pl>
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version.
10 #include <linux/irq_sim.h>
11 #include <linux/irq.h>
13 struct irq_sim_devres
{
17 static void irq_sim_irqmask(struct irq_data
*data
)
19 struct irq_sim_irq_ctx
*irq_ctx
= irq_data_get_irq_chip_data(data
);
21 irq_ctx
->enabled
= false;
24 static void irq_sim_irqunmask(struct irq_data
*data
)
26 struct irq_sim_irq_ctx
*irq_ctx
= irq_data_get_irq_chip_data(data
);
28 irq_ctx
->enabled
= true;
31 static struct irq_chip irq_sim_irqchip
= {
33 .irq_mask
= irq_sim_irqmask
,
34 .irq_unmask
= irq_sim_irqunmask
,
37 static void irq_sim_handle_irq(struct irq_work
*work
)
39 struct irq_sim_work_ctx
*work_ctx
;
41 work_ctx
= container_of(work
, struct irq_sim_work_ctx
, work
);
42 handle_simple_irq(irq_to_desc(work_ctx
->irq
));
46 * irq_sim_init - Initialize the interrupt simulator: allocate a range of
49 * @sim: The interrupt simulator object to initialize.
50 * @num_irqs: Number of interrupts to allocate
52 * Returns 0 on success and a negative error number on failure.
54 int irq_sim_init(struct irq_sim
*sim
, unsigned int num_irqs
)
58 sim
->irqs
= kmalloc_array(num_irqs
, sizeof(*sim
->irqs
), GFP_KERNEL
);
62 sim
->irq_base
= irq_alloc_descs(-1, 0, num_irqs
, 0);
63 if (sim
->irq_base
< 0) {
68 for (i
= 0; i
< num_irqs
; i
++) {
69 sim
->irqs
[i
].irqnum
= sim
->irq_base
+ i
;
70 sim
->irqs
[i
].enabled
= false;
71 irq_set_chip(sim
->irq_base
+ i
, &irq_sim_irqchip
);
72 irq_set_chip_data(sim
->irq_base
+ i
, &sim
->irqs
[i
]);
73 irq_set_handler(sim
->irq_base
+ i
, &handle_simple_irq
);
74 irq_modify_status(sim
->irq_base
+ i
,
75 IRQ_NOREQUEST
| IRQ_NOAUTOEN
, IRQ_NOPROBE
);
78 init_irq_work(&sim
->work_ctx
.work
, irq_sim_handle_irq
);
79 sim
->irq_count
= num_irqs
;
83 EXPORT_SYMBOL_GPL(irq_sim_init
);
86 * irq_sim_fini - Deinitialize the interrupt simulator: free the interrupt
87 * descriptors and allocated memory.
89 * @sim: The interrupt simulator to tear down.
91 void irq_sim_fini(struct irq_sim
*sim
)
93 irq_work_sync(&sim
->work_ctx
.work
);
94 irq_free_descs(sim
->irq_base
, sim
->irq_count
);
97 EXPORT_SYMBOL_GPL(irq_sim_fini
);
99 static void devm_irq_sim_release(struct device
*dev
, void *res
)
101 struct irq_sim_devres
*this = res
;
103 irq_sim_fini(this->sim
);
107 * irq_sim_init - Initialize the interrupt simulator for a managed device.
109 * @dev: Device to initialize the simulator object for.
110 * @sim: The interrupt simulator object to initialize.
111 * @num_irqs: Number of interrupts to allocate
113 * Returns 0 on success and a negative error number on failure.
115 int devm_irq_sim_init(struct device
*dev
, struct irq_sim
*sim
,
116 unsigned int num_irqs
)
118 struct irq_sim_devres
*dr
;
121 dr
= devres_alloc(devm_irq_sim_release
, sizeof(*dr
), GFP_KERNEL
);
125 rv
= irq_sim_init(sim
, num_irqs
);
136 EXPORT_SYMBOL_GPL(devm_irq_sim_init
);
139 * irq_sim_fire - Enqueue an interrupt.
141 * @sim: The interrupt simulator object.
142 * @offset: Offset of the simulated interrupt which should be fired.
144 void irq_sim_fire(struct irq_sim
*sim
, unsigned int offset
)
146 if (sim
->irqs
[offset
].enabled
) {
147 sim
->work_ctx
.irq
= irq_sim_irqnum(sim
, offset
);
148 irq_work_queue(&sim
->work_ctx
.work
);
151 EXPORT_SYMBOL_GPL(irq_sim_fire
);
154 * irq_sim_irqnum - Get the allocated number of a dummy interrupt.
156 * @sim: The interrupt simulator object.
157 * @offset: Offset of the simulated interrupt for which to retrieve
160 int irq_sim_irqnum(struct irq_sim
*sim
, unsigned int offset
)
162 return sim
->irqs
[offset
].irqnum
;
164 EXPORT_SYMBOL_GPL(irq_sim_irqnum
);