[PATCH / RFC] lis302dl-queue-work-in-interrupt-handler.patch (and the hanging accelerometers issue)

Simon Kagstrom simon.kagstrom at gmail.com
Mon Sep 22 07:27:12 CEST 2008


Hi!

I've again taken a look at the accelerometers, and one thing I've
always thought looked strange was the interrupt handling. Since the SPI
layer doesn't work in interrupt context, the "bitbanging" (I won't
pretend I know much of SPI, sorry...) is done manually in mach-gta02.c
instead of using the normal SPI support.

This patch implements an alternative to that by instead adding a work
queue and queuing a "second level" interrupt handler that reads the
data from the accelerometer.


The patch also includes a workaround for the hanging accelerometers,
which seem to work for me. I simply rerun the "manual" bitbang just
like on device open if the overrun or new data available is seen in the
interrupt handler. However, I'm not sure this captures all cases, so it
would be good if someone else could test this.


Perhaps using a workqueue (or any delayed handler) is a silly thing in
this case, and the workaround is obviously silly (since the
lis320dl_bitbang_read function will also add entries to the input
queue), so let's consider it as RFC for now. I too late saw the patch
on Andy's branch, which perhaps invalidates all of this work anyway.

// Simon

lis302dl-queue-work-in-interrupt-handler.patch:
----
From: Simon Kagstrom <simon.kagstrom at gmail.com>

Use work queue for interrupts (since bitbang cannot be used in the interrupt
handler).

This patch also includes a workaround for the "hang" on reading the
accelerometers by redoing the bitbang read.

Signed-off-by: Simon Kagstrom <simon.kagstrom at gmail.com>
---

 drivers/input/misc/lis302dl.c |   37 ++++++++++++++++++++++++++++++++++---
 include/linux/lis302dl.h      |    3 +++
 2 files changed, 37 insertions(+), 3 deletions(-)

diff --git a/drivers/input/misc/lis302dl.c b/drivers/input/misc/lis302dl.c
index b01ca04..c8fa223 100644
--- a/drivers/input/misc/lis302dl.c
+++ b/drivers/input/misc/lis302dl.c
@@ -160,12 +160,38 @@ static void _report_btn_double(struct input_dev *inp, int btn)
 }
 #endif
 
+#define MG_PER_SAMPLE 18
+
+static void lis302dl_queued_interrupt(struct work_struct *work)
+{
+    struct lis302dl_info *lis = container_of(work,
+    		struct lis302dl_info, interrupt_work);
+    s8 x, y, z;
+    u8 status;
+
+    x = (s8)reg_read(lis, LIS302DL_REG_OUT_X);
+    y = (s8)reg_read(lis, LIS302DL_REG_OUT_Y);
+    z = (s8)reg_read(lis, LIS302DL_REG_OUT_Z);
+
+    status = reg_read(lis, LIS302DL_REG_STATUS);
+
+    input_report_rel(lis->input_dev, REL_X, MG_PER_SAMPLE * x);
+    input_report_rel(lis->input_dev, REL_Y, MG_PER_SAMPLE * y);
+    input_report_rel(lis->input_dev, REL_Z, MG_PER_SAMPLE * z);
+    input_sync(lis->input_dev);
+
+    /* Possibly work around the "hanging accelerometer" issues */
+	if (status & (LIS302DL_STATUS_XYZOR | LIS302DL_STATUS_XDA |
+		LIS302DL_STATUS_YDA | LIS302DL_STATUS_ZDA))
+		(lis->pdata->lis302dl_bitbang_read)(lis);
+}
 
 static irqreturn_t lis302dl_interrupt(int irq, void *_lis)
 {
 	struct lis302dl_info *lis = _lis;
 
-	(lis->pdata->lis302dl_bitbang_read)(lis);
+	queue_work(lis->interrupt_queue, &lis->interrupt_work);
+
 	return IRQ_HANDLED;
 }
 
@@ -478,8 +504,8 @@ static int lis302dl_input_open(struct input_dev *inp)
 			 LIS302DL_CTRL1_Yen | LIS302DL_CTRL1_Zen;
 	unsigned long flags;
 
-	local_irq_save(flags);
 	/* make sure we're powered up and generate data ready */
+	local_irq_save(flags);
 	reg_set_bit_mask(lis, LIS302DL_REG_CTRL1, ctrl1, ctrl1);
 	local_irq_restore(flags);
 
@@ -546,6 +572,9 @@ static int __devinit lis302dl_probe(struct spi_device *spi)
 	if (!lis)
 		return -ENOMEM;
 
+    INIT_WORK(&lis->interrupt_work, lis302dl_queued_interrupt);
+    lis->interrupt_queue = create_workqueue("LIS302dl interrupt queue");
+
 	local_irq_save(flags);
 
 	mutex_init(&lis->lock);
@@ -682,7 +711,9 @@ static int __devexit lis302dl_remove(struct spi_device *spi)
 	local_irq_restore(flags);
 
 	/* Cleanup resources */
-	free_irq(lis->spi_dev->irq, lis);
+    destroy_workqueue(lis->interrupt_queue);
+
+    free_irq(lis->spi_dev->irq, lis);
 	sysfs_remove_group(&spi->dev.kobj, &lis302dl_attr_group);
 	input_unregister_device(lis->input_dev);
 	if (lis->input_dev)
diff --git a/include/linux/lis302dl.h b/include/linux/lis302dl.h
index 7daa8b3..5853afc 100644
--- a/include/linux/lis302dl.h
+++ b/include/linux/lis302dl.h
@@ -4,6 +4,7 @@
 #include <linux/types.h>
 #include <linux/spi/spi.h>
 #include <linux/input.h>
+#include <linux/workqueue.h>
 
 
 struct lis302dl_info;
@@ -23,6 +24,8 @@ struct lis302dl_info {
 	struct lis302dl_platform_data *pdata;
 	struct spi_device *spi_dev;
 	struct input_dev *input_dev;
+    struct workqueue_struct *interrupt_queue;
+    struct work_struct interrupt_work;
 	struct mutex lock;
 	unsigned int flags;
 	u_int8_t regs[0x40];



More information about the openmoko-kernel mailing list