Version:  2.0.40 2.2.26 2.4.37 2.6.39 3.0 3.1 3.2 3.3 3.4 3.5 3.6 3.7 3.8 3.9 3.10 3.11 3.12 3.13 3.14 3.15

Linux/drivers/input/keyboard/tc3589x-keypad.c

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

This page was automatically generated by LXR 0.3.1 (source).  •  Linux is a registered trademark of Linus Torvalds  •  Contact us