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";
