linux/drivers/input/keyboard/omap4-keypad.c
<<
>>
Prefs
   1/*
   2 * OMAP4 Keypad Driver
   3 *
   4 * Copyright (C) 2010 Texas Instruments
   5 *
   6 * Author: Abraham Arce <x0066660@ti.com>
   7 * Initial Code: Syed Rafiuddin <rafiuddin.syed@ti.com>
   8 *
   9 * This program is free software; you can redistribute it and/or modify
  10 * it under the terms of the GNU General Public License as published by
  11 * the Free Software Foundation; either version 2 of the License, or
  12 * (at your option) any later version.
  13 *
  14 * This program is distributed in the hope that it will be useful,
  15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17 * GNU General Public License for more details.
  18 *
  19 * You should have received a copy of the GNU General Public License
  20 * along with this program; if not, write to the Free Software
  21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  22 */
  23
  24#include <linux/module.h>
  25#include <linux/init.h>
  26#include <linux/interrupt.h>
  27#include <linux/platform_device.h>
  28#include <linux/errno.h>
  29#include <linux/io.h>
  30#include <linux/input.h>
  31#include <linux/slab.h>
  32
  33#include <plat/omap4-keypad.h>
  34
  35/* OMAP4 registers */
  36#define OMAP4_KBD_REVISION              0x00
  37#define OMAP4_KBD_SYSCONFIG             0x10
  38#define OMAP4_KBD_SYSSTATUS             0x14
  39#define OMAP4_KBD_IRQSTATUS             0x18
  40#define OMAP4_KBD_IRQENABLE             0x1C
  41#define OMAP4_KBD_WAKEUPENABLE          0x20
  42#define OMAP4_KBD_PENDING               0x24
  43#define OMAP4_KBD_CTRL                  0x28
  44#define OMAP4_KBD_DEBOUNCINGTIME        0x2C
  45#define OMAP4_KBD_LONGKEYTIME           0x30
  46#define OMAP4_KBD_TIMEOUT               0x34
  47#define OMAP4_KBD_STATEMACHINE          0x38
  48#define OMAP4_KBD_ROWINPUTS             0x3C
  49#define OMAP4_KBD_COLUMNOUTPUTS         0x40
  50#define OMAP4_KBD_FULLCODE31_0          0x44
  51#define OMAP4_KBD_FULLCODE63_32         0x48
  52
  53/* OMAP4 bit definitions */
  54#define OMAP4_DEF_IRQENABLE_EVENTEN     (1 << 0)
  55#define OMAP4_DEF_IRQENABLE_LONGKEY     (1 << 1)
  56#define OMAP4_DEF_IRQENABLE_TIMEOUTEN   (1 << 2)
  57#define OMAP4_DEF_WUP_EVENT_ENA         (1 << 0)
  58#define OMAP4_DEF_WUP_LONG_KEY_ENA      (1 << 1)
  59#define OMAP4_DEF_CTRL_NOSOFTMODE       (1 << 1)
  60#define OMAP4_DEF_CTRLPTVVALUE          (1 << 2)
  61#define OMAP4_DEF_CTRLPTV               (1 << 1)
  62
  63/* OMAP4 values */
  64#define OMAP4_VAL_IRQDISABLE            0x00
  65#define OMAP4_VAL_DEBOUNCINGTIME        0x07
  66#define OMAP4_VAL_FUNCTIONALCFG         0x1E
  67
  68#define OMAP4_MASK_IRQSTATUSDISABLE     0xFFFF
  69
  70struct omap4_keypad {
  71        struct input_dev *input;
  72
  73        void __iomem *base;
  74        int irq;
  75
  76        unsigned int rows;
  77        unsigned int cols;
  78        unsigned int row_shift;
  79        unsigned char key_state[8];
  80        unsigned short keymap[];
  81};
  82
  83static void __devinit omap4_keypad_config(struct omap4_keypad *keypad_data)
  84{
  85        __raw_writel(OMAP4_VAL_FUNCTIONALCFG,
  86                        keypad_data->base + OMAP4_KBD_CTRL);
  87        __raw_writel(OMAP4_VAL_DEBOUNCINGTIME,
  88                        keypad_data->base + OMAP4_KBD_DEBOUNCINGTIME);
  89        __raw_writel(OMAP4_VAL_IRQDISABLE,
  90                        keypad_data->base + OMAP4_KBD_IRQSTATUS);
  91        __raw_writel(OMAP4_DEF_IRQENABLE_EVENTEN | OMAP4_DEF_IRQENABLE_LONGKEY,
  92                        keypad_data->base + OMAP4_KBD_IRQENABLE);
  93        __raw_writel(OMAP4_DEF_WUP_EVENT_ENA | OMAP4_DEF_WUP_LONG_KEY_ENA,
  94                        keypad_data->base + OMAP4_KBD_WAKEUPENABLE);
  95}
  96
  97/* Interrupt handler */
  98static irqreturn_t omap4_keypad_interrupt(int irq, void *dev_id)
  99{
 100        struct omap4_keypad *keypad_data = dev_id;
 101        struct input_dev *input_dev = keypad_data->input;
 102        unsigned char key_state[ARRAY_SIZE(keypad_data->key_state)];
 103        unsigned int col, row, code, changed;
 104        u32 *new_state = (u32 *) key_state;
 105
 106        /* Disable interrupts */
 107        __raw_writel(OMAP4_VAL_IRQDISABLE,
 108                     keypad_data->base + OMAP4_KBD_IRQENABLE);
 109
 110        *new_state = __raw_readl(keypad_data->base + OMAP4_KBD_FULLCODE31_0);
 111        *(new_state + 1) = __raw_readl(keypad_data->base
 112                                                + OMAP4_KBD_FULLCODE63_32);
 113
 114        for (row = 0; row < keypad_data->rows; row++) {
 115                changed = key_state[row] ^ keypad_data->key_state[row];
 116                if (!changed)
 117                        continue;
 118
 119                for (col = 0; col < keypad_data->cols; col++) {
 120                        if (changed & (1 << col)) {
 121                                code = MATRIX_SCAN_CODE(row, col,
 122                                                keypad_data->row_shift);
 123                                input_event(input_dev, EV_MSC, MSC_SCAN, code);
 124                                input_report_key(input_dev,
 125                                                 keypad_data->keymap[code],
 126                                                 key_state[row] & (1 << col));
 127                        }
 128                }
 129        }
 130
 131        input_sync(input_dev);
 132
 133        memcpy(keypad_data->key_state, key_state,
 134                sizeof(keypad_data->key_state));
 135
 136        /* clear pending interrupts */
 137        __raw_writel(__raw_readl(keypad_data->base + OMAP4_KBD_IRQSTATUS),
 138                        keypad_data->base + OMAP4_KBD_IRQSTATUS);
 139
 140        /* enable interrupts */
 141        __raw_writel(OMAP4_DEF_IRQENABLE_EVENTEN | OMAP4_DEF_IRQENABLE_LONGKEY,
 142                        keypad_data->base + OMAP4_KBD_IRQENABLE);
 143
 144        return IRQ_HANDLED;
 145}
 146
 147static int __devinit omap4_keypad_probe(struct platform_device *pdev)
 148{
 149        const struct omap4_keypad_platform_data *pdata;
 150        struct omap4_keypad *keypad_data;
 151        struct input_dev *input_dev;
 152        struct resource *res;
 153        resource_size_t size;
 154        unsigned int row_shift, max_keys;
 155        int irq;
 156        int error;
 157
 158        /* platform data */
 159        pdata = pdev->dev.platform_data;
 160        if (!pdata) {
 161                dev_err(&pdev->dev, "no platform data defined\n");
 162                return -EINVAL;
 163        }
 164
 165        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 166        if (!res) {
 167                dev_err(&pdev->dev, "no base address specified\n");
 168                return -EINVAL;
 169        }
 170
 171        irq = platform_get_irq(pdev, 0);
 172        if (!irq) {
 173                dev_err(&pdev->dev, "no keyboard irq assigned\n");
 174                return -EINVAL;
 175        }
 176
 177        if (!pdata->keymap_data) {
 178                dev_err(&pdev->dev, "no keymap data defined\n");
 179                return -EINVAL;
 180        }
 181
 182        row_shift = get_count_order(pdata->cols);
 183        max_keys = pdata->rows << row_shift;
 184
 185        keypad_data = kzalloc(sizeof(struct omap4_keypad) +
 186                                max_keys * sizeof(keypad_data->keymap[0]),
 187                              GFP_KERNEL);
 188        if (!keypad_data) {
 189                dev_err(&pdev->dev, "keypad_data memory allocation failed\n");
 190                return -ENOMEM;
 191        }
 192
 193        size = resource_size(res);
 194
 195        res = request_mem_region(res->start, size, pdev->name);
 196        if (!res) {
 197                dev_err(&pdev->dev, "can't request mem region\n");
 198                error = -EBUSY;
 199                goto err_free_keypad;
 200        }
 201
 202        keypad_data->base = ioremap(res->start, resource_size(res));
 203        if (!keypad_data->base) {
 204                dev_err(&pdev->dev, "can't ioremap mem resource\n");
 205                error = -ENOMEM;
 206                goto err_release_mem;
 207        }
 208
 209        keypad_data->irq = irq;
 210        keypad_data->row_shift = row_shift;
 211        keypad_data->rows = pdata->rows;
 212        keypad_data->cols = pdata->cols;
 213
 214        /* input device allocation */
 215        keypad_data->input = input_dev = input_allocate_device();
 216        if (!input_dev) {
 217                error = -ENOMEM;
 218                goto err_unmap;
 219        }
 220
 221        input_dev->name = pdev->name;
 222        input_dev->dev.parent = &pdev->dev;
 223        input_dev->id.bustype = BUS_HOST;
 224        input_dev->id.vendor = 0x0001;
 225        input_dev->id.product = 0x0001;
 226        input_dev->id.version = 0x0001;
 227
 228        input_dev->keycode      = keypad_data->keymap;
 229        input_dev->keycodesize  = sizeof(keypad_data->keymap[0]);
 230        input_dev->keycodemax   = max_keys;
 231
 232        __set_bit(EV_KEY, input_dev->evbit);
 233        __set_bit(EV_REP, input_dev->evbit);
 234
 235        input_set_capability(input_dev, EV_MSC, MSC_SCAN);
 236
 237        input_set_drvdata(input_dev, keypad_data);
 238
 239        matrix_keypad_build_keymap(pdata->keymap_data, row_shift,
 240                        input_dev->keycode, input_dev->keybit);
 241
 242        omap4_keypad_config(keypad_data);
 243
 244        error = request_irq(keypad_data->irq, omap4_keypad_interrupt,
 245                             IRQF_TRIGGER_RISING,
 246                             "omap4-keypad", keypad_data);
 247        if (error) {
 248                dev_err(&pdev->dev, "failed to register interrupt\n");
 249                goto err_free_input;
 250        }
 251
 252        error = input_register_device(keypad_data->input);
 253        if (error < 0) {
 254                dev_err(&pdev->dev, "failed to register input device\n");
 255                goto err_free_irq;
 256        }
 257
 258
 259        platform_set_drvdata(pdev, keypad_data);
 260        return 0;
 261
 262err_free_irq:
 263        free_irq(keypad_data->irq, keypad_data);
 264err_free_input:
 265        input_free_device(input_dev);
 266err_unmap:
 267        iounmap(keypad_data->base);
 268err_release_mem:
 269        release_mem_region(res->start, size);
 270err_free_keypad:
 271        kfree(keypad_data);
 272        return error;
 273}
 274
 275static int __devexit omap4_keypad_remove(struct platform_device *pdev)
 276{
 277        struct omap4_keypad *keypad_data = platform_get_drvdata(pdev);
 278        struct resource *res;
 279
 280        free_irq(keypad_data->irq, keypad_data);
 281        input_unregister_device(keypad_data->input);
 282
 283        iounmap(keypad_data->base);
 284
 285        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 286        release_mem_region(res->start, resource_size(res));
 287
 288        kfree(keypad_data);
 289        platform_set_drvdata(pdev, NULL);
 290
 291        return 0;
 292}
 293
 294static struct platform_driver omap4_keypad_driver = {
 295        .probe          = omap4_keypad_probe,
 296        .remove         = __devexit_p(omap4_keypad_remove),
 297        .driver         = {
 298                .name   = "omap4-keypad",
 299                .owner  = THIS_MODULE,
 300        },
 301};
 302
 303static int __init omap4_keypad_init(void)
 304{
 305        return platform_driver_register(&omap4_keypad_driver);
 306}
 307module_init(omap4_keypad_init);
 308
 309static void __exit omap4_keypad_exit(void)
 310{
 311        platform_driver_unregister(&omap4_keypad_driver);
 312}
 313module_exit(omap4_keypad_exit);
 314
 315MODULE_AUTHOR("Texas Instruments");
 316MODULE_DESCRIPTION("OMAP4 Keypad Driver");
 317MODULE_LICENSE("GPL");
 318MODULE_ALIAS("platform:omap4-keypad");
 319