2 * Copyright (c) 2011 Jonathan Cameron
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 2 as published by
6 * the Free Software Foundation.
8 * Companion module to the iio simple dummy example driver.
9 * The purpose of this is to generate 'fake' event interrupts thus
10 * allowing that driver's code to be as close as possible to that of
11 * a normal driver talking to hardware. The approach used here
12 * is not intended to be general and just happens to work for this
13 * particular use case.
16 #include <linux/kernel.h>
17 #include <linux/slab.h>
18 #include <linux/interrupt.h>
19 #include <linux/irq.h>
20 #include <linux/mutex.h>
21 #include <linux/module.h>
22 #include <linux/sysfs.h>
24 #include "iio_dummy_evgen.h"
25 #include <linux/iio/iio.h>
26 #include <linux/iio/sysfs.h>
27 #include <linux/irq_work.h>
29 /* Fiddly bit of faking and irq without hardware */
30 #define IIO_EVENTGEN_NO 10
33 * struct iio_dummy_handle_irq - helper struct to simulate interrupt generation
34 * @work: irq_work used to run handlers from hardirq context
35 * @irq: fake irq line number to trigger an interrupt
37 struct iio_dummy_handle_irq
{
43 * struct iio_dummy_evgen - evgen state
44 * @chip: irq chip we are faking
45 * @base: base of irq range
46 * @enabled: mask of which irqs are enabled
47 * @inuse: mask of which irqs are connected
48 * @regs: irq regs we are faking
49 * @lock: protect the evgen state
50 * @handler: helper for a 'hardware-like' interrupt simulation
52 struct iio_dummy_eventgen
{
55 bool enabled
[IIO_EVENTGEN_NO
];
56 bool inuse
[IIO_EVENTGEN_NO
];
57 struct iio_dummy_regs regs
[IIO_EVENTGEN_NO
];
59 struct iio_dummy_handle_irq handler
;
62 /* We can only ever have one instance of this 'device' */
63 static struct iio_dummy_eventgen
*iio_evgen
;
64 static const char *iio_evgen_name
= "iio_dummy_evgen";
66 static void iio_dummy_event_irqmask(struct irq_data
*d
)
68 struct irq_chip
*chip
= irq_data_get_irq_chip(d
);
69 struct iio_dummy_eventgen
*evgen
=
70 container_of(chip
, struct iio_dummy_eventgen
, chip
);
72 evgen
->enabled
[d
->irq
- evgen
->base
] = false;
75 static void iio_dummy_event_irqunmask(struct irq_data
*d
)
77 struct irq_chip
*chip
= irq_data_get_irq_chip(d
);
78 struct iio_dummy_eventgen
*evgen
=
79 container_of(chip
, struct iio_dummy_eventgen
, chip
);
81 evgen
->enabled
[d
->irq
- evgen
->base
] = true;
84 static void iio_dummy_work_handler(struct irq_work
*work
)
86 struct iio_dummy_handle_irq
*irq_handler
;
88 irq_handler
= container_of(work
, struct iio_dummy_handle_irq
, work
);
89 handle_simple_irq(irq_to_desc(irq_handler
->irq
));
92 static int iio_dummy_evgen_create(void)
96 iio_evgen
= kzalloc(sizeof(*iio_evgen
), GFP_KERNEL
);
100 iio_evgen
->base
= irq_alloc_descs(-1, 0, IIO_EVENTGEN_NO
, 0);
101 if (iio_evgen
->base
< 0) {
102 ret
= iio_evgen
->base
;
106 iio_evgen
->chip
.name
= iio_evgen_name
;
107 iio_evgen
->chip
.irq_mask
= &iio_dummy_event_irqmask
;
108 iio_evgen
->chip
.irq_unmask
= &iio_dummy_event_irqunmask
;
109 for (i
= 0; i
< IIO_EVENTGEN_NO
; i
++) {
110 irq_set_chip(iio_evgen
->base
+ i
, &iio_evgen
->chip
);
111 irq_set_handler(iio_evgen
->base
+ i
, &handle_simple_irq
);
112 irq_modify_status(iio_evgen
->base
+ i
,
113 IRQ_NOREQUEST
| IRQ_NOAUTOEN
,
116 init_irq_work(&iio_evgen
->handler
.work
, iio_dummy_work_handler
);
117 mutex_init(&iio_evgen
->lock
);
122 * iio_dummy_evgen_get_irq() - get an evgen provided irq for a device
124 * This function will give a free allocated irq to a client device.
125 * That irq can then be caused to 'fire' by using the associated sysfs file.
127 int iio_dummy_evgen_get_irq(void)
134 mutex_lock(&iio_evgen
->lock
);
135 for (i
= 0; i
< IIO_EVENTGEN_NO
; i
++)
136 if (!iio_evgen
->inuse
[i
]) {
137 ret
= iio_evgen
->base
+ i
;
138 iio_evgen
->inuse
[i
] = true;
141 mutex_unlock(&iio_evgen
->lock
);
142 if (i
== IIO_EVENTGEN_NO
)
146 EXPORT_SYMBOL_GPL(iio_dummy_evgen_get_irq
);
149 * iio_dummy_evgen_release_irq() - give the irq back.
150 * @irq: irq being returned to the pool
152 * Used by client driver instances to give the irqs back when they disconnect
154 void iio_dummy_evgen_release_irq(int irq
)
156 mutex_lock(&iio_evgen
->lock
);
157 iio_evgen
->inuse
[irq
- iio_evgen
->base
] = false;
158 mutex_unlock(&iio_evgen
->lock
);
160 EXPORT_SYMBOL_GPL(iio_dummy_evgen_release_irq
);
162 struct iio_dummy_regs
*iio_dummy_evgen_get_regs(int irq
)
164 return &iio_evgen
->regs
[irq
- iio_evgen
->base
];
166 EXPORT_SYMBOL_GPL(iio_dummy_evgen_get_regs
);
168 static void iio_dummy_evgen_free(void)
170 irq_free_descs(iio_evgen
->base
, IIO_EVENTGEN_NO
);
174 static void iio_evgen_release(struct device
*dev
)
176 iio_dummy_evgen_free();
179 static ssize_t
iio_evgen_poke(struct device
*dev
,
180 struct device_attribute
*attr
,
184 struct iio_dev_attr
*this_attr
= to_iio_dev_attr(attr
);
188 ret
= kstrtoul(buf
, 10, &event
);
192 iio_evgen
->regs
[this_attr
->address
].reg_id
= this_attr
->address
;
193 iio_evgen
->regs
[this_attr
->address
].reg_data
= event
;
195 iio_evgen
->handler
.irq
= iio_evgen
->base
+ this_attr
->address
;
196 if (iio_evgen
->enabled
[this_attr
->address
])
197 irq_work_queue(&iio_evgen
->handler
.work
);
202 static IIO_DEVICE_ATTR(poke_ev0
, S_IWUSR
, NULL
, &iio_evgen_poke
, 0);
203 static IIO_DEVICE_ATTR(poke_ev1
, S_IWUSR
, NULL
, &iio_evgen_poke
, 1);
204 static IIO_DEVICE_ATTR(poke_ev2
, S_IWUSR
, NULL
, &iio_evgen_poke
, 2);
205 static IIO_DEVICE_ATTR(poke_ev3
, S_IWUSR
, NULL
, &iio_evgen_poke
, 3);
206 static IIO_DEVICE_ATTR(poke_ev4
, S_IWUSR
, NULL
, &iio_evgen_poke
, 4);
207 static IIO_DEVICE_ATTR(poke_ev5
, S_IWUSR
, NULL
, &iio_evgen_poke
, 5);
208 static IIO_DEVICE_ATTR(poke_ev6
, S_IWUSR
, NULL
, &iio_evgen_poke
, 6);
209 static IIO_DEVICE_ATTR(poke_ev7
, S_IWUSR
, NULL
, &iio_evgen_poke
, 7);
210 static IIO_DEVICE_ATTR(poke_ev8
, S_IWUSR
, NULL
, &iio_evgen_poke
, 8);
211 static IIO_DEVICE_ATTR(poke_ev9
, S_IWUSR
, NULL
, &iio_evgen_poke
, 9);
213 static struct attribute
*iio_evgen_attrs
[] = {
214 &iio_dev_attr_poke_ev0
.dev_attr
.attr
,
215 &iio_dev_attr_poke_ev1
.dev_attr
.attr
,
216 &iio_dev_attr_poke_ev2
.dev_attr
.attr
,
217 &iio_dev_attr_poke_ev3
.dev_attr
.attr
,
218 &iio_dev_attr_poke_ev4
.dev_attr
.attr
,
219 &iio_dev_attr_poke_ev5
.dev_attr
.attr
,
220 &iio_dev_attr_poke_ev6
.dev_attr
.attr
,
221 &iio_dev_attr_poke_ev7
.dev_attr
.attr
,
222 &iio_dev_attr_poke_ev8
.dev_attr
.attr
,
223 &iio_dev_attr_poke_ev9
.dev_attr
.attr
,
227 static const struct attribute_group iio_evgen_group
= {
228 .attrs
= iio_evgen_attrs
,
231 static const struct attribute_group
*iio_evgen_groups
[] = {
236 static struct device iio_evgen_dev
= {
237 .bus
= &iio_bus_type
,
238 .groups
= iio_evgen_groups
,
239 .release
= &iio_evgen_release
,
242 static __init
int iio_dummy_evgen_init(void)
244 int ret
= iio_dummy_evgen_create();
248 device_initialize(&iio_evgen_dev
);
249 dev_set_name(&iio_evgen_dev
, "iio_evgen");
250 return device_add(&iio_evgen_dev
);
252 module_init(iio_dummy_evgen_init
);
254 static __exit
void iio_dummy_evgen_exit(void)
256 device_unregister(&iio_evgen_dev
);
258 module_exit(iio_dummy_evgen_exit
);
260 MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>");
261 MODULE_DESCRIPTION("IIO dummy driver");
262 MODULE_LICENSE("GPL v2");