1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0-only
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Copyright (c) 2011 Jonathan Cameron
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * Companion module to the iio simple dummy example driver.
6*4882a593Smuzhiyun * The purpose of this is to generate 'fake' event interrupts thus
7*4882a593Smuzhiyun * allowing that driver's code to be as close as possible to that of
8*4882a593Smuzhiyun * a normal driver talking to hardware. The approach used here
9*4882a593Smuzhiyun * is not intended to be general and just happens to work for this
10*4882a593Smuzhiyun * particular use case.
11*4882a593Smuzhiyun */
12*4882a593Smuzhiyun
13*4882a593Smuzhiyun #include <linux/kernel.h>
14*4882a593Smuzhiyun #include <linux/slab.h>
15*4882a593Smuzhiyun #include <linux/interrupt.h>
16*4882a593Smuzhiyun #include <linux/irq.h>
17*4882a593Smuzhiyun #include <linux/mutex.h>
18*4882a593Smuzhiyun #include <linux/module.h>
19*4882a593Smuzhiyun #include <linux/sysfs.h>
20*4882a593Smuzhiyun
21*4882a593Smuzhiyun #include "iio_dummy_evgen.h"
22*4882a593Smuzhiyun #include <linux/iio/iio.h>
23*4882a593Smuzhiyun #include <linux/iio/sysfs.h>
24*4882a593Smuzhiyun #include <linux/irq_sim.h>
25*4882a593Smuzhiyun
26*4882a593Smuzhiyun /* Fiddly bit of faking and irq without hardware */
27*4882a593Smuzhiyun #define IIO_EVENTGEN_NO 10
28*4882a593Smuzhiyun
29*4882a593Smuzhiyun /**
30*4882a593Smuzhiyun * struct iio_dummy_eventgen - event generator specific state
31*4882a593Smuzhiyun * @regs: irq regs we are faking
32*4882a593Smuzhiyun * @lock: protect the evgen state
33*4882a593Smuzhiyun * @inuse: mask of which irqs are connected
34*4882a593Smuzhiyun * @irq_sim: interrupt simulator
35*4882a593Smuzhiyun * @base: base of irq range
36*4882a593Smuzhiyun * @irq_sim_domain: irq simulator domain
37*4882a593Smuzhiyun */
38*4882a593Smuzhiyun struct iio_dummy_eventgen {
39*4882a593Smuzhiyun struct iio_dummy_regs regs[IIO_EVENTGEN_NO];
40*4882a593Smuzhiyun struct mutex lock;
41*4882a593Smuzhiyun bool inuse[IIO_EVENTGEN_NO];
42*4882a593Smuzhiyun struct irq_domain *irq_sim_domain;
43*4882a593Smuzhiyun };
44*4882a593Smuzhiyun
45*4882a593Smuzhiyun /* We can only ever have one instance of this 'device' */
46*4882a593Smuzhiyun static struct iio_dummy_eventgen *iio_evgen;
47*4882a593Smuzhiyun
iio_dummy_evgen_create(void)48*4882a593Smuzhiyun static int iio_dummy_evgen_create(void)
49*4882a593Smuzhiyun {
50*4882a593Smuzhiyun int ret;
51*4882a593Smuzhiyun
52*4882a593Smuzhiyun iio_evgen = kzalloc(sizeof(*iio_evgen), GFP_KERNEL);
53*4882a593Smuzhiyun if (!iio_evgen)
54*4882a593Smuzhiyun return -ENOMEM;
55*4882a593Smuzhiyun
56*4882a593Smuzhiyun iio_evgen->irq_sim_domain = irq_domain_create_sim(NULL,
57*4882a593Smuzhiyun IIO_EVENTGEN_NO);
58*4882a593Smuzhiyun if (IS_ERR(iio_evgen->irq_sim_domain)) {
59*4882a593Smuzhiyun ret = PTR_ERR(iio_evgen->irq_sim_domain);
60*4882a593Smuzhiyun kfree(iio_evgen);
61*4882a593Smuzhiyun return ret;
62*4882a593Smuzhiyun }
63*4882a593Smuzhiyun
64*4882a593Smuzhiyun mutex_init(&iio_evgen->lock);
65*4882a593Smuzhiyun
66*4882a593Smuzhiyun return 0;
67*4882a593Smuzhiyun }
68*4882a593Smuzhiyun
69*4882a593Smuzhiyun /**
70*4882a593Smuzhiyun * iio_dummy_evgen_get_irq() - get an evgen provided irq for a device
71*4882a593Smuzhiyun *
72*4882a593Smuzhiyun * This function will give a free allocated irq to a client device.
73*4882a593Smuzhiyun * That irq can then be caused to 'fire' by using the associated sysfs file.
74*4882a593Smuzhiyun */
iio_dummy_evgen_get_irq(void)75*4882a593Smuzhiyun int iio_dummy_evgen_get_irq(void)
76*4882a593Smuzhiyun {
77*4882a593Smuzhiyun int i, ret = 0;
78*4882a593Smuzhiyun
79*4882a593Smuzhiyun if (!iio_evgen)
80*4882a593Smuzhiyun return -ENODEV;
81*4882a593Smuzhiyun
82*4882a593Smuzhiyun mutex_lock(&iio_evgen->lock);
83*4882a593Smuzhiyun for (i = 0; i < IIO_EVENTGEN_NO; i++) {
84*4882a593Smuzhiyun if (!iio_evgen->inuse[i]) {
85*4882a593Smuzhiyun ret = irq_create_mapping(iio_evgen->irq_sim_domain, i);
86*4882a593Smuzhiyun iio_evgen->inuse[i] = true;
87*4882a593Smuzhiyun break;
88*4882a593Smuzhiyun }
89*4882a593Smuzhiyun }
90*4882a593Smuzhiyun mutex_unlock(&iio_evgen->lock);
91*4882a593Smuzhiyun if (i == IIO_EVENTGEN_NO)
92*4882a593Smuzhiyun return -ENOMEM;
93*4882a593Smuzhiyun
94*4882a593Smuzhiyun return ret;
95*4882a593Smuzhiyun }
96*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(iio_dummy_evgen_get_irq);
97*4882a593Smuzhiyun
98*4882a593Smuzhiyun /**
99*4882a593Smuzhiyun * iio_dummy_evgen_release_irq() - give the irq back.
100*4882a593Smuzhiyun * @irq: irq being returned to the pool
101*4882a593Smuzhiyun *
102*4882a593Smuzhiyun * Used by client driver instances to give the irqs back when they disconnect
103*4882a593Smuzhiyun */
iio_dummy_evgen_release_irq(int irq)104*4882a593Smuzhiyun void iio_dummy_evgen_release_irq(int irq)
105*4882a593Smuzhiyun {
106*4882a593Smuzhiyun struct irq_data *irqd = irq_get_irq_data(irq);
107*4882a593Smuzhiyun
108*4882a593Smuzhiyun mutex_lock(&iio_evgen->lock);
109*4882a593Smuzhiyun iio_evgen->inuse[irqd_to_hwirq(irqd)] = false;
110*4882a593Smuzhiyun irq_dispose_mapping(irq);
111*4882a593Smuzhiyun mutex_unlock(&iio_evgen->lock);
112*4882a593Smuzhiyun }
113*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(iio_dummy_evgen_release_irq);
114*4882a593Smuzhiyun
iio_dummy_evgen_get_regs(int irq)115*4882a593Smuzhiyun struct iio_dummy_regs *iio_dummy_evgen_get_regs(int irq)
116*4882a593Smuzhiyun {
117*4882a593Smuzhiyun struct irq_data *irqd = irq_get_irq_data(irq);
118*4882a593Smuzhiyun
119*4882a593Smuzhiyun return &iio_evgen->regs[irqd_to_hwirq(irqd)];
120*4882a593Smuzhiyun
121*4882a593Smuzhiyun }
122*4882a593Smuzhiyun EXPORT_SYMBOL_GPL(iio_dummy_evgen_get_regs);
123*4882a593Smuzhiyun
iio_dummy_evgen_free(void)124*4882a593Smuzhiyun static void iio_dummy_evgen_free(void)
125*4882a593Smuzhiyun {
126*4882a593Smuzhiyun irq_domain_remove_sim(iio_evgen->irq_sim_domain);
127*4882a593Smuzhiyun kfree(iio_evgen);
128*4882a593Smuzhiyun }
129*4882a593Smuzhiyun
iio_evgen_release(struct device * dev)130*4882a593Smuzhiyun static void iio_evgen_release(struct device *dev)
131*4882a593Smuzhiyun {
132*4882a593Smuzhiyun iio_dummy_evgen_free();
133*4882a593Smuzhiyun }
134*4882a593Smuzhiyun
iio_evgen_poke(struct device * dev,struct device_attribute * attr,const char * buf,size_t len)135*4882a593Smuzhiyun static ssize_t iio_evgen_poke(struct device *dev,
136*4882a593Smuzhiyun struct device_attribute *attr,
137*4882a593Smuzhiyun const char *buf,
138*4882a593Smuzhiyun size_t len)
139*4882a593Smuzhiyun {
140*4882a593Smuzhiyun struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
141*4882a593Smuzhiyun unsigned long event;
142*4882a593Smuzhiyun int ret, irq;
143*4882a593Smuzhiyun
144*4882a593Smuzhiyun ret = kstrtoul(buf, 10, &event);
145*4882a593Smuzhiyun if (ret)
146*4882a593Smuzhiyun return ret;
147*4882a593Smuzhiyun
148*4882a593Smuzhiyun iio_evgen->regs[this_attr->address].reg_id = this_attr->address;
149*4882a593Smuzhiyun iio_evgen->regs[this_attr->address].reg_data = event;
150*4882a593Smuzhiyun
151*4882a593Smuzhiyun irq = irq_find_mapping(iio_evgen->irq_sim_domain, this_attr->address);
152*4882a593Smuzhiyun ret = irq_set_irqchip_state(irq, IRQCHIP_STATE_PENDING, true);
153*4882a593Smuzhiyun if (ret)
154*4882a593Smuzhiyun return ret;
155*4882a593Smuzhiyun
156*4882a593Smuzhiyun return len;
157*4882a593Smuzhiyun }
158*4882a593Smuzhiyun
159*4882a593Smuzhiyun static IIO_DEVICE_ATTR(poke_ev0, S_IWUSR, NULL, &iio_evgen_poke, 0);
160*4882a593Smuzhiyun static IIO_DEVICE_ATTR(poke_ev1, S_IWUSR, NULL, &iio_evgen_poke, 1);
161*4882a593Smuzhiyun static IIO_DEVICE_ATTR(poke_ev2, S_IWUSR, NULL, &iio_evgen_poke, 2);
162*4882a593Smuzhiyun static IIO_DEVICE_ATTR(poke_ev3, S_IWUSR, NULL, &iio_evgen_poke, 3);
163*4882a593Smuzhiyun static IIO_DEVICE_ATTR(poke_ev4, S_IWUSR, NULL, &iio_evgen_poke, 4);
164*4882a593Smuzhiyun static IIO_DEVICE_ATTR(poke_ev5, S_IWUSR, NULL, &iio_evgen_poke, 5);
165*4882a593Smuzhiyun static IIO_DEVICE_ATTR(poke_ev6, S_IWUSR, NULL, &iio_evgen_poke, 6);
166*4882a593Smuzhiyun static IIO_DEVICE_ATTR(poke_ev7, S_IWUSR, NULL, &iio_evgen_poke, 7);
167*4882a593Smuzhiyun static IIO_DEVICE_ATTR(poke_ev8, S_IWUSR, NULL, &iio_evgen_poke, 8);
168*4882a593Smuzhiyun static IIO_DEVICE_ATTR(poke_ev9, S_IWUSR, NULL, &iio_evgen_poke, 9);
169*4882a593Smuzhiyun
170*4882a593Smuzhiyun static struct attribute *iio_evgen_attrs[] = {
171*4882a593Smuzhiyun &iio_dev_attr_poke_ev0.dev_attr.attr,
172*4882a593Smuzhiyun &iio_dev_attr_poke_ev1.dev_attr.attr,
173*4882a593Smuzhiyun &iio_dev_attr_poke_ev2.dev_attr.attr,
174*4882a593Smuzhiyun &iio_dev_attr_poke_ev3.dev_attr.attr,
175*4882a593Smuzhiyun &iio_dev_attr_poke_ev4.dev_attr.attr,
176*4882a593Smuzhiyun &iio_dev_attr_poke_ev5.dev_attr.attr,
177*4882a593Smuzhiyun &iio_dev_attr_poke_ev6.dev_attr.attr,
178*4882a593Smuzhiyun &iio_dev_attr_poke_ev7.dev_attr.attr,
179*4882a593Smuzhiyun &iio_dev_attr_poke_ev8.dev_attr.attr,
180*4882a593Smuzhiyun &iio_dev_attr_poke_ev9.dev_attr.attr,
181*4882a593Smuzhiyun NULL,
182*4882a593Smuzhiyun };
183*4882a593Smuzhiyun
184*4882a593Smuzhiyun static const struct attribute_group iio_evgen_group = {
185*4882a593Smuzhiyun .attrs = iio_evgen_attrs,
186*4882a593Smuzhiyun };
187*4882a593Smuzhiyun
188*4882a593Smuzhiyun static const struct attribute_group *iio_evgen_groups[] = {
189*4882a593Smuzhiyun &iio_evgen_group,
190*4882a593Smuzhiyun NULL
191*4882a593Smuzhiyun };
192*4882a593Smuzhiyun
193*4882a593Smuzhiyun static struct device iio_evgen_dev = {
194*4882a593Smuzhiyun .bus = &iio_bus_type,
195*4882a593Smuzhiyun .groups = iio_evgen_groups,
196*4882a593Smuzhiyun .release = &iio_evgen_release,
197*4882a593Smuzhiyun };
198*4882a593Smuzhiyun
iio_dummy_evgen_init(void)199*4882a593Smuzhiyun static __init int iio_dummy_evgen_init(void)
200*4882a593Smuzhiyun {
201*4882a593Smuzhiyun int ret = iio_dummy_evgen_create();
202*4882a593Smuzhiyun
203*4882a593Smuzhiyun if (ret < 0)
204*4882a593Smuzhiyun return ret;
205*4882a593Smuzhiyun device_initialize(&iio_evgen_dev);
206*4882a593Smuzhiyun dev_set_name(&iio_evgen_dev, "iio_evgen");
207*4882a593Smuzhiyun ret = device_add(&iio_evgen_dev);
208*4882a593Smuzhiyun if (ret)
209*4882a593Smuzhiyun put_device(&iio_evgen_dev);
210*4882a593Smuzhiyun return ret;
211*4882a593Smuzhiyun }
212*4882a593Smuzhiyun module_init(iio_dummy_evgen_init);
213*4882a593Smuzhiyun
iio_dummy_evgen_exit(void)214*4882a593Smuzhiyun static __exit void iio_dummy_evgen_exit(void)
215*4882a593Smuzhiyun {
216*4882a593Smuzhiyun device_unregister(&iio_evgen_dev);
217*4882a593Smuzhiyun }
218*4882a593Smuzhiyun module_exit(iio_dummy_evgen_exit);
219*4882a593Smuzhiyun
220*4882a593Smuzhiyun MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>");
221*4882a593Smuzhiyun MODULE_DESCRIPTION("IIO dummy driver");
222*4882a593Smuzhiyun MODULE_LICENSE("GPL v2");
223