Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
awilliam
GitHub Repository: awilliam/linux-vfio
Path: blob/master/drivers/input/keyboard/tc3589x-keypad.c
15111 views
1
/*
2
* Copyright (C) ST-Ericsson SA 2010
3
*
4
* Author: Jayeeta Banerjee <[email protected]>
5
* Author: Sundar Iyer <[email protected]>
6
*
7
* License Terms: GNU General Public License, version 2
8
*
9
* TC35893 MFD Keypad Controller driver
10
*/
11
12
#include <linux/module.h>
13
#include <linux/init.h>
14
#include <linux/interrupt.h>
15
#include <linux/input.h>
16
#include <linux/platform_device.h>
17
#include <linux/input/matrix_keypad.h>
18
#include <linux/i2c.h>
19
#include <linux/slab.h>
20
#include <linux/mfd/tc3589x.h>
21
22
/* Maximum supported keypad matrix row/columns size */
23
#define TC3589x_MAX_KPROW 8
24
#define TC3589x_MAX_KPCOL 12
25
26
/* keypad related Constants */
27
#define TC3589x_MAX_DEBOUNCE_SETTLE 0xFF
28
#define DEDICATED_KEY_VAL 0xFF
29
30
/* Pull up/down masks */
31
#define TC3589x_NO_PULL_MASK 0x0
32
#define TC3589x_PULL_DOWN_MASK 0x1
33
#define TC3589x_PULL_UP_MASK 0x2
34
#define TC3589x_PULLUP_ALL_MASK 0xAA
35
#define TC3589x_IO_PULL_VAL(index, mask) ((mask)<<((index)%4)*2))
36
37
/* Bit masks for IOCFG register */
38
#define IOCFG_BALLCFG 0x01
39
#define IOCFG_IG 0x08
40
41
#define KP_EVCODE_COL_MASK 0x0F
42
#define KP_EVCODE_ROW_MASK 0x70
43
#define KP_RELEASE_EVT_MASK 0x80
44
45
#define KP_ROW_SHIFT 4
46
47
#define KP_NO_VALID_KEY_MASK 0x7F
48
49
/* bit masks for RESTCTRL register */
50
#define TC3589x_KBDRST 0x2
51
#define TC3589x_IRQRST 0x10
52
#define TC3589x_RESET_ALL 0x1B
53
54
/* KBDMFS register bit mask */
55
#define TC3589x_KBDMFS_EN 0x1
56
57
/* CLKEN register bitmask */
58
#define KPD_CLK_EN 0x1
59
60
/* RSTINTCLR register bit mask */
61
#define IRQ_CLEAR 0x1
62
63
/* bit masks for keyboard interrupts*/
64
#define TC3589x_EVT_LOSS_INT 0x8
65
#define TC3589x_EVT_INT 0x4
66
#define TC3589x_KBD_LOSS_INT 0x2
67
#define TC3589x_KBD_INT 0x1
68
69
/* bit masks for keyboard interrupt clear*/
70
#define TC3589x_EVT_INT_CLR 0x2
71
#define TC3589x_KBD_INT_CLR 0x1
72
73
#define TC3589x_KBD_KEYMAP_SIZE 64
74
75
/**
76
* struct tc_keypad - data structure used by keypad driver
77
* @input: pointer to input device object
78
* @board: keypad platform device
79
* @krow: number of rows
80
* @kcol: number of coloumns
81
* @keymap: matrix scan code table for keycodes
82
*/
83
struct tc_keypad {
84
struct tc3589x *tc3589x;
85
struct input_dev *input;
86
const struct tc3589x_keypad_platform_data *board;
87
unsigned int krow;
88
unsigned int kcol;
89
unsigned short keymap[TC3589x_KBD_KEYMAP_SIZE];
90
bool keypad_stopped;
91
};
92
93
static int __devinit tc3589x_keypad_init_key_hardware(struct tc_keypad *keypad)
94
{
95
int ret;
96
struct tc3589x *tc3589x = keypad->tc3589x;
97
u8 settle_time = keypad->board->settle_time;
98
u8 dbounce_period = keypad->board->debounce_period;
99
u8 rows = keypad->board->krow & 0xf; /* mask out the nibble */
100
u8 column = keypad->board->kcol & 0xf; /* mask out the nibble */
101
102
/* validate platform configurations */
103
if (keypad->board->kcol > TC3589x_MAX_KPCOL ||
104
keypad->board->krow > TC3589x_MAX_KPROW ||
105
keypad->board->debounce_period > TC3589x_MAX_DEBOUNCE_SETTLE ||
106
keypad->board->settle_time > TC3589x_MAX_DEBOUNCE_SETTLE)
107
return -EINVAL;
108
109
/* configure KBDSIZE 4 LSbits for cols and 4 MSbits for rows */
110
ret = tc3589x_reg_write(tc3589x, TC3589x_KBDSIZE,
111
(rows << KP_ROW_SHIFT) | column);
112
if (ret < 0)
113
return ret;
114
115
/* configure dedicated key config, no dedicated key selected */
116
ret = tc3589x_reg_write(tc3589x, TC3589x_KBCFG_LSB, DEDICATED_KEY_VAL);
117
if (ret < 0)
118
return ret;
119
120
ret = tc3589x_reg_write(tc3589x, TC3589x_KBCFG_MSB, DEDICATED_KEY_VAL);
121
if (ret < 0)
122
return ret;
123
124
/* Configure settle time */
125
ret = tc3589x_reg_write(tc3589x, TC3589x_KBDSETTLE_REG, settle_time);
126
if (ret < 0)
127
return ret;
128
129
/* Configure debounce time */
130
ret = tc3589x_reg_write(tc3589x, TC3589x_KBDBOUNCE, dbounce_period);
131
if (ret < 0)
132
return ret;
133
134
/* Start of initialise keypad GPIOs */
135
ret = tc3589x_set_bits(tc3589x, TC3589x_IOCFG, 0x0, IOCFG_IG);
136
if (ret < 0)
137
return ret;
138
139
/* Configure pull-up resistors for all row GPIOs */
140
ret = tc3589x_reg_write(tc3589x, TC3589x_IOPULLCFG0_LSB,
141
TC3589x_PULLUP_ALL_MASK);
142
if (ret < 0)
143
return ret;
144
145
ret = tc3589x_reg_write(tc3589x, TC3589x_IOPULLCFG0_MSB,
146
TC3589x_PULLUP_ALL_MASK);
147
if (ret < 0)
148
return ret;
149
150
/* Configure pull-up resistors for all column GPIOs */
151
ret = tc3589x_reg_write(tc3589x, TC3589x_IOPULLCFG1_LSB,
152
TC3589x_PULLUP_ALL_MASK);
153
if (ret < 0)
154
return ret;
155
156
ret = tc3589x_reg_write(tc3589x, TC3589x_IOPULLCFG1_MSB,
157
TC3589x_PULLUP_ALL_MASK);
158
if (ret < 0)
159
return ret;
160
161
ret = tc3589x_reg_write(tc3589x, TC3589x_IOPULLCFG2_LSB,
162
TC3589x_PULLUP_ALL_MASK);
163
164
return ret;
165
}
166
167
#define TC35893_DATA_REGS 4
168
#define TC35893_KEYCODE_FIFO_EMPTY 0x7f
169
#define TC35893_KEYCODE_FIFO_CLEAR 0xff
170
#define TC35893_KEYPAD_ROW_SHIFT 0x3
171
172
static irqreturn_t tc3589x_keypad_irq(int irq, void *dev)
173
{
174
struct tc_keypad *keypad = dev;
175
struct tc3589x *tc3589x = keypad->tc3589x;
176
u8 i, row_index, col_index, kbd_code, up;
177
u8 code;
178
179
for (i = 0; i < TC35893_DATA_REGS * 2; i++) {
180
kbd_code = tc3589x_reg_read(tc3589x, TC3589x_EVTCODE_FIFO);
181
182
/* loop till fifo is empty and no more keys are pressed */
183
if (kbd_code == TC35893_KEYCODE_FIFO_EMPTY ||
184
kbd_code == TC35893_KEYCODE_FIFO_CLEAR)
185
continue;
186
187
/* valid key is found */
188
col_index = kbd_code & KP_EVCODE_COL_MASK;
189
row_index = (kbd_code & KP_EVCODE_ROW_MASK) >> KP_ROW_SHIFT;
190
code = MATRIX_SCAN_CODE(row_index, col_index,
191
TC35893_KEYPAD_ROW_SHIFT);
192
up = kbd_code & KP_RELEASE_EVT_MASK;
193
194
input_event(keypad->input, EV_MSC, MSC_SCAN, code);
195
input_report_key(keypad->input, keypad->keymap[code], !up);
196
input_sync(keypad->input);
197
}
198
199
/* clear IRQ */
200
tc3589x_set_bits(tc3589x, TC3589x_KBDIC,
201
0x0, TC3589x_EVT_INT_CLR | TC3589x_KBD_INT_CLR);
202
/* enable IRQ */
203
tc3589x_set_bits(tc3589x, TC3589x_KBDMSK,
204
0x0, TC3589x_EVT_LOSS_INT | TC3589x_EVT_INT);
205
206
return IRQ_HANDLED;
207
}
208
209
static int tc3589x_keypad_enable(struct tc_keypad *keypad)
210
{
211
struct tc3589x *tc3589x = keypad->tc3589x;
212
int ret;
213
214
/* pull the keypad module out of reset */
215
ret = tc3589x_set_bits(tc3589x, TC3589x_RSTCTRL, TC3589x_KBDRST, 0x0);
216
if (ret < 0)
217
return ret;
218
219
/* configure KBDMFS */
220
ret = tc3589x_set_bits(tc3589x, TC3589x_KBDMFS, 0x0, TC3589x_KBDMFS_EN);
221
if (ret < 0)
222
return ret;
223
224
/* enable the keypad clock */
225
ret = tc3589x_set_bits(tc3589x, TC3589x_CLKEN, 0x0, KPD_CLK_EN);
226
if (ret < 0)
227
return ret;
228
229
/* clear pending IRQs */
230
ret = tc3589x_set_bits(tc3589x, TC3589x_RSTINTCLR, 0x0, 0x1);
231
if (ret < 0)
232
return ret;
233
234
/* enable the IRQs */
235
ret = tc3589x_set_bits(tc3589x, TC3589x_KBDMSK, 0x0,
236
TC3589x_EVT_LOSS_INT | TC3589x_EVT_INT);
237
if (ret < 0)
238
return ret;
239
240
keypad->keypad_stopped = false;
241
242
return ret;
243
}
244
245
static int tc3589x_keypad_disable(struct tc_keypad *keypad)
246
{
247
struct tc3589x *tc3589x = keypad->tc3589x;
248
int ret;
249
250
/* clear IRQ */
251
ret = tc3589x_set_bits(tc3589x, TC3589x_KBDIC,
252
0x0, TC3589x_EVT_INT_CLR | TC3589x_KBD_INT_CLR);
253
if (ret < 0)
254
return ret;
255
256
/* disable all interrupts */
257
ret = tc3589x_set_bits(tc3589x, TC3589x_KBDMSK,
258
~(TC3589x_EVT_LOSS_INT | TC3589x_EVT_INT), 0x0);
259
if (ret < 0)
260
return ret;
261
262
/* disable the keypad module */
263
ret = tc3589x_set_bits(tc3589x, TC3589x_CLKEN, 0x1, 0x0);
264
if (ret < 0)
265
return ret;
266
267
/* put the keypad module into reset */
268
ret = tc3589x_set_bits(tc3589x, TC3589x_RSTCTRL, TC3589x_KBDRST, 0x1);
269
270
keypad->keypad_stopped = true;
271
272
return ret;
273
}
274
275
static int tc3589x_keypad_open(struct input_dev *input)
276
{
277
int error;
278
struct tc_keypad *keypad = input_get_drvdata(input);
279
280
/* enable the keypad module */
281
error = tc3589x_keypad_enable(keypad);
282
if (error < 0) {
283
dev_err(&input->dev, "failed to enable keypad module\n");
284
return error;
285
}
286
287
error = tc3589x_keypad_init_key_hardware(keypad);
288
if (error < 0) {
289
dev_err(&input->dev, "failed to configure keypad module\n");
290
return error;
291
}
292
293
return 0;
294
}
295
296
static void tc3589x_keypad_close(struct input_dev *input)
297
{
298
struct tc_keypad *keypad = input_get_drvdata(input);
299
300
/* disable the keypad module */
301
tc3589x_keypad_disable(keypad);
302
}
303
304
static int __devinit tc3589x_keypad_probe(struct platform_device *pdev)
305
{
306
struct tc3589x *tc3589x = dev_get_drvdata(pdev->dev.parent);
307
struct tc_keypad *keypad;
308
struct input_dev *input;
309
const struct tc3589x_keypad_platform_data *plat;
310
int error, irq;
311
312
plat = tc3589x->pdata->keypad;
313
if (!plat) {
314
dev_err(&pdev->dev, "invalid keypad platform data\n");
315
return -EINVAL;
316
}
317
318
irq = platform_get_irq(pdev, 0);
319
if (irq < 0)
320
return irq;
321
322
keypad = kzalloc(sizeof(struct tc_keypad), GFP_KERNEL);
323
input = input_allocate_device();
324
if (!keypad || !input) {
325
dev_err(&pdev->dev, "failed to allocate keypad memory\n");
326
error = -ENOMEM;
327
goto err_free_mem;
328
}
329
330
keypad->board = plat;
331
keypad->input = input;
332
keypad->tc3589x = tc3589x;
333
334
input->id.bustype = BUS_I2C;
335
input->name = pdev->name;
336
input->dev.parent = &pdev->dev;
337
338
input->keycode = keypad->keymap;
339
input->keycodesize = sizeof(keypad->keymap[0]);
340
input->keycodemax = ARRAY_SIZE(keypad->keymap);
341
342
input->open = tc3589x_keypad_open;
343
input->close = tc3589x_keypad_close;
344
345
input_set_drvdata(input, keypad);
346
347
input_set_capability(input, EV_MSC, MSC_SCAN);
348
349
__set_bit(EV_KEY, input->evbit);
350
if (!plat->no_autorepeat)
351
__set_bit(EV_REP, input->evbit);
352
353
matrix_keypad_build_keymap(plat->keymap_data, 0x3,
354
input->keycode, input->keybit);
355
356
error = request_threaded_irq(irq, NULL,
357
tc3589x_keypad_irq, plat->irqtype,
358
"tc3589x-keypad", keypad);
359
if (error < 0) {
360
dev_err(&pdev->dev,
361
"Could not allocate irq %d,error %d\n",
362
irq, error);
363
goto err_free_mem;
364
}
365
366
error = input_register_device(input);
367
if (error) {
368
dev_err(&pdev->dev, "Could not register input device\n");
369
goto err_free_irq;
370
}
371
372
/* let platform decide if keypad is a wakeup source or not */
373
device_init_wakeup(&pdev->dev, plat->enable_wakeup);
374
device_set_wakeup_capable(&pdev->dev, plat->enable_wakeup);
375
376
platform_set_drvdata(pdev, keypad);
377
378
return 0;
379
380
err_free_irq:
381
free_irq(irq, keypad);
382
err_free_mem:
383
input_free_device(input);
384
kfree(keypad);
385
return error;
386
}
387
388
static int __devexit tc3589x_keypad_remove(struct platform_device *pdev)
389
{
390
struct tc_keypad *keypad = platform_get_drvdata(pdev);
391
int irq = platform_get_irq(pdev, 0);
392
393
if (!keypad->keypad_stopped)
394
tc3589x_keypad_disable(keypad);
395
396
free_irq(irq, keypad);
397
398
input_unregister_device(keypad->input);
399
400
kfree(keypad);
401
402
return 0;
403
}
404
405
#ifdef CONFIG_PM_SLEEP
406
static int tc3589x_keypad_suspend(struct device *dev)
407
{
408
struct platform_device *pdev = to_platform_device(dev);
409
struct tc_keypad *keypad = platform_get_drvdata(pdev);
410
int irq = platform_get_irq(pdev, 0);
411
412
/* keypad is already off; we do nothing */
413
if (keypad->keypad_stopped)
414
return 0;
415
416
/* if device is not a wakeup source, disable it for powersave */
417
if (!device_may_wakeup(&pdev->dev))
418
tc3589x_keypad_disable(keypad);
419
else
420
enable_irq_wake(irq);
421
422
return 0;
423
}
424
425
static int tc3589x_keypad_resume(struct device *dev)
426
{
427
struct platform_device *pdev = to_platform_device(dev);
428
struct tc_keypad *keypad = platform_get_drvdata(pdev);
429
int irq = platform_get_irq(pdev, 0);
430
431
if (!keypad->keypad_stopped)
432
return 0;
433
434
/* enable the device to resume normal operations */
435
if (!device_may_wakeup(&pdev->dev))
436
tc3589x_keypad_enable(keypad);
437
else
438
disable_irq_wake(irq);
439
440
return 0;
441
}
442
#endif
443
444
static SIMPLE_DEV_PM_OPS(tc3589x_keypad_dev_pm_ops,
445
tc3589x_keypad_suspend, tc3589x_keypad_resume);
446
447
static struct platform_driver tc3589x_keypad_driver = {
448
.driver = {
449
.name = "tc3589x-keypad",
450
.owner = THIS_MODULE,
451
.pm = &tc3589x_keypad_dev_pm_ops,
452
},
453
.probe = tc3589x_keypad_probe,
454
.remove = __devexit_p(tc3589x_keypad_remove),
455
};
456
457
static int __init tc3589x_keypad_init(void)
458
{
459
return platform_driver_register(&tc3589x_keypad_driver);
460
}
461
module_init(tc3589x_keypad_init);
462
463
static void __exit tc3589x_keypad_exit(void)
464
{
465
return platform_driver_unregister(&tc3589x_keypad_driver);
466
}
467
module_exit(tc3589x_keypad_exit);
468
469
MODULE_LICENSE("GPL v2");
470
MODULE_AUTHOR("Jayeeta Banerjee/Sundar Iyer");
471
MODULE_DESCRIPTION("TC35893 Keypad Driver");
472
MODULE_ALIAS("platform:tc3589x-keypad");
473
474