]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blob - drivers/input/keyboard/omap-twl4030keypad.c
Merge omap-upstream
[linux-2.6-omap-h63xx.git] / drivers / input / keyboard / omap-twl4030keypad.c
1 /*
2  * drivers/input/keyboard/omap-twl4030keypad.c
3  *
4  * Copyright (C) 2007 Texas Instruments, Inc.
5  *
6  * Code re-written for 2430SDP by:
7  * Syed Mohammed Khasim <x0khasim@ti.com>
8  *
9  * Initial Code:
10  * Manjunatha G K <manjugk@ti.com>
11  *
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.
16  *
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.
21  *
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
25  */
26
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>
36 #include <asm/irq.h>
37 #include <asm/arch/keypad.h>
38 #include <asm/arch/twl4030.h>
39 #include "twl4030-keypad.h"
40
41 #define OMAP_TWL4030KP_LOG_LEVEL        0
42
43 #define KEY(col, row, val)      (((col) << 28) | ((row) << 24) | (val))
44 #define NUM_ROWS                5
45 #define NUM_COLS                6
46
47 #define ROW_MASK                ((1<<NUM_ROWS)-1)
48
49 #define SCAN_RATE               HZ/20
50 #define KEYNUM_MASK             0xFF000000
51 #define ROWCOL_MASK             0x00FFFFFF
52
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"},
59 };
60
61 /* Global variables */
62 static int *keymap;
63 static unsigned char kp_state[NUM_ROWS];
64 static struct device * dbg_dev;
65
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);
73
74 static int twl4030_kpread_u8(u32 module, u8 * data, u32 reg)
75 {
76         int ret;
77
78         ret = twl4030_i2c_read_u8(module, data, reg);
79         if (ret < 0) {
80                 dev_warn(dbg_dev, "Couldn't read TWL4030 register %X - ret %d[%x]\n",
81                          reg, ret, ret);
82                 return ret;
83         }
84         return ret;
85 }
86
87 static int twl4030_kpwrite_u8(u32 module, u8 data, u32 reg)
88 {
89         int ret;
90
91         ret = twl4030_i2c_write_u8(module, data, reg);
92         if (ret < 0) {
93                 dev_warn(dbg_dev, "Could not write TWL4030 register %X - ret %d[%x]\n",
94                          reg, ret, ret);
95                 return ret;
96         }
97         return ret;
98 }
99
100 static inline int omap_kp_find_key(int col, int row)
101 {
102         int i, key;
103
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;
108
109         return -EINVAL;
110 }
111
112 static void twl4030_kp_scan(void)
113 {
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;
117         int ret;
118
119         /* check for any changes */
120         ret =
121                 twl4030_i2c_read(TWL4030_MODULE_KEYPAD, new_state, code_reg,
122                                  NUM_ROWS);
123         if (ret < 0)
124                 dev_warn(dbg_dev, "Could not read TWL4030 register %X - ret %d[%x]\n",
125                          code_reg, ret, ret);
126
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];
131
132                 if (changed == 0)
133                         continue;
134
135                 for (col = 0; col < NUM_COLS; col++) {
136                         int key;
137
138                         if (!(changed & (1 << col)))
139                                 continue;
140
141                         dev_dbg(dbg_dev, "key %s %s\n", switch_name[row][col],
142                                 (new_state[row] & (1 << col)) ?
143                                 "press" : "release");
144
145                         key = omap_kp_find_key(col, row);
146                         if (key < 0) {
147                                 dev_warn(dbg_dev, "omap-kp: Spurious key event %d-%d\n",
148                                          col, row);
149                                 /* We scan again after a couple of seconds */
150                                 spurious = 1;
151                                 continue;
152                         }
153                         input_report_key(omap_twl4030kp, key,
154                                          new_state[row] & (1 << col));
155                 }
156         }
157         if (key_down) {
158                 /*
159                  * some key is pressed - keep irq disabled and use timer
160                  * to poll for key release
161                  */
162                 if (spurious)
163                         mod_timer(&kptimer, jiffies + SCAN_RATE * 2);
164                 else
165                         mod_timer(&kptimer, jiffies + SCAN_RATE);
166         }
167         memcpy(kp_state, new_state, sizeof(kp_state));
168 }
169
170 static void twl4030_timer_work(struct work_struct *unused)
171 {
172         twl4030_kp_scan();
173 }
174
175 void omap_kp_timer(unsigned long data)
176 {
177         schedule_work(&timer_work);
178 }
179
180 /*
181  * Keypad interrupt handler
182  */
183 static irqreturn_t do_kp_irq(int irq, void *dev_id)
184 {
185         u8 reg;
186         int ret;
187
188         /* Mask keypad interrupts */
189         reg = KEYP_IMR1_MASK;
190         ret = twl4030_kpwrite_u8(TWL4030_MODULE_KEYPAD, reg, REG_KEYP_IMR1);
191
192         /*
193          * Scan keypad for any changes
194          * in keypad matrix.
195          */
196         twl4030_kp_scan();
197
198         /* Clear TWL4030 PIH interrupt */
199         ret = twl4030_kpread_u8(TWL4030_MODULE_KEYPAD, &reg, REG_KEYP_ISR1);
200
201         /* Enable interrupts */
202         reg = KEYP_IMR1_UNMASK;
203         ret = twl4030_kpwrite_u8(TWL4030_MODULE_KEYPAD, reg, REG_KEYP_IMR1);
204
205         return IRQ_HANDLED;
206 }
207
208 /*
209  * Registers keypad device with input sub system
210  * and configures TWL4030 keypad registers
211  *
212  */
213 static int __init omap_kp_probe(struct platform_device *pdev)
214 {
215         u8 reg, i;
216         u8 code_reg = REG_FULL_CODE_7_0;
217         int ret = 0;
218         struct omap_kp_platform_data *pdata =  pdev->dev.platform_data;
219
220         /* Get the debug Device */
221         dbg_dev = &(pdev->dev);
222
223         if (!pdata->rows || !pdata->cols || !pdata->keymap) {
224                 dev_err(dbg_dev, "No rows, cols or keymap from pdata\n");
225                 return -EINVAL;
226         }
227
228         omap_twl4030kp = input_allocate_device();
229         if (omap_twl4030kp == NULL)
230                 return -ENOMEM;
231
232         keymap = pdata->keymap;
233
234         /* setup input device */
235         set_bit(EV_KEY, omap_twl4030kp->evbit);
236
237         /* Enable auto repeat feature of Linux input subsystem */
238         set_bit(EV_REP, omap_twl4030kp->evbit);
239
240         for (i = 0; keymap[i] != 0; i++)
241                 set_bit(keymap[i] & 0x00ffffff, omap_twl4030kp->keybit);
242
243         omap_twl4030kp->name            = "omap_twl4030keypad";
244         omap_twl4030kp->phys            = "omap_twl4030keypad/input0";
245         omap_twl4030kp->dev.parent      = &pdev->dev;
246
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;
251
252         omap_twl4030kp->keycode         = keymap;
253         omap_twl4030kp->keycodesize     = sizeof(unsigned int);
254         omap_twl4030kp->keycodemax      = pdata->keymapsize;
255
256         ret = input_register_device(omap_twl4030kp);
257         if (ret < 0) {
258                 dev_err(dbg_dev, "Unable to register twl4030 keypad device\n");
259                 goto err2;
260         }
261
262         setup_timer(&kptimer,omap_kp_timer,(unsigned long) omap_twl4030kp);
263
264         /*
265          * Since keypad driver uses I2C for reading
266          * twl4030 keypad registers, tasklets cannot
267          * be used.
268          */
269         INIT_WORK(&timer_work, twl4030_timer_work);
270
271         reg = KEYP_CTRL_REG_MASK_NOAUTORPT;
272         ret = twl4030_kpwrite_u8(TWL4030_MODULE_KEYPAD, reg,
273                                                 REG_KEYP_CTRL_REG);
274         if (ret < 0)
275                 goto err3;
276
277         /* Set all events to Falling Edge detection */
278         reg = KEYP_EDR_MASK;
279         ret = twl4030_kpwrite_u8(TWL4030_MODULE_KEYPAD, reg, REG_KEYP_EDR);
280         if (ret < 0)
281                 goto err3;
282
283         /* Set Pre Scalar Field PTV to 4 */
284         reg = BIT_LK_PTV_REG_PTV_MASK & (BIT_PTV_REG_PTV4 << BIT_LK_PTV_REG_PTV);
285
286         ret = twl4030_kpwrite_u8(TWL4030_MODULE_KEYPAD, reg, REG_LK_PTV_REG);
287         if (ret < 0)
288                 goto err3;
289
290         /*
291          * Set key debounce time to 10 ms using equation
292          * Tint = Tclk * (LOAD_TIM+1) * 2^(PTV+1)
293          * Where Tclk = 31.25 us ( since kbd_if_clk is 32KHz)
294          * PTV = 4 for all the operations.
295          */
296         ret = twl4030_kpwrite_u8(TWL4030_MODULE_KEYPAD, 0x3f,
297                                                 REG_KEY_DEB_REG);
298         if (ret < 0)
299                 goto err3;
300
301         /* Set SIH Ctrl register */
302         reg = KEYP_SIH_CTRL_MASK;
303         ret = twl4030_kpwrite_u8(TWL4030_MODULE_KEYPAD, reg,
304                                                 REG_KEYP_SIH_CTRL);
305         if (ret < 0)
306                 goto err3;
307
308         /*
309          * This ISR will always execute in kernel thread context because of
310          * the need to access the TWL4030 over the I2C bus.
311          */
312         ret = request_irq(TWL4030_MODIRQ_KEYPAD, do_kp_irq,
313                 IRQF_DISABLED, "TWL4030 Keypad", omap_twl4030kp);
314         if (ret < 0) {
315                 dev_info(dbg_dev, "request_irq failed for irq no=%d\n",
316                         TWL4030_MODIRQ_KEYPAD);
317                 goto err3;
318         } else {
319                 /* Enable keypad module interrupts now. */
320                 reg = KEYP_IMR1_UNMASK;
321                 ret = twl4030_kpwrite_u8(TWL4030_MODULE_KEYPAD, reg,
322                                                 REG_KEYP_IMR1);
323                 if (ret < 0) {
324                         /* mask all events - dont care abt result */
325                         (void)twl4030_kpwrite_u8(TWL4030_MODULE_KEYPAD, 0xff,
326                                                  REG_KEYP_IMR1);
327                         goto err4;
328                 }
329         }
330
331         /* Read initial state of keypad matrix. */
332         ret = twl4030_i2c_read(TWL4030_MODULE_KEYPAD, kp_state, code_reg,
333                 NUM_ROWS);
334         if (ret < 0) {
335                 dev_warn(dbg_dev, "Could not read TWL4030 register %X - ret %d[%x]\n",
336                          reg, ret, ret);
337                 goto err4;
338         }
339         return (ret);
340 err4:
341         free_irq(TWL4030_MODIRQ_KEYPAD, NULL);
342 err3:
343         input_unregister_device(omap_twl4030kp);
344 err2:
345         input_free_device(omap_twl4030kp);
346         return -ENODEV;
347 }
348
349 static int omap_kp_remove(struct platform_device *pdev)
350 {
351         free_irq(TWL4030_MODIRQ_KEYPAD, NULL);
352         del_timer_sync(&kptimer);
353
354         input_unregister_device(omap_twl4030kp);
355         return 0;
356 }
357
358
359 static struct platform_driver omap_kp_driver = {
360         .probe          = omap_kp_probe,
361         .remove         = omap_kp_remove,
362         .driver         = {
363                 .name   = "omap_twl4030keypad",
364         },
365 };
366
367 /*
368  * OMAP TWL4030 Keypad init
369  */
370 static int __devinit omap_kp_init(void)
371 {
372         return platform_driver_register(&omap_kp_driver);
373 }
374
375 static void __exit omap_kp_exit(void)
376 {
377         platform_driver_unregister(&omap_kp_driver);
378 }
379
380 module_init(omap_kp_init);
381 module_exit(omap_kp_exit);
382 MODULE_AUTHOR("Texas Instruments");
383 MODULE_DESCRIPTION("OMAP TWL4030 Keypad Driver");
384 MODULE_LICENSE("GPL");