12874c5fdSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2312e8e8aSJoonyoung Shim /*
3adf779c1SHeungjun Kim * Touchkey driver for MELFAS MCS5000/5080 controller
4312e8e8aSJoonyoung Shim *
5312e8e8aSJoonyoung Shim * Copyright (C) 2010 Samsung Electronics Co.Ltd
6312e8e8aSJoonyoung Shim * Author: HeungJun Kim <riverful.kim@samsung.com>
7312e8e8aSJoonyoung Shim * Author: Joonyoung Shim <jy0922.shim@samsung.com>
8312e8e8aSJoonyoung Shim */
9312e8e8aSJoonyoung Shim
10312e8e8aSJoonyoung Shim #include <linux/module.h>
11312e8e8aSJoonyoung Shim #include <linux/i2c.h>
12312e8e8aSJoonyoung Shim #include <linux/interrupt.h>
13312e8e8aSJoonyoung Shim #include <linux/input.h>
14312e8e8aSJoonyoung Shim #include <linux/irq.h>
15312e8e8aSJoonyoung Shim #include <linux/slab.h>
168cd9ab9eSWolfram Sang #include <linux/platform_data/mcs.h>
17adf779c1SHeungjun Kim #include <linux/pm.h>
18312e8e8aSJoonyoung Shim
19312e8e8aSJoonyoung Shim /* MCS5000 Touchkey */
20312e8e8aSJoonyoung Shim #define MCS5000_TOUCHKEY_STATUS 0x04
21312e8e8aSJoonyoung Shim #define MCS5000_TOUCHKEY_STATUS_PRESS 7
22312e8e8aSJoonyoung Shim #define MCS5000_TOUCHKEY_FW 0x0a
23312e8e8aSJoonyoung Shim #define MCS5000_TOUCHKEY_BASE_VAL 0x61
24312e8e8aSJoonyoung Shim
25312e8e8aSJoonyoung Shim /* MCS5080 Touchkey */
26312e8e8aSJoonyoung Shim #define MCS5080_TOUCHKEY_STATUS 0x00
27312e8e8aSJoonyoung Shim #define MCS5080_TOUCHKEY_STATUS_PRESS 3
28312e8e8aSJoonyoung Shim #define MCS5080_TOUCHKEY_FW 0x01
29312e8e8aSJoonyoung Shim #define MCS5080_TOUCHKEY_BASE_VAL 0x1
30312e8e8aSJoonyoung Shim
31312e8e8aSJoonyoung Shim enum mcs_touchkey_type {
32312e8e8aSJoonyoung Shim MCS5000_TOUCHKEY,
33312e8e8aSJoonyoung Shim MCS5080_TOUCHKEY,
34312e8e8aSJoonyoung Shim };
35312e8e8aSJoonyoung Shim
36312e8e8aSJoonyoung Shim struct mcs_touchkey_chip {
37312e8e8aSJoonyoung Shim unsigned int status_reg;
38312e8e8aSJoonyoung Shim unsigned int pressbit;
39312e8e8aSJoonyoung Shim unsigned int press_invert;
40312e8e8aSJoonyoung Shim unsigned int baseval;
41312e8e8aSJoonyoung Shim };
42312e8e8aSJoonyoung Shim
43312e8e8aSJoonyoung Shim struct mcs_touchkey_data {
44adf779c1SHeungjun Kim void (*poweron)(bool);
45adf779c1SHeungjun Kim
46312e8e8aSJoonyoung Shim struct i2c_client *client;
47312e8e8aSJoonyoung Shim struct input_dev *input_dev;
48312e8e8aSJoonyoung Shim struct mcs_touchkey_chip chip;
49312e8e8aSJoonyoung Shim unsigned int key_code;
50312e8e8aSJoonyoung Shim unsigned int key_val;
51312e8e8aSJoonyoung Shim unsigned short keycodes[];
52312e8e8aSJoonyoung Shim };
53312e8e8aSJoonyoung Shim
mcs_touchkey_interrupt(int irq,void * dev_id)54312e8e8aSJoonyoung Shim static irqreturn_t mcs_touchkey_interrupt(int irq, void *dev_id)
55312e8e8aSJoonyoung Shim {
56312e8e8aSJoonyoung Shim struct mcs_touchkey_data *data = dev_id;
57312e8e8aSJoonyoung Shim struct mcs_touchkey_chip *chip = &data->chip;
58312e8e8aSJoonyoung Shim struct i2c_client *client = data->client;
59312e8e8aSJoonyoung Shim struct input_dev *input = data->input_dev;
60312e8e8aSJoonyoung Shim unsigned int key_val;
61312e8e8aSJoonyoung Shim unsigned int pressed;
62312e8e8aSJoonyoung Shim int val;
63312e8e8aSJoonyoung Shim
64312e8e8aSJoonyoung Shim val = i2c_smbus_read_byte_data(client, chip->status_reg);
65312e8e8aSJoonyoung Shim if (val < 0) {
66312e8e8aSJoonyoung Shim dev_err(&client->dev, "i2c read error [%d]\n", val);
67312e8e8aSJoonyoung Shim goto out;
68312e8e8aSJoonyoung Shim }
69312e8e8aSJoonyoung Shim
70312e8e8aSJoonyoung Shim pressed = (val & (1 << chip->pressbit)) >> chip->pressbit;
71312e8e8aSJoonyoung Shim if (chip->press_invert)
72312e8e8aSJoonyoung Shim pressed ^= chip->press_invert;
73312e8e8aSJoonyoung Shim
74312e8e8aSJoonyoung Shim /* key_val is 0 when released, so we should use key_val of press. */
75312e8e8aSJoonyoung Shim if (pressed) {
76312e8e8aSJoonyoung Shim key_val = val & (0xff >> (8 - chip->pressbit));
77312e8e8aSJoonyoung Shim if (!key_val)
78312e8e8aSJoonyoung Shim goto out;
79312e8e8aSJoonyoung Shim key_val -= chip->baseval;
80312e8e8aSJoonyoung Shim data->key_code = data->keycodes[key_val];
81312e8e8aSJoonyoung Shim data->key_val = key_val;
82312e8e8aSJoonyoung Shim }
83312e8e8aSJoonyoung Shim
84312e8e8aSJoonyoung Shim input_event(input, EV_MSC, MSC_SCAN, data->key_val);
85312e8e8aSJoonyoung Shim input_report_key(input, data->key_code, pressed);
86312e8e8aSJoonyoung Shim input_sync(input);
87312e8e8aSJoonyoung Shim
88312e8e8aSJoonyoung Shim dev_dbg(&client->dev, "key %d %d %s\n", data->key_val, data->key_code,
89312e8e8aSJoonyoung Shim pressed ? "pressed" : "released");
90312e8e8aSJoonyoung Shim
91312e8e8aSJoonyoung Shim out:
92312e8e8aSJoonyoung Shim return IRQ_HANDLED;
93312e8e8aSJoonyoung Shim }
94312e8e8aSJoonyoung Shim
mcs_touchkey_poweroff(void * data)95e175eae1SYangtao Li static void mcs_touchkey_poweroff(void *data)
96e175eae1SYangtao Li {
97e175eae1SYangtao Li struct mcs_touchkey_data *touchkey = data;
98e175eae1SYangtao Li
99e175eae1SYangtao Li touchkey->poweron(false);
100e175eae1SYangtao Li }
101e175eae1SYangtao Li
mcs_touchkey_probe(struct i2c_client * client)102742c4687SUwe Kleine-König static int mcs_touchkey_probe(struct i2c_client *client)
103312e8e8aSJoonyoung Shim {
104742c4687SUwe Kleine-König const struct i2c_device_id *id = i2c_client_get_device_id(client);
105312e8e8aSJoonyoung Shim const struct mcs_platform_data *pdata;
106312e8e8aSJoonyoung Shim struct mcs_touchkey_data *data;
107312e8e8aSJoonyoung Shim struct input_dev *input_dev;
108312e8e8aSJoonyoung Shim unsigned int fw_reg;
109312e8e8aSJoonyoung Shim int fw_ver;
110312e8e8aSJoonyoung Shim int error;
111312e8e8aSJoonyoung Shim int i;
112312e8e8aSJoonyoung Shim
113c838cb3dSJingoo Han pdata = dev_get_platdata(&client->dev);
114312e8e8aSJoonyoung Shim if (!pdata) {
115312e8e8aSJoonyoung Shim dev_err(&client->dev, "no platform data defined\n");
116312e8e8aSJoonyoung Shim return -EINVAL;
117312e8e8aSJoonyoung Shim }
118312e8e8aSJoonyoung Shim
119e175eae1SYangtao Li data = devm_kzalloc(&client->dev,
120e175eae1SYangtao Li struct_size(data, keycodes, pdata->key_maxval + 1),
121312e8e8aSJoonyoung Shim GFP_KERNEL);
122e175eae1SYangtao Li if (!data)
123e175eae1SYangtao Li return -ENOMEM;
124e175eae1SYangtao Li
125e175eae1SYangtao Li input_dev = devm_input_allocate_device(&client->dev);
126e175eae1SYangtao Li if (!input_dev) {
127e175eae1SYangtao Li dev_err(&client->dev, "Failed to allocate input device\n");
128e175eae1SYangtao Li return -ENOMEM;
129312e8e8aSJoonyoung Shim }
130312e8e8aSJoonyoung Shim
131312e8e8aSJoonyoung Shim data->client = client;
132312e8e8aSJoonyoung Shim data->input_dev = input_dev;
133312e8e8aSJoonyoung Shim
134312e8e8aSJoonyoung Shim if (id->driver_data == MCS5000_TOUCHKEY) {
135312e8e8aSJoonyoung Shim data->chip.status_reg = MCS5000_TOUCHKEY_STATUS;
136312e8e8aSJoonyoung Shim data->chip.pressbit = MCS5000_TOUCHKEY_STATUS_PRESS;
137312e8e8aSJoonyoung Shim data->chip.baseval = MCS5000_TOUCHKEY_BASE_VAL;
138312e8e8aSJoonyoung Shim fw_reg = MCS5000_TOUCHKEY_FW;
139312e8e8aSJoonyoung Shim } else {
140312e8e8aSJoonyoung Shim data->chip.status_reg = MCS5080_TOUCHKEY_STATUS;
141312e8e8aSJoonyoung Shim data->chip.pressbit = MCS5080_TOUCHKEY_STATUS_PRESS;
142312e8e8aSJoonyoung Shim data->chip.press_invert = 1;
143312e8e8aSJoonyoung Shim data->chip.baseval = MCS5080_TOUCHKEY_BASE_VAL;
144312e8e8aSJoonyoung Shim fw_reg = MCS5080_TOUCHKEY_FW;
145312e8e8aSJoonyoung Shim }
146312e8e8aSJoonyoung Shim
147312e8e8aSJoonyoung Shim fw_ver = i2c_smbus_read_byte_data(client, fw_reg);
148312e8e8aSJoonyoung Shim if (fw_ver < 0) {
149*8362bf82SNathan Chancellor dev_err(&client->dev, "i2c read error[%d]\n", fw_ver);
150e175eae1SYangtao Li return fw_ver;
151312e8e8aSJoonyoung Shim }
152312e8e8aSJoonyoung Shim dev_info(&client->dev, "Firmware version: %d\n", fw_ver);
153312e8e8aSJoonyoung Shim
1549ab65da3SBeomho Seo input_dev->name = "MELFAS MCS Touchkey";
155312e8e8aSJoonyoung Shim input_dev->id.bustype = BUS_I2C;
156312e8e8aSJoonyoung Shim input_dev->evbit[0] = BIT_MASK(EV_KEY);
157312e8e8aSJoonyoung Shim if (!pdata->no_autorepeat)
158312e8e8aSJoonyoung Shim input_dev->evbit[0] |= BIT_MASK(EV_REP);
159312e8e8aSJoonyoung Shim input_dev->keycode = data->keycodes;
160312e8e8aSJoonyoung Shim input_dev->keycodesize = sizeof(data->keycodes[0]);
161312e8e8aSJoonyoung Shim input_dev->keycodemax = pdata->key_maxval + 1;
162312e8e8aSJoonyoung Shim
163312e8e8aSJoonyoung Shim for (i = 0; i < pdata->keymap_size; i++) {
164312e8e8aSJoonyoung Shim unsigned int val = MCS_KEY_VAL(pdata->keymap[i]);
165312e8e8aSJoonyoung Shim unsigned int code = MCS_KEY_CODE(pdata->keymap[i]);
166312e8e8aSJoonyoung Shim
167312e8e8aSJoonyoung Shim data->keycodes[val] = code;
168312e8e8aSJoonyoung Shim __set_bit(code, input_dev->keybit);
169312e8e8aSJoonyoung Shim }
170312e8e8aSJoonyoung Shim
171312e8e8aSJoonyoung Shim input_set_capability(input_dev, EV_MSC, MSC_SCAN);
172312e8e8aSJoonyoung Shim input_set_drvdata(input_dev, data);
173312e8e8aSJoonyoung Shim
174312e8e8aSJoonyoung Shim if (pdata->cfg_pin)
175312e8e8aSJoonyoung Shim pdata->cfg_pin();
176312e8e8aSJoonyoung Shim
177adf779c1SHeungjun Kim if (pdata->poweron) {
178adf779c1SHeungjun Kim data->poweron = pdata->poweron;
179adf779c1SHeungjun Kim data->poweron(true);
180e175eae1SYangtao Li
181e175eae1SYangtao Li error = devm_add_action_or_reset(&client->dev,
182e175eae1SYangtao Li mcs_touchkey_poweroff, data);
183e175eae1SYangtao Li if (error)
184e175eae1SYangtao Li return error;
185adf779c1SHeungjun Kim }
186adf779c1SHeungjun Kim
187e175eae1SYangtao Li error = devm_request_threaded_irq(&client->dev, client->irq,
188e175eae1SYangtao Li NULL, mcs_touchkey_interrupt,
1899b7e31bbSLars-Peter Clausen IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
1909b7e31bbSLars-Peter Clausen client->dev.driver->name, data);
191312e8e8aSJoonyoung Shim if (error) {
192312e8e8aSJoonyoung Shim dev_err(&client->dev, "Failed to register interrupt\n");
193e175eae1SYangtao Li return error;
194312e8e8aSJoonyoung Shim }
195312e8e8aSJoonyoung Shim
196312e8e8aSJoonyoung Shim error = input_register_device(input_dev);
197312e8e8aSJoonyoung Shim if (error)
198e175eae1SYangtao Li return error;
199312e8e8aSJoonyoung Shim
200312e8e8aSJoonyoung Shim i2c_set_clientdata(client, data);
201312e8e8aSJoonyoung Shim return 0;
202312e8e8aSJoonyoung Shim }
203312e8e8aSJoonyoung Shim
mcs_touchkey_shutdown(struct i2c_client * client)2045f62615eSHeungJun Kim static void mcs_touchkey_shutdown(struct i2c_client *client)
2055f62615eSHeungJun Kim {
2065f62615eSHeungJun Kim struct mcs_touchkey_data *data = i2c_get_clientdata(client);
2075f62615eSHeungJun Kim
2085f62615eSHeungJun Kim if (data->poweron)
2095f62615eSHeungJun Kim data->poweron(false);
2105f62615eSHeungJun Kim }
2115f62615eSHeungJun Kim
mcs_touchkey_suspend(struct device * dev)212adf779c1SHeungjun Kim static int mcs_touchkey_suspend(struct device *dev)
213adf779c1SHeungjun Kim {
214adf779c1SHeungjun Kim struct mcs_touchkey_data *data = dev_get_drvdata(dev);
215adf779c1SHeungjun Kim struct i2c_client *client = data->client;
216adf779c1SHeungjun Kim
217adf779c1SHeungjun Kim /* Disable the work */
218adf779c1SHeungjun Kim disable_irq(client->irq);
219adf779c1SHeungjun Kim
220adf779c1SHeungjun Kim /* Finally turn off the power */
221adf779c1SHeungjun Kim if (data->poweron)
222adf779c1SHeungjun Kim data->poweron(false);
223adf779c1SHeungjun Kim
224adf779c1SHeungjun Kim return 0;
225adf779c1SHeungjun Kim }
226adf779c1SHeungjun Kim
mcs_touchkey_resume(struct device * dev)227adf779c1SHeungjun Kim static int mcs_touchkey_resume(struct device *dev)
228adf779c1SHeungjun Kim {
229adf779c1SHeungjun Kim struct mcs_touchkey_data *data = dev_get_drvdata(dev);
230adf779c1SHeungjun Kim struct i2c_client *client = data->client;
231adf779c1SHeungjun Kim
232adf779c1SHeungjun Kim /* Enable the device first */
233adf779c1SHeungjun Kim if (data->poweron)
234adf779c1SHeungjun Kim data->poweron(true);
235adf779c1SHeungjun Kim
236adf779c1SHeungjun Kim /* Enable irq again */
237adf779c1SHeungjun Kim enable_irq(client->irq);
238adf779c1SHeungjun Kim
239adf779c1SHeungjun Kim return 0;
240adf779c1SHeungjun Kim }
241adf779c1SHeungjun Kim
2426b94d7b6SJonathan Cameron static DEFINE_SIMPLE_DEV_PM_OPS(mcs_touchkey_pm_ops,
243adf779c1SHeungjun Kim mcs_touchkey_suspend, mcs_touchkey_resume);
244adf779c1SHeungjun Kim
245312e8e8aSJoonyoung Shim static const struct i2c_device_id mcs_touchkey_id[] = {
246312e8e8aSJoonyoung Shim { "mcs5000_touchkey", MCS5000_TOUCHKEY },
247312e8e8aSJoonyoung Shim { "mcs5080_touchkey", MCS5080_TOUCHKEY },
248312e8e8aSJoonyoung Shim { }
249312e8e8aSJoonyoung Shim };
250312e8e8aSJoonyoung Shim MODULE_DEVICE_TABLE(i2c, mcs_touchkey_id);
251312e8e8aSJoonyoung Shim
252312e8e8aSJoonyoung Shim static struct i2c_driver mcs_touchkey_driver = {
253312e8e8aSJoonyoung Shim .driver = {
254312e8e8aSJoonyoung Shim .name = "mcs_touchkey",
2556b94d7b6SJonathan Cameron .pm = pm_sleep_ptr(&mcs_touchkey_pm_ops),
256312e8e8aSJoonyoung Shim },
257d8bde56dSUwe Kleine-König .probe = mcs_touchkey_probe,
2585f62615eSHeungJun Kim .shutdown = mcs_touchkey_shutdown,
259312e8e8aSJoonyoung Shim .id_table = mcs_touchkey_id,
260312e8e8aSJoonyoung Shim };
261312e8e8aSJoonyoung Shim
2621b92c1cfSAxel Lin module_i2c_driver(mcs_touchkey_driver);
263312e8e8aSJoonyoung Shim
264312e8e8aSJoonyoung Shim /* Module information */
265312e8e8aSJoonyoung Shim MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
266312e8e8aSJoonyoung Shim MODULE_AUTHOR("HeungJun Kim <riverful.kim@samsung.com>");
267312e8e8aSJoonyoung Shim MODULE_DESCRIPTION("Touchkey driver for MELFAS MCS5000/5080 controller");
268312e8e8aSJoonyoung Shim MODULE_LICENSE("GPL");
269