linux/drivers/staging/iio/iio_dummy_evgen.c
<<
>>
Prefs
   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"
  25#include <linux/iio/iio.h>
  26#include <linux/iio/sysfs.h>
  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
  35 * @inuse: mask of which irqs are connected
  36 * @lock: protect the evgen state
  37 */
  38struct iio_dummy_eventgen {
  39        struct irq_chip chip;
  40        int base;
  41        bool enabled[IIO_EVENTGEN_NO];
  42        bool inuse[IIO_EVENTGEN_NO];
  43        struct mutex lock;
  44};
  45
  46/* We can only ever have one instance of this 'device' */
  47static struct iio_dummy_eventgen *iio_evgen;
  48static const char *iio_evgen_name = "iio_dummy_evgen";
  49
  50static void iio_dummy_event_irqmask(struct irq_data *d)
  51{
  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);
  55
  56        evgen->enabled[d->irq - evgen->base] = false;
  57}
  58
  59static void iio_dummy_event_irqunmask(struct irq_data *d)
  60{
  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);
  64
  65        evgen->enabled[d->irq - evgen->base] = true;
  66}
  67
  68static int iio_dummy_evgen_create(void)
  69{
  70        int ret, i;
  71
  72        iio_evgen = kzalloc(sizeof(*iio_evgen), GFP_KERNEL);
  73        if (iio_evgen == NULL)
  74                return -ENOMEM;
  75
  76        iio_evgen->base = irq_alloc_descs(-1, 0, IIO_EVENTGEN_NO, 0);
  77        if (iio_evgen->base < 0) {
  78                ret = iio_evgen->base;
  79                kfree(iio_evgen);
  80                return ret;
  81        }
  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,
  90                                  IRQ_NOPROBE);
  91        }
  92        mutex_init(&iio_evgen->lock);
  93        return 0;
  94}
  95
  96/**
  97 * iio_dummy_evgen_get_irq() - get an evgen provided irq for a device
  98 *
  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.
 101 */
 102int iio_dummy_evgen_get_irq(void)
 103{
 104        int i, ret = 0;
 105
 106        if (iio_evgen == NULL)
 107                return -ENODEV;
 108
 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;
 114                        break;
 115                }
 116        mutex_unlock(&iio_evgen->lock);
 117        if (i == IIO_EVENTGEN_NO)
 118                return -ENOMEM;
 119        return ret;
 120}
 121EXPORT_SYMBOL_GPL(iio_dummy_evgen_get_irq);
 122
 123/**
 124 * iio_dummy_evgen_release_irq() - give the irq back.
 125 * @irq: irq being returned to the pool
 126 *
 127 * Used by client driver instances to give the irqs back when they disconnect
 128 */
 129int iio_dummy_evgen_release_irq(int irq)
 130{
 131        mutex_lock(&iio_evgen->lock);
 132        iio_evgen->inuse[irq - iio_evgen->base] = false;
 133        mutex_unlock(&iio_evgen->lock);
 134
 135        return 0;
 136}
 137EXPORT_SYMBOL_GPL(iio_dummy_evgen_release_irq);
 138
 139static void iio_dummy_evgen_free(void)
 140{
 141        irq_free_descs(iio_evgen->base, IIO_EVENTGEN_NO);
 142        kfree(iio_evgen);
 143}
 144
 145static void iio_evgen_release(struct device *dev)
 146{
 147        iio_dummy_evgen_free();
 148}
 149
 150static ssize_t iio_evgen_poke(struct device *dev,
 151                              struct device_attribute *attr,
 152                              const char *buf,
 153                              size_t len)
 154{
 155        struct iio_dev_attr *this_attr = to_iio_dev_attr(attr);
 156
 157        if (iio_evgen->enabled[this_attr->address])
 158                handle_nested_irq(iio_evgen->base + this_attr->address);
 159
 160        return len;
 161}
 162
 163static IIO_DEVICE_ATTR(poke_ev0, S_IWUSR, NULL, &iio_evgen_poke, 0);
 164static IIO_DEVICE_ATTR(poke_ev1, S_IWUSR, NULL, &iio_evgen_poke, 1);
 165static IIO_DEVICE_ATTR(poke_ev2, S_IWUSR, NULL, &iio_evgen_poke, 2);
 166static IIO_DEVICE_ATTR(poke_ev3, S_IWUSR, NULL, &iio_evgen_poke, 3);
 167static IIO_DEVICE_ATTR(poke_ev4, S_IWUSR, NULL, &iio_evgen_poke, 4);
 168static IIO_DEVICE_ATTR(poke_ev5, S_IWUSR, NULL, &iio_evgen_poke, 5);
 169static IIO_DEVICE_ATTR(poke_ev6, S_IWUSR, NULL, &iio_evgen_poke, 6);
 170static IIO_DEVICE_ATTR(poke_ev7, S_IWUSR, NULL, &iio_evgen_poke, 7);
 171static IIO_DEVICE_ATTR(poke_ev8, S_IWUSR, NULL, &iio_evgen_poke, 8);
 172static IIO_DEVICE_ATTR(poke_ev9, S_IWUSR, NULL, &iio_evgen_poke, 9);
 173
 174static 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,
 185        NULL,
 186};
 187
 188static const struct attribute_group iio_evgen_group = {
 189        .attrs = iio_evgen_attrs,
 190};
 191
 192static const struct attribute_group *iio_evgen_groups[] = {
 193        &iio_evgen_group,
 194        NULL
 195};
 196
 197static struct device iio_evgen_dev = {
 198        .bus = &iio_bus_type,
 199        .groups = iio_evgen_groups,
 200        .release = &iio_evgen_release,
 201};
 202static __init int iio_dummy_evgen_init(void)
 203{
 204        int ret = iio_dummy_evgen_create();
 205        if (ret < 0)
 206                return ret;
 207        device_initialize(&iio_evgen_dev);
 208        dev_set_name(&iio_evgen_dev, "iio_evgen");
 209        return device_add(&iio_evgen_dev);
 210}
 211module_init(iio_dummy_evgen_init);
 212
 213static __exit void iio_dummy_evgen_exit(void)
 214{
 215        device_unregister(&iio_evgen_dev);
 216}
 217module_exit(iio_dummy_evgen_exit);
 218
 219MODULE_AUTHOR("Jonathan Cameron <jic23@kernel.org>");
 220MODULE_DESCRIPTION("IIO dummy driver");
 221MODULE_LICENSE("GPL v2");
 222