Path: blob/master/drivers/input/keyboard/ep93xx_keypad.c
15111 views
/*1* Driver for the Cirrus EP93xx matrix keypad controller.2*3* Copyright (c) 2008 H Hartley Sweeten <[email protected]>4*5* Based on the pxa27x matrix keypad controller by Rodolfo Giometti.6*7* This program is free software; you can redistribute it and/or modify8* it under the terms of the GNU General Public License version 2 as9* published by the Free Software Foundation.10*11* NOTE:12*13* The 3-key reset is triggered by pressing the 3 keys in14* Row 0, Columns 2, 4, and 7 at the same time. This action can15* be disabled by setting the EP93XX_KEYPAD_DISABLE_3_KEY flag.16*17* Normal operation for the matrix does not autorepeat the key press.18* This action can be enabled by setting the EP93XX_KEYPAD_AUTOREPEAT19* flag.20*/2122#include <linux/platform_device.h>23#include <linux/interrupt.h>24#include <linux/clk.h>25#include <linux/io.h>26#include <linux/input/matrix_keypad.h>27#include <linux/slab.h>2829#include <mach/hardware.h>30#include <mach/ep93xx_keypad.h>3132/*33* Keypad Interface Register offsets34*/35#define KEY_INIT 0x00 /* Key Scan Initialization register */36#define KEY_DIAG 0x04 /* Key Scan Diagnostic register */37#define KEY_REG 0x08 /* Key Value Capture register */3839/* Key Scan Initialization Register bit defines */40#define KEY_INIT_DBNC_MASK (0x00ff0000)41#define KEY_INIT_DBNC_SHIFT (16)42#define KEY_INIT_DIS3KY (1<<15)43#define KEY_INIT_DIAG (1<<14)44#define KEY_INIT_BACK (1<<13)45#define KEY_INIT_T2 (1<<12)46#define KEY_INIT_PRSCL_MASK (0x000003ff)47#define KEY_INIT_PRSCL_SHIFT (0)4849/* Key Scan Diagnostic Register bit defines */50#define KEY_DIAG_MASK (0x0000003f)51#define KEY_DIAG_SHIFT (0)5253/* Key Value Capture Register bit defines */54#define KEY_REG_K (1<<15)55#define KEY_REG_INT (1<<14)56#define KEY_REG_2KEYS (1<<13)57#define KEY_REG_1KEY (1<<12)58#define KEY_REG_KEY2_MASK (0x00000fc0)59#define KEY_REG_KEY2_SHIFT (6)60#define KEY_REG_KEY1_MASK (0x0000003f)61#define KEY_REG_KEY1_SHIFT (0)6263#define EP93XX_MATRIX_SIZE (EP93XX_MATRIX_ROWS * EP93XX_MATRIX_COLS)6465struct ep93xx_keypad {66struct ep93xx_keypad_platform_data *pdata;67struct input_dev *input_dev;68struct clk *clk;6970void __iomem *mmio_base;7172unsigned short keycodes[EP93XX_MATRIX_SIZE];7374int key1;75int key2;7677int irq;7879bool enabled;80};8182static irqreturn_t ep93xx_keypad_irq_handler(int irq, void *dev_id)83{84struct ep93xx_keypad *keypad = dev_id;85struct input_dev *input_dev = keypad->input_dev;86unsigned int status;87int keycode, key1, key2;8889status = __raw_readl(keypad->mmio_base + KEY_REG);9091keycode = (status & KEY_REG_KEY1_MASK) >> KEY_REG_KEY1_SHIFT;92key1 = keypad->keycodes[keycode];9394keycode = (status & KEY_REG_KEY2_MASK) >> KEY_REG_KEY2_SHIFT;95key2 = keypad->keycodes[keycode];9697if (status & KEY_REG_2KEYS) {98if (keypad->key1 && key1 != keypad->key1 && key2 != keypad->key1)99input_report_key(input_dev, keypad->key1, 0);100101if (keypad->key2 && key1 != keypad->key2 && key2 != keypad->key2)102input_report_key(input_dev, keypad->key2, 0);103104input_report_key(input_dev, key1, 1);105input_report_key(input_dev, key2, 1);106107keypad->key1 = key1;108keypad->key2 = key2;109110} else if (status & KEY_REG_1KEY) {111if (keypad->key1 && key1 != keypad->key1)112input_report_key(input_dev, keypad->key1, 0);113114if (keypad->key2 && key1 != keypad->key2)115input_report_key(input_dev, keypad->key2, 0);116117input_report_key(input_dev, key1, 1);118119keypad->key1 = key1;120keypad->key2 = 0;121122} else {123input_report_key(input_dev, keypad->key1, 0);124input_report_key(input_dev, keypad->key2, 0);125126keypad->key1 = keypad->key2 = 0;127}128input_sync(input_dev);129130return IRQ_HANDLED;131}132133static void ep93xx_keypad_config(struct ep93xx_keypad *keypad)134{135struct ep93xx_keypad_platform_data *pdata = keypad->pdata;136unsigned int val = 0;137138if (pdata->flags & EP93XX_KEYPAD_KDIV)139clk_set_rate(keypad->clk, EP93XX_KEYTCHCLK_DIV4);140else141clk_set_rate(keypad->clk, EP93XX_KEYTCHCLK_DIV16);142143if (pdata->flags & EP93XX_KEYPAD_DISABLE_3_KEY)144val |= KEY_INIT_DIS3KY;145if (pdata->flags & EP93XX_KEYPAD_DIAG_MODE)146val |= KEY_INIT_DIAG;147if (pdata->flags & EP93XX_KEYPAD_BACK_DRIVE)148val |= KEY_INIT_BACK;149if (pdata->flags & EP93XX_KEYPAD_TEST_MODE)150val |= KEY_INIT_T2;151152val |= ((pdata->debounce << KEY_INIT_DBNC_SHIFT) & KEY_INIT_DBNC_MASK);153154val |= ((pdata->prescale << KEY_INIT_PRSCL_SHIFT) & KEY_INIT_PRSCL_MASK);155156__raw_writel(val, keypad->mmio_base + KEY_INIT);157}158159static int ep93xx_keypad_open(struct input_dev *pdev)160{161struct ep93xx_keypad *keypad = input_get_drvdata(pdev);162163if (!keypad->enabled) {164ep93xx_keypad_config(keypad);165clk_enable(keypad->clk);166keypad->enabled = true;167}168169return 0;170}171172static void ep93xx_keypad_close(struct input_dev *pdev)173{174struct ep93xx_keypad *keypad = input_get_drvdata(pdev);175176if (keypad->enabled) {177clk_disable(keypad->clk);178keypad->enabled = false;179}180}181182183#ifdef CONFIG_PM184/*185* NOTE: I don't know if this is correct, or will work on the ep93xx.186*187* None of the existing ep93xx drivers have power management support.188* But, this is basically what the pxa27x_keypad driver does.189*/190static int ep93xx_keypad_suspend(struct platform_device *pdev,191pm_message_t state)192{193struct ep93xx_keypad *keypad = platform_get_drvdata(pdev);194struct input_dev *input_dev = keypad->input_dev;195196mutex_lock(&input_dev->mutex);197198if (keypad->enabled) {199clk_disable(keypad->clk);200keypad->enabled = false;201}202203mutex_unlock(&input_dev->mutex);204205if (device_may_wakeup(&pdev->dev))206enable_irq_wake(keypad->irq);207208return 0;209}210211static int ep93xx_keypad_resume(struct platform_device *pdev)212{213struct ep93xx_keypad *keypad = platform_get_drvdata(pdev);214struct input_dev *input_dev = keypad->input_dev;215216if (device_may_wakeup(&pdev->dev))217disable_irq_wake(keypad->irq);218219mutex_lock(&input_dev->mutex);220221if (input_dev->users) {222if (!keypad->enabled) {223ep93xx_keypad_config(keypad);224clk_enable(keypad->clk);225keypad->enabled = true;226}227}228229mutex_unlock(&input_dev->mutex);230231return 0;232}233#else /* !CONFIG_PM */234#define ep93xx_keypad_suspend NULL235#define ep93xx_keypad_resume NULL236#endif /* !CONFIG_PM */237238static int __devinit ep93xx_keypad_probe(struct platform_device *pdev)239{240struct ep93xx_keypad *keypad;241const struct matrix_keymap_data *keymap_data;242struct input_dev *input_dev;243struct resource *res;244int err;245246keypad = kzalloc(sizeof(struct ep93xx_keypad), GFP_KERNEL);247if (!keypad)248return -ENOMEM;249250keypad->pdata = pdev->dev.platform_data;251if (!keypad->pdata) {252err = -EINVAL;253goto failed_free;254}255256keymap_data = keypad->pdata->keymap_data;257if (!keymap_data) {258err = -EINVAL;259goto failed_free;260}261262keypad->irq = platform_get_irq(pdev, 0);263if (!keypad->irq) {264err = -ENXIO;265goto failed_free;266}267268res = platform_get_resource(pdev, IORESOURCE_MEM, 0);269if (!res) {270err = -ENXIO;271goto failed_free;272}273274res = request_mem_region(res->start, resource_size(res), pdev->name);275if (!res) {276err = -EBUSY;277goto failed_free;278}279280keypad->mmio_base = ioremap(res->start, resource_size(res));281if (keypad->mmio_base == NULL) {282err = -ENXIO;283goto failed_free_mem;284}285286err = ep93xx_keypad_acquire_gpio(pdev);287if (err)288goto failed_free_io;289290keypad->clk = clk_get(&pdev->dev, NULL);291if (IS_ERR(keypad->clk)) {292err = PTR_ERR(keypad->clk);293goto failed_free_gpio;294}295296input_dev = input_allocate_device();297if (!input_dev) {298err = -ENOMEM;299goto failed_put_clk;300}301302keypad->input_dev = input_dev;303304input_dev->name = pdev->name;305input_dev->id.bustype = BUS_HOST;306input_dev->open = ep93xx_keypad_open;307input_dev->close = ep93xx_keypad_close;308input_dev->dev.parent = &pdev->dev;309input_dev->keycode = keypad->keycodes;310input_dev->keycodesize = sizeof(keypad->keycodes[0]);311input_dev->keycodemax = ARRAY_SIZE(keypad->keycodes);312313input_set_drvdata(input_dev, keypad);314315input_dev->evbit[0] = BIT_MASK(EV_KEY);316if (keypad->pdata->flags & EP93XX_KEYPAD_AUTOREPEAT)317input_dev->evbit[0] |= BIT_MASK(EV_REP);318319matrix_keypad_build_keymap(keymap_data, 3,320input_dev->keycode, input_dev->keybit);321platform_set_drvdata(pdev, keypad);322323err = request_irq(keypad->irq, ep93xx_keypad_irq_handler,324IRQF_DISABLED, pdev->name, keypad);325if (err)326goto failed_free_dev;327328err = input_register_device(input_dev);329if (err)330goto failed_free_irq;331332device_init_wakeup(&pdev->dev, 1);333334return 0;335336failed_free_irq:337free_irq(keypad->irq, pdev);338platform_set_drvdata(pdev, NULL);339failed_free_dev:340input_free_device(input_dev);341failed_put_clk:342clk_put(keypad->clk);343failed_free_gpio:344ep93xx_keypad_release_gpio(pdev);345failed_free_io:346iounmap(keypad->mmio_base);347failed_free_mem:348release_mem_region(res->start, resource_size(res));349failed_free:350kfree(keypad);351return err;352}353354static int __devexit ep93xx_keypad_remove(struct platform_device *pdev)355{356struct ep93xx_keypad *keypad = platform_get_drvdata(pdev);357struct resource *res;358359free_irq(keypad->irq, pdev);360361platform_set_drvdata(pdev, NULL);362363if (keypad->enabled)364clk_disable(keypad->clk);365clk_put(keypad->clk);366367input_unregister_device(keypad->input_dev);368369ep93xx_keypad_release_gpio(pdev);370371iounmap(keypad->mmio_base);372373res = platform_get_resource(pdev, IORESOURCE_MEM, 0);374release_mem_region(res->start, resource_size(res));375376kfree(keypad);377378return 0;379}380381static struct platform_driver ep93xx_keypad_driver = {382.driver = {383.name = "ep93xx-keypad",384.owner = THIS_MODULE,385},386.probe = ep93xx_keypad_probe,387.remove = __devexit_p(ep93xx_keypad_remove),388.suspend = ep93xx_keypad_suspend,389.resume = ep93xx_keypad_resume,390};391392static int __init ep93xx_keypad_init(void)393{394return platform_driver_register(&ep93xx_keypad_driver);395}396397static void __exit ep93xx_keypad_exit(void)398{399platform_driver_unregister(&ep93xx_keypad_driver);400}401402module_init(ep93xx_keypad_init);403module_exit(ep93xx_keypad_exit);404405MODULE_LICENSE("GPL");406MODULE_AUTHOR("H Hartley Sweeten <[email protected]>");407MODULE_DESCRIPTION("EP93xx Matrix Keypad Controller");408MODULE_ALIAS("platform:ep93xx-keypad");409410411