linux/drivers/input/keyboard/samsung-keypad.c
<<
>>
Prefs
   1/*
   2 * Samsung keypad driver
   3 *
   4 * Copyright (C) 2010 Samsung Electronics Co.Ltd
   5 * Author: Joonyoung Shim <jy0922.shim@samsung.com>
   6 * Author: Donghwa Lee <dh09.lee@samsung.com>
   7 *
   8 * This program is free software; you can redistribute  it and/or modify it
   9 * under  the terms of  the GNU General  Public License as published by the
  10 * Free Software Foundation;  either version 2 of the  License, or (at your
  11 * option) any later version.
  12 */
  13
  14#include <linux/clk.h>
  15#include <linux/delay.h>
  16#include <linux/err.h>
  17#include <linux/init.h>
  18#include <linux/input.h>
  19#include <linux/interrupt.h>
  20#include <linux/io.h>
  21#include <linux/module.h>
  22#include <linux/platform_device.h>
  23#include <linux/pm.h>
  24#include <linux/pm_runtime.h>
  25#include <linux/slab.h>
  26#include <linux/of.h>
  27#include <linux/sched.h>
  28#include <linux/input/samsung-keypad.h>
  29
  30#define SAMSUNG_KEYIFCON                        0x00
  31#define SAMSUNG_KEYIFSTSCLR                     0x04
  32#define SAMSUNG_KEYIFCOL                        0x08
  33#define SAMSUNG_KEYIFROW                        0x0c
  34#define SAMSUNG_KEYIFFC                         0x10
  35
  36/* SAMSUNG_KEYIFCON */
  37#define SAMSUNG_KEYIFCON_INT_F_EN               (1 << 0)
  38#define SAMSUNG_KEYIFCON_INT_R_EN               (1 << 1)
  39#define SAMSUNG_KEYIFCON_DF_EN                  (1 << 2)
  40#define SAMSUNG_KEYIFCON_FC_EN                  (1 << 3)
  41#define SAMSUNG_KEYIFCON_WAKEUPEN               (1 << 4)
  42
  43/* SAMSUNG_KEYIFSTSCLR */
  44#define SAMSUNG_KEYIFSTSCLR_P_INT_MASK          (0xff << 0)
  45#define SAMSUNG_KEYIFSTSCLR_R_INT_MASK          (0xff << 8)
  46#define SAMSUNG_KEYIFSTSCLR_R_INT_OFFSET        8
  47#define S5PV210_KEYIFSTSCLR_P_INT_MASK          (0x3fff << 0)
  48#define S5PV210_KEYIFSTSCLR_R_INT_MASK          (0x3fff << 16)
  49#define S5PV210_KEYIFSTSCLR_R_INT_OFFSET        16
  50
  51/* SAMSUNG_KEYIFCOL */
  52#define SAMSUNG_KEYIFCOL_MASK                   (0xff << 0)
  53#define S5PV210_KEYIFCOLEN_MASK                 (0xff << 8)
  54
  55/* SAMSUNG_KEYIFROW */
  56#define SAMSUNG_KEYIFROW_MASK                   (0xff << 0)
  57#define S5PV210_KEYIFROW_MASK                   (0x3fff << 0)
  58
  59/* SAMSUNG_KEYIFFC */
  60#define SAMSUNG_KEYIFFC_MASK                    (0x3ff << 0)
  61
  62enum samsung_keypad_type {
  63        KEYPAD_TYPE_SAMSUNG,
  64        KEYPAD_TYPE_S5PV210,
  65};
  66
  67struct samsung_keypad {
  68        struct input_dev *input_dev;
  69        struct platform_device *pdev;
  70        struct clk *clk;
  71        void __iomem *base;
  72        wait_queue_head_t wait;
  73        bool stopped;
  74        bool wake_enabled;
  75        int irq;
  76        enum samsung_keypad_type type;
  77        unsigned int row_shift;
  78        unsigned int rows;
  79        unsigned int cols;
  80        unsigned int row_state[SAMSUNG_MAX_COLS];
  81        unsigned short keycodes[];
  82};
  83
  84static void samsung_keypad_scan(struct samsung_keypad *keypad,
  85                                unsigned int *row_state)
  86{
  87        unsigned int col;
  88        unsigned int val;
  89
  90        for (col = 0; col < keypad->cols; col++) {
  91                if (keypad->type == KEYPAD_TYPE_S5PV210) {
  92                        val = S5PV210_KEYIFCOLEN_MASK;
  93                        val &= ~(1 << col) << 8;
  94                } else {
  95                        val = SAMSUNG_KEYIFCOL_MASK;
  96                        val &= ~(1 << col);
  97                }
  98
  99                writel(val, keypad->base + SAMSUNG_KEYIFCOL);
 100                mdelay(1);
 101
 102                val = readl(keypad->base + SAMSUNG_KEYIFROW);
 103                row_state[col] = ~val & ((1 << keypad->rows) - 1);
 104        }
 105
 106        /* KEYIFCOL reg clear */
 107        writel(0, keypad->base + SAMSUNG_KEYIFCOL);
 108}
 109
 110static bool samsung_keypad_report(struct samsung_keypad *keypad,
 111                                  unsigned int *row_state)
 112{
 113        struct input_dev *input_dev = keypad->input_dev;
 114        unsigned int changed;
 115        unsigned int pressed;
 116        unsigned int key_down = 0;
 117        unsigned int val;
 118        unsigned int col, row;
 119
 120        for (col = 0; col < keypad->cols; col++) {
 121                changed = row_state[col] ^ keypad->row_state[col];
 122                key_down |= row_state[col];
 123                if (!changed)
 124                        continue;
 125
 126                for (row = 0; row < keypad->rows; row++) {
 127                        if (!(changed & (1 << row)))
 128                                continue;
 129
 130                        pressed = row_state[col] & (1 << row);
 131
 132                        dev_dbg(&keypad->input_dev->dev,
 133                                "key %s, row: %d, col: %d\n",
 134                                pressed ? "pressed" : "released", row, col);
 135
 136                        val = MATRIX_SCAN_CODE(row, col, keypad->row_shift);
 137
 138                        input_event(input_dev, EV_MSC, MSC_SCAN, val);
 139                        input_report_key(input_dev,
 140                                        keypad->keycodes[val], pressed);
 141                }
 142                input_sync(keypad->input_dev);
 143        }
 144
 145        memcpy(keypad->row_state, row_state, sizeof(keypad->row_state));
 146
 147        return key_down;
 148}
 149
 150static irqreturn_t samsung_keypad_irq(int irq, void *dev_id)
 151{
 152        struct samsung_keypad *keypad = dev_id;
 153        unsigned int row_state[SAMSUNG_MAX_COLS];
 154        unsigned int val;
 155        bool key_down;
 156
 157        pm_runtime_get_sync(&keypad->pdev->dev);
 158
 159        do {
 160                val = readl(keypad->base + SAMSUNG_KEYIFSTSCLR);
 161                /* Clear interrupt. */
 162                writel(~0x0, keypad->base + SAMSUNG_KEYIFSTSCLR);
 163
 164                samsung_keypad_scan(keypad, row_state);
 165
 166                key_down = samsung_keypad_report(keypad, row_state);
 167                if (key_down)
 168                        wait_event_timeout(keypad->wait, keypad->stopped,
 169                                           msecs_to_jiffies(50));
 170
 171        } while (key_down && !keypad->stopped);
 172
 173        pm_runtime_put(&keypad->pdev->dev);
 174
 175        return IRQ_HANDLED;
 176}
 177
 178static void samsung_keypad_start(struct samsung_keypad *keypad)
 179{
 180        unsigned int val;
 181
 182        pm_runtime_get_sync(&keypad->pdev->dev);
 183
 184        /* Tell IRQ thread that it may poll the device. */
 185        keypad->stopped = false;
 186
 187        clk_enable(keypad->clk);
 188
 189        /* Enable interrupt bits. */
 190        val = readl(keypad->base + SAMSUNG_KEYIFCON);
 191        val |= SAMSUNG_KEYIFCON_INT_F_EN | SAMSUNG_KEYIFCON_INT_R_EN;
 192        writel(val, keypad->base + SAMSUNG_KEYIFCON);
 193
 194        /* KEYIFCOL reg clear. */
 195        writel(0, keypad->base + SAMSUNG_KEYIFCOL);
 196
 197        pm_runtime_put(&keypad->pdev->dev);
 198}
 199
 200static void samsung_keypad_stop(struct samsung_keypad *keypad)
 201{
 202        unsigned int val;
 203
 204        pm_runtime_get_sync(&keypad->pdev->dev);
 205
 206        /* Signal IRQ thread to stop polling and disable the handler. */
 207        keypad->stopped = true;
 208        wake_up(&keypad->wait);
 209        disable_irq(keypad->irq);
 210
 211        /* Clear interrupt. */
 212        writel(~0x0, keypad->base + SAMSUNG_KEYIFSTSCLR);
 213
 214        /* Disable interrupt bits. */
 215        val = readl(keypad->base + SAMSUNG_KEYIFCON);
 216        val &= ~(SAMSUNG_KEYIFCON_INT_F_EN | SAMSUNG_KEYIFCON_INT_R_EN);
 217        writel(val, keypad->base + SAMSUNG_KEYIFCON);
 218
 219        clk_disable(keypad->clk);
 220
 221        /*
 222         * Now that chip should not generate interrupts we can safely
 223         * re-enable the handler.
 224         */
 225        enable_irq(keypad->irq);
 226
 227        pm_runtime_put(&keypad->pdev->dev);
 228}
 229
 230static int samsung_keypad_open(struct input_dev *input_dev)
 231{
 232        struct samsung_keypad *keypad = input_get_drvdata(input_dev);
 233
 234        samsung_keypad_start(keypad);
 235
 236        return 0;
 237}
 238
 239static void samsung_keypad_close(struct input_dev *input_dev)
 240{
 241        struct samsung_keypad *keypad = input_get_drvdata(input_dev);
 242
 243        samsung_keypad_stop(keypad);
 244}
 245
 246#ifdef CONFIG_OF
 247static struct samsung_keypad_platdata *samsung_keypad_parse_dt(
 248                                struct device *dev)
 249{
 250        struct samsung_keypad_platdata *pdata;
 251        struct matrix_keymap_data *keymap_data;
 252        uint32_t *keymap, num_rows = 0, num_cols = 0;
 253        struct device_node *np = dev->of_node, *key_np;
 254        unsigned int key_count;
 255
 256        pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
 257        if (!pdata) {
 258                dev_err(dev, "could not allocate memory for platform data\n");
 259                return NULL;
 260        }
 261
 262        of_property_read_u32(np, "samsung,keypad-num-rows", &num_rows);
 263        of_property_read_u32(np, "samsung,keypad-num-columns", &num_cols);
 264        if (!num_rows || !num_cols) {
 265                dev_err(dev, "number of keypad rows/columns not specified\n");
 266                return NULL;
 267        }
 268        pdata->rows = num_rows;
 269        pdata->cols = num_cols;
 270
 271        keymap_data = devm_kzalloc(dev, sizeof(*keymap_data), GFP_KERNEL);
 272        if (!keymap_data) {
 273                dev_err(dev, "could not allocate memory for keymap data\n");
 274                return NULL;
 275        }
 276        pdata->keymap_data = keymap_data;
 277
 278        key_count = of_get_child_count(np);
 279        keymap_data->keymap_size = key_count;
 280        keymap = devm_kzalloc(dev, sizeof(uint32_t) * key_count, GFP_KERNEL);
 281        if (!keymap) {
 282                dev_err(dev, "could not allocate memory for keymap\n");
 283                return NULL;
 284        }
 285        keymap_data->keymap = keymap;
 286
 287        for_each_child_of_node(np, key_np) {
 288                u32 row, col, key_code;
 289                of_property_read_u32(key_np, "keypad,row", &row);
 290                of_property_read_u32(key_np, "keypad,column", &col);
 291                of_property_read_u32(key_np, "linux,code", &key_code);
 292                *keymap++ = KEY(row, col, key_code);
 293        }
 294
 295        if (of_get_property(np, "linux,input-no-autorepeat", NULL))
 296                pdata->no_autorepeat = true;
 297        if (of_get_property(np, "linux,input-wakeup", NULL))
 298                pdata->wakeup = true;
 299
 300        return pdata;
 301}
 302#else
 303static
 304struct samsung_keypad_platdata *samsung_keypad_parse_dt(struct device *dev)
 305{
 306        return NULL;
 307}
 308#endif
 309
 310static int samsung_keypad_probe(struct platform_device *pdev)
 311{
 312        const struct samsung_keypad_platdata *pdata;
 313        const struct matrix_keymap_data *keymap_data;
 314        struct samsung_keypad *keypad;
 315        struct resource *res;
 316        struct input_dev *input_dev;
 317        unsigned int row_shift;
 318        unsigned int keymap_size;
 319        int error;
 320
 321        if (pdev->dev.of_node)
 322                pdata = samsung_keypad_parse_dt(&pdev->dev);
 323        else
 324                pdata = pdev->dev.platform_data;
 325        if (!pdata) {
 326                dev_err(&pdev->dev, "no platform data defined\n");
 327                return -EINVAL;
 328        }
 329
 330        keymap_data = pdata->keymap_data;
 331        if (!keymap_data) {
 332                dev_err(&pdev->dev, "no keymap data defined\n");
 333                return -EINVAL;
 334        }
 335
 336        if (!pdata->rows || pdata->rows > SAMSUNG_MAX_ROWS)
 337                return -EINVAL;
 338
 339        if (!pdata->cols || pdata->cols > SAMSUNG_MAX_COLS)
 340                return -EINVAL;
 341
 342        /* initialize the gpio */
 343        if (pdata->cfg_gpio)
 344                pdata->cfg_gpio(pdata->rows, pdata->cols);
 345
 346        row_shift = get_count_order(pdata->cols);
 347        keymap_size = (pdata->rows << row_shift) * sizeof(keypad->keycodes[0]);
 348
 349        keypad = devm_kzalloc(&pdev->dev, sizeof(*keypad) + keymap_size,
 350                              GFP_KERNEL);
 351        input_dev = devm_input_allocate_device(&pdev->dev);
 352        if (!keypad || !input_dev)
 353                return -ENOMEM;
 354
 355        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 356        if (!res)
 357                return -ENODEV;
 358
 359        keypad->base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
 360        if (!keypad->base)
 361                return -EBUSY;
 362
 363        keypad->clk = devm_clk_get(&pdev->dev, "keypad");
 364        if (IS_ERR(keypad->clk)) {
 365                dev_err(&pdev->dev, "failed to get keypad clk\n");
 366                return PTR_ERR(keypad->clk);
 367        }
 368
 369        error = clk_prepare(keypad->clk);
 370        if (error) {
 371                dev_err(&pdev->dev, "keypad clock prepare failed\n");
 372                return error;
 373        }
 374
 375        keypad->input_dev = input_dev;
 376        keypad->pdev = pdev;
 377        keypad->row_shift = row_shift;
 378        keypad->rows = pdata->rows;
 379        keypad->cols = pdata->cols;
 380        keypad->stopped = true;
 381        init_waitqueue_head(&keypad->wait);
 382
 383        if (pdev->dev.of_node)
 384                keypad->type = of_device_is_compatible(pdev->dev.of_node,
 385                                        "samsung,s5pv210-keypad");
 386        else
 387                keypad->type = platform_get_device_id(pdev)->driver_data;
 388
 389        input_dev->name = pdev->name;
 390        input_dev->id.bustype = BUS_HOST;
 391        input_dev->dev.parent = &pdev->dev;
 392
 393        input_dev->open = samsung_keypad_open;
 394        input_dev->close = samsung_keypad_close;
 395
 396        error = matrix_keypad_build_keymap(keymap_data, NULL,
 397                                           pdata->rows, pdata->cols,
 398                                           keypad->keycodes, input_dev);
 399        if (error) {
 400                dev_err(&pdev->dev, "failed to build keymap\n");
 401                goto err_unprepare_clk;
 402        }
 403
 404        input_set_capability(input_dev, EV_MSC, MSC_SCAN);
 405        if (!pdata->no_autorepeat)
 406                __set_bit(EV_REP, input_dev->evbit);
 407
 408        input_set_drvdata(input_dev, keypad);
 409
 410        keypad->irq = platform_get_irq(pdev, 0);
 411        if (keypad->irq < 0) {
 412                error = keypad->irq;
 413                goto err_unprepare_clk;
 414        }
 415
 416        error = devm_request_threaded_irq(&pdev->dev, keypad->irq, NULL,
 417                                          samsung_keypad_irq, IRQF_ONESHOT,
 418                                          dev_name(&pdev->dev), keypad);
 419        if (error) {
 420                dev_err(&pdev->dev, "failed to register keypad interrupt\n");
 421                goto err_unprepare_clk;
 422        }
 423
 424        device_init_wakeup(&pdev->dev, pdata->wakeup);
 425        platform_set_drvdata(pdev, keypad);
 426        pm_runtime_enable(&pdev->dev);
 427
 428        error = input_register_device(keypad->input_dev);
 429        if (error)
 430                goto err_disable_runtime_pm;
 431
 432        if (pdev->dev.of_node) {
 433                devm_kfree(&pdev->dev, (void *)pdata->keymap_data->keymap);
 434                devm_kfree(&pdev->dev, (void *)pdata->keymap_data);
 435                devm_kfree(&pdev->dev, (void *)pdata);
 436        }
 437        return 0;
 438
 439err_disable_runtime_pm:
 440        pm_runtime_disable(&pdev->dev);
 441        device_init_wakeup(&pdev->dev, 0);
 442err_unprepare_clk:
 443        clk_unprepare(keypad->clk);
 444        return error;
 445}
 446
 447static int samsung_keypad_remove(struct platform_device *pdev)
 448{
 449        struct samsung_keypad *keypad = platform_get_drvdata(pdev);
 450
 451        pm_runtime_disable(&pdev->dev);
 452        device_init_wakeup(&pdev->dev, 0);
 453
 454        input_unregister_device(keypad->input_dev);
 455
 456        clk_unprepare(keypad->clk);
 457
 458        return 0;
 459}
 460
 461#ifdef CONFIG_PM_RUNTIME
 462static int samsung_keypad_runtime_suspend(struct device *dev)
 463{
 464        struct platform_device *pdev = to_platform_device(dev);
 465        struct samsung_keypad *keypad = platform_get_drvdata(pdev);
 466        unsigned int val;
 467        int error;
 468
 469        if (keypad->stopped)
 470                return 0;
 471
 472        /* This may fail on some SoCs due to lack of controller support */
 473        error = enable_irq_wake(keypad->irq);
 474        if (!error)
 475                keypad->wake_enabled = true;
 476
 477        val = readl(keypad->base + SAMSUNG_KEYIFCON);
 478        val |= SAMSUNG_KEYIFCON_WAKEUPEN;
 479        writel(val, keypad->base + SAMSUNG_KEYIFCON);
 480
 481        clk_disable(keypad->clk);
 482
 483        return 0;
 484}
 485
 486static int samsung_keypad_runtime_resume(struct device *dev)
 487{
 488        struct platform_device *pdev = to_platform_device(dev);
 489        struct samsung_keypad *keypad = platform_get_drvdata(pdev);
 490        unsigned int val;
 491
 492        if (keypad->stopped)
 493                return 0;
 494
 495        clk_enable(keypad->clk);
 496
 497        val = readl(keypad->base + SAMSUNG_KEYIFCON);
 498        val &= ~SAMSUNG_KEYIFCON_WAKEUPEN;
 499        writel(val, keypad->base + SAMSUNG_KEYIFCON);
 500
 501        if (keypad->wake_enabled)
 502                disable_irq_wake(keypad->irq);
 503
 504        return 0;
 505}
 506#endif
 507
 508#ifdef CONFIG_PM_SLEEP
 509static void samsung_keypad_toggle_wakeup(struct samsung_keypad *keypad,
 510                                         bool enable)
 511{
 512        unsigned int val;
 513
 514        clk_enable(keypad->clk);
 515
 516        val = readl(keypad->base + SAMSUNG_KEYIFCON);
 517        if (enable) {
 518                val |= SAMSUNG_KEYIFCON_WAKEUPEN;
 519                if (device_may_wakeup(&keypad->pdev->dev))
 520                        enable_irq_wake(keypad->irq);
 521        } else {
 522                val &= ~SAMSUNG_KEYIFCON_WAKEUPEN;
 523                if (device_may_wakeup(&keypad->pdev->dev))
 524                        disable_irq_wake(keypad->irq);
 525        }
 526        writel(val, keypad->base + SAMSUNG_KEYIFCON);
 527
 528        clk_disable(keypad->clk);
 529}
 530
 531static int samsung_keypad_suspend(struct device *dev)
 532{
 533        struct platform_device *pdev = to_platform_device(dev);
 534        struct samsung_keypad *keypad = platform_get_drvdata(pdev);
 535        struct input_dev *input_dev = keypad->input_dev;
 536
 537        mutex_lock(&input_dev->mutex);
 538
 539        if (input_dev->users)
 540                samsung_keypad_stop(keypad);
 541
 542        samsung_keypad_toggle_wakeup(keypad, true);
 543
 544        mutex_unlock(&input_dev->mutex);
 545
 546        return 0;
 547}
 548
 549static int samsung_keypad_resume(struct device *dev)
 550{
 551        struct platform_device *pdev = to_platform_device(dev);
 552        struct samsung_keypad *keypad = platform_get_drvdata(pdev);
 553        struct input_dev *input_dev = keypad->input_dev;
 554
 555        mutex_lock(&input_dev->mutex);
 556
 557        samsung_keypad_toggle_wakeup(keypad, false);
 558
 559        if (input_dev->users)
 560                samsung_keypad_start(keypad);
 561
 562        mutex_unlock(&input_dev->mutex);
 563
 564        return 0;
 565}
 566#endif
 567
 568static const struct dev_pm_ops samsung_keypad_pm_ops = {
 569        SET_SYSTEM_SLEEP_PM_OPS(samsung_keypad_suspend, samsung_keypad_resume)
 570        SET_RUNTIME_PM_OPS(samsung_keypad_runtime_suspend,
 571                           samsung_keypad_runtime_resume, NULL)
 572};
 573
 574#ifdef CONFIG_OF
 575static const struct of_device_id samsung_keypad_dt_match[] = {
 576        { .compatible = "samsung,s3c6410-keypad" },
 577        { .compatible = "samsung,s5pv210-keypad" },
 578        {},
 579};
 580MODULE_DEVICE_TABLE(of, samsung_keypad_dt_match);
 581#endif
 582
 583static struct platform_device_id samsung_keypad_driver_ids[] = {
 584        {
 585                .name           = "samsung-keypad",
 586                .driver_data    = KEYPAD_TYPE_SAMSUNG,
 587        }, {
 588                .name           = "s5pv210-keypad",
 589                .driver_data    = KEYPAD_TYPE_S5PV210,
 590        },
 591        { },
 592};
 593MODULE_DEVICE_TABLE(platform, samsung_keypad_driver_ids);
 594
 595static struct platform_driver samsung_keypad_driver = {
 596        .probe          = samsung_keypad_probe,
 597        .remove         = samsung_keypad_remove,
 598        .driver         = {
 599                .name   = "samsung-keypad",
 600                .owner  = THIS_MODULE,
 601                .of_match_table = of_match_ptr(samsung_keypad_dt_match),
 602                .pm     = &samsung_keypad_pm_ops,
 603        },
 604        .id_table       = samsung_keypad_driver_ids,
 605};
 606module_platform_driver(samsung_keypad_driver);
 607
 608MODULE_DESCRIPTION("Samsung keypad driver");
 609MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
 610MODULE_AUTHOR("Donghwa Lee <dh09.lee@samsung.com>");
 611MODULE_LICENSE("GPL");
 612