summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinus Walleij <linus.walleij@linaro.org>2017-11-30 10:23:27 +0100
committerLinus Walleij <linus.walleij@linaro.org>2018-01-23 14:43:57 +0100
commitd58f2bf261fdf3a3fc916c9999a686f959dcf6b6 (patch)
tree8ce132c9047329bbe752cfa6870b9be11ed87503
parent24bd3efc9d1efb5f756a7c6f807a36ddb6adc671 (diff)
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: Felipe Balbi <felipe.balbi@linux.intel.com> Reported-by: Felipe Balbi <felipe.balbi@linux.intel.com> Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
-rw-r--r--drivers/gpio/gpiolib.c21
1 files changed, 19 insertions, 2 deletions
diff --git a/drivers/gpio/gpiolib.c b/drivers/gpio/gpiolib.c
index 754836e4ca0e..1eebd69f369e 100644
--- a/drivers/gpio/gpiolib.c
+++ b/drivers/gpio/gpiolib.c
@@ -600,6 +600,9 @@ out_free_lh:
* @events: KFIFO for the GPIO events
* @read_lock: mutex lock to protect reads from colliding with adding
* 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 gpio_device *gdev;
@@ -610,6 +613,7 @@ struct lineevent_state {
wait_queue_head_t wait;
DECLARE_KFIFO(events, struct gpioevent_data, 16);
struct mutex read_lock;
+ u64 timestamp;
};
#define GPIOEVENT_REQUEST_VALID_FLAGS \
@@ -747,7 +751,7 @@ static irqreturn_t lineevent_irq_thread(int irq, void *p)
/* Do not leak kernel stack to userspace */
memset(&ge, 0, sizeof(ge));
- ge.timestamp = ktime_get_real_ns();
+ ge.timestamp = le->timestamp;
level = gpiod_get_value_cansleep(le->desc);
if (le->eflags & GPIOEVENT_REQUEST_RISING_EDGE
@@ -775,6 +779,19 @@ static irqreturn_t lineevent_irq_thread(int irq, void *p)
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)
{
struct gpioevent_request eventreq;
@@ -867,7 +884,7 @@ static int lineevent_create(struct gpio_device *gdev, void __user *ip)
/* Request a thread to read the events */
ret = request_threaded_irq(le->irq,
- NULL,
+ lineevent_irq_handler,
lineevent_irq_thread,
irqflags,
le->label,