2 * drivers/input/keyboard/omap-twl4030keypad.c
4 * Copyright (C) 2007 Texas Instruments, Inc.
6 * Code re-written for 2430SDP by:
7 * Syed Mohammed Khasim <x0khasim@ti.com>
10 * Manjunatha G K <manjugk@ti.com>
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27 #include <linux/module.h>
28 #include <linux/init.h>
29 #include <linux/interrupt.h>
30 #include <linux/types.h>
31 #include <linux/input.h>
32 #include <linux/kernel.h>
33 #include <linux/delay.h>
34 #include <linux/platform_device.h>
35 #include <linux/i2c.h>
37 #include <asm/arch/keypad.h>
38 #include <asm/arch/twl4030.h>
39 #include "twl4030-keypad.h"
41 #define OMAP_TWL4030KP_LOG_LEVEL 0
43 #define KEY(col, row, val) (((col) << 28) | ((row) << 24) | (val))
47 #define ROW_MASK ((1<<NUM_ROWS)-1)
49 #define SCAN_RATE HZ/20
50 #define KEYNUM_MASK 0xFF000000
51 #define ROWCOL_MASK 0x00FFFFFF
53 static char *switch_name[NUM_ROWS][NUM_COLS] = {
54 {"S2_L", "S2_D", "S2_S", "S3", "S4", "S23"},
55 {"S2_R", "S2_U", "S5", "S6", "S7", "S24"},
56 {"S8", "S9", "S10", "S11", "S12", "S25"},
57 {"S13", "S14", "S15", "S16", "S17", "S26"},
58 {"S18", "S19", "S20", "S21", "S22", "S27"},
61 /* Global variables */
63 static unsigned char kp_state[NUM_ROWS];
64 static struct device * dbg_dev;
66 /* Function Templates */
67 static struct input_dev *omap_twl4030kp;
68 struct timer_list kptimer;
69 static void omap_kp_timer(unsigned long);
70 static void twl4030_kp_scan(void);
71 struct work_struct timer_work;
72 static void twl4030_timer_work(struct work_struct *unused);
74 static int twl4030_kpread_u8(u32 module, u8 * data, u32 reg)
78 ret = twl4030_i2c_read_u8(module, data, reg);
80 dev_warn(dbg_dev, "Couldn't read TWL4030 register %X - ret %d[%x]\n",
87 static int twl4030_kpwrite_u8(u32 module, u8 data, u32 reg)
91 ret = twl4030_i2c_write_u8(module, data, reg);
93 dev_warn(dbg_dev, "Could not write TWL4030 register %X - ret %d[%x]\n",
100 static inline int omap_kp_find_key(int col, int row)
104 key = KEY(col, row, 0);
105 for (i = 0; keymap[i] != 0; i++)
106 if ((keymap[i] & KEYNUM_MASK) == key)
107 return keymap[i] & ROWCOL_MASK;
112 static void twl4030_kp_scan(void)
114 unsigned char new_state[NUM_ROWS], changed, key_down = 0;
115 u8 col, row, spurious = 0;
116 u8 code_reg = REG_FULL_CODE_7_0;
119 /* check for any changes */
121 twl4030_i2c_read(TWL4030_MODULE_KEYPAD, new_state, code_reg,
124 dev_warn(dbg_dev, "Could not read TWL4030 register %X - ret %d[%x]\n",
127 /* check for changes and print those */
128 for (row = 0; row < NUM_ROWS; row++) {
129 changed = new_state[row] ^ kp_state[row];
130 key_down |= new_state[row];
135 for (col = 0; col < NUM_COLS; col++) {
138 if (!(changed & (1 << col)))
141 dev_dbg(dbg_dev, "key %s %s\n", switch_name[row][col],
142 (new_state[row] & (1 << col)) ?
143 "press" : "release");
145 key = omap_kp_find_key(col, row);
147 dev_warn(dbg_dev, "omap-kp: Spurious key event %d-%d\n",
149 /* We scan again after a couple of seconds */
153 input_report_key(omap_twl4030kp, key,
154 new_state[row] & (1 << col));
159 * some key is pressed - keep irq disabled and use timer
160 * to poll for key release
163 mod_timer(&kptimer, jiffies + SCAN_RATE * 2);
165 mod_timer(&kptimer, jiffies + SCAN_RATE);
167 memcpy(kp_state, new_state, sizeof(kp_state));
170 static void twl4030_timer_work(struct work_struct *unused)
175 void omap_kp_timer(unsigned long data)
177 schedule_work(&timer_work);
181 * Keypad interrupt handler
183 static irqreturn_t do_kp_irq(int irq, void *dev_id)
188 /* Mask keypad interrupts */
189 reg = KEYP_IMR1_MASK;
190 ret = twl4030_kpwrite_u8(TWL4030_MODULE_KEYPAD, reg, REG_KEYP_IMR1);
193 * Scan keypad for any changes
198 /* Clear TWL4030 PIH interrupt */
199 ret = twl4030_kpread_u8(TWL4030_MODULE_KEYPAD, ®, REG_KEYP_ISR1);
201 /* Enable interrupts */
202 reg = KEYP_IMR1_UNMASK;
203 ret = twl4030_kpwrite_u8(TWL4030_MODULE_KEYPAD, reg, REG_KEYP_IMR1);
209 * Registers keypad device with input sub system
210 * and configures TWL4030 keypad registers
213 static int __init omap_kp_probe(struct platform_device *pdev)
216 u8 code_reg = REG_FULL_CODE_7_0;
218 struct omap_kp_platform_data *pdata = pdev->dev.platform_data;
220 /* Get the debug Device */
221 dbg_dev = &(pdev->dev);
223 if (!pdata->rows || !pdata->cols || !pdata->keymap) {
224 dev_err(dbg_dev, "No rows, cols or keymap from pdata\n");
228 omap_twl4030kp = input_allocate_device();
229 if (omap_twl4030kp == NULL)
232 keymap = pdata->keymap;
234 /* setup input device */
235 set_bit(EV_KEY, omap_twl4030kp->evbit);
237 /* Enable auto repeat feature of Linux input subsystem */
238 set_bit(EV_REP, omap_twl4030kp->evbit);
240 for (i = 0; keymap[i] != 0; i++)
241 set_bit(keymap[i] & 0x00ffffff, omap_twl4030kp->keybit);
243 omap_twl4030kp->name = "omap_twl4030keypad";
244 omap_twl4030kp->phys = "omap_twl4030keypad/input0";
245 omap_twl4030kp->dev.parent = &pdev->dev;
247 omap_twl4030kp->id.bustype = BUS_HOST;
248 omap_twl4030kp->id.vendor = 0x0001;
249 omap_twl4030kp->id.product = 0x0001;
250 omap_twl4030kp->id.version = 0x0003;
252 omap_twl4030kp->keycode = keymap;
253 omap_twl4030kp->keycodesize = sizeof(unsigned int);
254 omap_twl4030kp->keycodemax = pdata->keymapsize;
256 /* Get the debug Device to omap keypad device */
257 dbg_dev = &(omap_twl4030kp->dev);
259 ret = input_register_device(omap_twl4030kp);
261 dev_err(dbg_dev, "Unable to register twl4030 keypad device\n");
265 setup_timer(&kptimer,omap_kp_timer,(unsigned long) omap_twl4030kp);
268 * Since keypad driver uses I2C for reading
269 * twl4030 keypad registers, tasklets cannot
272 INIT_WORK(&timer_work, twl4030_timer_work);
274 reg = KEYP_CTRL_REG_MASK_NOAUTORPT;
275 ret = twl4030_kpwrite_u8(TWL4030_MODULE_KEYPAD, reg,
280 /* Set all events to Falling Edge detection */
282 ret = twl4030_kpwrite_u8(TWL4030_MODULE_KEYPAD, reg, REG_KEYP_EDR);
286 /* Set Pre Scalar Field PTV to 4 */
287 reg = BIT_LK_PTV_REG_PTV_MASK & (BIT_PTV_REG_PTV4 << BIT_LK_PTV_REG_PTV);
289 ret = twl4030_kpwrite_u8(TWL4030_MODULE_KEYPAD, reg, REG_LK_PTV_REG);
294 * Set key debounce time to 10 ms using equation
295 * Tint = Tclk * (LOAD_TIM+1) * 2^(PTV+1)
296 * Where Tclk = 31.25 us ( since kbd_if_clk is 32KHz)
297 * PTV = 4 for all the operations.
299 ret = twl4030_kpwrite_u8(TWL4030_MODULE_KEYPAD, 0x3f,
304 /* Set SIH Ctrl register */
305 reg = KEYP_SIH_CTRL_MASK;
306 ret = twl4030_kpwrite_u8(TWL4030_MODULE_KEYPAD, reg,
312 * This ISR will always execute in kernel thread context because of
313 * the need to access the TWL4030 over the I2C bus.
315 ret = request_irq(TWL4030_MODIRQ_KEYPAD, do_kp_irq,
316 IRQF_DISABLED, "TWL4030 Keypad", omap_twl4030kp);
318 dev_info(dbg_dev, "request_irq failed for irq no=%d\n",
319 TWL4030_MODIRQ_KEYPAD);
322 /* Enable keypad module interrupts now. */
323 reg = KEYP_IMR1_UNMASK;
324 ret = twl4030_kpwrite_u8(TWL4030_MODULE_KEYPAD, reg,
327 /* mask all events - dont care abt result */
328 (void)twl4030_kpwrite_u8(TWL4030_MODULE_KEYPAD, 0xff,
334 /* Read initial state of keypad matrix. */
335 ret = twl4030_i2c_read(TWL4030_MODULE_KEYPAD, kp_state, code_reg,
338 dev_warn(dbg_dev, "Could not read TWL4030 register %X - ret %d[%x]\n",
344 free_irq(TWL4030_MODIRQ_KEYPAD, NULL);
346 input_unregister_device(omap_twl4030kp);
348 input_free_device(omap_twl4030kp);
352 static int omap_kp_remove(struct platform_device *pdev)
354 free_irq(TWL4030_MODIRQ_KEYPAD, NULL);
355 del_timer_sync(&kptimer);
357 input_unregister_device(omap_twl4030kp);
362 static struct platform_driver omap_kp_driver = {
363 .probe = omap_kp_probe,
364 .remove = omap_kp_remove,
366 .name = "omap_twl4030keypad",
371 * OMAP TWL4030 Keypad init
373 static int __devinit omap_kp_init(void)
375 return platform_driver_register(&omap_kp_driver);
378 static void __exit omap_kp_exit(void)
380 platform_driver_unregister(&omap_kp_driver);
383 module_init(omap_kp_init);
384 module_exit(omap_kp_exit);
385 MODULE_AUTHOR("Texas Instruments");
386 MODULE_DESCRIPTION("OMAP TWL4030 Keypad Driver");
387 MODULE_LICENSE("GPL");