[PATCH] (repost) lis302dl-queue-work-in-interrupt-handler.patch
Sean McNeil
sean at mcneil.com
Mon Sep 22 18:35:12 CEST 2008
2 things:
1) If you change the interrupt to a level instead of edge trigger, then
you can get rid of the code to jump start and the while loop. It also
prevents lockup when the system is extremely busy. I don't think your
implementation would prevent that.
2) I was under the impression that the reg_read didn't prevent
simultaneous access of the two accelerometers. This would cause problems
since the chip selects need to be mutually exclusive when accessing them.
Cheers,
Sean
Simon Kagstrom wrote:
> Use work queue for lis302dl interrupts, fix accelerometer hang.
>
> From: Simon Kagstrom <simon.kagstrom at gmail.com>
>
> Use work queue for interrupts (since bitbang cannot be used in the
> interrupt handler). This gets rid of the special bitbang_read in
> mach-gta02.c as well, so that function has been removed.
>
> The accelerometer "hang" seems to be caused by missed interrupts, so the
> interrupt handler now reads the x,y,z values until the status shows no
> outstanding data.
>
> Signed-off-by: Simon Kagstrom <simon.kagstrom at gmail.com>
> ---
>
> arch/arm/mach-s3c2440/mach-gta02.c | 104 ------------------------------------
> drivers/input/misc/lis302dl.c | 41 +++++++++++++-
> include/linux/lis302dl.h | 4 +
> 3 files changed, 42 insertions(+), 107 deletions(-)
>
> diff --git a/arch/arm/mach-s3c2440/mach-gta02.c b/arch/arm/mach-s3c2440/mach-gta02.c
> index c15f072..1da48cb 100644
> --- a/arch/arm/mach-s3c2440/mach-gta02.c
> +++ b/arch/arm/mach-s3c2440/mach-gta02.c
> @@ -1034,109 +1034,9 @@ static struct platform_device gta02_vibrator_dev = {
>
> /*
> * Situation is that Linux SPI can't work in an interrupt context, so we
> - * implement our own bitbang here. Arbitration is needed because not only
> - * can this interrupt happen at any time even if foreground wants to use
> - * the bitbang API from Linux, but multiple motion sensors can be on the
> - * same SPI bus, and multiple interrupts can happen.
> - *
> - * Foreground / interrupt arbitration is okay because the interrupts are
> - * disabled around all the foreground SPI code.
> - *
> - * Interrupt / Interrupt arbitration is evidently needed, otherwise we
> - * lose edge-triggered service after a while due to the two sensors sharing
> - * the SPI bus having irqs at the same time eventually.
> - *
> - * Servicing is typ 75 - 100us at 400MHz.
> + * implement our own bitbang here.
> */
>
> -/* #define DEBUG_SPEW_MS */
> -#define MG_PER_SAMPLE 18
> -
> -struct lis302dl_platform_data lis302_pdata[];
> -
> -void gta02_lis302dl_bitbang_read(struct lis302dl_info *lis)
> -{
> - struct lis302dl_platform_data *pdata = lis->pdata;
> - u8 shifter = 0xc0 | LIS302DL_REG_OUT_X; /* read, autoincrement */
> - int n, n1;
> - unsigned long other_cs;
> - unsigned long flags;
> -#ifdef DEBUG_SPEW_MS
> - s8 x, y, z;
> -#endif
> -
> - local_irq_save(flags);
> -
> - /*
> - * Huh.. "quirk"... CS on this device is not really "CS" like you can
> - * expect. Instead when 1 it selects I2C interface mode. Because we
> - * have 2 devices on one interface, the "disabled" device when we talk
> - * to an "enabled" device sees the clocks as I2C clocks, creating
> - * havoc.
> - * I2C sees MOSI going LOW while CLK HIGH as a START action, we must
> - * ensure this is never issued.
> - */
> -
> - if (&lis302_pdata[0] == pdata)
> - other_cs = lis302_pdata[1].pin_chip_select;
> - else
> - other_cs = lis302_pdata[0].pin_chip_select;
> -
> - s3c2410_gpio_setpin(other_cs, 1);
> - s3c2410_gpio_setpin(pdata->pin_chip_select, 1);
> - s3c2410_gpio_setpin(pdata->pin_clk, 1);
> - s3c2410_gpio_setpin(pdata->pin_chip_select, 0);
> - for (n = 0; n < 8; n++) { /* write the r/w, inc and address */
> - s3c2410_gpio_setpin(pdata->pin_clk, 0);
> - s3c2410_gpio_setpin(pdata->pin_mosi, (shifter >> 7) & 1);
> - s3c2410_gpio_setpin(pdata->pin_clk, 0);
> - s3c2410_gpio_setpin(pdata->pin_clk, 1);
> - s3c2410_gpio_setpin(pdata->pin_clk, 1);
> - shifter <<= 1;
> - }
> -
> - for (n = 0; n < 5; n++) { /* 5 consequetive registers */
> - for (n1 = 0; n1 < 8; n1++) { /* 8 bits each */
> - s3c2410_gpio_setpin(pdata->pin_clk, 0);
> - s3c2410_gpio_setpin(pdata->pin_clk, 0);
> - shifter <<= 1;
> - if (s3c2410_gpio_getpin(pdata->pin_miso))
> - shifter |= 1;
> - s3c2410_gpio_setpin(pdata->pin_clk, 1);
> - s3c2410_gpio_setpin(pdata->pin_clk, 1);
> - }
> - switch (n) {
> - case 0:
> -#ifdef DEBUG_SPEW_MS
> - x = shifter;
> -#endif
> - input_report_rel(lis->input_dev, REL_X, MG_PER_SAMPLE * (s8)shifter);
> - break;
> - case 2:
> -#ifdef DEBUG_SPEW_MS
> - y = shifter;
> -#endif
> - input_report_rel(lis->input_dev, REL_Y, MG_PER_SAMPLE * (s8)shifter);
> - break;
> - case 4:
> -#ifdef DEBUG_SPEW_MS
> - z = shifter;
> -#endif
> - input_report_rel(lis->input_dev, REL_Z, MG_PER_SAMPLE * (s8)shifter);
> - break;
> - }
> - }
> - s3c2410_gpio_setpin(pdata->pin_chip_select, 1);
> - s3c2410_gpio_setpin(other_cs, 1);
> - local_irq_restore(flags);
> -
> - input_sync(lis->input_dev);
> -#ifdef DEBUG_SPEW_MS
> - printk("%s: %d %d %d\n", pdata->name, x, y, z);
> -#endif
> -}
> -
> -
> void gta02_lis302dl_suspend_io(struct lis302dl_info *lis, int resume)
> {
> struct lis302dl_platform_data *pdata = lis->pdata;
> @@ -1169,7 +1069,6 @@ struct lis302dl_platform_data lis302_pdata[] = {
> .pin_mosi = S3C2410_GPG6,
> .pin_miso = S3C2410_GPG5,
> .open_drain = 1, /* altered at runtime by PCB rev */
> - .lis302dl_bitbang_read = gta02_lis302dl_bitbang_read,
> .lis302dl_suspend_io = gta02_lis302dl_suspend_io,
> }, {
> .name = "lis302-2 (bottom)",
> @@ -1178,7 +1077,6 @@ struct lis302dl_platform_data lis302_pdata[] = {
> .pin_mosi = S3C2410_GPG6,
> .pin_miso = S3C2410_GPG5,
> .open_drain = 1, /* altered at runtime by PCB rev */
> - .lis302dl_bitbang_read = gta02_lis302dl_bitbang_read,
> .lis302dl_suspend_io = gta02_lis302dl_suspend_io,
> },
> };
> diff --git a/drivers/input/misc/lis302dl.c b/drivers/input/misc/lis302dl.c
> index b01ca04..c725e72 100644
> --- a/drivers/input/misc/lis302dl.c
> +++ b/drivers/input/misc/lis302dl.c
> @@ -160,12 +160,42 @@ static void _report_btn_double(struct input_dev *inp, int btn)
> }
> #endif
>
> +#define MG_PER_SAMPLE 18
> +
> +static void lis302dl_read_input(struct lis302dl_info *lis)
> +{
> + s8 x, y, z;
> + u8 status;
> +
> + /* Loop until there is no data available */
> + while ((status = reg_read(lis, LIS302DL_REG_STATUS)) &
> + (LIS302DL_STATUS_XYZOR | LIS302DL_STATUS_XDA |
> + LIS302DL_STATUS_YDA | LIS302DL_STATUS_ZDA)) {
> + 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);
> +
> + 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);
> + }
> +}
> +
> +static void lis302dl_queued_interrupt(struct work_struct *work)
> +{
> + struct lis302dl_info *lis = container_of(work,
> + struct lis302dl_info, interrupt_work);
> +
> + lis302dl_read_input(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 +508,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);
>
> @@ -487,7 +517,7 @@ static int lis302dl_input_open(struct input_dev *inp)
>
> /* kick it off -- since we are edge triggered, if we missed the edge
> * permanent low interrupt is death for us */
> - (lis->pdata->lis302dl_bitbang_read)(lis);
> + lis302dl_read_input(lis);
>
> return 0;
> }
> @@ -546,6 +576,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,6 +715,8 @@ static int __devexit lis302dl_remove(struct spi_device *spi)
> local_irq_restore(flags);
>
> /* Cleanup resources */
> + 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);
> diff --git a/include/linux/lis302dl.h b/include/linux/lis302dl.h
> index 7daa8b3..c708bdc 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;
> @@ -15,7 +16,6 @@ struct lis302dl_platform_data {
> unsigned long pin_mosi;
> unsigned long pin_miso;
> int open_drain;
> - void (*lis302dl_bitbang_read)(struct lis302dl_info *);
> void (*lis302dl_suspend_io)(struct lis302dl_info *, int resuming);
> };
>
> @@ -23,6 +23,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