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>
28 /* Fiddly bit of faking and irq without hardware */
29 #define IIO_EVENTGEN_NO 10
31 * struct iio_dummy_evgen - evgen state
32 * @chip: irq chip we are faking
33 * @base: base of irq range
34 * @enabled: mask of which irqs are enabled
35 * @inuse: mask of which irqs are connected
36 * @lock: protect the evgen state
38 struct iio_dummy_eventgen
{
41 bool enabled
[IIO_EVENTGEN_NO
];
42 bool inuse
[IIO_EVENTGEN_NO
];
46 /* We can only ever have one instance of this 'device' */
47 static struct iio_dummy_eventgen
*iio_evgen
;
48 static const char *iio_evgen_name
= "iio_dummy_evgen";
50 static void iio_dummy_event_irqmask(struct irq_data
*d
)
52 struct irq_chip
*chip
= irq_data_get_irq_chip(d
);
53 struct iio_dummy_eventgen
*evgen
=
54 container_of(chip
, struct iio_dummy_eventgen
, chip
);
56 evgen
->enabled
[d
->irq
- evgen
->base
] = false;
59 static void iio_dummy_event_irqunmask(struct irq_data
*d
)
61 struct irq_chip
*chip
= irq_data_get_irq_chip(d
);
62 struct iio_dummy_eventgen
*evgen
=
63 container_of(chip
, struct iio_dummy_eventgen
, chip
);
65 evgen
->enabled
[d
->irq
- evgen
->base
] = true;
68 static int iio_dummy_evgen_create(void)
72 iio_evgen
= kzalloc(sizeof(*iio_evgen
), GFP_KERNEL
);
73 if (iio_evgen
== NULL
)
76 iio_evgen
->base
= irq_alloc_descs(-1, 0, IIO_EVENTGEN_NO
, 0);
77 if (iio_evgen
->base
< 0) {
78 ret
= iio_evgen
->base
;
82 iio_evgen
->chip
.name
= iio_evgen_name
;
83 iio_evgen
->chip
.irq_mask
= &iio_dummy_event_irqmask
;
84 iio_evgen
->chip
.irq_unmask
= &iio_dummy_event_irqunmask
;
85 for (i
= 0; i
< IIO_EVENTGEN_NO
; i
++) {
86 irq_set_chip(iio_evgen
->base
+ i
, &iio_evgen
->chip
);
87 irq_set_handler(iio_evgen
->base
+ i
, &handle_simple_irq
);
88 irq_modify_status(iio_evgen
->base
+ i
,
89 IRQ_NOREQUEST
| IRQ_NOAUTOEN
,
92 mutex_init(&iio_evgen
->lock
);
97 * iio_dummy_evgen_get_irq() - get an evgen provided irq for a device
99 * This function will give a free allocated irq to a client device.
100 * That irq can then be caused to 'fire' by using the associated sysfs file.
102 int iio_dummy_evgen_get_irq(void)
106 if (iio_evgen
== NULL
)
109 mutex_lock(&iio_evgen
->lock
);
110 for (i
= 0; i
< IIO_EVENTGEN_NO
; i
++)
111 if (!iio_evgen
->inuse
[i
]) {
112 ret
= iio_evgen
->base
+ i
;
113 iio_evgen
->inuse
[i
] = true;
116 mutex_unlock(&iio_evgen
->lock
);
117 if (i
== IIO_EVENTGEN_NO
)
121 EXPORT_SYMBOL_GPL(iio_dummy_evgen_get_irq
);
124 * iio_dummy_evgen_release_irq() - give the irq back.
125 * @irq: irq being returned to the pool
127 * Used by client driver instances to give the irqs back when they disconnect
129 int iio_dummy_evgen_release_irq(int irq
)
131 mutex_lock(&iio_evgen
->lock
);
132 iio_evgen
->inuse
[irq
- iio_evgen
->base
] = false;
133 mutex_unlock(&iio_evgen
->lock
);
137 EXPORT_SYMBOL_GPL(iio_dummy_evgen_release_irq
);
139 static void iio_dummy_evgen_free(void)
141 irq_free_descs(iio_evgen
->base
, IIO_EVENTGEN_NO
);
145 static void iio_evgen_release(struct device
*dev
)
147 iio_dummy_evgen_free();
150 static ssize_t
iio_evgen_poke(struct device
*dev
,
151 struct device_attribute
*attr
,
155 struct iio_dev_attr
*this_attr
= to_iio_dev_attr(attr
);
157 if (iio_evgen
->enabled
[this_attr
->address
])
158 handle_nested_irq(iio_evgen
->base
+ this_attr
->address
);
163 static IIO_DEVICE_ATTR(poke_ev0
, S_IWUSR
, NULL
, &iio_evgen_poke
, 0);
164 static IIO_DEVICE_ATTR(poke_ev1
, S_IWUSR
, NULL
, &iio_evgen_poke
, 1);
165 static IIO_DEVICE_ATTR(poke_ev2
, S_IWUSR
, NULL
, &iio_evgen_poke
, 2);
166 static IIO_DEVICE_ATTR(poke_ev3
, S_IWUSR
, NULL
, &iio_evgen_poke
, 3);
167 static IIO_DEVICE_ATTR(poke_ev4
, S_IWUSR
, NULL
, &iio_evgen_poke
, 4);
168 static IIO_DEVICE_ATTR(poke_ev5
, S_IWUSR
, NULL
, &iio_evgen_poke
, 5);
169 static IIO_DEVICE_ATTR(poke_ev6
, S_IWUSR
, NULL
, &iio_evgen_poke
, 6);
170 static IIO_DEVICE_ATTR(poke_ev7
, S_IWUSR
, NULL
, &iio_evgen_poke
, 7);
171 static IIO_DEVICE_ATTR(poke_ev8
, S_IWUSR
, NULL
, &iio_evgen_poke
, 8);
172 static IIO_DEVICE_ATTR(poke_ev9
, S_IWUSR
, NULL
, &iio_evgen_poke
, 9);
174 static struct attribute
*iio_evgen_attrs
[] = {
175 &iio_dev_attr_poke_ev0
.dev_attr
.attr
,
176 &iio_dev_attr_poke_ev1
.dev_attr
.attr
,
177 &iio_dev_attr_poke_ev2
.dev_attr
.attr
,
178 &iio_dev_attr_poke_ev3
.dev_attr
.attr
,
179 &iio_dev_attr_poke_ev4
.dev_attr
.attr
,
180 &iio_dev_attr_poke_ev5
.dev_attr
.attr
,
181 &iio_dev_attr_poke_ev6
.dev_attr
.attr
,
182 &iio_dev_attr_poke_ev7
.dev_attr
.attr
,
183 &iio_dev_attr_poke_ev8
.dev_attr
.attr
,
184 &iio_dev_attr_poke_ev9
.dev_attr
.attr
,
188 static const struct attribute_group iio_evgen_group
= {
189 .attrs
= iio_evgen_attrs
,
192 static const struct attribute_group
*iio_evgen_groups
[] = {
197 static struct device iio_evgen_dev
= {
198 .bus
= &iio_bus_type
,
199 .groups
= iio_evgen_groups
,
200 .release
= &iio_evgen_release
,
202 static __init
int iio_dummy_evgen_init(void)
204 int ret
= iio_dummy_evgen_create();
207 device_initialize(&iio_evgen_dev
);
208 dev_set_name(&iio_evgen_dev
, "iio_evgen");
209 return device_add(&iio_evgen_dev
);
211 module_init(iio_dummy_evgen_init
);
213 static __exit
void iio_dummy_evgen_exit(void)
215 device_unregister(&iio_evgen_dev
);
217 module_exit(iio_dummy_evgen_exit
);
219 MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>");
220 MODULE_DESCRIPTION("IIO dummy driver");
221 MODULE_LICENSE("GPL v2");