From 02e725ca989f392158b2a15cde6dd0d925bc8e50 Mon Sep 17 00:00:00 2001 From: Cory Maccarrone Date: Wed, 29 Jul 2009 10:15:23 -0700 Subject: [PATCH 2/3] htcpld: Various fixes to the I2C HTC PLD * Replace tasklets with work queues to avoid schedule while atomic bugs * Change keypad event handling to only report events once if the same button is pushed * Add a call to htcpld_chip_set() to enable a bit that activates direction-pad interrupts on each call (the bit gets disabled each time a button is pushed) Signed-off-by: Cory Maccarrone Signed-off-by: Angelo Arrifano --- drivers/i2c/chips/i2c-htcpld.c | 101 ++++++++++++++++++++++------------------ 1 files changed, 56 insertions(+), 45 deletions(-) diff --git a/drivers/i2c/chips/i2c-htcpld.c b/drivers/i2c/chips/i2c-htcpld.c index 377ad4f..8fef7f1 100644 --- a/drivers/i2c/chips/i2c-htcpld.c +++ b/drivers/i2c/chips/i2c-htcpld.c @@ -5,6 +5,7 @@ * vibrator and other power devices. The cpld also returns buttons status * of the directional pads found on this HTC devices. * + * Copyright (C) 2009 Cory Maccarrone * Copyright (C) 2008 Angelo Arrifano * * This program is free software; you can redistribute it and/or modify @@ -54,14 +55,20 @@ static const unsigned char htcpld_btns_map[] = { struct htcpld_btns_dev { unsigned char keycode[ARRAY_SIZE(htcpld_btns_map)]; - char dpad[ARRAY_SIZE(htcpld_btns_map)]; - char dkbd[ARRAY_SIZE(htcpld_btns_map)]; + char keymap[ARRAY_SIZE(htcpld_btns_map)]; + char previous_keymap[ARRAY_SIZE(htcpld_btns_map)]; int irq; int poll_interval; struct input_dev *input; struct timer_list timer; }; +struct platform_device *htcpld_pdev = NULL; +void htcpld_btns_poll(unsigned long data); +void htcpld_btns_dowork(struct work_struct *data); + +DECLARE_DELAYED_WORK(htcpld_work, &htcpld_btns_dowork); + void htcpld_chip_set(u8 chip_addr, u8 val) { struct htcpld_chip_data *chip_data; @@ -185,73 +192,72 @@ static int htcpld_remove(struct i2c_client *client) static irqreturn_t htcpld_btns_irq(int irq, void *dev_id) { - struct platform_device *pdev = dev_id; - struct device *dev = &pdev->dev; + struct device *dev = &htcpld_pdev->dev; struct htcpld_btns_dev *bdev = dev_get_drvdata(dev); struct htcpld_btns_platform_data *pdata = dev->platform_data; - u8 dpad_status, dkbd_status; - int i; - - dkbd_status = _htcpld_chip_cache_read(htcpld_dkbd_chip); - dpad_status = _htcpld_chip_cache_read(htcpld_dpad_chip); - /* Send key press events */ - for (i=0; i> (7 - i)) & 0x01)) { - bdev->dpad[i] = 1; - input_report_key(bdev->input, bdev->keycode[i], 1); - } - if (!((dkbd_status >> (7 - i)) & 0x01)) { - bdev->dkbd[i] = 1; - input_report_key(bdev->input, bdev->keycode[i], 1); - } - } - input_sync(bdev->input); + /* Disable further IRQs until we process the one we just got */ + disable_irq(irq); - /* Poll for key release */ - /* FIXME: We really should disable this irq before polling */ - mod_timer(&bdev->timer, jiffies + pdata->poll_interval); + /* Set that an interrupt is pending so we can go process it */ + schedule_delayed_work(&htcpld_work, 0); return IRQ_HANDLED; } -static void htcpld_btns_poll(unsigned long data) +void htcpld_btns_dowork(struct work_struct *data) { + htcpld_btns_poll(0); +} + +void htcpld_btns_poll(unsigned long data) { - struct platform_device *pdev = (struct platform_device *)data; - struct device *dev = &pdev->dev; + struct device *dev = &htcpld_pdev->dev; struct htcpld_btns_dev *bdev = dev_get_drvdata(dev); struct htcpld_btns_platform_data *pdata = dev->platform_data; u8 dpad_status, dkbd_status; int i, we_continue = 0; - + dkbd_status = _htcpld_chip_cache_read(htcpld_dkbd_chip); dpad_status = _htcpld_chip_cache_read(htcpld_dpad_chip); - /* Send key release events */ + /* Check for changes in the maps */ for (i=0; idpad[i]) { - if ((dpad_status >> (7 - i)) & 0x01) { - bdev->dpad[i] = 0; - input_report_key(bdev->input, bdev->keycode[i], 0); - } else { - we_continue = 1; - } + /* Key up by default*/ + int val = 0; + + /* If any key is down, send the keycode for down. */ + if (!((dpad_status >> (7 - i)) & 0x01) || !((dkbd_status >> (7 - i)) & 0x01)) { + val = 1; + we_continue = 1; } - if (bdev->dkbd[i]) { - if ((dkbd_status >> (7 - i)) & 0x01) { - bdev->dkbd[i] = 0; - input_report_key(bdev->input, bdev->keycode[i], 0); - } else - we_continue = 1; + + bdev->keymap[i] = val; + } + + /* Send events */ + for (i = 0; i < ARRAY_SIZE(htcpld_btns_map); i++) { + if (bdev->keymap[i] != bdev->previous_keymap[i]) { + input_report_key(bdev->input, bdev->keycode[i], bdev->keymap[i]); + bdev->previous_keymap[i] = bdev->keymap[i]; } } + input_sync(bdev->input); + /* Copy the new state into the previous state */ + memcpy(bdev->previous_keymap, bdev->keymap, sizeof(bdev->keymap)); + if (we_continue) { /* Poll again */ - mod_timer(&bdev->timer, jiffies + pdata->poll_interval); + schedule_delayed_work(&htcpld_work, pdata->poll_interval); return; } + + /* Enable front DPAD interrupts */ + htcpld_chip_set(0x05, (htcpld_chip_get(0x05) | 0x80) & 0xfe); + + /* Reenable the IRQ */ + enable_irq(bdev->irq); } static int __devinit htcpld_btns_probe(struct platform_device *pdev) @@ -306,18 +312,23 @@ static int __devinit htcpld_btns_probe(struct platform_device *pdev) if (error) goto err_free_mem; - setup_timer(&bdev->timer, htcpld_btns_poll, (unsigned long)pdev); - error = request_irq(bdev->irq, htcpld_btns_irq, IRQF_TRIGGER_FALLING | IRQF_SAMPLE_RANDOM, pdev->name, pdev); if (error) goto err_unreg; + htcpld_pdev = pdev; + + memset(bdev->previous_keymap, 0, sizeof(bdev->keymap)); + printk("i2c-htcpld: HTC I2C CPLD Buttons\n"); printk("i2c-htcpld: Using IRQ: %d\n", bdev->irq); printk("i2c-htcpld: DKbd chip at 0x%x\n", htcpld_dkbd_chip->addr); printk("i2c-htcpld: DPad chip at 0x%x\n", htcpld_dpad_chip->addr); + /* Enable front DPAD interrupts */ + htcpld_chip_set(0x05, (htcpld_chip_get(0x05) | 0x80) & 0xfe); + return 0; err_unreg: -- 1.6.3.3