linux/drivers/input/keyboard/omap-keypad.c
<<
>>
Prefs
   1/*
   2 * linux/drivers/input/keyboard/omap-keypad.c
   3 *
   4 * OMAP Keypad Driver
   5 *
   6 * Copyright (C) 2003 Nokia Corporation
   7 * Written by Timo Teräs <ext-timo.teras@nokia.com>
   8 *
   9 * Added support for H2 & H3 Keypad
  10 * Copyright (C) 2004 Texas Instruments
  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/interrupt.h>
  29#include <linux/types.h>
  30#include <linux/input.h>
  31#include <linux/kernel.h>
  32#include <linux/delay.h>
  33#include <linux/platform_device.h>
  34#include <linux/mutex.h>
  35#include <linux/errno.h>
  36#include <linux/slab.h>
  37#include <linux/gpio.h>
  38#include <linux/platform_data/gpio-omap.h>
  39#include <linux/platform_data/keypad-omap.h>
  40
  41#undef NEW_BOARD_LEARNING_MODE
  42
  43static void omap_kp_tasklet(unsigned long);
  44static void omap_kp_timer(struct timer_list *);
  45
  46static unsigned char keypad_state[8];
  47static DEFINE_MUTEX(kp_enable_mutex);
  48static int kp_enable = 1;
  49static int kp_cur_group = -1;
  50
  51struct omap_kp {
  52        struct input_dev *input;
  53        struct timer_list timer;
  54        int irq;
  55        unsigned int rows;
  56        unsigned int cols;
  57        unsigned long delay;
  58        unsigned int debounce;
  59        unsigned short keymap[];
  60};
  61
  62static DECLARE_TASKLET_DISABLED(kp_tasklet, omap_kp_tasklet, 0);
  63
  64static unsigned int *row_gpios;
  65static unsigned int *col_gpios;
  66
  67static irqreturn_t omap_kp_interrupt(int irq, void *dev_id)
  68{
  69        /* disable keyboard interrupt and schedule for handling */
  70        omap_writew(1, OMAP1_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT);
  71
  72        tasklet_schedule(&kp_tasklet);
  73
  74        return IRQ_HANDLED;
  75}
  76
  77static void omap_kp_timer(struct timer_list *unused)
  78{
  79        tasklet_schedule(&kp_tasklet);
  80}
  81
  82static void omap_kp_scan_keypad(struct omap_kp *omap_kp, unsigned char *state)
  83{
  84        int col = 0;
  85
  86        /* disable keyboard interrupt and schedule for handling */
  87        omap_writew(1, OMAP1_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT);
  88
  89        /* read the keypad status */
  90        omap_writew(0xff, OMAP1_MPUIO_BASE + OMAP_MPUIO_KBC);
  91        for (col = 0; col < omap_kp->cols; col++) {
  92                omap_writew(~(1 << col) & 0xff,
  93                            OMAP1_MPUIO_BASE + OMAP_MPUIO_KBC);
  94
  95                udelay(omap_kp->delay);
  96
  97                state[col] = ~omap_readw(OMAP1_MPUIO_BASE +
  98                                         OMAP_MPUIO_KBR_LATCH) & 0xff;
  99        }
 100        omap_writew(0x00, OMAP1_MPUIO_BASE + OMAP_MPUIO_KBC);
 101        udelay(2);
 102}
 103
 104static void omap_kp_tasklet(unsigned long data)
 105{
 106        struct omap_kp *omap_kp_data = (struct omap_kp *) data;
 107        unsigned short *keycodes = omap_kp_data->input->keycode;
 108        unsigned int row_shift = get_count_order(omap_kp_data->cols);
 109        unsigned char new_state[8], changed, key_down = 0;
 110        int col, row;
 111
 112        /* check for any changes */
 113        omap_kp_scan_keypad(omap_kp_data, new_state);
 114
 115        /* check for changes and print those */
 116        for (col = 0; col < omap_kp_data->cols; col++) {
 117                changed = new_state[col] ^ keypad_state[col];
 118                key_down |= new_state[col];
 119                if (changed == 0)
 120                        continue;
 121
 122                for (row = 0; row < omap_kp_data->rows; row++) {
 123                        int key;
 124                        if (!(changed & (1 << row)))
 125                                continue;
 126#ifdef NEW_BOARD_LEARNING_MODE
 127                        printk(KERN_INFO "omap-keypad: key %d-%d %s\n", col,
 128                               row, (new_state[col] & (1 << row)) ?
 129                               "pressed" : "released");
 130#else
 131                        key = keycodes[MATRIX_SCAN_CODE(row, col, row_shift)];
 132
 133                        if (!(kp_cur_group == (key & GROUP_MASK) ||
 134                              kp_cur_group == -1))
 135                                continue;
 136
 137                        kp_cur_group = key & GROUP_MASK;
 138                        input_report_key(omap_kp_data->input, key & ~GROUP_MASK,
 139                                         new_state[col] & (1 << row));
 140#endif
 141                }
 142        }
 143        input_sync(omap_kp_data->input);
 144        memcpy(keypad_state, new_state, sizeof(keypad_state));
 145
 146        if (key_down) {
 147                /* some key is pressed - keep irq disabled and use timer
 148                 * to poll the keypad */
 149                mod_timer(&omap_kp_data->timer, jiffies + HZ / 20);
 150        } else {
 151                /* enable interrupts */
 152                omap_writew(0, OMAP1_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT);
 153                kp_cur_group = -1;
 154        }
 155}
 156
 157static ssize_t omap_kp_enable_show(struct device *dev,
 158                                   struct device_attribute *attr, char *buf)
 159{
 160        return sprintf(buf, "%u\n", kp_enable);
 161}
 162
 163static ssize_t omap_kp_enable_store(struct device *dev, struct device_attribute *attr,
 164                                    const char *buf, size_t count)
 165{
 166        struct omap_kp *omap_kp = dev_get_drvdata(dev);
 167        int state;
 168
 169        if (sscanf(buf, "%u", &state) != 1)
 170                return -EINVAL;
 171
 172        if ((state != 1) && (state != 0))
 173                return -EINVAL;
 174
 175        mutex_lock(&kp_enable_mutex);
 176        if (state != kp_enable) {
 177                if (state)
 178                        enable_irq(omap_kp->irq);
 179                else
 180                        disable_irq(omap_kp->irq);
 181                kp_enable = state;
 182        }
 183        mutex_unlock(&kp_enable_mutex);
 184
 185        return strnlen(buf, count);
 186}
 187
 188static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR, omap_kp_enable_show, omap_kp_enable_store);
 189
 190static int omap_kp_probe(struct platform_device *pdev)
 191{
 192        struct omap_kp *omap_kp;
 193        struct input_dev *input_dev;
 194        struct omap_kp_platform_data *pdata = dev_get_platdata(&pdev->dev);
 195        int i, col_idx, row_idx, ret;
 196        unsigned int row_shift, keycodemax;
 197
 198        if (!pdata->rows || !pdata->cols || !pdata->keymap_data) {
 199                printk(KERN_ERR "No rows, cols or keymap_data from pdata\n");
 200                return -EINVAL;
 201        }
 202
 203        row_shift = get_count_order(pdata->cols);
 204        keycodemax = pdata->rows << row_shift;
 205
 206        omap_kp = kzalloc(sizeof(struct omap_kp) +
 207                        keycodemax * sizeof(unsigned short), GFP_KERNEL);
 208        input_dev = input_allocate_device();
 209        if (!omap_kp || !input_dev) {
 210                kfree(omap_kp);
 211                input_free_device(input_dev);
 212                return -ENOMEM;
 213        }
 214
 215        platform_set_drvdata(pdev, omap_kp);
 216
 217        omap_kp->input = input_dev;
 218
 219        /* Disable the interrupt for the MPUIO keyboard */
 220        omap_writew(1, OMAP1_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT);
 221
 222        if (pdata->delay)
 223                omap_kp->delay = pdata->delay;
 224
 225        if (pdata->row_gpios && pdata->col_gpios) {
 226                row_gpios = pdata->row_gpios;
 227                col_gpios = pdata->col_gpios;
 228        }
 229
 230        omap_kp->rows = pdata->rows;
 231        omap_kp->cols = pdata->cols;
 232
 233        col_idx = 0;
 234        row_idx = 0;
 235
 236        timer_setup(&omap_kp->timer, omap_kp_timer, 0);
 237
 238        /* get the irq and init timer*/
 239        kp_tasklet.data = (unsigned long) omap_kp;
 240        tasklet_enable(&kp_tasklet);
 241
 242        ret = device_create_file(&pdev->dev, &dev_attr_enable);
 243        if (ret < 0)
 244                goto err2;
 245
 246        /* setup input device */
 247        input_dev->name = "omap-keypad";
 248        input_dev->phys = "omap-keypad/input0";
 249        input_dev->dev.parent = &pdev->dev;
 250
 251        input_dev->id.bustype = BUS_HOST;
 252        input_dev->id.vendor = 0x0001;
 253        input_dev->id.product = 0x0001;
 254        input_dev->id.version = 0x0100;
 255
 256        if (pdata->rep)
 257                __set_bit(EV_REP, input_dev->evbit);
 258
 259        ret = matrix_keypad_build_keymap(pdata->keymap_data, NULL,
 260                                         pdata->rows, pdata->cols,
 261                                         omap_kp->keymap, input_dev);
 262        if (ret < 0)
 263                goto err3;
 264
 265        ret = input_register_device(omap_kp->input);
 266        if (ret < 0) {
 267                printk(KERN_ERR "Unable to register omap-keypad input device\n");
 268                goto err3;
 269        }
 270
 271        if (pdata->dbounce)
 272                omap_writew(0xff, OMAP1_MPUIO_BASE + OMAP_MPUIO_GPIO_DEBOUNCING);
 273
 274        /* scan current status and enable interrupt */
 275        omap_kp_scan_keypad(omap_kp, keypad_state);
 276        omap_kp->irq = platform_get_irq(pdev, 0);
 277        if (omap_kp->irq >= 0) {
 278                if (request_irq(omap_kp->irq, omap_kp_interrupt, 0,
 279                                "omap-keypad", omap_kp) < 0)
 280                        goto err4;
 281        }
 282        omap_writew(0, OMAP1_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT);
 283
 284        return 0;
 285
 286err4:
 287        input_unregister_device(omap_kp->input);
 288        input_dev = NULL;
 289err3:
 290        device_remove_file(&pdev->dev, &dev_attr_enable);
 291err2:
 292        for (i = row_idx - 1; i >= 0; i--)
 293                gpio_free(row_gpios[i]);
 294        for (i = col_idx - 1; i >= 0; i--)
 295                gpio_free(col_gpios[i]);
 296
 297        kfree(omap_kp);
 298        input_free_device(input_dev);
 299
 300        return -EINVAL;
 301}
 302
 303static int omap_kp_remove(struct platform_device *pdev)
 304{
 305        struct omap_kp *omap_kp = platform_get_drvdata(pdev);
 306
 307        /* disable keypad interrupt handling */
 308        tasklet_disable(&kp_tasklet);
 309        omap_writew(1, OMAP1_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT);
 310        free_irq(omap_kp->irq, omap_kp);
 311
 312        del_timer_sync(&omap_kp->timer);
 313        tasklet_kill(&kp_tasklet);
 314
 315        /* unregister everything */
 316        input_unregister_device(omap_kp->input);
 317
 318        kfree(omap_kp);
 319
 320        return 0;
 321}
 322
 323static struct platform_driver omap_kp_driver = {
 324        .probe          = omap_kp_probe,
 325        .remove         = omap_kp_remove,
 326        .driver         = {
 327                .name   = "omap-keypad",
 328        },
 329};
 330module_platform_driver(omap_kp_driver);
 331
 332MODULE_AUTHOR("Timo Teräs");
 333MODULE_DESCRIPTION("OMAP Keypad Driver");
 334MODULE_LICENSE("GPL");
 335MODULE_ALIAS("platform:omap-keypad");
 336