Path: blob/master/drivers/input/keyboard/mcs_touchkey.c
15111 views
/*1* Touchkey driver for MELFAS MCS5000/5080 controller2*3* Copyright (C) 2010 Samsung Electronics Co.Ltd4* Author: HeungJun Kim <[email protected]>5* Author: Joonyoung Shim <[email protected]>6*7* This program is free software; you can redistribute it and/or modify it8* under the terms of the GNU General Public License as published by the9* Free Software Foundation; either version 2 of the License, or (at your10* option) any later version.11*/1213#include <linux/module.h>14#include <linux/init.h>15#include <linux/i2c.h>16#include <linux/i2c/mcs.h>17#include <linux/interrupt.h>18#include <linux/input.h>19#include <linux/irq.h>20#include <linux/slab.h>21#include <linux/pm.h>2223/* MCS5000 Touchkey */24#define MCS5000_TOUCHKEY_STATUS 0x0425#define MCS5000_TOUCHKEY_STATUS_PRESS 726#define MCS5000_TOUCHKEY_FW 0x0a27#define MCS5000_TOUCHKEY_BASE_VAL 0x612829/* MCS5080 Touchkey */30#define MCS5080_TOUCHKEY_STATUS 0x0031#define MCS5080_TOUCHKEY_STATUS_PRESS 332#define MCS5080_TOUCHKEY_FW 0x0133#define MCS5080_TOUCHKEY_BASE_VAL 0x13435enum mcs_touchkey_type {36MCS5000_TOUCHKEY,37MCS5080_TOUCHKEY,38};3940struct mcs_touchkey_chip {41unsigned int status_reg;42unsigned int pressbit;43unsigned int press_invert;44unsigned int baseval;45};4647struct mcs_touchkey_data {48void (*poweron)(bool);4950struct i2c_client *client;51struct input_dev *input_dev;52struct mcs_touchkey_chip chip;53unsigned int key_code;54unsigned int key_val;55unsigned short keycodes[];56};5758static irqreturn_t mcs_touchkey_interrupt(int irq, void *dev_id)59{60struct mcs_touchkey_data *data = dev_id;61struct mcs_touchkey_chip *chip = &data->chip;62struct i2c_client *client = data->client;63struct input_dev *input = data->input_dev;64unsigned int key_val;65unsigned int pressed;66int val;6768val = i2c_smbus_read_byte_data(client, chip->status_reg);69if (val < 0) {70dev_err(&client->dev, "i2c read error [%d]\n", val);71goto out;72}7374pressed = (val & (1 << chip->pressbit)) >> chip->pressbit;75if (chip->press_invert)76pressed ^= chip->press_invert;7778/* key_val is 0 when released, so we should use key_val of press. */79if (pressed) {80key_val = val & (0xff >> (8 - chip->pressbit));81if (!key_val)82goto out;83key_val -= chip->baseval;84data->key_code = data->keycodes[key_val];85data->key_val = key_val;86}8788input_event(input, EV_MSC, MSC_SCAN, data->key_val);89input_report_key(input, data->key_code, pressed);90input_sync(input);9192dev_dbg(&client->dev, "key %d %d %s\n", data->key_val, data->key_code,93pressed ? "pressed" : "released");9495out:96return IRQ_HANDLED;97}9899static int __devinit mcs_touchkey_probe(struct i2c_client *client,100const struct i2c_device_id *id)101{102const struct mcs_platform_data *pdata;103struct mcs_touchkey_data *data;104struct input_dev *input_dev;105unsigned int fw_reg;106int fw_ver;107int error;108int i;109110pdata = client->dev.platform_data;111if (!pdata) {112dev_err(&client->dev, "no platform data defined\n");113return -EINVAL;114}115116data = kzalloc(sizeof(struct mcs_touchkey_data) +117sizeof(data->keycodes[0]) * (pdata->key_maxval + 1),118GFP_KERNEL);119input_dev = input_allocate_device();120if (!data || !input_dev) {121dev_err(&client->dev, "Failed to allocate memory\n");122error = -ENOMEM;123goto err_free_mem;124}125126data->client = client;127data->input_dev = input_dev;128129if (id->driver_data == MCS5000_TOUCHKEY) {130data->chip.status_reg = MCS5000_TOUCHKEY_STATUS;131data->chip.pressbit = MCS5000_TOUCHKEY_STATUS_PRESS;132data->chip.baseval = MCS5000_TOUCHKEY_BASE_VAL;133fw_reg = MCS5000_TOUCHKEY_FW;134} else {135data->chip.status_reg = MCS5080_TOUCHKEY_STATUS;136data->chip.pressbit = MCS5080_TOUCHKEY_STATUS_PRESS;137data->chip.press_invert = 1;138data->chip.baseval = MCS5080_TOUCHKEY_BASE_VAL;139fw_reg = MCS5080_TOUCHKEY_FW;140}141142fw_ver = i2c_smbus_read_byte_data(client, fw_reg);143if (fw_ver < 0) {144error = fw_ver;145dev_err(&client->dev, "i2c read error[%d]\n", error);146goto err_free_mem;147}148dev_info(&client->dev, "Firmware version: %d\n", fw_ver);149150input_dev->name = "MELPAS MCS Touchkey";151input_dev->id.bustype = BUS_I2C;152input_dev->dev.parent = &client->dev;153input_dev->evbit[0] = BIT_MASK(EV_KEY);154if (!pdata->no_autorepeat)155input_dev->evbit[0] |= BIT_MASK(EV_REP);156input_dev->keycode = data->keycodes;157input_dev->keycodesize = sizeof(data->keycodes[0]);158input_dev->keycodemax = pdata->key_maxval + 1;159160for (i = 0; i < pdata->keymap_size; i++) {161unsigned int val = MCS_KEY_VAL(pdata->keymap[i]);162unsigned int code = MCS_KEY_CODE(pdata->keymap[i]);163164data->keycodes[val] = code;165__set_bit(code, input_dev->keybit);166}167168input_set_capability(input_dev, EV_MSC, MSC_SCAN);169input_set_drvdata(input_dev, data);170171if (pdata->cfg_pin)172pdata->cfg_pin();173174if (pdata->poweron) {175data->poweron = pdata->poweron;176data->poweron(true);177}178179error = request_threaded_irq(client->irq, NULL, mcs_touchkey_interrupt,180IRQF_TRIGGER_FALLING, client->dev.driver->name, data);181if (error) {182dev_err(&client->dev, "Failed to register interrupt\n");183goto err_free_mem;184}185186error = input_register_device(input_dev);187if (error)188goto err_free_irq;189190i2c_set_clientdata(client, data);191return 0;192193err_free_irq:194free_irq(client->irq, data);195err_free_mem:196input_free_device(input_dev);197kfree(data);198return error;199}200201static int __devexit mcs_touchkey_remove(struct i2c_client *client)202{203struct mcs_touchkey_data *data = i2c_get_clientdata(client);204205free_irq(client->irq, data);206if (data->poweron)207data->poweron(false);208input_unregister_device(data->input_dev);209kfree(data);210211return 0;212}213214static void mcs_touchkey_shutdown(struct i2c_client *client)215{216struct mcs_touchkey_data *data = i2c_get_clientdata(client);217218if (data->poweron)219data->poweron(false);220}221222#ifdef CONFIG_PM_SLEEP223static int mcs_touchkey_suspend(struct device *dev)224{225struct mcs_touchkey_data *data = dev_get_drvdata(dev);226struct i2c_client *client = data->client;227228/* Disable the work */229disable_irq(client->irq);230231/* Finally turn off the power */232if (data->poweron)233data->poweron(false);234235return 0;236}237238static int mcs_touchkey_resume(struct device *dev)239{240struct mcs_touchkey_data *data = dev_get_drvdata(dev);241struct i2c_client *client = data->client;242243/* Enable the device first */244if (data->poweron)245data->poweron(true);246247/* Enable irq again */248enable_irq(client->irq);249250return 0;251}252#endif253254static SIMPLE_DEV_PM_OPS(mcs_touchkey_pm_ops,255mcs_touchkey_suspend, mcs_touchkey_resume);256257static const struct i2c_device_id mcs_touchkey_id[] = {258{ "mcs5000_touchkey", MCS5000_TOUCHKEY },259{ "mcs5080_touchkey", MCS5080_TOUCHKEY },260{ }261};262MODULE_DEVICE_TABLE(i2c, mcs_touchkey_id);263264static struct i2c_driver mcs_touchkey_driver = {265.driver = {266.name = "mcs_touchkey",267.owner = THIS_MODULE,268.pm = &mcs_touchkey_pm_ops,269},270.probe = mcs_touchkey_probe,271.remove = __devexit_p(mcs_touchkey_remove),272.shutdown = mcs_touchkey_shutdown,273.id_table = mcs_touchkey_id,274};275276static int __init mcs_touchkey_init(void)277{278return i2c_add_driver(&mcs_touchkey_driver);279}280281static void __exit mcs_touchkey_exit(void)282{283i2c_del_driver(&mcs_touchkey_driver);284}285286module_init(mcs_touchkey_init);287module_exit(mcs_touchkey_exit);288289/* Module information */290MODULE_AUTHOR("Joonyoung Shim <[email protected]>");291MODULE_AUTHOR("HeungJun Kim <[email protected]>");292MODULE_DESCRIPTION("Touchkey driver for MELFAS MCS5000/5080 controller");293MODULE_LICENSE("GPL");294295296