Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/drivers/input/keyboard/mcs_touchkey.c
15111 views
1
/*
2
* Touchkey driver for MELFAS MCS5000/5080 controller
3
*
4
* Copyright (C) 2010 Samsung Electronics Co.Ltd
5
* Author: HeungJun Kim <[email protected]>
6
* Author: Joonyoung Shim <[email protected]>
7
*
8
* This program is free software; you can redistribute it and/or modify it
9
* under the terms of the GNU General Public License as published by the
10
* Free Software Foundation; either version 2 of the License, or (at your
11
* option) any later version.
12
*/
13
14
#include <linux/module.h>
15
#include <linux/init.h>
16
#include <linux/i2c.h>
17
#include <linux/i2c/mcs.h>
18
#include <linux/interrupt.h>
19
#include <linux/input.h>
20
#include <linux/irq.h>
21
#include <linux/slab.h>
22
#include <linux/pm.h>
23
24
/* MCS5000 Touchkey */
25
#define MCS5000_TOUCHKEY_STATUS 0x04
26
#define MCS5000_TOUCHKEY_STATUS_PRESS 7
27
#define MCS5000_TOUCHKEY_FW 0x0a
28
#define MCS5000_TOUCHKEY_BASE_VAL 0x61
29
30
/* MCS5080 Touchkey */
31
#define MCS5080_TOUCHKEY_STATUS 0x00
32
#define MCS5080_TOUCHKEY_STATUS_PRESS 3
33
#define MCS5080_TOUCHKEY_FW 0x01
34
#define MCS5080_TOUCHKEY_BASE_VAL 0x1
35
36
enum mcs_touchkey_type {
37
MCS5000_TOUCHKEY,
38
MCS5080_TOUCHKEY,
39
};
40
41
struct mcs_touchkey_chip {
42
unsigned int status_reg;
43
unsigned int pressbit;
44
unsigned int press_invert;
45
unsigned int baseval;
46
};
47
48
struct mcs_touchkey_data {
49
void (*poweron)(bool);
50
51
struct i2c_client *client;
52
struct input_dev *input_dev;
53
struct mcs_touchkey_chip chip;
54
unsigned int key_code;
55
unsigned int key_val;
56
unsigned short keycodes[];
57
};
58
59
static irqreturn_t mcs_touchkey_interrupt(int irq, void *dev_id)
60
{
61
struct mcs_touchkey_data *data = dev_id;
62
struct mcs_touchkey_chip *chip = &data->chip;
63
struct i2c_client *client = data->client;
64
struct input_dev *input = data->input_dev;
65
unsigned int key_val;
66
unsigned int pressed;
67
int val;
68
69
val = i2c_smbus_read_byte_data(client, chip->status_reg);
70
if (val < 0) {
71
dev_err(&client->dev, "i2c read error [%d]\n", val);
72
goto out;
73
}
74
75
pressed = (val & (1 << chip->pressbit)) >> chip->pressbit;
76
if (chip->press_invert)
77
pressed ^= chip->press_invert;
78
79
/* key_val is 0 when released, so we should use key_val of press. */
80
if (pressed) {
81
key_val = val & (0xff >> (8 - chip->pressbit));
82
if (!key_val)
83
goto out;
84
key_val -= chip->baseval;
85
data->key_code = data->keycodes[key_val];
86
data->key_val = key_val;
87
}
88
89
input_event(input, EV_MSC, MSC_SCAN, data->key_val);
90
input_report_key(input, data->key_code, pressed);
91
input_sync(input);
92
93
dev_dbg(&client->dev, "key %d %d %s\n", data->key_val, data->key_code,
94
pressed ? "pressed" : "released");
95
96
out:
97
return IRQ_HANDLED;
98
}
99
100
static int __devinit mcs_touchkey_probe(struct i2c_client *client,
101
const struct i2c_device_id *id)
102
{
103
const struct mcs_platform_data *pdata;
104
struct mcs_touchkey_data *data;
105
struct input_dev *input_dev;
106
unsigned int fw_reg;
107
int fw_ver;
108
int error;
109
int i;
110
111
pdata = client->dev.platform_data;
112
if (!pdata) {
113
dev_err(&client->dev, "no platform data defined\n");
114
return -EINVAL;
115
}
116
117
data = kzalloc(sizeof(struct mcs_touchkey_data) +
118
sizeof(data->keycodes[0]) * (pdata->key_maxval + 1),
119
GFP_KERNEL);
120
input_dev = input_allocate_device();
121
if (!data || !input_dev) {
122
dev_err(&client->dev, "Failed to allocate memory\n");
123
error = -ENOMEM;
124
goto err_free_mem;
125
}
126
127
data->client = client;
128
data->input_dev = input_dev;
129
130
if (id->driver_data == MCS5000_TOUCHKEY) {
131
data->chip.status_reg = MCS5000_TOUCHKEY_STATUS;
132
data->chip.pressbit = MCS5000_TOUCHKEY_STATUS_PRESS;
133
data->chip.baseval = MCS5000_TOUCHKEY_BASE_VAL;
134
fw_reg = MCS5000_TOUCHKEY_FW;
135
} else {
136
data->chip.status_reg = MCS5080_TOUCHKEY_STATUS;
137
data->chip.pressbit = MCS5080_TOUCHKEY_STATUS_PRESS;
138
data->chip.press_invert = 1;
139
data->chip.baseval = MCS5080_TOUCHKEY_BASE_VAL;
140
fw_reg = MCS5080_TOUCHKEY_FW;
141
}
142
143
fw_ver = i2c_smbus_read_byte_data(client, fw_reg);
144
if (fw_ver < 0) {
145
error = fw_ver;
146
dev_err(&client->dev, "i2c read error[%d]\n", error);
147
goto err_free_mem;
148
}
149
dev_info(&client->dev, "Firmware version: %d\n", fw_ver);
150
151
input_dev->name = "MELPAS MCS Touchkey";
152
input_dev->id.bustype = BUS_I2C;
153
input_dev->dev.parent = &client->dev;
154
input_dev->evbit[0] = BIT_MASK(EV_KEY);
155
if (!pdata->no_autorepeat)
156
input_dev->evbit[0] |= BIT_MASK(EV_REP);
157
input_dev->keycode = data->keycodes;
158
input_dev->keycodesize = sizeof(data->keycodes[0]);
159
input_dev->keycodemax = pdata->key_maxval + 1;
160
161
for (i = 0; i < pdata->keymap_size; i++) {
162
unsigned int val = MCS_KEY_VAL(pdata->keymap[i]);
163
unsigned int code = MCS_KEY_CODE(pdata->keymap[i]);
164
165
data->keycodes[val] = code;
166
__set_bit(code, input_dev->keybit);
167
}
168
169
input_set_capability(input_dev, EV_MSC, MSC_SCAN);
170
input_set_drvdata(input_dev, data);
171
172
if (pdata->cfg_pin)
173
pdata->cfg_pin();
174
175
if (pdata->poweron) {
176
data->poweron = pdata->poweron;
177
data->poweron(true);
178
}
179
180
error = request_threaded_irq(client->irq, NULL, mcs_touchkey_interrupt,
181
IRQF_TRIGGER_FALLING, client->dev.driver->name, data);
182
if (error) {
183
dev_err(&client->dev, "Failed to register interrupt\n");
184
goto err_free_mem;
185
}
186
187
error = input_register_device(input_dev);
188
if (error)
189
goto err_free_irq;
190
191
i2c_set_clientdata(client, data);
192
return 0;
193
194
err_free_irq:
195
free_irq(client->irq, data);
196
err_free_mem:
197
input_free_device(input_dev);
198
kfree(data);
199
return error;
200
}
201
202
static int __devexit mcs_touchkey_remove(struct i2c_client *client)
203
{
204
struct mcs_touchkey_data *data = i2c_get_clientdata(client);
205
206
free_irq(client->irq, data);
207
if (data->poweron)
208
data->poweron(false);
209
input_unregister_device(data->input_dev);
210
kfree(data);
211
212
return 0;
213
}
214
215
static void mcs_touchkey_shutdown(struct i2c_client *client)
216
{
217
struct mcs_touchkey_data *data = i2c_get_clientdata(client);
218
219
if (data->poweron)
220
data->poweron(false);
221
}
222
223
#ifdef CONFIG_PM_SLEEP
224
static int mcs_touchkey_suspend(struct device *dev)
225
{
226
struct mcs_touchkey_data *data = dev_get_drvdata(dev);
227
struct i2c_client *client = data->client;
228
229
/* Disable the work */
230
disable_irq(client->irq);
231
232
/* Finally turn off the power */
233
if (data->poweron)
234
data->poweron(false);
235
236
return 0;
237
}
238
239
static int mcs_touchkey_resume(struct device *dev)
240
{
241
struct mcs_touchkey_data *data = dev_get_drvdata(dev);
242
struct i2c_client *client = data->client;
243
244
/* Enable the device first */
245
if (data->poweron)
246
data->poweron(true);
247
248
/* Enable irq again */
249
enable_irq(client->irq);
250
251
return 0;
252
}
253
#endif
254
255
static SIMPLE_DEV_PM_OPS(mcs_touchkey_pm_ops,
256
mcs_touchkey_suspend, mcs_touchkey_resume);
257
258
static const struct i2c_device_id mcs_touchkey_id[] = {
259
{ "mcs5000_touchkey", MCS5000_TOUCHKEY },
260
{ "mcs5080_touchkey", MCS5080_TOUCHKEY },
261
{ }
262
};
263
MODULE_DEVICE_TABLE(i2c, mcs_touchkey_id);
264
265
static struct i2c_driver mcs_touchkey_driver = {
266
.driver = {
267
.name = "mcs_touchkey",
268
.owner = THIS_MODULE,
269
.pm = &mcs_touchkey_pm_ops,
270
},
271
.probe = mcs_touchkey_probe,
272
.remove = __devexit_p(mcs_touchkey_remove),
273
.shutdown = mcs_touchkey_shutdown,
274
.id_table = mcs_touchkey_id,
275
};
276
277
static int __init mcs_touchkey_init(void)
278
{
279
return i2c_add_driver(&mcs_touchkey_driver);
280
}
281
282
static void __exit mcs_touchkey_exit(void)
283
{
284
i2c_del_driver(&mcs_touchkey_driver);
285
}
286
287
module_init(mcs_touchkey_init);
288
module_exit(mcs_touchkey_exit);
289
290
/* Module information */
291
MODULE_AUTHOR("Joonyoung Shim <[email protected]>");
292
MODULE_AUTHOR("HeungJun Kim <[email protected]>");
293
MODULE_DESCRIPTION("Touchkey driver for MELFAS MCS5000/5080 controller");
294
MODULE_LICENSE("GPL");
295
296