diff options
author | Roshni Shah <shah.roshni@yahoo.com> | 2011-03-14 06:49:42 -0400 |
---|---|---|
committer | Justin Waters <justin.waters@timesys.com> | 2012-03-02 16:59:46 -0500 |
commit | 2731b2eadeaa141e6f305fa8086106608112bbaa (patch) | |
tree | 2702d45bac84073cd580ccb1bd3eafb9a000d3b6 /drivers/input/keyboard/mpr121.c | |
parent | 6d23f5084c975be637f7d748db82116bf84d3872 (diff) |
Add support for the i.MX53 QSB2.6.35.3-mx53-early-201103141049
This patch seems to have originated from the 11.01.00 release
from Freescale, which is no longer available except through the
gitweb interface from Freescale.
http://opensource.freescale.com/git?p=imx/linux-2.6-imx.git;a=commit;h=27fdf7bae11978d21e8aba09bb635f49b07edd4a
Diffstat (limited to 'drivers/input/keyboard/mpr121.c')
-rw-r--r-- | drivers/input/keyboard/mpr121.c | 305 |
1 files changed, 305 insertions, 0 deletions
diff --git a/drivers/input/keyboard/mpr121.c b/drivers/input/keyboard/mpr121.c new file mode 100644 index 000000000000..6a723235805d --- /dev/null +++ b/drivers/input/keyboard/mpr121.c @@ -0,0 +1,305 @@ +/* + * Copyright (C) 2010-2011 Freescale Semiconductor, Inc. All Rights Reserved. + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +/* + * mpr121.c - Touchkey driver for Freescale MPR121 Capacitive Touch + * Sensor Controllor + */ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/i2c.h> +#include <linux/i2c/mpr.h> +#include <linux/interrupt.h> +#include <linux/input.h> +#include <linux/irq.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/bitops.h> + +struct mpr121_touchkey_data { + struct i2c_client *client; + struct input_dev *input_dev; + unsigned int key_val; + int statusbits; + int keycount; + u16 keycodes[MPR121_MAX_KEY_COUNT]; +}; + +struct mpr121_init_register { + int addr; + u8 val; +}; + +static struct mpr121_init_register init_reg_table[] = { + {MHD_RISING_ADDR, 0x1}, + {NHD_RISING_ADDR, 0x1}, + {NCL_RISING_ADDR, 0x0}, + {FDL_RISING_ADDR, 0x0}, + {MHD_FALLING_ADDR, 0x1}, + {NHD_FALLING_ADDR, 0x1}, + {NCL_FALLING_ADDR, 0xff}, + {FDL_FALLING_ADDR, 0x02}, + {FILTER_CONF_ADDR, 0x04}, + {AFE_CONF_ADDR, 0x0b}, + {AUTO_CONFIG_CTRL_ADDR, 0x0b}, +}; + +static irqreturn_t mpr_touchkey_interrupt(int irq, void *dev_id) +{ + struct mpr121_touchkey_data *data = dev_id; + struct i2c_client *client = data->client; + struct input_dev *input = data->input_dev; + unsigned int key_num, pressed; + int reg; + + reg = i2c_smbus_read_byte_data(client, ELE_TOUCH_STATUS_1_ADDR); + if (reg < 0) { + dev_err(&client->dev, "i2c read error [%d]\n", reg); + goto out; + } + + reg <<= 8; + reg |= i2c_smbus_read_byte_data(client, ELE_TOUCH_STATUS_0_ADDR); + if (reg < 0) { + dev_err(&client->dev, "i2c read error [%d]\n", reg); + goto out; + } + + reg &= TOUCH_STATUS_MASK; + /* use old press bit to figure out which bit changed */ + key_num = ffs(reg ^ data->statusbits) - 1; + /* use the bit check the press status */ + pressed = (reg & (1 << (key_num))) >> key_num; + data->statusbits = reg; + data->key_val = data->keycodes[key_num]; + + input_report_key(input, data->key_val, pressed); + input_sync(input); + + dev_dbg(&client->dev, "key %d %d %s\n", key_num, data->key_val, + pressed ? "pressed" : "released"); + +out: + return IRQ_HANDLED; +} + +static int mpr121_phys_init(struct mpr121_platform_data *pdata, + struct mpr121_touchkey_data *data, + struct i2c_client *client) +{ + struct mpr121_init_register *reg; + unsigned char usl, lsl, tl; + int i, t, vdd, ret; + + /* setup touch/release threshold for ele0-ele11 */ + for (i = 0; i <= MPR121_MAX_KEY_COUNT; i++) { + t = ELE0_TOUCH_THRESHOLD_ADDR + (i * 2); + ret = i2c_smbus_write_byte_data(client, t, TOUCH_THRESHOLD); + if (ret < 0) + goto err_i2c_write; + ret = i2c_smbus_write_byte_data(client, t + 1, + RELEASE_THRESHOLD); + if (ret < 0) + goto err_i2c_write; + } + /* setup init register */ + for (i = 0; i < ARRAY_SIZE(init_reg_table); i++) { + reg = &init_reg_table[i]; + ret = i2c_smbus_write_byte_data(client, reg->addr, reg->val); + if (ret < 0) + goto err_i2c_write; + } + /* setup auto-register by vdd,the formula please ref:AN3889 */ + vdd = pdata->vdd_uv / 1000; + usl = ((vdd - 700) * 256) / vdd; + lsl = (usl * 65) / 100; + tl = (usl * 90) / 100; + ret = i2c_smbus_write_byte_data(client, AUTO_CONFIG_USL_ADDR, usl); + if (ret < 0) + goto err_i2c_write; + ret = i2c_smbus_write_byte_data(client, AUTO_CONFIG_LSL_ADDR, lsl); + if (ret < 0) + goto err_i2c_write; + ret = i2c_smbus_write_byte_data(client, AUTO_CONFIG_TL_ADDR, tl); + if (ret < 0) + goto err_i2c_write; + ret = i2c_smbus_write_byte_data(client, ELECTRODE_CONF_ADDR, + data->keycount); + if (ret < 0) + goto err_i2c_write; + + dev_info(&client->dev, "mpr121: config as enable %x of electrode.\n", + data->keycount); + + return 0; + +err_i2c_write: + dev_err(&client->dev, "i2c write error[%d]\n", ret); + return ret; +} + +static int __devinit mpr_touchkey_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct mpr121_platform_data *pdata; + struct mpr121_touchkey_data *data; + struct input_dev *input_dev; + int error; + int i; + + pdata = client->dev.platform_data; + if (!pdata) { + dev_err(&client->dev, "no platform data defined\n"); + return -EINVAL; + } + + data = kzalloc(sizeof(struct mpr121_touchkey_data), GFP_KERNEL); + input_dev = input_allocate_device(); + if (!data || !input_dev) { + dev_err(&client->dev, "Falied to allocate memory\n"); + error = -ENOMEM; + goto err_free_mem; + } + + data->client = client; + data->input_dev = input_dev; + data->keycount = pdata->keycount; + + if (data->keycount > MPR121_MAX_KEY_COUNT) { + dev_err(&client->dev, "Too many key defined\n"); + error = -EINVAL; + goto err_free_mem; + } + + error = mpr121_phys_init(pdata, data, client); + if (error < 0) { + dev_err(&client->dev, "Failed to init register\n"); + goto err_free_mem; + } + + i2c_set_clientdata(client, data); + + input_dev->name = "FSL MPR121 Touchkey"; + input_dev->id.bustype = BUS_I2C; + input_dev->dev.parent = &client->dev; + input_dev->keycode = pdata->matrix; + input_dev->keycodesize = sizeof(pdata->matrix[0]); + input_dev->keycodemax = data->keycount; + + for (i = 0; i < input_dev->keycodemax; i++) { + __set_bit(pdata->matrix[i], input_dev->keybit); + data->keycodes[i] = pdata->matrix[i]; + input_set_capability(input_dev, EV_KEY, pdata->matrix[i]); + } + input_set_drvdata(input_dev, data); + + error = request_threaded_irq(client->irq, NULL, + mpr_touchkey_interrupt, + IRQF_TRIGGER_FALLING, + client->dev.driver->name, data); + if (error) { + dev_err(&client->dev, "Failed to register interrupt\n"); + goto err_free_mem; + } + + error = input_register_device(input_dev); + if (error) + goto err_free_irq; + i2c_set_clientdata(client, data); + device_init_wakeup(&client->dev, pdata->wakeup); + dev_info(&client->dev, "Mpr121 touch keyboard init success.\n"); + return 0; + +err_free_irq: + free_irq(client->irq, data); +err_free_mem: + input_free_device(input_dev); + kfree(data); + return error; +} + +static int __devexit mpr_touchkey_remove(struct i2c_client *client) +{ + struct mpr121_touchkey_data *data = i2c_get_clientdata(client); + + free_irq(client->irq, data); + input_unregister_device(data->input_dev); + kfree(data); + + return 0; +} + +#ifdef CONFIG_PM +static int mpr_suspend(struct i2c_client *client, pm_message_t mesg) +{ + if (device_may_wakeup(&client->dev)) + enable_irq_wake(client->irq); + + i2c_smbus_write_byte_data(client, ELECTRODE_CONF_ADDR, 0x00); + + return 0; +} + +static int mpr_resume(struct i2c_client *client) +{ + struct mpr121_touchkey_data *data = i2c_get_clientdata(client); + + if (device_may_wakeup(&client->dev)) + disable_irq_wake(client->irq); + + i2c_smbus_write_byte_data(client, ELECTRODE_CONF_ADDR, data->keycount); + + return 0; +} +#else +static int mpr_suspend(struct i2c_client *client, pm_message_t mesg) {} +static int mpr_resume(struct i2c_client *client) {} +#endif + +static const struct i2c_device_id mpr121_id[] = { + {"mpr121_touchkey", 0}, + { } +}; + +static struct i2c_driver mpr_touchkey_driver = { + .driver = { + .name = "mpr121", + .owner = THIS_MODULE, + }, + .id_table = mpr121_id, + .probe = mpr_touchkey_probe, + .remove = __devexit_p(mpr_touchkey_remove), + .suspend = mpr_suspend, + .resume = mpr_resume, +}; + +static int __init mpr_touchkey_init(void) +{ + return i2c_add_driver(&mpr_touchkey_driver); +} + +static void __exit mpr_touchkey_exit(void) +{ + i2c_del_driver(&mpr_touchkey_driver); +} + +module_init(mpr_touchkey_init); +module_exit(mpr_touchkey_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("Touch Key driver for FSL MPR121 Chip"); |