linux/drivers/input/keyboard/corgikbd.c
<<
>>
Prefs
   1/*
   2 *  Keyboard driver for Sharp Corgi models (SL-C7xx)
   3 *
   4 *  Copyright (c) 2004-2005 Richard Purdie
   5 *
   6 *  Based on xtkbd.c/locomkbd.c
   7 *
   8 *  This program is free software; you can redistribute it and/or modify
   9 *  it under the terms of the GNU General Public License version 2 as
  10 *  published by the Free Software Foundation.
  11 *
  12 */
  13
  14#include <linux/delay.h>
  15#include <linux/platform_device.h>
  16#include <linux/init.h>
  17#include <linux/input.h>
  18#include <linux/interrupt.h>
  19#include <linux/jiffies.h>
  20#include <linux/module.h>
  21#include <linux/slab.h>
  22
  23#include <mach/corgi.h>
  24#include <mach/pxa2xx-gpio.h>
  25#include <asm/hardware/scoop.h>
  26
  27#define KB_ROWS                         8
  28#define KB_COLS                         12
  29#define KB_ROWMASK(r)           (1 << (r))
  30#define SCANCODE(r,c)           ( ((r)<<4) + (c) + 1 )
  31/* zero code, 124 scancodes */
  32#define NR_SCANCODES            ( SCANCODE(KB_ROWS-1,KB_COLS-1) +1 +1 )
  33
  34#define SCAN_INTERVAL           (50) /* ms */
  35#define HINGE_SCAN_INTERVAL     (250) /* ms */
  36
  37#define CORGI_KEY_CALENDER      KEY_F1
  38#define CORGI_KEY_ADDRESS       KEY_F2
  39#define CORGI_KEY_FN            KEY_F3
  40#define CORGI_KEY_CANCEL        KEY_F4
  41#define CORGI_KEY_OFF           KEY_SUSPEND
  42#define CORGI_KEY_EXOK          KEY_F5
  43#define CORGI_KEY_EXCANCEL      KEY_F6
  44#define CORGI_KEY_EXJOGDOWN     KEY_F7
  45#define CORGI_KEY_EXJOGUP       KEY_F8
  46#define CORGI_KEY_JAP1          KEY_LEFTCTRL
  47#define CORGI_KEY_JAP2          KEY_LEFTALT
  48#define CORGI_KEY_MAIL          KEY_F10
  49#define CORGI_KEY_OK            KEY_F11
  50#define CORGI_KEY_MENU          KEY_F12
  51
  52static unsigned char corgikbd_keycode[NR_SCANCODES] = {
  53        0,                                                                                                                /* 0 */
  54        0, KEY_1, KEY_3, KEY_5, KEY_6, KEY_7, KEY_9, KEY_0, KEY_BACKSPACE, 0, 0, 0, 0, 0, 0, 0,                               /* 1-16 */
  55        0, KEY_2, KEY_4, KEY_R, KEY_Y, KEY_8, KEY_I, KEY_O, KEY_P, 0, 0, 0, 0, 0, 0, 0,                                   /* 17-32 */
  56        KEY_TAB, KEY_Q, KEY_E, KEY_T, KEY_G, KEY_U, KEY_J, KEY_K, 0, 0, 0, 0, 0, 0, 0, 0,                                 /* 33-48 */
  57        CORGI_KEY_CALENDER, KEY_W, KEY_S, KEY_F, KEY_V, KEY_H, KEY_M, KEY_L, 0, KEY_RIGHTSHIFT, 0, 0, 0, 0, 0, 0,         /* 49-64 */
  58        CORGI_KEY_ADDRESS, KEY_A, KEY_D, KEY_C, KEY_B, KEY_N, KEY_DOT, 0, KEY_ENTER, 0, KEY_LEFTSHIFT, 0, 0, 0, 0, 0,     /* 65-80 */
  59        CORGI_KEY_MAIL, KEY_Z, KEY_X, KEY_MINUS, KEY_SPACE, KEY_COMMA, 0, KEY_UP, 0, 0, 0, CORGI_KEY_FN, 0, 0, 0, 0,            /* 81-96 */
  60        KEY_SYSRQ, CORGI_KEY_JAP1, CORGI_KEY_JAP2, CORGI_KEY_CANCEL, CORGI_KEY_OK, CORGI_KEY_MENU, KEY_LEFT, KEY_DOWN, KEY_RIGHT, 0, 0, 0, 0, 0, 0, 0,  /* 97-112 */
  61        CORGI_KEY_OFF, CORGI_KEY_EXOK, CORGI_KEY_EXCANCEL, CORGI_KEY_EXJOGDOWN, CORGI_KEY_EXJOGUP, 0, 0, 0, 0, 0, 0, 0,   /* 113-124 */
  62};
  63
  64
  65struct corgikbd {
  66        unsigned char keycode[ARRAY_SIZE(corgikbd_keycode)];
  67        struct input_dev *input;
  68
  69        spinlock_t lock;
  70        struct timer_list timer;
  71        struct timer_list htimer;
  72
  73        unsigned int suspended;
  74        unsigned long suspend_jiffies;
  75};
  76
  77#define KB_DISCHARGE_DELAY      10
  78#define KB_ACTIVATE_DELAY       10
  79
  80/* Helper functions for reading the keyboard matrix
  81 * Note: We should really be using the generic gpio functions to alter
  82 *       GPDR but it requires a function call per GPIO bit which is
  83 *       excessive when we need to access 12 bits at once, multiple times.
  84 * These functions must be called within local_irq_save()/local_irq_restore()
  85 * or similar.
  86 */
  87static inline void corgikbd_discharge_all(void)
  88{
  89        /* STROBE All HiZ */
  90        GPCR2  = CORGI_GPIO_ALL_STROBE_BIT;
  91        GPDR2 &= ~CORGI_GPIO_ALL_STROBE_BIT;
  92}
  93
  94static inline void corgikbd_activate_all(void)
  95{
  96        /* STROBE ALL -> High */
  97        GPSR2  = CORGI_GPIO_ALL_STROBE_BIT;
  98        GPDR2 |= CORGI_GPIO_ALL_STROBE_BIT;
  99
 100        udelay(KB_DISCHARGE_DELAY);
 101
 102        /* Clear any interrupts we may have triggered when altering the GPIO lines */
 103        GEDR1 = CORGI_GPIO_HIGH_SENSE_BIT;
 104        GEDR2 = CORGI_GPIO_LOW_SENSE_BIT;
 105}
 106
 107static inline void corgikbd_activate_col(int col)
 108{
 109        /* STROBE col -> High, not col -> HiZ */
 110        GPSR2 = CORGI_GPIO_STROBE_BIT(col);
 111        GPDR2 = (GPDR2 & ~CORGI_GPIO_ALL_STROBE_BIT) | CORGI_GPIO_STROBE_BIT(col);
 112}
 113
 114static inline void corgikbd_reset_col(int col)
 115{
 116        /* STROBE col -> Low */
 117        GPCR2 = CORGI_GPIO_STROBE_BIT(col);
 118        /* STROBE col -> out, not col -> HiZ */
 119        GPDR2 = (GPDR2 & ~CORGI_GPIO_ALL_STROBE_BIT) | CORGI_GPIO_STROBE_BIT(col);
 120}
 121
 122#define GET_ROWS_STATUS(c)      (((GPLR1 & CORGI_GPIO_HIGH_SENSE_BIT) >> CORGI_GPIO_HIGH_SENSE_RSHIFT) | ((GPLR2 & CORGI_GPIO_LOW_SENSE_BIT) << CORGI_GPIO_LOW_SENSE_LSHIFT))
 123
 124/*
 125 * The corgi keyboard only generates interrupts when a key is pressed.
 126 * When a key is pressed, we enable a timer which then scans the
 127 * keyboard to detect when the key is released.
 128 */
 129
 130/* Scan the hardware keyboard and push any changes up through the input layer */
 131static void corgikbd_scankeyboard(struct corgikbd *corgikbd_data)
 132{
 133        unsigned int row, col, rowd;
 134        unsigned long flags;
 135        unsigned int num_pressed;
 136
 137        if (corgikbd_data->suspended)
 138                return;
 139
 140        spin_lock_irqsave(&corgikbd_data->lock, flags);
 141
 142        num_pressed = 0;
 143        for (col = 0; col < KB_COLS; col++) {
 144                /*
 145                 * Discharge the output driver capacitatance
 146                 * in the keyboard matrix. (Yes it is significant..)
 147                 */
 148
 149                corgikbd_discharge_all();
 150                udelay(KB_DISCHARGE_DELAY);
 151
 152                corgikbd_activate_col(col);
 153                udelay(KB_ACTIVATE_DELAY);
 154
 155                rowd = GET_ROWS_STATUS(col);
 156                for (row = 0; row < KB_ROWS; row++) {
 157                        unsigned int scancode, pressed;
 158
 159                        scancode = SCANCODE(row, col);
 160                        pressed = rowd & KB_ROWMASK(row);
 161
 162                        input_report_key(corgikbd_data->input, corgikbd_data->keycode[scancode], pressed);
 163
 164                        if (pressed)
 165                                num_pressed++;
 166
 167                        if (pressed && (corgikbd_data->keycode[scancode] == CORGI_KEY_OFF)
 168                                        && time_after(jiffies, corgikbd_data->suspend_jiffies + HZ)) {
 169                                input_event(corgikbd_data->input, EV_PWR, CORGI_KEY_OFF, 1);
 170                                corgikbd_data->suspend_jiffies=jiffies;
 171                        }
 172                }
 173                corgikbd_reset_col(col);
 174        }
 175
 176        corgikbd_activate_all();
 177
 178        input_sync(corgikbd_data->input);
 179
 180        /* if any keys are pressed, enable the timer */
 181        if (num_pressed)
 182                mod_timer(&corgikbd_data->timer, jiffies + msecs_to_jiffies(SCAN_INTERVAL));
 183
 184        spin_unlock_irqrestore(&corgikbd_data->lock, flags);
 185}
 186
 187/*
 188 * corgi keyboard interrupt handler.
 189 */
 190static irqreturn_t corgikbd_interrupt(int irq, void *dev_id)
 191{
 192        struct corgikbd *corgikbd_data = dev_id;
 193
 194        if (!timer_pending(&corgikbd_data->timer)) {
 195                /** wait chattering delay **/
 196                udelay(20);
 197                corgikbd_scankeyboard(corgikbd_data);
 198        }
 199
 200        return IRQ_HANDLED;
 201}
 202
 203/*
 204 * corgi timer checking for released keys
 205 */
 206static void corgikbd_timer_callback(unsigned long data)
 207{
 208        struct corgikbd *corgikbd_data = (struct corgikbd *) data;
 209        corgikbd_scankeyboard(corgikbd_data);
 210}
 211
 212/*
 213 * The hinge switches generate no interrupt so they need to be
 214 * monitored by a timer.
 215 *
 216 * We debounce the switches and pass them to the input system.
 217 *
 218 *  gprr == 0x00 - Keyboard with Landscape Screen
 219 *          0x08 - No Keyboard with Portrait Screen
 220 *          0x0c - Keyboard and Screen Closed
 221 */
 222
 223#define READ_GPIO_BIT(x)    (GPLR(x) & GPIO_bit(x))
 224#define HINGE_STABLE_COUNT 2
 225static int sharpsl_hinge_state;
 226static int hinge_count;
 227
 228static void corgikbd_hinge_timer(unsigned long data)
 229{
 230        struct corgikbd *corgikbd_data = (struct corgikbd *) data;
 231        unsigned long gprr;
 232        unsigned long flags;
 233
 234        gprr = read_scoop_reg(&corgiscoop_device.dev, SCOOP_GPRR) & (CORGI_SCP_SWA | CORGI_SCP_SWB);
 235        gprr |= (READ_GPIO_BIT(CORGI_GPIO_AK_INT) != 0);
 236        if (gprr != sharpsl_hinge_state) {
 237                hinge_count = 0;
 238                sharpsl_hinge_state = gprr;
 239        } else if (hinge_count < HINGE_STABLE_COUNT) {
 240                hinge_count++;
 241                if (hinge_count >= HINGE_STABLE_COUNT) {
 242                        spin_lock_irqsave(&corgikbd_data->lock, flags);
 243
 244                        input_report_switch(corgikbd_data->input, SW_LID, ((sharpsl_hinge_state & CORGI_SCP_SWA) != 0));
 245                        input_report_switch(corgikbd_data->input, SW_TABLET_MODE, ((sharpsl_hinge_state & CORGI_SCP_SWB) != 0));
 246                        input_report_switch(corgikbd_data->input, SW_HEADPHONE_INSERT, (READ_GPIO_BIT(CORGI_GPIO_AK_INT) != 0));
 247                        input_sync(corgikbd_data->input);
 248
 249                        spin_unlock_irqrestore(&corgikbd_data->lock, flags);
 250                }
 251        }
 252        mod_timer(&corgikbd_data->htimer, jiffies + msecs_to_jiffies(HINGE_SCAN_INTERVAL));
 253}
 254
 255#ifdef CONFIG_PM
 256static int corgikbd_suspend(struct platform_device *dev, pm_message_t state)
 257{
 258        int i;
 259        struct corgikbd *corgikbd = platform_get_drvdata(dev);
 260
 261        corgikbd->suspended = 1;
 262        /* strobe 0 is the power key so this can't be made an input for
 263           powersaving therefore i = 1 */
 264        for (i = 1; i < CORGI_KEY_STROBE_NUM; i++)
 265                pxa_gpio_mode(CORGI_GPIO_KEY_STROBE(i) | GPIO_IN);
 266
 267        return 0;
 268}
 269
 270static int corgikbd_resume(struct platform_device *dev)
 271{
 272        int i;
 273        struct corgikbd *corgikbd = platform_get_drvdata(dev);
 274
 275        for (i = 1; i < CORGI_KEY_STROBE_NUM; i++)
 276                pxa_gpio_mode(CORGI_GPIO_KEY_STROBE(i) | GPIO_OUT | GPIO_DFLT_HIGH);
 277
 278        /* Upon resume, ignore the suspend key for a short while */
 279        corgikbd->suspend_jiffies=jiffies;
 280        corgikbd->suspended = 0;
 281
 282        return 0;
 283}
 284#else
 285#define corgikbd_suspend        NULL
 286#define corgikbd_resume         NULL
 287#endif
 288
 289static int __devinit corgikbd_probe(struct platform_device *pdev)
 290{
 291        struct corgikbd *corgikbd;
 292        struct input_dev *input_dev;
 293        int i, err = -ENOMEM;
 294
 295        corgikbd = kzalloc(sizeof(struct corgikbd), GFP_KERNEL);
 296        input_dev = input_allocate_device();
 297        if (!corgikbd || !input_dev)
 298                goto fail;
 299
 300        platform_set_drvdata(pdev, corgikbd);
 301
 302        corgikbd->input = input_dev;
 303        spin_lock_init(&corgikbd->lock);
 304
 305        /* Init Keyboard rescan timer */
 306        init_timer(&corgikbd->timer);
 307        corgikbd->timer.function = corgikbd_timer_callback;
 308        corgikbd->timer.data = (unsigned long) corgikbd;
 309
 310        /* Init Hinge Timer */
 311        init_timer(&corgikbd->htimer);
 312        corgikbd->htimer.function = corgikbd_hinge_timer;
 313        corgikbd->htimer.data = (unsigned long) corgikbd;
 314
 315        corgikbd->suspend_jiffies=jiffies;
 316
 317        memcpy(corgikbd->keycode, corgikbd_keycode, sizeof(corgikbd->keycode));
 318
 319        input_dev->name = "Corgi Keyboard";
 320        input_dev->phys = "corgikbd/input0";
 321        input_dev->id.bustype = BUS_HOST;
 322        input_dev->id.vendor = 0x0001;
 323        input_dev->id.product = 0x0001;
 324        input_dev->id.version = 0x0100;
 325        input_dev->dev.parent = &pdev->dev;
 326
 327        input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP) |
 328                BIT_MASK(EV_PWR) | BIT_MASK(EV_SW);
 329        input_dev->keycode = corgikbd->keycode;
 330        input_dev->keycodesize = sizeof(unsigned char);
 331        input_dev->keycodemax = ARRAY_SIZE(corgikbd_keycode);
 332
 333        for (i = 0; i < ARRAY_SIZE(corgikbd_keycode); i++)
 334                set_bit(corgikbd->keycode[i], input_dev->keybit);
 335        clear_bit(0, input_dev->keybit);
 336        set_bit(SW_LID, input_dev->swbit);
 337        set_bit(SW_TABLET_MODE, input_dev->swbit);
 338        set_bit(SW_HEADPHONE_INSERT, input_dev->swbit);
 339
 340        err = input_register_device(corgikbd->input);
 341        if (err)
 342                goto fail;
 343
 344        mod_timer(&corgikbd->htimer, jiffies + msecs_to_jiffies(HINGE_SCAN_INTERVAL));
 345
 346        /* Setup sense interrupts - RisingEdge Detect, sense lines as inputs */
 347        for (i = 0; i < CORGI_KEY_SENSE_NUM; i++) {
 348                pxa_gpio_mode(CORGI_GPIO_KEY_SENSE(i) | GPIO_IN);
 349                if (request_irq(CORGI_IRQ_GPIO_KEY_SENSE(i), corgikbd_interrupt,
 350                                IRQF_DISABLED | IRQF_TRIGGER_RISING,
 351                                "corgikbd", corgikbd))
 352                        printk(KERN_WARNING "corgikbd: Can't get IRQ: %d!\n", i);
 353        }
 354
 355        /* Set Strobe lines as outputs - set high */
 356        for (i = 0; i < CORGI_KEY_STROBE_NUM; i++)
 357                pxa_gpio_mode(CORGI_GPIO_KEY_STROBE(i) | GPIO_OUT | GPIO_DFLT_HIGH);
 358
 359        /* Setup the headphone jack as an input */
 360        pxa_gpio_mode(CORGI_GPIO_AK_INT | GPIO_IN);
 361
 362        return 0;
 363
 364 fail:  input_free_device(input_dev);
 365        kfree(corgikbd);
 366        return err;
 367}
 368
 369static int __devexit corgikbd_remove(struct platform_device *pdev)
 370{
 371        int i;
 372        struct corgikbd *corgikbd = platform_get_drvdata(pdev);
 373
 374        for (i = 0; i < CORGI_KEY_SENSE_NUM; i++)
 375                free_irq(CORGI_IRQ_GPIO_KEY_SENSE(i), corgikbd);
 376
 377        del_timer_sync(&corgikbd->htimer);
 378        del_timer_sync(&corgikbd->timer);
 379
 380        input_unregister_device(corgikbd->input);
 381
 382        kfree(corgikbd);
 383
 384        return 0;
 385}
 386
 387static struct platform_driver corgikbd_driver = {
 388        .probe          = corgikbd_probe,
 389        .remove         = __devexit_p(corgikbd_remove),
 390        .suspend        = corgikbd_suspend,
 391        .resume         = corgikbd_resume,
 392        .driver         = {
 393                .name   = "corgi-keyboard",
 394                .owner  = THIS_MODULE,
 395        },
 396};
 397
 398static int __init corgikbd_init(void)
 399{
 400        return platform_driver_register(&corgikbd_driver);
 401}
 402
 403static void __exit corgikbd_exit(void)
 404{
 405        platform_driver_unregister(&corgikbd_driver);
 406}
 407
 408module_init(corgikbd_init);
 409module_exit(corgikbd_exit);
 410
 411MODULE_AUTHOR("Richard Purdie <rpurdie@rpsys.net>");
 412MODULE_DESCRIPTION("Corgi Keyboard Driver");
 413MODULE_LICENSE("GPL v2");
 414MODULE_ALIAS("platform:corgi-keyboard");
 415