From patchwork Fri Apr 16 05:25:00 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Dmitry Torokhov X-Patchwork-Id: 93053 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by demeter.kernel.org (8.14.3/8.14.3) with ESMTP id o3G5OJFZ006469 for ; Fri, 16 Apr 2010 05:25:09 GMT Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752847Ab0DPFZI (ORCPT ); Fri, 16 Apr 2010 01:25:08 -0400 Received: from mail-gx0-f217.google.com ([209.85.217.217]:49361 "EHLO mail-gx0-f217.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752768Ab0DPFZG (ORCPT ); Fri, 16 Apr 2010 01:25:06 -0400 Received: by gxk9 with SMTP id 9so1309037gxk.8 for ; Thu, 15 Apr 2010 22:25:05 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=gamma; h=domainkey-signature:received:received:date:from:to:cc:subject :message-id:references:mime-version:content-type:content-disposition :in-reply-to:user-agent; bh=0DUV1jRmWIFTRhI/C4hoUx70DBP1RJdBwg030ejGAoo=; b=wyb7a/oy00+MhF5bJuBnwTkURVExAoDDopA4/jfRnv30jfsShglZTOF7UuE2dKP5fv 3n7FCF0NUjHke6XxaAw2ek/am1SGtgmHCVD2XtWc/sm526GR4R8eFHCzgTHlaLvPzMN9 NRhK5XcCdP3CkwrxBuGJul/BrbtsJE6QzsHBU= DomainKey-Signature: a=rsa-sha1; c=nofws; d=gmail.com; s=gamma; h=date:from:to:cc:subject:message-id:references:mime-version :content-type:content-disposition:in-reply-to:user-agent; b=gw5y+mn/Z5dcNABc6i2BPEGEyK/6nnogMUKUVIEhIz7IzTOPVn31yd0+8nXFns7BXH kuDv3OH7TGIP/iDBvJAKEdNHWKM4hb//gaO2CXIvG1mf7dgpr+lW/HGofMN19udcFlcx +9bau6kkUYOJmR7u6MyE+zcG0T2am6lnRGhxM= Received: by 10.151.92.9 with SMTP id u9mr1305953ybl.336.1271395505210; Thu, 15 Apr 2010 22:25:05 -0700 (PDT) Received: from mailhub.coreip.homeip.net (c-24-6-153-206.hsd1.ca.comcast.net [24.6.153.206]) by mx.google.com with ESMTPS id 6sm669509ywc.56.2010.04.15.22.25.03 (version=TLSv1/SSLv3 cipher=RC4-MD5); Thu, 15 Apr 2010 22:25:04 -0700 (PDT) Date: Thu, 15 Apr 2010 22:25:00 -0700 From: Dmitry Torokhov To: Sriramakrishnan Cc: linux-omap@vger.kernel.org, linux-input@vger.kernel.org Subject: Re: [PATCHv3 1/3] TCA6416 keypad : Implement keypad driver for keys interfaced to TCA6416 Message-ID: <20100416052500.GA1078@core.coreip.homeip.net> References: <1269357035-19754-1-git-send-email-srk@ti.com> <1269357035-19754-2-git-send-email-srk@ti.com> MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: <1269357035-19754-2-git-send-email-srk@ti.com> User-Agent: Mutt/1.5.20 (2009-08-17) Sender: linux-omap-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-omap@vger.kernel.org X-Greylist: IP, sender and recipient auto-whitelisted, not delayed by milter-greylist-4.2.3 (demeter.kernel.org [140.211.167.41]); Fri, 16 Apr 2010 05:25:09 +0000 (UTC) diff --git a/drivers/input/keyboard/tca6416-keypad.c b/drivers/input/keyboard/tca6416-keypad.c index 17df832..0943af3 100644 --- a/drivers/input/keyboard/tca6416-keypad.c +++ b/drivers/input/keyboard/tca6416-keypad.c @@ -10,16 +10,17 @@ * published by the Free Software Foundation. */ +#include #include #include +#include +#include +#include +#include #include #include #include #include -#include -#include -#include -#include #define TCA6416_INPUT 0 #define TCA6416_OUTPUT 1 @@ -43,79 +44,63 @@ struct tca6416_keypad_chip { uint16_t reg_input; struct i2c_client *client; - struct tca6416_drv_data *drv_data; + struct input_dev *input; struct delayed_work dwork; - uint16_t pinmask; + u16 pinmask; int irqnum; - int use_polling; + bool use_polling; + struct tca6416_button buttons[0]; }; -static int tca6416_write_reg(struct tca6416_keypad_chip *chip, int reg, - uint16_t val) +static int tca6416_write_reg(struct tca6416_keypad_chip *chip, int reg, u16 val) { - int ret; - - ret = i2c_smbus_write_word_data(chip->client, reg << 1, val); - - if (ret < 0) { - dev_err(&chip->client->dev, "failed writing register\n"); - return ret; + int error; + + error = i2c_smbus_write_word_data(chip->client, reg << 1, val); + if (error < 0) { + dev_err(&chip->client->dev, + "%s failed, reg: %d, val: %d, error: %d\n", + __func__, reg, val, error); + return error; } return 0; } -static int tca6416_read_reg(struct tca6416_keypad_chip *chip, int reg, - uint16_t *val) +static int tca6416_read_reg(struct tca6416_keypad_chip *chip, int reg, u16 *val) { - int ret; - - ret = i2c_smbus_read_word_data(chip->client, reg << 1); + int retval; - if (ret < 0) { - dev_err(&chip->client->dev, "failed reading register\n"); - return ret; + retval = i2c_smbus_read_word_data(chip->client, reg << 1); + if (retval < 0) { + dev_err(&chip->client->dev, "%s failed, reg: %d, error: %d\n", + __func__, reg, retval); + return retval; } - *val = (uint16_t)ret; + *val = (u16)retval; return 0; } -static irqreturn_t tca6416_keys_isr(int irq, void *dev_id) -{ - struct tca6416_keypad_chip *chip = - (struct tca6416_keypad_chip *) dev_id; - - disable_irq(irq); - schedule_delayed_work(&chip->dwork, 0); - return IRQ_HANDLED; - -} - -static void tca6416_keys_work_func(struct work_struct *workstruct) +static void tca6416_keys_scan(struct tca6416_keypad_chip *chip) { - struct delayed_work *delay_work = - container_of(workstruct, struct delayed_work, work); - struct tca6416_keypad_chip *chip = - container_of(delay_work, struct tca6416_keypad_chip, dwork); - struct tca6416_drv_data *ddata = chip->drv_data; - uint16_t reg_val, val; - int ret, i, pin_index; + struct input_dev *input = chip->input; + u16 reg_val, val; + int error, i, pin_index; - ret = tca6416_read_reg(chip, TCA6416_INPUT, ®_val); - if (ret) + error = tca6416_read_reg(chip, TCA6416_INPUT, ®_val); + if (error) return; reg_val &= chip->pinmask; /* Figure out which lines have changed */ - val = reg_val ^ (chip->reg_input); + val = reg_val ^ chip->reg_input; chip->reg_input = reg_val; for (i = 0, pin_index = 0; i < 16; i++) { if (val & (1 << i)) { - struct tca6416_button *button = &ddata->data[pin_index]; - struct input_dev *input = ddata->input; + struct tca6416_button *button = &chip->buttons[pin_index]; unsigned int type = button->type ?: EV_KEY; int state = ((reg_val & (1 << i)) ? 1 : 0) ^ button->active_low; @@ -127,97 +112,128 @@ static void tca6416_keys_work_func(struct work_struct *workstruct) if (chip->pinmask & (1 << i)) pin_index++; } +} + +/* + * This is threaded IRQ handler and this can (and will) sleep. + */ +static irqreturn_t tca6416_keys_isr(int irq, void *dev_id) +{ + struct tca6416_keypad_chip *chip = dev_id; + + tca6416_keys_scan(chip); + + return IRQ_HANDLED; +} + +static void tca6416_keys_work_func(struct work_struct *work) +{ + struct tca6416_keypad_chip *chip = + container_of(work, struct tca6416_keypad_chip, dwork.work); + + tca6416_keys_scan(chip); + schedule_delayed_work(&chip->dwork, msecs_to_jiffies(100)); +} + +static int tca6416_keys_open(struct input_dev *dev) +{ + struct tca6416_keypad_chip *chip = input_get_drvdata(dev); + + /* Get initial device state in case it has switches */ + tca6416_keys_scan(chip); if (chip->use_polling) schedule_delayed_work(&chip->dwork, msecs_to_jiffies(100)); else enable_irq(chip->irqnum); + return 0; +} + +static void tca6416_keys_close(struct input_dev *dev) +{ + struct tca6416_keypad_chip *chip = input_get_drvdata(dev); + + if (chip->use_polling) + cancel_delayed_work_sync(&chip->dwork); + else + free_irq(chip->irqnum, chip); } +static int __devinit tca6416_setup_registers(struct tca6416_keypad_chip *chip) +{ + int error; + + error = tca6416_read_reg(chip, TCA6416_OUTPUT, &chip->reg_output); + if (error) + return error; + + error = tca6416_read_reg(chip, TCA6416_DIRECTION, &chip->reg_direction); + if (error) + return error; + + /* ensure that keypad pins are set to input */ + error = tca6416_write_reg(chip, TCA6416_DIRECTION, + chip->reg_direction | chip->pinmask); + if (error) + return error; + + error = tca6416_read_reg(chip, TCA6416_DIRECTION, &chip->reg_direction); + if (error) + return error; + + error = tca6416_read_reg(chip, TCA6416_INPUT, &chip->reg_input); + if (error) + return error; + + return 0; +} static int __devinit tca6416_keypad_probe(struct i2c_client *client, const struct i2c_device_id *id) { struct tca6416_keys_platform_data *pdata; struct tca6416_keypad_chip *chip; - struct tca6416_drv_data *ddata; struct input_dev *input; - int i, ret, pin_index, err; - uint16_t reg_val; + int error; + int i; /* Check functionality */ - err = i2c_check_functionality(client->adapter, - I2C_FUNC_SMBUS_BYTE); - if (!err) { + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE)) { dev_err(&client->dev, "%s adapter not supported\n", dev_driver_string(&client->adapter->dev)); return -ENODEV; } - - chip = kzalloc(sizeof(struct tca6416_keypad_chip), GFP_KERNEL); - if (chip == NULL) - return -ENOMEM; - pdata = client->dev.platform_data; - if (pdata == NULL) { + if (!pdata) { dev_dbg(&client->dev, "no platform data\n"); - ret = -EINVAL; + return -EINVAL; + } + + chip = kzalloc(sizeof(struct tca6416_keypad_chip) + + pdata->nbuttons * sizeof(struct tca6416_button), + GFP_KERNEL); + input = input_allocate_device(); + if (!chip || !input) { + error = -ENOMEM; goto fail1; } chip->client = client; + chip->input = input; chip->pinmask = pdata->pinmask; + chip->use_polling = pdata->use_polling; - /* initialize cached registers from their original values. - * we can't share this chip with another i2c master. - */ - ret = tca6416_read_reg(chip, TCA6416_OUTPUT, &chip->reg_output); - if (ret) - goto fail1; - - ret = tca6416_read_reg(chip, TCA6416_DIRECTION, &chip->reg_direction); - if (ret) - goto fail1; - - /* ensure that keypad pins are set to input */ - reg_val = chip->reg_direction | chip->pinmask; - ret = tca6416_write_reg(chip, TCA6416_DIRECTION, reg_val); - if (ret) - goto fail1; - - ret = tca6416_read_reg(chip, TCA6416_DIRECTION, &chip->reg_direction); - if (ret) - goto fail1; - - ret = tca6416_read_reg(chip, TCA6416_INPUT, &chip->reg_input); - if (ret) - goto fail1; - - i2c_set_clientdata(client, chip); - - - ddata = kzalloc(sizeof(struct tca6416_drv_data) + - pdata->nbuttons * sizeof(struct tca6416_button), - GFP_KERNEL); - if (!ddata) { - ret = -ENOMEM; - goto fail1; - } - - input = input_allocate_device(); - if (!input) { - dev_dbg(&client->dev, "failed to allocate state\n"); - ret = -ENOMEM; - kfree(ddata); - goto fail2; - } + INIT_DELAYED_WORK(&chip->dwork, tca6416_keys_work_func); input->phys = "tca6416-keys/input0"; input->name = client->name; input->dev.parent = &client->dev; + input->open = tca6416_keys_open; + input->close = tca6416_keys_close; + input->id.bustype = BUS_HOST; input->id.vendor = 0x0001; input->id.product = 0x0001; @@ -227,20 +243,23 @@ static int __devinit tca6416_keypad_probe(struct i2c_client *client, if (pdata->rep) __set_bit(EV_REP, input->evbit); - ddata->input = input; - for (i = 0; i < pdata->nbuttons; i++) { unsigned int type; - ddata->data[i] = pdata->buttons[i]; + chip->buttons[i] = pdata->buttons[i]; type = (pdata->buttons[i].type) ?: EV_KEY; - input_set_capability(input, type, (pdata->buttons[i].code)); + input_set_capability(input, type, pdata->buttons[i].code); } - chip->drv_data = ddata; - chip->use_polling = pdata->use_polling; + input_set_drvdata(input, chip); - INIT_DELAYED_WORK(&chip->dwork, tca6416_keys_work_func); + /* + * Initialize cached registers from their original values. + * we can't share this chip with another i2c master. + */ + error = tca6416_setup_registers(chip); + if (error) + goto fail1; if (!chip->use_polling) { if (pdata->irq_is_gpio) @@ -248,81 +267,55 @@ static int __devinit tca6416_keypad_probe(struct i2c_client *client, else chip->irqnum = client->irq; - ret = request_irq(chip->irqnum, tca6416_keys_isr, - IRQF_SHARED | IRQF_TRIGGER_FALLING , - "tca6416-keypad", chip); - if (ret) { + error = request_threaded_irq(chip->irqnum, NULL, + tca6416_keys_isr, + IRQF_TRIGGER_FALLING, + "tca6416-keypad", chip); + if (error) { dev_dbg(&client->dev, "Unable to claim irq %d; error %d\n", - chip->irqnum, ret); - goto fail3; + chip->irqnum, error); + goto fail1; } disable_irq(chip->irqnum); } - ret = input_register_device(input); - if (ret) { - dev_dbg(&client->dev, "Unable to register input device, " - "error: %d\n", ret); - goto fail4; - } - - /* get current state of buttons */ - - ret = tca6416_read_reg(chip, TCA6416_INPUT, ®_val); - if (ret) - goto fail5; - - chip->reg_input = reg_val & chip->pinmask; - - for (i = 0, pin_index = 0; i < 16; i++) { - if (chip->pinmask & (1 << i)) { - struct tca6416_button *button = &ddata->data[pin_index]; - unsigned int type = button->type ?: EV_KEY; - int state = ((reg_val & (1 << i)) ? 1 : 0) - ^ button->active_low; - - input_event(input, type, button->code, !!state); - input_sync(input); - pin_index++; - } + error = input_register_device(input); + if (error) { + dev_dbg(&client->dev, + "Unable to register input device, error: %d\n", error); + goto fail2; } - input_sync(input); - if (chip->use_polling) - schedule_delayed_work(&chip->dwork, msecs_to_jiffies(100)); - else - enable_irq(chip->irqnum); + i2c_set_clientdata(client, chip); return 0; -fail5: - input_unregister_device(input); -fail4: - if (!chip->use_polling) - free_irq(chip->irqnum, chip); -fail3: - input_free_device(input); fail2: - kfree(ddata); + if (!chip->use_polling) { + free_irq(chip->irqnum, chip); + enable_irq(chip->irqnum); + } fail1: + input_free_device(input); kfree(chip); - return ret; + return error; } -static int tca6416_keypad_remove(struct i2c_client *client) +static int __devexit tca6416_keypad_remove(struct i2c_client *client) { struct tca6416_keypad_chip *chip = i2c_get_clientdata(client); - struct tca6416_drv_data *ddata = chip->drv_data; - struct input_dev *input = ddata->input; - if (!chip->use_polling) + if (!chip->use_polling) { free_irq(chip->irqnum, chip); - cancel_delayed_work_sync(&chip->dwork); - input_unregister_device(input); - input_free_device(input); - kfree(ddata); + enable_irq(chip->irqnum); + } + + input_unregister_device(chip->input); kfree(chip); + + i2c_set_clientdata(client, NULL); + return 0; } @@ -332,7 +325,7 @@ static struct i2c_driver tca6416_keypad_driver = { .name = "tca6416-keypad", }, .probe = tca6416_keypad_probe, - .remove = tca6416_keypad_remove, + .remove = __devexit_p(tca6416_keypad_remove), .id_table = tca6416_id, };