]>
Commit | Line | Data |
---|---|---|
e6477000 JC |
1 | /** |
2 | * Copyright (c) 2011 Jonathan Cameron | |
3 | * | |
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. | |
7 | * | |
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. | |
14 | */ | |
15 | ||
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> | |
23 | ||
24 | #include "iio_dummy_evgen.h" | |
06458e27 JC |
25 | #include <linux/iio/iio.h> |
26 | #include <linux/iio/sysfs.h> | |
e6477000 JC |
27 | |
28 | /* Fiddly bit of faking and irq without hardware */ | |
29 | #define IIO_EVENTGEN_NO 10 | |
30 | /** | |
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 | |
4abf6f8b | 35 | * @inuse: mask of which irqs are connected |
356ae946 | 36 | * @regs: irq regs we are faking |
e6477000 JC |
37 | * @lock: protect the evgen state |
38 | */ | |
39 | struct iio_dummy_eventgen { | |
40 | struct irq_chip chip; | |
41 | int base; | |
42 | bool enabled[IIO_EVENTGEN_NO]; | |
43 | bool inuse[IIO_EVENTGEN_NO]; | |
356ae946 | 44 | struct iio_dummy_regs regs[IIO_EVENTGEN_NO]; |
e6477000 JC |
45 | struct mutex lock; |
46 | }; | |
47 | ||
48 | /* We can only ever have one instance of this 'device' */ | |
49 | static struct iio_dummy_eventgen *iio_evgen; | |
50 | static const char *iio_evgen_name = "iio_dummy_evgen"; | |
51 | ||
52 | static void iio_dummy_event_irqmask(struct irq_data *d) | |
53 | { | |
54 | struct irq_chip *chip = irq_data_get_irq_chip(d); | |
55 | struct iio_dummy_eventgen *evgen = | |
56 | container_of(chip, struct iio_dummy_eventgen, chip); | |
57 | ||
58 | evgen->enabled[d->irq - evgen->base] = false; | |
59 | } | |
60 | ||
61 | static void iio_dummy_event_irqunmask(struct irq_data *d) | |
62 | { | |
63 | struct irq_chip *chip = irq_data_get_irq_chip(d); | |
64 | struct iio_dummy_eventgen *evgen = | |
65 | container_of(chip, struct iio_dummy_eventgen, chip); | |
66 | ||
67 | evgen->enabled[d->irq - evgen->base] = true; | |
68 | } | |
69 | ||
70 | static int iio_dummy_evgen_create(void) | |
71 | { | |
72 | int ret, i; | |
73 | ||
74 | iio_evgen = kzalloc(sizeof(*iio_evgen), GFP_KERNEL); | |
025c7da9 | 75 | if (!iio_evgen) |
e6477000 JC |
76 | return -ENOMEM; |
77 | ||
78 | iio_evgen->base = irq_alloc_descs(-1, 0, IIO_EVENTGEN_NO, 0); | |
79 | if (iio_evgen->base < 0) { | |
80 | ret = iio_evgen->base; | |
81 | kfree(iio_evgen); | |
82 | return ret; | |
83 | } | |
84 | iio_evgen->chip.name = iio_evgen_name; | |
85 | iio_evgen->chip.irq_mask = &iio_dummy_event_irqmask; | |
86 | iio_evgen->chip.irq_unmask = &iio_dummy_event_irqunmask; | |
87 | for (i = 0; i < IIO_EVENTGEN_NO; i++) { | |
88 | irq_set_chip(iio_evgen->base + i, &iio_evgen->chip); | |
89 | irq_set_handler(iio_evgen->base + i, &handle_simple_irq); | |
90 | irq_modify_status(iio_evgen->base + i, | |
91 | IRQ_NOREQUEST | IRQ_NOAUTOEN, | |
92 | IRQ_NOPROBE); | |
93 | } | |
94 | mutex_init(&iio_evgen->lock); | |
95 | return 0; | |
96 | } | |
97 | ||
98 | /** | |
99 | * iio_dummy_evgen_get_irq() - get an evgen provided irq for a device | |
100 | * | |
101 | * This function will give a free allocated irq to a client device. | |
102 | * That irq can then be caused to 'fire' by using the associated sysfs file. | |
103 | */ | |
104 | int iio_dummy_evgen_get_irq(void) | |
105 | { | |
106 | int i, ret = 0; | |
5ae8f440 | 107 | |
025c7da9 | 108 | if (!iio_evgen) |
5ae8f440 SL |
109 | return -ENODEV; |
110 | ||
e6477000 JC |
111 | mutex_lock(&iio_evgen->lock); |
112 | for (i = 0; i < IIO_EVENTGEN_NO; i++) | |
6fae58f3 | 113 | if (!iio_evgen->inuse[i]) { |
e6477000 JC |
114 | ret = iio_evgen->base + i; |
115 | iio_evgen->inuse[i] = true; | |
116 | break; | |
117 | } | |
118 | mutex_unlock(&iio_evgen->lock); | |
119 | if (i == IIO_EVENTGEN_NO) | |
120 | return -ENOMEM; | |
121 | return ret; | |
122 | } | |
123 | EXPORT_SYMBOL_GPL(iio_dummy_evgen_get_irq); | |
124 | ||
125 | /** | |
126 | * iio_dummy_evgen_release_irq() - give the irq back. | |
127 | * @irq: irq being returned to the pool | |
128 | * | |
129 | * Used by client driver instances to give the irqs back when they disconnect | |
130 | */ | |
62a90da6 | 131 | void iio_dummy_evgen_release_irq(int irq) |
e6477000 JC |
132 | { |
133 | mutex_lock(&iio_evgen->lock); | |
134 | iio_evgen->inuse[irq - iio_evgen->base] = false; | |
135 | mutex_unlock(&iio_evgen->lock); | |
e6477000 JC |
136 | } |
137 | EXPORT_SYMBOL_GPL(iio_dummy_evgen_release_irq); | |
138 | ||
356ae946 DB |
139 | struct iio_dummy_regs *iio_dummy_evgen_get_regs(int irq) |
140 | { | |
141 | return &iio_evgen->regs[irq - iio_evgen->base]; | |
142 | } | |
143 | EXPORT_SYMBOL_GPL(iio_dummy_evgen_get_regs); | |
144 | ||
e6477000 JC |
145 | static void iio_dummy_evgen_free(void) |
146 | { | |
147 | irq_free_descs(iio_evgen->base, IIO_EVENTGEN_NO); | |
148 | kfree(iio_evgen); | |
149 | } | |
150 | ||
151 | static void iio_evgen_release(struct device *dev) | |
152 | { | |
153 | iio_dummy_evgen_free(); | |
154 | } | |
155 | ||
156 | static ssize_t iio_evgen_poke(struct device *dev, | |
157 | struct device_attribute *attr, | |
158 | const char *buf, | |
159 | size_t len) | |
160 | { | |
161 | struct iio_dev_attr *this_attr = to_iio_dev_attr(attr); | |
356ae946 DB |
162 | unsigned long event; |
163 | int ret; | |
164 | ||
165 | ret = kstrtoul(buf, 10, &event); | |
166 | if (ret) | |
167 | return ret; | |
168 | ||
169 | iio_evgen->regs[this_attr->address].reg_id = this_attr->address; | |
170 | iio_evgen->regs[this_attr->address].reg_data = event; | |
e6477000 JC |
171 | |
172 | if (iio_evgen->enabled[this_attr->address]) | |
173 | handle_nested_irq(iio_evgen->base + this_attr->address); | |
174 | ||
175 | return len; | |
176 | } | |
177 | ||
178 | static IIO_DEVICE_ATTR(poke_ev0, S_IWUSR, NULL, &iio_evgen_poke, 0); | |
179 | static IIO_DEVICE_ATTR(poke_ev1, S_IWUSR, NULL, &iio_evgen_poke, 1); | |
180 | static IIO_DEVICE_ATTR(poke_ev2, S_IWUSR, NULL, &iio_evgen_poke, 2); | |
181 | static IIO_DEVICE_ATTR(poke_ev3, S_IWUSR, NULL, &iio_evgen_poke, 3); | |
182 | static IIO_DEVICE_ATTR(poke_ev4, S_IWUSR, NULL, &iio_evgen_poke, 4); | |
183 | static IIO_DEVICE_ATTR(poke_ev5, S_IWUSR, NULL, &iio_evgen_poke, 5); | |
184 | static IIO_DEVICE_ATTR(poke_ev6, S_IWUSR, NULL, &iio_evgen_poke, 6); | |
185 | static IIO_DEVICE_ATTR(poke_ev7, S_IWUSR, NULL, &iio_evgen_poke, 7); | |
186 | static IIO_DEVICE_ATTR(poke_ev8, S_IWUSR, NULL, &iio_evgen_poke, 8); | |
187 | static IIO_DEVICE_ATTR(poke_ev9, S_IWUSR, NULL, &iio_evgen_poke, 9); | |
188 | ||
189 | static struct attribute *iio_evgen_attrs[] = { | |
190 | &iio_dev_attr_poke_ev0.dev_attr.attr, | |
191 | &iio_dev_attr_poke_ev1.dev_attr.attr, | |
192 | &iio_dev_attr_poke_ev2.dev_attr.attr, | |
193 | &iio_dev_attr_poke_ev3.dev_attr.attr, | |
194 | &iio_dev_attr_poke_ev4.dev_attr.attr, | |
195 | &iio_dev_attr_poke_ev5.dev_attr.attr, | |
196 | &iio_dev_attr_poke_ev6.dev_attr.attr, | |
197 | &iio_dev_attr_poke_ev7.dev_attr.attr, | |
198 | &iio_dev_attr_poke_ev8.dev_attr.attr, | |
199 | &iio_dev_attr_poke_ev9.dev_attr.attr, | |
200 | NULL, | |
201 | }; | |
202 | ||
203 | static const struct attribute_group iio_evgen_group = { | |
204 | .attrs = iio_evgen_attrs, | |
205 | }; | |
206 | ||
207 | static const struct attribute_group *iio_evgen_groups[] = { | |
208 | &iio_evgen_group, | |
209 | NULL | |
210 | }; | |
211 | ||
212 | static struct device iio_evgen_dev = { | |
213 | .bus = &iio_bus_type, | |
214 | .groups = iio_evgen_groups, | |
215 | .release = &iio_evgen_release, | |
216 | }; | |
862cb6ce | 217 | |
e6477000 JC |
218 | static __init int iio_dummy_evgen_init(void) |
219 | { | |
220 | int ret = iio_dummy_evgen_create(); | |
4b4c7275 | 221 | |
e6477000 JC |
222 | if (ret < 0) |
223 | return ret; | |
224 | device_initialize(&iio_evgen_dev); | |
225 | dev_set_name(&iio_evgen_dev, "iio_evgen"); | |
226 | return device_add(&iio_evgen_dev); | |
227 | } | |
228 | module_init(iio_dummy_evgen_init); | |
229 | ||
230 | static __exit void iio_dummy_evgen_exit(void) | |
231 | { | |
232 | device_unregister(&iio_evgen_dev); | |
233 | } | |
234 | module_exit(iio_dummy_evgen_exit); | |
235 | ||
0f8c9620 | 236 | MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>"); |
e6477000 JC |
237 | MODULE_DESCRIPTION("IIO dummy driver"); |
238 | MODULE_LICENSE("GPL v2"); |