Path: blob/master/drivers/input/keyboard/w90p910_keypad.c
15109 views
/*1* Copyright (c) 2008-2009 Nuvoton technology corporation.2*3* Wan ZongShun <[email protected]>4*5* This program is free software; you can redistribute it and/or modify6* it under the terms of the GNU General Public License as published by7* the Free Software Foundation;version 2 of the License.8*9*/1011#include <linux/kernel.h>12#include <linux/module.h>13#include <linux/init.h>14#include <linux/interrupt.h>15#include <linux/input.h>16#include <linux/device.h>17#include <linux/platform_device.h>18#include <linux/clk.h>19#include <linux/err.h>20#include <linux/io.h>21#include <linux/slab.h>2223#include <mach/w90p910_keypad.h>2425/* Keypad Interface Control Registers */26#define KPI_CONF 0x0027#define KPI_3KCONF 0x0428#define KPI_LPCONF 0x0829#define KPI_STATUS 0x0C3031#define IS1KEY (0x01 << 16)32#define INTTR (0x01 << 21)33#define KEY0R (0x0f << 3)34#define KEY0C 0x0735#define DEBOUNCE_BIT 0x0836#define KSIZE0 (0x01 << 16)37#define KSIZE1 (0x01 << 17)38#define KPSEL (0x01 << 19)39#define ENKP (0x01 << 18)4041#define KGET_RAW(n) (((n) & KEY0R) >> 3)42#define KGET_COLUMN(n) ((n) & KEY0C)4344#define W90P910_MAX_KEY_NUM (8 * 8)45#define W90P910_ROW_SHIFT 34647struct w90p910_keypad {48const struct w90p910_keypad_platform_data *pdata;49struct clk *clk;50struct input_dev *input_dev;51void __iomem *mmio_base;52int irq;53unsigned short keymap[W90P910_MAX_KEY_NUM];54};5556static void w90p910_keypad_scan_matrix(struct w90p910_keypad *keypad,57unsigned int status)58{59struct input_dev *input_dev = keypad->input_dev;60unsigned int row = KGET_RAW(status);61unsigned int col = KGET_COLUMN(status);62unsigned int code = MATRIX_SCAN_CODE(row, col, W90P910_ROW_SHIFT);63unsigned int key = keypad->keymap[code];6465input_event(input_dev, EV_MSC, MSC_SCAN, code);66input_report_key(input_dev, key, 1);67input_sync(input_dev);6869input_event(input_dev, EV_MSC, MSC_SCAN, code);70input_report_key(input_dev, key, 0);71input_sync(input_dev);72}7374static irqreturn_t w90p910_keypad_irq_handler(int irq, void *dev_id)75{76struct w90p910_keypad *keypad = dev_id;77unsigned int kstatus, val;7879kstatus = __raw_readl(keypad->mmio_base + KPI_STATUS);8081val = INTTR | IS1KEY;8283if (kstatus & val)84w90p910_keypad_scan_matrix(keypad, kstatus);8586return IRQ_HANDLED;87}8889static int w90p910_keypad_open(struct input_dev *dev)90{91struct w90p910_keypad *keypad = input_get_drvdata(dev);92const struct w90p910_keypad_platform_data *pdata = keypad->pdata;93unsigned int val, config;9495/* Enable unit clock */96clk_enable(keypad->clk);9798val = __raw_readl(keypad->mmio_base + KPI_CONF);99val |= (KPSEL | ENKP);100val &= ~(KSIZE0 | KSIZE1);101102config = pdata->prescale | (pdata->debounce << DEBOUNCE_BIT);103104val |= config;105106__raw_writel(val, keypad->mmio_base + KPI_CONF);107108return 0;109}110111static void w90p910_keypad_close(struct input_dev *dev)112{113struct w90p910_keypad *keypad = input_get_drvdata(dev);114115/* Disable clock unit */116clk_disable(keypad->clk);117}118119static int __devinit w90p910_keypad_probe(struct platform_device *pdev)120{121const struct w90p910_keypad_platform_data *pdata =122pdev->dev.platform_data;123const struct matrix_keymap_data *keymap_data;124struct w90p910_keypad *keypad;125struct input_dev *input_dev;126struct resource *res;127int irq;128int error;129130if (!pdata) {131dev_err(&pdev->dev, "no platform data defined\n");132return -EINVAL;133}134135keymap_data = pdata->keymap_data;136137irq = platform_get_irq(pdev, 0);138if (irq < 0) {139dev_err(&pdev->dev, "failed to get keypad irq\n");140return -ENXIO;141}142143keypad = kzalloc(sizeof(struct w90p910_keypad), GFP_KERNEL);144input_dev = input_allocate_device();145if (!keypad || !input_dev) {146dev_err(&pdev->dev, "failed to allocate driver data\n");147error = -ENOMEM;148goto failed_free;149}150151keypad->pdata = pdata;152keypad->input_dev = input_dev;153keypad->irq = irq;154155res = platform_get_resource(pdev, IORESOURCE_MEM, 0);156if (res == NULL) {157dev_err(&pdev->dev, "failed to get I/O memory\n");158error = -ENXIO;159goto failed_free;160}161162res = request_mem_region(res->start, resource_size(res), pdev->name);163if (res == NULL) {164dev_err(&pdev->dev, "failed to request I/O memory\n");165error = -EBUSY;166goto failed_free;167}168169keypad->mmio_base = ioremap(res->start, resource_size(res));170if (keypad->mmio_base == NULL) {171dev_err(&pdev->dev, "failed to remap I/O memory\n");172error = -ENXIO;173goto failed_free_res;174}175176keypad->clk = clk_get(&pdev->dev, NULL);177if (IS_ERR(keypad->clk)) {178dev_err(&pdev->dev, "failed to get keypad clock\n");179error = PTR_ERR(keypad->clk);180goto failed_free_io;181}182183/* set multi-function pin for w90p910 kpi. */184mfp_set_groupi(&pdev->dev);185186input_dev->name = pdev->name;187input_dev->id.bustype = BUS_HOST;188input_dev->open = w90p910_keypad_open;189input_dev->close = w90p910_keypad_close;190input_dev->dev.parent = &pdev->dev;191192input_dev->keycode = keypad->keymap;193input_dev->keycodesize = sizeof(keypad->keymap[0]);194input_dev->keycodemax = ARRAY_SIZE(keypad->keymap);195196input_set_drvdata(input_dev, keypad);197198input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);199input_set_capability(input_dev, EV_MSC, MSC_SCAN);200201matrix_keypad_build_keymap(keymap_data, W90P910_ROW_SHIFT,202input_dev->keycode, input_dev->keybit);203204error = request_irq(keypad->irq, w90p910_keypad_irq_handler,205IRQF_DISABLED, pdev->name, keypad);206if (error) {207dev_err(&pdev->dev, "failed to request IRQ\n");208goto failed_put_clk;209}210211/* Register the input device */212error = input_register_device(input_dev);213if (error) {214dev_err(&pdev->dev, "failed to register input device\n");215goto failed_free_irq;216}217218platform_set_drvdata(pdev, keypad);219return 0;220221failed_free_irq:222free_irq(irq, pdev);223failed_put_clk:224clk_put(keypad->clk);225failed_free_io:226iounmap(keypad->mmio_base);227failed_free_res:228release_mem_region(res->start, resource_size(res));229failed_free:230input_free_device(input_dev);231kfree(keypad);232return error;233}234235static int __devexit w90p910_keypad_remove(struct platform_device *pdev)236{237struct w90p910_keypad *keypad = platform_get_drvdata(pdev);238struct resource *res;239240free_irq(keypad->irq, pdev);241242clk_put(keypad->clk);243244input_unregister_device(keypad->input_dev);245246iounmap(keypad->mmio_base);247res = platform_get_resource(pdev, IORESOURCE_MEM, 0);248release_mem_region(res->start, resource_size(res));249250platform_set_drvdata(pdev, NULL);251kfree(keypad);252253return 0;254}255256static struct platform_driver w90p910_keypad_driver = {257.probe = w90p910_keypad_probe,258.remove = __devexit_p(w90p910_keypad_remove),259.driver = {260.name = "nuc900-kpi",261.owner = THIS_MODULE,262},263};264265static int __init w90p910_keypad_init(void)266{267return platform_driver_register(&w90p910_keypad_driver);268}269270static void __exit w90p910_keypad_exit(void)271{272platform_driver_unregister(&w90p910_keypad_driver);273}274275module_init(w90p910_keypad_init);276module_exit(w90p910_keypad_exit);277278MODULE_AUTHOR("Wan ZongShun <[email protected]>");279MODULE_DESCRIPTION("w90p910 keypad driver");280MODULE_LICENSE("GPL");281MODULE_ALIAS("platform:nuc900-keypad");282283284