From: Nelson Castillo <[email protected]>

This patch tries to address #2185 : AUX button makes interrupt storm [1].
We tried doing a pull-down of S3C2410_GPF6 but in my tests it did not
work (please check the ticket to see the code used for the test).

[1] https://docs.openmoko.org/trac/ticket/2185

We are still waiting for the confirmation that the fix works.
In my tests it did work but I do not get as many spurious
interrupts as the reporter does.

Signed-off-by: Nelson Castillo <[email protected]>
---

 drivers/input/keyboard/neo1973kbd.c |   79 +++++++++++++++++++++++++++--------
 1 files changed, 62 insertions(+), 17 deletions(-)

diff --git a/drivers/input/keyboard/neo1973kbd.c 
b/drivers/input/keyboard/neo1973kbd.c
index 4d412e0..0f07e0b 100644
--- a/drivers/input/keyboard/neo1973kbd.c
+++ b/drivers/input/keyboard/neo1973kbd.c
@@ -32,8 +32,11 @@ struct neo1973kbd {
        struct platform_device *pdev;
        struct input_dev *input;
        struct device *cdev;
-       struct work_struct work;
-       int work_in_progress;
+       struct work_struct aux_work;
+       struct work_struct jack_work;
+       int hp_work_in_progress;
+       int aux_work_in_progress;
+       int aux_irq_count;
        int hp_irq_count_in_work;
        int hp_irq_count;
        int jack_irq;
@@ -88,23 +91,63 @@ static struct neo1973kbd_key keys[] = {
        },
 };
 
-
-static irqreturn_t neo1973kbd_aux_irq(int irq, void *dev_id)
+static void neo1973kbd_debounce_aux(struct work_struct *work)
 {
-       struct neo1973kbd *kbd = dev_id;
-       int key_pressed = !gpio_get_value(
-                                   kbd->pdev->resource[NEO1973_KEY_AUX].start);
-       int *p = NULL;
-
-       if (global_inside_suspend)
-               printk("death %d\n", *p);
+       struct neo1973kbd *kbd =
+               container_of(work, struct neo1973kbd, aux_work);
+       unsigned long flags;
+       int dont_loop;
+       int key_pressed;
+       int key_pressed0;
 
+       key_pressed =
+               !gpio_get_value(kbd->pdev->resource[NEO1973_KEY_AUX].start);
+       key_pressed0 = key_pressed;
        /* GTA02 has inverted sense level compared to GTA01 */
        if (machine_is_neo1973_gta02())
                key_pressed = !key_pressed;
        input_report_key(kbd->input, KEY_PHONE, key_pressed);
        input_sync(kbd->input);
 
+       do {
+               int count = kbd->aux_irq_count;
+               msleep(60);
+
+               local_save_flags(flags);
+               dont_loop = count == kbd->aux_irq_count;
+               if (dont_loop)
+                       kbd->aux_work_in_progress = 0;
+               local_irq_restore(flags);
+       } while (!dont_loop);
+
+       key_pressed =
+               !gpio_get_value(kbd->pdev->resource[NEO1973_KEY_AUX].start);
+       if (key_pressed0 != key_pressed) {
+               if (machine_is_neo1973_gta02())
+                       key_pressed = !key_pressed;
+               input_report_key(kbd->input, KEY_PHONE, key_pressed);
+               input_sync(kbd->input);
+       }
+}
+
+static irqreturn_t neo1973kbd_aux_irq(int irq, void *dev)
+{
+       struct neo1973kbd *data = dev;
+       int *p = NULL;
+
+       if (global_inside_suspend)
+               printk(KERN_ERR "death %d\n", *p);
+
+       data->aux_irq_count++;
+
+       if (!data->aux_work_in_progress) {
+               if (unlikely(!schedule_work(&data->aux_work)))
+                       printk(KERN_ERR
+                              "Unable to schedule AUX debounce\n");
+               else
+                       data->aux_work_in_progress = 1;
+       }
+
        return IRQ_HANDLED;
 }
 
@@ -150,7 +193,8 @@ static void neo1973kbd_jack_event(struct device *dev, int 
num)
 
 static void neo1973kbd_debounce_jack(struct work_struct *work)
 {
-       struct neo1973kbd *kbd = container_of(work, struct neo1973kbd, work);
+       struct neo1973kbd *kbd =
+               container_of(work, struct neo1973kbd, jack_work);
        unsigned long flags;
        int loop = 0;
        int level;
@@ -181,7 +225,7 @@ static void neo1973kbd_debounce_jack(struct work_struct 
*work)
                /* no interrupts during this work means we can exit the work */
                loop = !!(kbd->hp_irq_count != kbd->hp_irq_count_in_work);
                if (!loop)
-                       kbd->work_in_progress = 0;
+                       kbd->hp_work_in_progress = 0;
                local_irq_restore(flags);
                /*
                 * interrupt that comes here will either queue a new work action
@@ -212,15 +256,15 @@ static irqreturn_t neo1973kbd_headphone_irq(int irq, void 
*dev_id)
         * come in the meanwhile, we can tell by the difference in that
         * stored count and hp_irq_count which increments every interrupt
         */
-       if (!neo1973kbd_data->work_in_progress) {
+       if (!neo1973kbd_data->hp_work_in_progress) {
                neo1973kbd_data->jack_irq = irq;
                neo1973kbd_data->hp_irq_count_in_work =
                                                neo1973kbd_data->hp_irq_count;
-               if (!schedule_work(&neo1973kbd_data->work))
+               if (!schedule_work(&neo1973kbd_data->jack_work))
                        printk(KERN_ERR
                                "Unable to schedule headphone debounce\n");
                else
-                       neo1973kbd_data->work_in_progress = 1;
+                       neo1973kbd_data->hp_work_in_progress = 1;
        }
 
        return IRQ_HANDLED;
@@ -283,7 +327,8 @@ static int neo1973kbd_probe(struct platform_device *pdev)
 
        neo1973kbd->input = input_dev;
 
-       INIT_WORK(&neo1973kbd->work, neo1973kbd_debounce_jack);
+       INIT_WORK(&neo1973kbd->aux_work, neo1973kbd_debounce_aux);
+       INIT_WORK(&neo1973kbd->jack_work, neo1973kbd_debounce_jack);
 
        input_dev->name = "Neo1973 Buttons";
        input_dev->phys = "neo1973kbd/input0";


Reply via email to