Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Copyright (c) 2011 Jonathan Cameron
0004  *
0005  * Companion module to the iio simple dummy example driver.
0006  * The purpose of this is to generate 'fake' event interrupts thus
0007  * allowing that driver's code to be as close as possible to that of
0008  * a normal driver talking to hardware.  The approach used here
0009  * is not intended to be general and just happens to work for this
0010  * particular use case.
0011  */
0012 
0013 #include <linux/kernel.h>
0014 #include <linux/slab.h>
0015 #include <linux/interrupt.h>
0016 #include <linux/irq.h>
0017 #include <linux/mutex.h>
0018 #include <linux/module.h>
0019 #include <linux/sysfs.h>
0020 
0021 #include "iio_dummy_evgen.h"
0022 #include <linux/iio/iio.h>
0023 #include <linux/iio/sysfs.h>
0024 #include <linux/irq_sim.h>
0025 
0026 /* Fiddly bit of faking and irq without hardware */
0027 #define IIO_EVENTGEN_NO 10
0028 
0029 /**
0030  * struct iio_dummy_eventgen - event generator specific state
0031  * @regs: irq regs we are faking
0032  * @lock: protect the evgen state
0033  * @inuse: mask of which irqs are connected
0034  * @irq_sim: interrupt simulator
0035  * @base: base of irq range
0036  * @irq_sim_domain: irq simulator domain
0037  */
0038 struct iio_dummy_eventgen {
0039     struct iio_dummy_regs regs[IIO_EVENTGEN_NO];
0040     struct mutex lock;
0041     bool inuse[IIO_EVENTGEN_NO];
0042     struct irq_domain *irq_sim_domain;
0043 };
0044 
0045 /* We can only ever have one instance of this 'device' */
0046 static struct iio_dummy_eventgen *iio_evgen;
0047 
0048 static int iio_dummy_evgen_create(void)
0049 {
0050     int ret;
0051 
0052     iio_evgen = kzalloc(sizeof(*iio_evgen), GFP_KERNEL);
0053     if (!iio_evgen)
0054         return -ENOMEM;
0055 
0056     iio_evgen->irq_sim_domain = irq_domain_create_sim(NULL,
0057                               IIO_EVENTGEN_NO);
0058     if (IS_ERR(iio_evgen->irq_sim_domain)) {
0059         ret = PTR_ERR(iio_evgen->irq_sim_domain);
0060         kfree(iio_evgen);
0061         return ret;
0062     }
0063 
0064     mutex_init(&iio_evgen->lock);
0065 
0066     return 0;
0067 }
0068 
0069 /**
0070  * iio_dummy_evgen_get_irq() - get an evgen provided irq for a device
0071  *
0072  * This function will give a free allocated irq to a client device.
0073  * That irq can then be caused to 'fire' by using the associated sysfs file.
0074  */
0075 int iio_dummy_evgen_get_irq(void)
0076 {
0077     int i, ret = 0;
0078 
0079     if (!iio_evgen)
0080         return -ENODEV;
0081 
0082     mutex_lock(&iio_evgen->lock);
0083     for (i = 0; i < IIO_EVENTGEN_NO; i++) {
0084         if (!iio_evgen->inuse[i]) {
0085             ret = irq_create_mapping(iio_evgen->irq_sim_domain, i);
0086             iio_evgen->inuse[i] = true;
0087             break;
0088         }
0089     }
0090     mutex_unlock(&iio_evgen->lock);
0091     if (i == IIO_EVENTGEN_NO)
0092         return -ENOMEM;
0093 
0094     return ret;
0095 }
0096 EXPORT_SYMBOL_GPL(iio_dummy_evgen_get_irq);
0097 
0098 /**
0099  * iio_dummy_evgen_release_irq() - give the irq back.
0100  * @irq: irq being returned to the pool
0101  *
0102  * Used by client driver instances to give the irqs back when they disconnect
0103  */
0104 void iio_dummy_evgen_release_irq(int irq)
0105 {
0106     struct irq_data *irqd = irq_get_irq_data(irq);
0107 
0108     mutex_lock(&iio_evgen->lock);
0109     iio_evgen->inuse[irqd_to_hwirq(irqd)] = false;
0110     irq_dispose_mapping(irq);
0111     mutex_unlock(&iio_evgen->lock);
0112 }
0113 EXPORT_SYMBOL_GPL(iio_dummy_evgen_release_irq);
0114 
0115 struct iio_dummy_regs *iio_dummy_evgen_get_regs(int irq)
0116 {
0117     struct irq_data *irqd = irq_get_irq_data(irq);
0118 
0119     return &iio_evgen->regs[irqd_to_hwirq(irqd)];
0120 
0121 }
0122 EXPORT_SYMBOL_GPL(iio_dummy_evgen_get_regs);
0123 
0124 static void iio_dummy_evgen_free(void)
0125 {
0126     irq_domain_remove_sim(iio_evgen->irq_sim_domain);
0127     kfree(iio_evgen);
0128 }
0129 
0130 static void iio_evgen_release(struct device *dev)
0131 {
0132     iio_dummy_evgen_free();
0133 }
0134 
0135 static ssize_t iio_evgen_poke(struct device *dev,
0136                   struct device_attribute *attr,
0137                   const char *buf,
0138                   size_t len)
0139 {
0140     struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
0141     unsigned long event;
0142     int ret, irq;
0143 
0144     ret = kstrtoul(buf, 10, &event);
0145     if (ret)
0146         return ret;
0147 
0148     iio_evgen->regs[this_attr->address].reg_id   = this_attr->address;
0149     iio_evgen->regs[this_attr->address].reg_data = event;
0150 
0151     irq = irq_find_mapping(iio_evgen->irq_sim_domain, this_attr->address);
0152     ret = irq_set_irqchip_state(irq, IRQCHIP_STATE_PENDING, true);
0153     if (ret)
0154         return ret;
0155 
0156     return len;
0157 }
0158 
0159 static IIO_DEVICE_ATTR(poke_ev0, S_IWUSR, NULL, &iio_evgen_poke, 0);
0160 static IIO_DEVICE_ATTR(poke_ev1, S_IWUSR, NULL, &iio_evgen_poke, 1);
0161 static IIO_DEVICE_ATTR(poke_ev2, S_IWUSR, NULL, &iio_evgen_poke, 2);
0162 static IIO_DEVICE_ATTR(poke_ev3, S_IWUSR, NULL, &iio_evgen_poke, 3);
0163 static IIO_DEVICE_ATTR(poke_ev4, S_IWUSR, NULL, &iio_evgen_poke, 4);
0164 static IIO_DEVICE_ATTR(poke_ev5, S_IWUSR, NULL, &iio_evgen_poke, 5);
0165 static IIO_DEVICE_ATTR(poke_ev6, S_IWUSR, NULL, &iio_evgen_poke, 6);
0166 static IIO_DEVICE_ATTR(poke_ev7, S_IWUSR, NULL, &iio_evgen_poke, 7);
0167 static IIO_DEVICE_ATTR(poke_ev8, S_IWUSR, NULL, &iio_evgen_poke, 8);
0168 static IIO_DEVICE_ATTR(poke_ev9, S_IWUSR, NULL, &iio_evgen_poke, 9);
0169 
0170 static struct attribute *iio_evgen_attrs[] = {
0171     &iio_dev_attr_poke_ev0.dev_attr.attr,
0172     &iio_dev_attr_poke_ev1.dev_attr.attr,
0173     &iio_dev_attr_poke_ev2.dev_attr.attr,
0174     &iio_dev_attr_poke_ev3.dev_attr.attr,
0175     &iio_dev_attr_poke_ev4.dev_attr.attr,
0176     &iio_dev_attr_poke_ev5.dev_attr.attr,
0177     &iio_dev_attr_poke_ev6.dev_attr.attr,
0178     &iio_dev_attr_poke_ev7.dev_attr.attr,
0179     &iio_dev_attr_poke_ev8.dev_attr.attr,
0180     &iio_dev_attr_poke_ev9.dev_attr.attr,
0181     NULL,
0182 };
0183 
0184 static const struct attribute_group iio_evgen_group = {
0185     .attrs = iio_evgen_attrs,
0186 };
0187 
0188 static const struct attribute_group *iio_evgen_groups[] = {
0189     &iio_evgen_group,
0190     NULL
0191 };
0192 
0193 static struct device iio_evgen_dev = {
0194     .bus = &iio_bus_type,
0195     .groups = iio_evgen_groups,
0196     .release = &iio_evgen_release,
0197 };
0198 
0199 static __init int iio_dummy_evgen_init(void)
0200 {
0201     int ret = iio_dummy_evgen_create();
0202 
0203     if (ret < 0)
0204         return ret;
0205     device_initialize(&iio_evgen_dev);
0206     dev_set_name(&iio_evgen_dev, "iio_evgen");
0207     ret = device_add(&iio_evgen_dev);
0208     if (ret)
0209         put_device(&iio_evgen_dev);
0210     return ret;
0211 }
0212 module_init(iio_dummy_evgen_init);
0213 
0214 static __exit void iio_dummy_evgen_exit(void)
0215 {
0216     device_unregister(&iio_evgen_dev);
0217 }
0218 module_exit(iio_dummy_evgen_exit);
0219 
0220 MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>");
0221 MODULE_DESCRIPTION("IIO dummy driver");
0222 MODULE_LICENSE("GPL v2");