Commit d58f2bf2 authored by Linus Walleij's avatar Linus Walleij

gpio: Timestamp events in hardirq handler

Add a hardirq handler to the GPIO userspace event loop, making
sure to pick up the timestamp there, as close as possible in time
relative to the actual event causing the interrupt.

Tested with a simple pushbutton GPIO on ux500 and seems to work
fine.

Cc: Bartosz Golaszewski <brgl@bgdev.pl>
Reviewed-by: default avatarFelipe Balbi <felipe.balbi@linux.intel.com>
Reported-by: default avatarFelipe Balbi <felipe.balbi@linux.intel.com>
Signed-off-by: default avatarLinus Walleij <linus.walleij@linaro.org>
parent 24bd3efc
...@@ -600,6 +600,9 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip) ...@@ -600,6 +600,9 @@ static int linehandle_create(struct gpio_device *gdev, void __user *ip)
* @events: KFIFO for the GPIO events * @events: KFIFO for the GPIO events
* @read_lock: mutex lock to protect reads from colliding with adding * @read_lock: mutex lock to protect reads from colliding with adding
* new events to the FIFO * new events to the FIFO
* @timestamp: cache for the timestamp storing it between hardirq
* and IRQ thread, used to bring the timestamp close to the actual
* event
*/ */
struct lineevent_state { struct lineevent_state {
struct gpio_device *gdev; struct gpio_device *gdev;
...@@ -610,6 +613,7 @@ struct lineevent_state { ...@@ -610,6 +613,7 @@ struct lineevent_state {
wait_queue_head_t wait; wait_queue_head_t wait;
DECLARE_KFIFO(events, struct gpioevent_data, 16); DECLARE_KFIFO(events, struct gpioevent_data, 16);
struct mutex read_lock; struct mutex read_lock;
u64 timestamp;
}; };
#define GPIOEVENT_REQUEST_VALID_FLAGS \ #define GPIOEVENT_REQUEST_VALID_FLAGS \
...@@ -747,7 +751,7 @@ static irqreturn_t lineevent_irq_thread(int irq, void *p) ...@@ -747,7 +751,7 @@ static irqreturn_t lineevent_irq_thread(int irq, void *p)
/* Do not leak kernel stack to userspace */ /* Do not leak kernel stack to userspace */
memset(&ge, 0, sizeof(ge)); memset(&ge, 0, sizeof(ge));
ge.timestamp = ktime_get_real_ns(); ge.timestamp = le->timestamp;
level = gpiod_get_value_cansleep(le->desc); level = gpiod_get_value_cansleep(le->desc);
if (le->eflags & GPIOEVENT_REQUEST_RISING_EDGE if (le->eflags & GPIOEVENT_REQUEST_RISING_EDGE
...@@ -775,6 +779,19 @@ static irqreturn_t lineevent_irq_thread(int irq, void *p) ...@@ -775,6 +779,19 @@ static irqreturn_t lineevent_irq_thread(int irq, void *p)
return IRQ_HANDLED; return IRQ_HANDLED;
} }
static irqreturn_t lineevent_irq_handler(int irq, void *p)
{
struct lineevent_state *le = p;
/*
* Just store the timestamp in hardirq context so we get it as
* close in time as possible to the actual event.
*/
le->timestamp = ktime_get_real_ns();
return IRQ_WAKE_THREAD;
}
static int lineevent_create(struct gpio_device *gdev, void __user *ip) static int lineevent_create(struct gpio_device *gdev, void __user *ip)
{ {
struct gpioevent_request eventreq; struct gpioevent_request eventreq;
...@@ -867,7 +884,7 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip) ...@@ -867,7 +884,7 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip)
/* Request a thread to read the events */ /* Request a thread to read the events */
ret = request_threaded_irq(le->irq, ret = request_threaded_irq(le->irq,
NULL, lineevent_irq_handler,
lineevent_irq_thread, lineevent_irq_thread,
irqflags, irqflags,
le->label, le->label,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment