summaryrefslogtreecommitdiff
path: root/drivers/input
diff options
context:
space:
mode:
authorZhang Jiejing <jiejing.zhang@freescale.com>2011-01-05 11:37:12 +0800
committerJason Liu <r64343@freescale.com>2012-07-20 13:10:16 +0800
commit3d6df22ad54a14bc8cebb7753c36f7b3cd811665 (patch)
treeb1d1e5d8f986d535ee8c5b21415e8c863425b513 /drivers/input
parent82e69e6fc05fe9075dc9312cf2fdd3fe38d34ff5 (diff)
ENGR00137602-1 input: FSL MPR121 capacitive touch button.
This button driver is based on Freescale MPR121 capacitive touch sensor controller. It can support 12 elements maximal. The chip is use i2c interface. You can find all the data sheet reference in code by google the name. Signed-off-by: Zhang Jiejing <jiejing.zhang@freescale.com>
Diffstat (limited to 'drivers/input')
-rw-r--r--drivers/input/keyboard/Kconfig37
-rw-r--r--drivers/input/keyboard/Makefile6
-rw-r--r--drivers/input/keyboard/mpr121.c308
3 files changed, 351 insertions, 0 deletions
diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
index b4dee9d5a055..b9474aa7f043 100644
--- a/drivers/input/keyboard/Kconfig
+++ b/drivers/input/keyboard/Kconfig
@@ -563,4 +563,41 @@ config KEYBOARD_W90P910
To compile this driver as a module, choose M here: the
module will be called w90p910_keypad.
+config KEYBOARD_MXC
+ tristate "MXC Keypad Driver"
+ depends on ARCH_MXC
+ help
+ This is the Keypad driver for the Freescale MXC application
+ processors.
+
+config KEYBOARD_MXS
+ tristate "MXS keyboard"
+ depends on ARCH_MXS
+ help
+ This is the Keypad driver for the Freescale mxs soc
+
+
+config KEYBOARD_MC9S08DZ60
+ tristate "mc9s08dz60 keyboard"
+ depends on MXC_PMIC_MC9S08DZ60
+ help
+ -to be written-
+
+config KEYBOARD_MPR084
+ tristate "Freescale MPR084 Touch Keypad Driver"
+ depends on ARCH_MX37
+ help
+ This is the Keypad driver for the Freescale Proximity Capacitive
+ Touch Sensor controller chip.
+
+config KEYBOARD_MPR121
+ tristate "Freescale MPR121 Touch Keypad Driver"
+ depends on I2C
+ help
+ Say Y here if you have the touchkey Freescale Proximity Capacitive
+ Touch Sensor controller chip in your system.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here
endif
diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
index ddde0fd476f7..6791e8b45289 100644
--- a/drivers/input/keyboard/Makefile
+++ b/drivers/input/keyboard/Makefile
@@ -51,3 +51,9 @@ obj-$(CONFIG_KEYBOARD_TNETV107X) += tnetv107x-keypad.o
obj-$(CONFIG_KEYBOARD_TWL4030) += twl4030_keypad.o
obj-$(CONFIG_KEYBOARD_XTKBD) += xtkbd.o
obj-$(CONFIG_KEYBOARD_W90P910) += w90p910_keypad.o
+obj-$(CONFIG_KEYBOARD_MXC) += mxc_keyb.o
+obj-$(CONFIG_KEYBOARD_MXC) += mxc_pwrkey.o
+obj-$(CONFIG_KEYBOARD_MPR084) += mpr084.o
+obj-$(CONFIG_KEYBOARD_MXS) += mxs-kbd.o
+obj-$(CONFIG_KEYBOARD_MC9S08DZ60) += mc9s08dz60_keyb.o
+obj-$(CONFIG_KEYBOARD_MPR121) += mpr121.o
diff --git a/drivers/input/keyboard/mpr121.c b/drivers/input/keyboard/mpr121.c
new file mode 100644
index 000000000000..7aae302758c5
--- /dev/null
+++ b/drivers/input/keyboard/mpr121.c
@@ -0,0 +1,308 @@
+/*
+ * 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_event(input, EV_MSC, MSC_SCAN, data->key_val);
+ 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_MSC, MSC_SCAN);
+ 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");