aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/input
diff options
context:
space:
mode:
authorDavid Brownell <dbrownell@users.sourceforge.net>2009-08-25 22:24:14 -0400
committerDmitry Torokhov <dmitry.torokhov@gmail.com>2009-08-28 01:06:15 -0400
commit9d8340687c524ce61e3c9c76758c4c81303acfc0 (patch)
tree7dc56e76ca748e00f76fc635d2799323b16dfeff /drivers/input
parent77a53fd21870c726b670c0d8179294ac1ea33468 (diff)
Input: add twl4030_keypad driver
Add a driver for the keypad controller on TWL4030 family chips. These support up to an 8x8 key matrix. The TWL4030 multifunction chips are mostly used on OMAP3 (or OMAP 2430) based boards. [dtor@mail.ru: switch to matrix-keypad framework, fix changing keymap from userspace] Reviewed-by: Trilok Soni <soni.trilok@gmail.com> Signed-off-by: David Brownell <dbrownell@users.sourceforge.net> Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
Diffstat (limited to 'drivers/input')
-rw-r--r--drivers/input/keyboard/Kconfig11
-rw-r--r--drivers/input/keyboard/Makefile1
-rw-r--r--drivers/input/keyboard/twl4030_keypad.c480
3 files changed, 492 insertions, 0 deletions
diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
index 50e407de8a78..3525c19be428 100644
--- a/drivers/input/keyboard/Kconfig
+++ b/drivers/input/keyboard/Kconfig
@@ -330,6 +330,17 @@ config KEYBOARD_OMAP
330 To compile this driver as a module, choose M here: the 330 To compile this driver as a module, choose M here: the
331 module will be called omap-keypad. 331 module will be called omap-keypad.
332 332
333config KEYBOARD_TWL4030
334 tristate "TI TWL4030/TWL5030/TPS659x0 keypad support"
335 depends on TWL4030_CORE
336 help
337 Say Y here if your board use the keypad controller on
338 TWL4030 family chips. It's safe to say enable this
339 even on boards that don't use the keypad controller.
340
341 To compile this driver as a module, choose M here: the
342 module will be called twl4030_keypad.
343
333config KEYBOARD_TOSA 344config KEYBOARD_TOSA
334 tristate "Tosa keyboard" 345 tristate "Tosa keyboard"
335 depends on MACH_TOSA 346 depends on MACH_TOSA
diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
index 152303029203..8a7a22b30266 100644
--- a/drivers/input/keyboard/Makefile
+++ b/drivers/input/keyboard/Makefile
@@ -30,5 +30,6 @@ obj-$(CONFIG_KEYBOARD_SPITZ) += spitzkbd.o
30obj-$(CONFIG_KEYBOARD_STOWAWAY) += stowaway.o 30obj-$(CONFIG_KEYBOARD_STOWAWAY) += stowaway.o
31obj-$(CONFIG_KEYBOARD_SUNKBD) += sunkbd.o 31obj-$(CONFIG_KEYBOARD_SUNKBD) += sunkbd.o
32obj-$(CONFIG_KEYBOARD_TOSA) += tosakbd.o 32obj-$(CONFIG_KEYBOARD_TOSA) += tosakbd.o
33obj-$(CONFIG_KEYBOARD_TWL4030) += twl4030_keypad.o
33obj-$(CONFIG_KEYBOARD_XTKBD) += xtkbd.o 34obj-$(CONFIG_KEYBOARD_XTKBD) += xtkbd.o
34obj-$(CONFIG_KEYBOARD_W90P910) += w90p910_keypad.o 35obj-$(CONFIG_KEYBOARD_W90P910) += w90p910_keypad.o
diff --git a/drivers/input/keyboard/twl4030_keypad.c b/drivers/input/keyboard/twl4030_keypad.c
new file mode 100644
index 000000000000..9a2977c21696
--- /dev/null
+++ b/drivers/input/keyboard/twl4030_keypad.c
@@ -0,0 +1,480 @@
1/*
2 * twl4030_keypad.c - driver for 8x8 keypad controller in twl4030 chips
3 *
4 * Copyright (C) 2007 Texas Instruments, Inc.
5 * Copyright (C) 2008 Nokia Corporation
6 *
7 * Code re-written for 2430SDP by:
8 * Syed Mohammed Khasim <x0khasim@ti.com>
9 *
10 * Initial Code:
11 * Manjunatha G K <manjugk@ti.com>
12 *
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2 of the License, or
16 * (at your option) any later version.
17 *
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
22 *
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
26 */
27
28#include <linux/kernel.h>
29#include <linux/module.h>
30#include <linux/init.h>
31#include <linux/interrupt.h>
32#include <linux/input.h>
33#include <linux/platform_device.h>
34#include <linux/i2c/twl4030.h>
35
36
37/*
38 * The TWL4030 family chips include a keypad controller that supports
39 * up to an 8x8 switch matrix. The controller can issue system wakeup
40 * events, since it uses only the always-on 32KiHz oscillator, and has
41 * an internal state machine that decodes pressed keys, including
42 * multi-key combinations.
43 *
44 * This driver lets boards define what keycodes they wish to report for
45 * which scancodes, as part of the "struct twl4030_keypad_data" used in
46 * the probe() routine.
47 *
48 * See the TPS65950 documentation; that's the general availability
49 * version of the TWL5030 second generation part.
50 */
51#define TWL4030_MAX_ROWS 8 /* TWL4030 hard limit */
52#define TWL4030_MAX_COLS 8
53#define TWL4030_ROW_SHIFT 3
54#define TWL4030_KEYMAP_SIZE (TWL4030_MAX_ROWS * TWL4030_MAX_COLS)
55
56struct twl4030_keypad {
57 unsigned short keymap[TWL4030_KEYMAP_SIZE];
58 u16 kp_state[TWL4030_MAX_ROWS];
59 unsigned n_rows;
60 unsigned n_cols;
61 unsigned irq;
62
63 struct device *dbg_dev;
64 struct input_dev *input;
65};
66
67/*----------------------------------------------------------------------*/
68
69/* arbitrary prescaler value 0..7 */
70#define PTV_PRESCALER 4
71
72/* Register Offsets */
73#define KEYP_CTRL 0x00
74#define KEYP_DEB 0x01
75#define KEYP_LONG_KEY 0x02
76#define KEYP_LK_PTV 0x03
77#define KEYP_TIMEOUT_L 0x04
78#define KEYP_TIMEOUT_H 0x05
79#define KEYP_KBC 0x06
80#define KEYP_KBR 0x07
81#define KEYP_SMS 0x08
82#define KEYP_FULL_CODE_7_0 0x09 /* row 0 column status */
83#define KEYP_FULL_CODE_15_8 0x0a /* ... row 1 ... */
84#define KEYP_FULL_CODE_23_16 0x0b
85#define KEYP_FULL_CODE_31_24 0x0c
86#define KEYP_FULL_CODE_39_32 0x0d
87#define KEYP_FULL_CODE_47_40 0x0e
88#define KEYP_FULL_CODE_55_48 0x0f
89#define KEYP_FULL_CODE_63_56 0x10
90#define KEYP_ISR1 0x11
91#define KEYP_IMR1 0x12
92#define KEYP_ISR2 0x13
93#define KEYP_IMR2 0x14
94#define KEYP_SIR 0x15
95#define KEYP_EDR 0x16 /* edge triggers */
96#define KEYP_SIH_CTRL 0x17
97
98/* KEYP_CTRL_REG Fields */
99#define KEYP_CTRL_SOFT_NRST BIT(0)
100#define KEYP_CTRL_SOFTMODEN BIT(1)
101#define KEYP_CTRL_LK_EN BIT(2)
102#define KEYP_CTRL_TOE_EN BIT(3)
103#define KEYP_CTRL_TOLE_EN BIT(4)
104#define KEYP_CTRL_RP_EN BIT(5)
105#define KEYP_CTRL_KBD_ON BIT(6)
106
107/* KEYP_DEB, KEYP_LONG_KEY, KEYP_TIMEOUT_x*/
108#define KEYP_PERIOD_US(t, prescale) ((t) / (31 << (prescale + 1)) - 1)
109
110/* KEYP_LK_PTV_REG Fields */
111#define KEYP_LK_PTV_PTV_SHIFT 5
112
113/* KEYP_{IMR,ISR,SIR} Fields */
114#define KEYP_IMR1_MIS BIT(3)
115#define KEYP_IMR1_TO BIT(2)
116#define KEYP_IMR1_LK BIT(1)
117#define KEYP_IMR1_KP BIT(0)
118
119/* KEYP_EDR Fields */
120#define KEYP_EDR_KP_FALLING 0x01
121#define KEYP_EDR_KP_RISING 0x02
122#define KEYP_EDR_KP_BOTH 0x03
123#define KEYP_EDR_LK_FALLING 0x04
124#define KEYP_EDR_LK_RISING 0x08
125#define KEYP_EDR_TO_FALLING 0x10
126#define KEYP_EDR_TO_RISING 0x20
127#define KEYP_EDR_MIS_FALLING 0x40
128#define KEYP_EDR_MIS_RISING 0x80
129
130
131/*----------------------------------------------------------------------*/
132
133static int twl4030_kpread(struct twl4030_keypad *kp,
134 u8 *data, u32 reg, u8 num_bytes)
135{
136 int ret = twl4030_i2c_read(TWL4030_MODULE_KEYPAD, data, reg, num_bytes);
137
138 if (ret < 0)
139 dev_warn(kp->dbg_dev,
140 "Couldn't read TWL4030: %X - ret %d[%x]\n",
141 reg, ret, ret);
142
143 return ret;
144}
145
146static int twl4030_kpwrite_u8(struct twl4030_keypad *kp, u8 data, u32 reg)
147{
148 int ret = twl4030_i2c_write_u8(TWL4030_MODULE_KEYPAD, data, reg);
149
150 if (ret < 0)
151 dev_warn(kp->dbg_dev,
152 "Could not write TWL4030: %X - ret %d[%x]\n",
153 reg, ret, ret);
154
155 return ret;
156}
157
158static inline u16 twl4030_col_xlate(struct twl4030_keypad *kp, u8 col)
159{
160 /* If all bits in a row are active for all coloumns then
161 * we have that row line connected to gnd. Mark this
162 * key on as if it was on matrix position n_cols (ie
163 * one higher than the size of the matrix).
164 */
165 if (col == 0xFF)
166 return 1 << kp->n_cols;
167 else
168 return col & ((1 << kp->n_cols) - 1);
169}
170
171static int twl4030_read_kp_matrix_state(struct twl4030_keypad *kp, u16 *state)
172{
173 u8 new_state[TWL4030_MAX_ROWS];
174 int row;
175 int ret = twl4030_kpread(kp, new_state,
176 KEYP_FULL_CODE_7_0, kp->n_rows);
177 if (ret >= 0)
178 for (row = 0; row < kp->n_rows; row++)
179 state[row] = twl4030_col_xlate(kp, new_state[row]);
180
181 return ret;
182}
183
184static int twl4030_is_in_ghost_state(struct twl4030_keypad *kp, u16 *key_state)
185{
186 int i;
187 u16 check = 0;
188
189 for (i = 0; i < kp->n_rows; i++) {
190 u16 col = key_state[i];
191
192 if ((col & check) && hweight16(col) > 1)
193 return 1;
194
195 check |= col;
196 }
197
198 return 0;
199}
200
201static void twl4030_kp_scan(struct twl4030_keypad *kp, bool release_all)
202{
203 struct input_dev *input = kp->input;
204 u16 new_state[TWL4030_MAX_ROWS];
205 int col, row;
206
207 if (release_all)
208 memset(new_state, 0, sizeof(new_state));
209 else {
210 /* check for any changes */
211 int ret = twl4030_read_kp_matrix_state(kp, new_state);
212
213 if (ret < 0) /* panic ... */
214 return;
215
216 if (twl4030_is_in_ghost_state(kp, new_state))
217 return;
218 }
219
220 /* check for changes and print those */
221 for (row = 0; row < kp->n_rows; row++) {
222 int changed = new_state[row] ^ kp->kp_state[row];
223
224 if (!changed)
225 continue;
226
227 for (col = 0; col < kp->n_cols; col++) {
228 int code;
229
230 if (!(changed & (1 << col)))
231 continue;
232
233 dev_dbg(kp->dbg_dev, "key [%d:%d] %s\n", row, col,
234 (new_state[row] & (1 << col)) ?
235 "press" : "release");
236
237 code = MATRIX_SCAN_CODE(row, col, TWL4030_ROW_SHIFT);
238 input_event(input, EV_MSC, MSC_SCAN, code);
239 input_report_key(input, kp->keymap[code],
240 new_state[row] & (1 << col));
241 }
242 kp->kp_state[row] = new_state[row];
243 }
244 input_sync(input);
245}
246
247/*
248 * Keypad interrupt handler
249 */
250static irqreturn_t do_kp_irq(int irq, void *_kp)
251{
252 struct twl4030_keypad *kp = _kp;
253 u8 reg;
254 int ret;
255
256#ifdef CONFIG_LOCKDEP
257 /* WORKAROUND for lockdep forcing IRQF_DISABLED on us, which
258 * we don't want and can't tolerate. Although it might be
259 * friendlier not to borrow this thread context...
260 */
261 local_irq_enable();
262#endif
263
264 /* Read & Clear TWL4030 pending interrupt */
265 ret = twl4030_kpread(kp, &reg, KEYP_ISR1, 1);
266
267 /* Release all keys if I2C has gone bad or
268 * the KEYP has gone to idle state */
269 if (ret >= 0 && (reg & KEYP_IMR1_KP))
270 twl4030_kp_scan(kp, false);
271 else
272 twl4030_kp_scan(kp, true);
273
274 return IRQ_HANDLED;
275}
276
277static int __devinit twl4030_kp_program(struct twl4030_keypad *kp)
278{
279 u8 reg;
280 int i;
281
282 /* Enable controller, with hardware decoding but not autorepeat */
283 reg = KEYP_CTRL_SOFT_NRST | KEYP_CTRL_SOFTMODEN
284 | KEYP_CTRL_TOE_EN | KEYP_CTRL_KBD_ON;
285 if (twl4030_kpwrite_u8(kp, reg, KEYP_CTRL) < 0)
286 return -EIO;
287
288 /* NOTE: we could use sih_setup() here to package keypad
289 * event sources as four different IRQs ... but we don't.
290 */
291
292 /* Enable TO rising and KP rising and falling edge detection */
293 reg = KEYP_EDR_KP_BOTH | KEYP_EDR_TO_RISING;
294 if (twl4030_kpwrite_u8(kp, reg, KEYP_EDR) < 0)
295 return -EIO;
296
297 /* Set PTV prescaler Field */
298 reg = (PTV_PRESCALER << KEYP_LK_PTV_PTV_SHIFT);
299 if (twl4030_kpwrite_u8(kp, reg, KEYP_LK_PTV) < 0)
300 return -EIO;
301
302 /* Set key debounce time to 20 ms */
303 i = KEYP_PERIOD_US(20000, PTV_PRESCALER);
304 if (twl4030_kpwrite_u8(kp, i, KEYP_DEB) < 0)
305 return -EIO;
306
307 /* Set timeout period to 100 ms */
308 i = KEYP_PERIOD_US(200000, PTV_PRESCALER);
309 if (twl4030_kpwrite_u8(kp, (i & 0xFF), KEYP_TIMEOUT_L) < 0)
310 return -EIO;
311
312 if (twl4030_kpwrite_u8(kp, (i >> 8), KEYP_TIMEOUT_H) < 0)
313 return -EIO;
314
315 /*
316 * Enable Clear-on-Read; disable remembering events that fire
317 * after the IRQ but before our handler acks (reads) them,
318 */
319 reg = TWL4030_SIH_CTRL_COR_MASK | TWL4030_SIH_CTRL_PENDDIS_MASK;
320 if (twl4030_kpwrite_u8(kp, reg, KEYP_SIH_CTRL) < 0)
321 return -EIO;
322
323 /* initialize key state; irqs update it from here on */
324 if (twl4030_read_kp_matrix_state(kp, kp->kp_state) < 0)
325 return -EIO;
326
327 return 0;
328}
329
330/*
331 * Registers keypad device with input subsystem
332 * and configures TWL4030 keypad registers
333 */
334static int __devinit twl4030_kp_probe(struct platform_device *pdev)
335{
336 struct twl4030_keypad_data *pdata = pdev->dev.platform_data;
337 const struct matrix_keymap_data *keymap_data = pdata->keymap_data;
338 struct twl4030_keypad *kp;
339 struct input_dev *input;
340 u8 reg;
341 int error;
342
343 if (!pdata || !pdata->rows || !pdata->cols ||
344 pdata->rows > TWL4030_MAX_ROWS || pdata->cols > TWL4030_MAX_COLS) {
345 dev_err(&pdev->dev, "Invalid platform_data\n");
346 return -EINVAL;
347 }
348
349 kp = kzalloc(sizeof(*kp), GFP_KERNEL);
350 input = input_allocate_device();
351 if (!kp || !input) {
352 error = -ENOMEM;
353 goto err1;
354 }
355
356 /* Get the debug Device */
357 kp->dbg_dev = &pdev->dev;
358 kp->input = input;
359
360 kp->n_rows = pdata->rows;
361 kp->n_cols = pdata->cols;
362 kp->irq = platform_get_irq(pdev, 0);
363
364 /* setup input device */
365 __set_bit(EV_KEY, input->evbit);
366
367 /* Enable auto repeat feature of Linux input subsystem */
368 if (pdata->rep)
369 __set_bit(EV_REP, input->evbit);
370
371 input_set_capability(input, EV_MSC, MSC_SCAN);
372
373 input->name = "TWL4030 Keypad";
374 input->phys = "twl4030_keypad/input0";
375 input->dev.parent = &pdev->dev;
376
377 input->id.bustype = BUS_HOST;
378 input->id.vendor = 0x0001;
379 input->id.product = 0x0001;
380 input->id.version = 0x0003;
381
382 input->keycode = kp->keymap;
383 input->keycodesize = sizeof(kp->keymap[0]);
384 input->keycodemax = ARRAY_SIZE(kp->keymap);
385
386 matrix_keypad_build_keymap(keymap_data, TWL4030_ROW_SHIFT,
387 input->keycode, input->keybit);
388
389 error = input_register_device(input);
390 if (error) {
391 dev_err(kp->dbg_dev,
392 "Unable to register twl4030 keypad device\n");
393 goto err1;
394 }
395
396 error = twl4030_kp_program(kp);
397 if (error)
398 goto err2;
399
400 /*
401 * This ISR will always execute in kernel thread context because of
402 * the need to access the TWL4030 over the I2C bus.
403 *
404 * NOTE: we assume this host is wired to TWL4040 INT1, not INT2 ...
405 */
406 error = request_irq(kp->irq, do_kp_irq, 0, pdev->name, kp);
407 if (error) {
408 dev_info(kp->dbg_dev, "request_irq failed for irq no=%d\n",
409 kp->irq);
410 goto err3;
411 }
412
413 /* Enable KP and TO interrupts now. */
414 reg = (u8) ~(KEYP_IMR1_KP | KEYP_IMR1_TO);
415 if (twl4030_kpwrite_u8(kp, reg, KEYP_IMR1)) {
416 error = -EIO;
417 goto err4;
418 }
419
420 platform_set_drvdata(pdev, kp);
421 return 0;
422
423err4:
424 /* mask all events - we don't care about the result */
425 (void) twl4030_kpwrite_u8(kp, 0xff, KEYP_IMR1);
426err3:
427 free_irq(kp->irq, NULL);
428err2:
429 input_unregister_device(input);
430 input = NULL;
431err1:
432 input_free_device(input);
433 kfree(kp);
434 return error;
435}
436
437static int __devexit twl4030_kp_remove(struct platform_device *pdev)
438{
439 struct twl4030_keypad *kp = platform_get_drvdata(pdev);
440
441 free_irq(kp->irq, kp);
442 input_unregister_device(kp->input);
443 platform_set_drvdata(pdev, NULL);
444 kfree(kp);
445
446 return 0;
447}
448
449/*
450 * NOTE: twl4030 are multi-function devices connected via I2C.
451 * So this device is a child of an I2C parent, thus it needs to
452 * support unplug/replug (which most platform devices don't).
453 */
454
455static struct platform_driver twl4030_kp_driver = {
456 .probe = twl4030_kp_probe,
457 .remove = __devexit_p(twl4030_kp_remove),
458 .driver = {
459 .name = "twl4030_keypad",
460 .owner = THIS_MODULE,
461 },
462};
463
464static int __init twl4030_kp_init(void)
465{
466 return platform_driver_register(&twl4030_kp_driver);
467}
468module_init(twl4030_kp_init);
469
470static void __exit twl4030_kp_exit(void)
471{
472 platform_driver_unregister(&twl4030_kp_driver);
473}
474module_exit(twl4030_kp_exit);
475
476MODULE_AUTHOR("Texas Instruments");
477MODULE_DESCRIPTION("TWL4030 Keypad Driver");
478MODULE_LICENSE("GPL");
479MODULE_ALIAS("platform:twl4030_keypad");
480