linux/drivers/input/keyboard/bf54x-keys.c
<<
>>
Prefs
   1/*
   2 * File:         drivers/input/keyboard/bf54x-keys.c
   3 * Based on:
   4 * Author:       Michael Hennerich <hennerich@blackfin.uclinux.org>
   5 *
   6 * Created:
   7 * Description:  keypad driver for Analog Devices Blackfin BF54x Processors
   8 *
   9 *
  10 * Modified:
  11 *               Copyright 2007-2008 Analog Devices Inc.
  12 *
  13 * Bugs:         Enter bugs at http://blackfin.uclinux.org/
  14 *
  15 * This program is free software; you can redistribute it and/or modify
  16 * it under the terms of the GNU General Public License as published by
  17 * the Free Software Foundation; either version 2 of the License, or
  18 * (at your option) any later version.
  19 *
  20 * This program is distributed in the hope that it will be useful,
  21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  23 * GNU General Public License for more details.
  24 *
  25 * You should have received a copy of the GNU General Public License
  26 * along with this program; if not, see the file COPYING, or write
  27 * to the Free Software Foundation, Inc.,
  28 * 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
  29 */
  30
  31#include <linux/module.h>
  32
  33#include <linux/fs.h>
  34#include <linux/interrupt.h>
  35#include <linux/irq.h>
  36#include <linux/slab.h>
  37#include <linux/sched.h>
  38#include <linux/pm.h>
  39#include <linux/sysctl.h>
  40#include <linux/proc_fs.h>
  41#include <linux/delay.h>
  42#include <linux/platform_device.h>
  43#include <linux/input.h>
  44
  45#include <asm/portmux.h>
  46#include <mach/bf54x_keys.h>
  47
  48#define DRV_NAME        "bf54x-keys"
  49#define TIME_SCALE      100     /* 100 ns */
  50#define MAX_MULT        (0xFF * TIME_SCALE)
  51#define MAX_RC          8       /* Max Row/Col */
  52
  53static const u16 per_rows[] = {
  54        P_KEY_ROW7,
  55        P_KEY_ROW6,
  56        P_KEY_ROW5,
  57        P_KEY_ROW4,
  58        P_KEY_ROW3,
  59        P_KEY_ROW2,
  60        P_KEY_ROW1,
  61        P_KEY_ROW0,
  62        0
  63};
  64
  65static const u16 per_cols[] = {
  66        P_KEY_COL7,
  67        P_KEY_COL6,
  68        P_KEY_COL5,
  69        P_KEY_COL4,
  70        P_KEY_COL3,
  71        P_KEY_COL2,
  72        P_KEY_COL1,
  73        P_KEY_COL0,
  74        0
  75};
  76
  77struct bf54x_kpad {
  78        struct input_dev *input;
  79        int irq;
  80        unsigned short lastkey;
  81        unsigned short *keycode;
  82        struct timer_list timer;
  83        unsigned int keyup_test_jiffies;
  84        unsigned short kpad_msel;
  85        unsigned short kpad_prescale;
  86        unsigned short kpad_ctl;
  87};
  88
  89static inline int bfin_kpad_find_key(struct bf54x_kpad *bf54x_kpad,
  90                        struct input_dev *input, u16 keyident)
  91{
  92        u16 i;
  93
  94        for (i = 0; i < input->keycodemax; i++)
  95                if (bf54x_kpad->keycode[i + input->keycodemax] == keyident)
  96                        return bf54x_kpad->keycode[i];
  97        return -1;
  98}
  99
 100static inline void bfin_keycodecpy(unsigned short *keycode,
 101                        const unsigned int *pdata_kc,
 102                        unsigned short keymapsize)
 103{
 104        unsigned int i;
 105
 106        for (i = 0; i < keymapsize; i++) {
 107                keycode[i] = pdata_kc[i] & 0xffff;
 108                keycode[i + keymapsize] = pdata_kc[i] >> 16;
 109        }
 110}
 111
 112static inline u16 bfin_kpad_get_prescale(u32 timescale)
 113{
 114        u32 sclk = get_sclk();
 115
 116        return ((((sclk / 1000) * timescale) / 1024) - 1);
 117}
 118
 119static inline u16 bfin_kpad_get_keypressed(struct bf54x_kpad *bf54x_kpad)
 120{
 121        return (bfin_read_KPAD_STAT() & KPAD_PRESSED);
 122}
 123
 124static inline void bfin_kpad_clear_irq(void)
 125{
 126        bfin_write_KPAD_STAT(0xFFFF);
 127        bfin_write_KPAD_ROWCOL(0xFFFF);
 128}
 129
 130static void bfin_kpad_timer(unsigned long data)
 131{
 132        struct platform_device *pdev =  (struct platform_device *) data;
 133        struct bf54x_kpad *bf54x_kpad = platform_get_drvdata(pdev);
 134
 135        if (bfin_kpad_get_keypressed(bf54x_kpad)) {
 136                /* Try again later */
 137                mod_timer(&bf54x_kpad->timer,
 138                          jiffies + bf54x_kpad->keyup_test_jiffies);
 139                return;
 140        }
 141
 142        input_report_key(bf54x_kpad->input, bf54x_kpad->lastkey, 0);
 143        input_sync(bf54x_kpad->input);
 144
 145        /* Clear IRQ Status */
 146
 147        bfin_kpad_clear_irq();
 148        enable_irq(bf54x_kpad->irq);
 149}
 150
 151static irqreturn_t bfin_kpad_isr(int irq, void *dev_id)
 152{
 153        struct platform_device *pdev = dev_id;
 154        struct bf54x_kpad *bf54x_kpad = platform_get_drvdata(pdev);
 155        struct input_dev *input = bf54x_kpad->input;
 156        int key;
 157        u16 rowcol = bfin_read_KPAD_ROWCOL();
 158
 159        key = bfin_kpad_find_key(bf54x_kpad, input, rowcol);
 160
 161        input_report_key(input, key, 1);
 162        input_sync(input);
 163
 164        if (bfin_kpad_get_keypressed(bf54x_kpad)) {
 165                disable_irq_nosync(bf54x_kpad->irq);
 166                bf54x_kpad->lastkey = key;
 167                mod_timer(&bf54x_kpad->timer,
 168                          jiffies + bf54x_kpad->keyup_test_jiffies);
 169        } else {
 170                input_report_key(input, key, 0);
 171                input_sync(input);
 172
 173                bfin_kpad_clear_irq();
 174        }
 175
 176        return IRQ_HANDLED;
 177}
 178
 179static int bfin_kpad_probe(struct platform_device *pdev)
 180{
 181        struct bf54x_kpad *bf54x_kpad;
 182        struct bfin_kpad_platform_data *pdata = dev_get_platdata(&pdev->dev);
 183        struct input_dev *input;
 184        int i, error;
 185
 186        if (!pdata->rows || !pdata->cols || !pdata->keymap) {
 187                dev_err(&pdev->dev, "no rows, cols or keymap from pdata\n");
 188                return -EINVAL;
 189        }
 190
 191        if (!pdata->keymapsize ||
 192            pdata->keymapsize > (pdata->rows * pdata->cols)) {
 193                dev_err(&pdev->dev, "invalid keymapsize\n");
 194                return -EINVAL;
 195        }
 196
 197        bf54x_kpad = kzalloc(sizeof(struct bf54x_kpad), GFP_KERNEL);
 198        if (!bf54x_kpad)
 199                return -ENOMEM;
 200
 201        platform_set_drvdata(pdev, bf54x_kpad);
 202
 203        /* Allocate memory for keymap followed by private LUT */
 204        bf54x_kpad->keycode = kmalloc(pdata->keymapsize *
 205                                        sizeof(unsigned short) * 2, GFP_KERNEL);
 206        if (!bf54x_kpad->keycode) {
 207                error = -ENOMEM;
 208                goto out;
 209        }
 210
 211        if (!pdata->debounce_time || pdata->debounce_time > MAX_MULT ||
 212            !pdata->coldrive_time || pdata->coldrive_time > MAX_MULT) {
 213                dev_warn(&pdev->dev,
 214                        "invalid platform debounce/columndrive time\n");
 215                bfin_write_KPAD_MSEL(0xFF0);    /* Default MSEL */
 216        } else {
 217                bfin_write_KPAD_MSEL(
 218                        ((pdata->debounce_time / TIME_SCALE)
 219                                                & DBON_SCALE) |
 220                        (((pdata->coldrive_time / TIME_SCALE) << 8)
 221                                                & COLDRV_SCALE));
 222
 223        }
 224
 225        if (!pdata->keyup_test_interval)
 226                bf54x_kpad->keyup_test_jiffies = msecs_to_jiffies(50);
 227        else
 228                bf54x_kpad->keyup_test_jiffies =
 229                        msecs_to_jiffies(pdata->keyup_test_interval);
 230
 231        if (peripheral_request_list((u16 *)&per_rows[MAX_RC - pdata->rows],
 232                                    DRV_NAME)) {
 233                dev_err(&pdev->dev, "requesting peripherals failed\n");
 234                error = -EFAULT;
 235                goto out0;
 236        }
 237
 238        if (peripheral_request_list((u16 *)&per_cols[MAX_RC - pdata->cols],
 239                                    DRV_NAME)) {
 240                dev_err(&pdev->dev, "requesting peripherals failed\n");
 241                error = -EFAULT;
 242                goto out1;
 243        }
 244
 245        bf54x_kpad->irq = platform_get_irq(pdev, 0);
 246        if (bf54x_kpad->irq < 0) {
 247                error = -ENODEV;
 248                goto out2;
 249        }
 250
 251        error = request_irq(bf54x_kpad->irq, bfin_kpad_isr,
 252                                0, DRV_NAME, pdev);
 253        if (error) {
 254                dev_err(&pdev->dev, "unable to claim irq %d\n",
 255                        bf54x_kpad->irq);
 256                goto out2;
 257        }
 258
 259        input = input_allocate_device();
 260        if (!input) {
 261                error = -ENOMEM;
 262                goto out3;
 263        }
 264
 265        bf54x_kpad->input = input;
 266
 267        input->name = pdev->name;
 268        input->phys = "bf54x-keys/input0";
 269        input->dev.parent = &pdev->dev;
 270
 271        input_set_drvdata(input, bf54x_kpad);
 272
 273        input->id.bustype = BUS_HOST;
 274        input->id.vendor = 0x0001;
 275        input->id.product = 0x0001;
 276        input->id.version = 0x0100;
 277
 278        input->keycodesize = sizeof(unsigned short);
 279        input->keycodemax = pdata->keymapsize;
 280        input->keycode = bf54x_kpad->keycode;
 281
 282        bfin_keycodecpy(bf54x_kpad->keycode, pdata->keymap, pdata->keymapsize);
 283
 284        /* setup input device */
 285        __set_bit(EV_KEY, input->evbit);
 286
 287        if (pdata->repeat)
 288                __set_bit(EV_REP, input->evbit);
 289
 290        for (i = 0; i < input->keycodemax; i++)
 291                if (bf54x_kpad->keycode[i] <= KEY_MAX)
 292                        __set_bit(bf54x_kpad->keycode[i], input->keybit);
 293        __clear_bit(KEY_RESERVED, input->keybit);
 294
 295        error = input_register_device(input);
 296        if (error) {
 297                dev_err(&pdev->dev, "unable to register input device\n");
 298                goto out4;
 299        }
 300
 301        /* Init Keypad Key Up/Release test timer */
 302
 303        setup_timer(&bf54x_kpad->timer, bfin_kpad_timer, (unsigned long) pdev);
 304
 305        bfin_write_KPAD_PRESCALE(bfin_kpad_get_prescale(TIME_SCALE));
 306
 307        bfin_write_KPAD_CTL((((pdata->cols - 1) << 13) & KPAD_COLEN) |
 308                                (((pdata->rows - 1) << 10) & KPAD_ROWEN) |
 309                                (2 & KPAD_IRQMODE));
 310
 311        bfin_write_KPAD_CTL(bfin_read_KPAD_CTL() | KPAD_EN);
 312
 313        device_init_wakeup(&pdev->dev, 1);
 314
 315        return 0;
 316
 317out4:
 318        input_free_device(input);
 319out3:
 320        free_irq(bf54x_kpad->irq, pdev);
 321out2:
 322        peripheral_free_list((u16 *)&per_cols[MAX_RC - pdata->cols]);
 323out1:
 324        peripheral_free_list((u16 *)&per_rows[MAX_RC - pdata->rows]);
 325out0:
 326        kfree(bf54x_kpad->keycode);
 327out:
 328        kfree(bf54x_kpad);
 329
 330        return error;
 331}
 332
 333static int bfin_kpad_remove(struct platform_device *pdev)
 334{
 335        struct bfin_kpad_platform_data *pdata = dev_get_platdata(&pdev->dev);
 336        struct bf54x_kpad *bf54x_kpad = platform_get_drvdata(pdev);
 337
 338        del_timer_sync(&bf54x_kpad->timer);
 339        free_irq(bf54x_kpad->irq, pdev);
 340
 341        input_unregister_device(bf54x_kpad->input);
 342
 343        peripheral_free_list((u16 *)&per_rows[MAX_RC - pdata->rows]);
 344        peripheral_free_list((u16 *)&per_cols[MAX_RC - pdata->cols]);
 345
 346        kfree(bf54x_kpad->keycode);
 347        kfree(bf54x_kpad);
 348
 349        return 0;
 350}
 351
 352#ifdef CONFIG_PM
 353static int bfin_kpad_suspend(struct platform_device *pdev, pm_message_t state)
 354{
 355        struct bf54x_kpad *bf54x_kpad = platform_get_drvdata(pdev);
 356
 357        bf54x_kpad->kpad_msel = bfin_read_KPAD_MSEL();
 358        bf54x_kpad->kpad_prescale = bfin_read_KPAD_PRESCALE();
 359        bf54x_kpad->kpad_ctl = bfin_read_KPAD_CTL();
 360
 361        if (device_may_wakeup(&pdev->dev))
 362                enable_irq_wake(bf54x_kpad->irq);
 363
 364        return 0;
 365}
 366
 367static int bfin_kpad_resume(struct platform_device *pdev)
 368{
 369        struct bf54x_kpad *bf54x_kpad = platform_get_drvdata(pdev);
 370
 371        bfin_write_KPAD_MSEL(bf54x_kpad->kpad_msel);
 372        bfin_write_KPAD_PRESCALE(bf54x_kpad->kpad_prescale);
 373        bfin_write_KPAD_CTL(bf54x_kpad->kpad_ctl);
 374
 375        if (device_may_wakeup(&pdev->dev))
 376                disable_irq_wake(bf54x_kpad->irq);
 377
 378        return 0;
 379}
 380#else
 381# define bfin_kpad_suspend NULL
 382# define bfin_kpad_resume  NULL
 383#endif
 384
 385static struct platform_driver bfin_kpad_device_driver = {
 386        .driver         = {
 387                .name   = DRV_NAME,
 388        },
 389        .probe          = bfin_kpad_probe,
 390        .remove         = bfin_kpad_remove,
 391        .suspend        = bfin_kpad_suspend,
 392        .resume         = bfin_kpad_resume,
 393};
 394module_platform_driver(bfin_kpad_device_driver);
 395
 396MODULE_LICENSE("GPL");
 397MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
 398MODULE_DESCRIPTION("Keypad driver for BF54x Processors");
 399MODULE_ALIAS("platform:bf54x-keys");
 400