Staging: iio: Move evgen interrupt generation to irq_work
Enhance interrupt generation in the dummy driver and expand its usage by introducing the irq_work infrastructure to trigger an interrupt. This way, the irq_work_queue() wrapper permits calling both of the top half and threaded part from a hard irq context, unlike handle_nested_irq(), which only calls the threaded part. As an outcome, the driver succeeds in simulating real hardware interrupts, while keeping the normal interrupt flow. Signed-off-by: Cristina Opriceana <cristina.opriceana@gmail.com> Acked-by: Daniel Baluta <daniel.baluta@intel.com> Signed-off-by: Jonathan Cameron <jic23@kernel.org>
This commit is contained in:
parent
cd8d97774f
commit
fd2bb310ca
@ -24,9 +24,21 @@
|
|||||||
#include "iio_dummy_evgen.h"
|
#include "iio_dummy_evgen.h"
|
||||||
#include <linux/iio/iio.h>
|
#include <linux/iio/iio.h>
|
||||||
#include <linux/iio/sysfs.h>
|
#include <linux/iio/sysfs.h>
|
||||||
|
#include <linux/irq_work.h>
|
||||||
|
|
||||||
/* Fiddly bit of faking and irq without hardware */
|
/* Fiddly bit of faking and irq without hardware */
|
||||||
#define IIO_EVENTGEN_NO 10
|
#define IIO_EVENTGEN_NO 10
|
||||||
|
|
||||||
|
/**
|
||||||
|
* struct iio_dummy_handle_irq - helper struct to simulate interrupt generation
|
||||||
|
* @work: irq_work used to run handlers from hardirq context
|
||||||
|
* @irq: fake irq line number to trigger an interrupt
|
||||||
|
*/
|
||||||
|
struct iio_dummy_handle_irq {
|
||||||
|
struct irq_work work;
|
||||||
|
int irq;
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* struct iio_dummy_evgen - evgen state
|
* struct iio_dummy_evgen - evgen state
|
||||||
* @chip: irq chip we are faking
|
* @chip: irq chip we are faking
|
||||||
@ -35,6 +47,7 @@
|
|||||||
* @inuse: mask of which irqs are connected
|
* @inuse: mask of which irqs are connected
|
||||||
* @regs: irq regs we are faking
|
* @regs: irq regs we are faking
|
||||||
* @lock: protect the evgen state
|
* @lock: protect the evgen state
|
||||||
|
* @handler: helper for a 'hardware-like' interrupt simulation
|
||||||
*/
|
*/
|
||||||
struct iio_dummy_eventgen {
|
struct iio_dummy_eventgen {
|
||||||
struct irq_chip chip;
|
struct irq_chip chip;
|
||||||
@ -43,6 +56,7 @@ struct iio_dummy_eventgen {
|
|||||||
bool inuse[IIO_EVENTGEN_NO];
|
bool inuse[IIO_EVENTGEN_NO];
|
||||||
struct iio_dummy_regs regs[IIO_EVENTGEN_NO];
|
struct iio_dummy_regs regs[IIO_EVENTGEN_NO];
|
||||||
struct mutex lock;
|
struct mutex lock;
|
||||||
|
struct iio_dummy_handle_irq handler;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* We can only ever have one instance of this 'device' */
|
/* We can only ever have one instance of this 'device' */
|
||||||
@ -67,6 +81,14 @@ static void iio_dummy_event_irqunmask(struct irq_data *d)
|
|||||||
evgen->enabled[d->irq - evgen->base] = true;
|
evgen->enabled[d->irq - evgen->base] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void iio_dummy_work_handler(struct irq_work *work)
|
||||||
|
{
|
||||||
|
struct iio_dummy_handle_irq *irq_handler;
|
||||||
|
|
||||||
|
irq_handler = container_of(work, struct iio_dummy_handle_irq, work);
|
||||||
|
handle_simple_irq(irq_handler->irq, irq_to_desc(irq_handler->irq));
|
||||||
|
}
|
||||||
|
|
||||||
static int iio_dummy_evgen_create(void)
|
static int iio_dummy_evgen_create(void)
|
||||||
{
|
{
|
||||||
int ret, i;
|
int ret, i;
|
||||||
@ -91,6 +113,7 @@ static int iio_dummy_evgen_create(void)
|
|||||||
IRQ_NOREQUEST | IRQ_NOAUTOEN,
|
IRQ_NOREQUEST | IRQ_NOAUTOEN,
|
||||||
IRQ_NOPROBE);
|
IRQ_NOPROBE);
|
||||||
}
|
}
|
||||||
|
init_irq_work(&iio_evgen->handler.work, iio_dummy_work_handler);
|
||||||
mutex_init(&iio_evgen->lock);
|
mutex_init(&iio_evgen->lock);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@ -169,8 +192,9 @@ static ssize_t iio_evgen_poke(struct device *dev,
|
|||||||
iio_evgen->regs[this_attr->address].reg_id = this_attr->address;
|
iio_evgen->regs[this_attr->address].reg_id = this_attr->address;
|
||||||
iio_evgen->regs[this_attr->address].reg_data = event;
|
iio_evgen->regs[this_attr->address].reg_data = event;
|
||||||
|
|
||||||
|
iio_evgen->handler.irq = iio_evgen->base + this_attr->address;
|
||||||
if (iio_evgen->enabled[this_attr->address])
|
if (iio_evgen->enabled[this_attr->address])
|
||||||
handle_nested_irq(iio_evgen->base + this_attr->address);
|
irq_work_queue(&iio_evgen->handler.work);
|
||||||
|
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
|
@ -46,6 +46,7 @@ struct iio_dummy_state {
|
|||||||
int event_irq;
|
int event_irq;
|
||||||
int event_val;
|
int event_val;
|
||||||
bool event_en;
|
bool event_en;
|
||||||
|
s64 event_timestamp;
|
||||||
#endif /* CONFIG_IIO_SIMPLE_DUMMY_EVENTS */
|
#endif /* CONFIG_IIO_SIMPLE_DUMMY_EVENTS */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -153,6 +153,15 @@ int iio_simple_dummy_write_event_value(struct iio_dev *indio_dev,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static irqreturn_t iio_simple_dummy_get_timestamp(int irq, void *private)
|
||||||
|
{
|
||||||
|
struct iio_dev *indio_dev = private;
|
||||||
|
struct iio_dummy_state *st = iio_priv(indio_dev);
|
||||||
|
|
||||||
|
st->event_timestamp = iio_get_time_ns();
|
||||||
|
return IRQ_HANDLED;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* iio_simple_dummy_event_handler() - identify and pass on event
|
* iio_simple_dummy_event_handler() - identify and pass on event
|
||||||
* @irq: irq of event line
|
* @irq: irq of event line
|
||||||
@ -177,7 +186,7 @@ static irqreturn_t iio_simple_dummy_event_handler(int irq, void *private)
|
|||||||
IIO_EVENT_CODE(IIO_VOLTAGE, 0, 0,
|
IIO_EVENT_CODE(IIO_VOLTAGE, 0, 0,
|
||||||
IIO_EV_DIR_RISING,
|
IIO_EV_DIR_RISING,
|
||||||
IIO_EV_TYPE_THRESH, 0, 0, 0),
|
IIO_EV_TYPE_THRESH, 0, 0, 0),
|
||||||
iio_get_time_ns());
|
st->event_timestamp);
|
||||||
break;
|
break;
|
||||||
case 1:
|
case 1:
|
||||||
if (st->activity_running > st->event_val)
|
if (st->activity_running > st->event_val)
|
||||||
@ -187,7 +196,7 @@ static irqreturn_t iio_simple_dummy_event_handler(int irq, void *private)
|
|||||||
IIO_EV_DIR_RISING,
|
IIO_EV_DIR_RISING,
|
||||||
IIO_EV_TYPE_THRESH,
|
IIO_EV_TYPE_THRESH,
|
||||||
0, 0, 0),
|
0, 0, 0),
|
||||||
iio_get_time_ns());
|
st->event_timestamp);
|
||||||
break;
|
break;
|
||||||
case 2:
|
case 2:
|
||||||
if (st->activity_walking < st->event_val)
|
if (st->activity_walking < st->event_val)
|
||||||
@ -197,14 +206,14 @@ static irqreturn_t iio_simple_dummy_event_handler(int irq, void *private)
|
|||||||
IIO_EV_DIR_FALLING,
|
IIO_EV_DIR_FALLING,
|
||||||
IIO_EV_TYPE_THRESH,
|
IIO_EV_TYPE_THRESH,
|
||||||
0, 0, 0),
|
0, 0, 0),
|
||||||
iio_get_time_ns());
|
st->event_timestamp);
|
||||||
break;
|
break;
|
||||||
case 3:
|
case 3:
|
||||||
iio_push_event(indio_dev,
|
iio_push_event(indio_dev,
|
||||||
IIO_EVENT_CODE(IIO_STEPS, 0, IIO_NO_MOD,
|
IIO_EVENT_CODE(IIO_STEPS, 0, IIO_NO_MOD,
|
||||||
IIO_EV_DIR_NONE,
|
IIO_EV_DIR_NONE,
|
||||||
IIO_EV_TYPE_CHANGE, 0, 0, 0),
|
IIO_EV_TYPE_CHANGE, 0, 0, 0),
|
||||||
iio_get_time_ns());
|
st->event_timestamp);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
@ -238,7 +247,7 @@ int iio_simple_dummy_events_register(struct iio_dev *indio_dev)
|
|||||||
st->regs = iio_dummy_evgen_get_regs(st->event_irq);
|
st->regs = iio_dummy_evgen_get_regs(st->event_irq);
|
||||||
|
|
||||||
ret = request_threaded_irq(st->event_irq,
|
ret = request_threaded_irq(st->event_irq,
|
||||||
NULL,
|
&iio_simple_dummy_get_timestamp,
|
||||||
&iio_simple_dummy_event_handler,
|
&iio_simple_dummy_event_handler,
|
||||||
IRQF_ONESHOT,
|
IRQF_ONESHOT,
|
||||||
"iio_simple_event",
|
"iio_simple_event",
|
||||||
|
Loading…
x
Reference in New Issue
Block a user