Path: blob/master/drivers/input/keyboard/omap4-keypad.c
15109 views
/*1* OMAP4 Keypad Driver2*3* Copyright (C) 2010 Texas Instruments4*5* Author: Abraham Arce <[email protected]>6* Initial Code: Syed Rafiuddin <[email protected]>7*8* This program is free software; you can redistribute it and/or modify9* it under the terms of the GNU General Public License as published by10* the Free Software Foundation; either version 2 of the License, or11* (at your option) any later version.12*13* This program is distributed in the hope that it will be useful,14* but WITHOUT ANY WARRANTY; without even the implied warranty of15* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the16* GNU General Public License for more details.17*18* You should have received a copy of the GNU General Public License19* along with this program; if not, write to the Free Software20* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA21*/2223#include <linux/module.h>24#include <linux/init.h>25#include <linux/interrupt.h>26#include <linux/platform_device.h>27#include <linux/errno.h>28#include <linux/io.h>29#include <linux/input.h>30#include <linux/slab.h>31#include <linux/pm_runtime.h>3233#include <plat/omap4-keypad.h>3435/* OMAP4 registers */36#define OMAP4_KBD_REVISION 0x0037#define OMAP4_KBD_SYSCONFIG 0x1038#define OMAP4_KBD_SYSSTATUS 0x1439#define OMAP4_KBD_IRQSTATUS 0x1840#define OMAP4_KBD_IRQENABLE 0x1C41#define OMAP4_KBD_WAKEUPENABLE 0x2042#define OMAP4_KBD_PENDING 0x2443#define OMAP4_KBD_CTRL 0x2844#define OMAP4_KBD_DEBOUNCINGTIME 0x2C45#define OMAP4_KBD_LONGKEYTIME 0x3046#define OMAP4_KBD_TIMEOUT 0x3447#define OMAP4_KBD_STATEMACHINE 0x3848#define OMAP4_KBD_ROWINPUTS 0x3C49#define OMAP4_KBD_COLUMNOUTPUTS 0x4050#define OMAP4_KBD_FULLCODE31_0 0x4451#define OMAP4_KBD_FULLCODE63_32 0x485253/* OMAP4 bit definitions */54#define OMAP4_DEF_IRQENABLE_EVENTEN (1 << 0)55#define OMAP4_DEF_IRQENABLE_LONGKEY (1 << 1)56#define OMAP4_DEF_IRQENABLE_TIMEOUTEN (1 << 2)57#define OMAP4_DEF_WUP_EVENT_ENA (1 << 0)58#define OMAP4_DEF_WUP_LONG_KEY_ENA (1 << 1)59#define OMAP4_DEF_CTRL_NOSOFTMODE (1 << 1)60#define OMAP4_DEF_CTRLPTVVALUE (1 << 2)61#define OMAP4_DEF_CTRLPTV (1 << 1)6263/* OMAP4 values */64#define OMAP4_VAL_IRQDISABLE 0x0065#define OMAP4_VAL_DEBOUNCINGTIME 0x0766#define OMAP4_VAL_FUNCTIONALCFG 0x1E6768#define OMAP4_MASK_IRQSTATUSDISABLE 0xFFFF6970struct omap4_keypad {71struct input_dev *input;7273void __iomem *base;74int irq;7576unsigned int rows;77unsigned int cols;78unsigned int row_shift;79unsigned char key_state[8];80unsigned short keymap[];81};8283/* Interrupt handler */84static irqreturn_t omap4_keypad_interrupt(int irq, void *dev_id)85{86struct omap4_keypad *keypad_data = dev_id;87struct input_dev *input_dev = keypad_data->input;88unsigned char key_state[ARRAY_SIZE(keypad_data->key_state)];89unsigned int col, row, code, changed;90u32 *new_state = (u32 *) key_state;9192/* Disable interrupts */93__raw_writel(OMAP4_VAL_IRQDISABLE,94keypad_data->base + OMAP4_KBD_IRQENABLE);9596*new_state = __raw_readl(keypad_data->base + OMAP4_KBD_FULLCODE31_0);97*(new_state + 1) = __raw_readl(keypad_data->base98+ OMAP4_KBD_FULLCODE63_32);99100for (row = 0; row < keypad_data->rows; row++) {101changed = key_state[row] ^ keypad_data->key_state[row];102if (!changed)103continue;104105for (col = 0; col < keypad_data->cols; col++) {106if (changed & (1 << col)) {107code = MATRIX_SCAN_CODE(row, col,108keypad_data->row_shift);109input_event(input_dev, EV_MSC, MSC_SCAN, code);110input_report_key(input_dev,111keypad_data->keymap[code],112key_state[row] & (1 << col));113}114}115}116117input_sync(input_dev);118119memcpy(keypad_data->key_state, key_state,120sizeof(keypad_data->key_state));121122/* clear pending interrupts */123__raw_writel(__raw_readl(keypad_data->base + OMAP4_KBD_IRQSTATUS),124keypad_data->base + OMAP4_KBD_IRQSTATUS);125126/* enable interrupts */127__raw_writel(OMAP4_DEF_IRQENABLE_EVENTEN | OMAP4_DEF_IRQENABLE_LONGKEY,128keypad_data->base + OMAP4_KBD_IRQENABLE);129130return IRQ_HANDLED;131}132133static int omap4_keypad_open(struct input_dev *input)134{135struct omap4_keypad *keypad_data = input_get_drvdata(input);136137pm_runtime_get_sync(input->dev.parent);138139disable_irq(keypad_data->irq);140141__raw_writel(OMAP4_VAL_FUNCTIONALCFG,142keypad_data->base + OMAP4_KBD_CTRL);143__raw_writel(OMAP4_VAL_DEBOUNCINGTIME,144keypad_data->base + OMAP4_KBD_DEBOUNCINGTIME);145__raw_writel(OMAP4_VAL_IRQDISABLE,146keypad_data->base + OMAP4_KBD_IRQSTATUS);147__raw_writel(OMAP4_DEF_IRQENABLE_EVENTEN | OMAP4_DEF_IRQENABLE_LONGKEY,148keypad_data->base + OMAP4_KBD_IRQENABLE);149__raw_writel(OMAP4_DEF_WUP_EVENT_ENA | OMAP4_DEF_WUP_LONG_KEY_ENA,150keypad_data->base + OMAP4_KBD_WAKEUPENABLE);151152enable_irq(keypad_data->irq);153154return 0;155}156157static void omap4_keypad_close(struct input_dev *input)158{159struct omap4_keypad *keypad_data = input_get_drvdata(input);160161disable_irq(keypad_data->irq);162163/* Disable interrupts */164__raw_writel(OMAP4_VAL_IRQDISABLE,165keypad_data->base + OMAP4_KBD_IRQENABLE);166167/* clear pending interrupts */168__raw_writel(__raw_readl(keypad_data->base + OMAP4_KBD_IRQSTATUS),169keypad_data->base + OMAP4_KBD_IRQSTATUS);170171enable_irq(keypad_data->irq);172173pm_runtime_put_sync(input->dev.parent);174}175176static int __devinit omap4_keypad_probe(struct platform_device *pdev)177{178const struct omap4_keypad_platform_data *pdata;179struct omap4_keypad *keypad_data;180struct input_dev *input_dev;181struct resource *res;182resource_size_t size;183unsigned int row_shift, max_keys;184int irq;185int error;186187/* platform data */188pdata = pdev->dev.platform_data;189if (!pdata) {190dev_err(&pdev->dev, "no platform data defined\n");191return -EINVAL;192}193194res = platform_get_resource(pdev, IORESOURCE_MEM, 0);195if (!res) {196dev_err(&pdev->dev, "no base address specified\n");197return -EINVAL;198}199200irq = platform_get_irq(pdev, 0);201if (!irq) {202dev_err(&pdev->dev, "no keyboard irq assigned\n");203return -EINVAL;204}205206if (!pdata->keymap_data) {207dev_err(&pdev->dev, "no keymap data defined\n");208return -EINVAL;209}210211row_shift = get_count_order(pdata->cols);212max_keys = pdata->rows << row_shift;213214keypad_data = kzalloc(sizeof(struct omap4_keypad) +215max_keys * sizeof(keypad_data->keymap[0]),216GFP_KERNEL);217if (!keypad_data) {218dev_err(&pdev->dev, "keypad_data memory allocation failed\n");219return -ENOMEM;220}221222size = resource_size(res);223224res = request_mem_region(res->start, size, pdev->name);225if (!res) {226dev_err(&pdev->dev, "can't request mem region\n");227error = -EBUSY;228goto err_free_keypad;229}230231keypad_data->base = ioremap(res->start, resource_size(res));232if (!keypad_data->base) {233dev_err(&pdev->dev, "can't ioremap mem resource\n");234error = -ENOMEM;235goto err_release_mem;236}237238keypad_data->irq = irq;239keypad_data->row_shift = row_shift;240keypad_data->rows = pdata->rows;241keypad_data->cols = pdata->cols;242243/* input device allocation */244keypad_data->input = input_dev = input_allocate_device();245if (!input_dev) {246error = -ENOMEM;247goto err_unmap;248}249250input_dev->name = pdev->name;251input_dev->dev.parent = &pdev->dev;252input_dev->id.bustype = BUS_HOST;253input_dev->id.vendor = 0x0001;254input_dev->id.product = 0x0001;255input_dev->id.version = 0x0001;256257input_dev->open = omap4_keypad_open;258input_dev->close = omap4_keypad_close;259260input_dev->keycode = keypad_data->keymap;261input_dev->keycodesize = sizeof(keypad_data->keymap[0]);262input_dev->keycodemax = max_keys;263264__set_bit(EV_KEY, input_dev->evbit);265__set_bit(EV_REP, input_dev->evbit);266267input_set_capability(input_dev, EV_MSC, MSC_SCAN);268269input_set_drvdata(input_dev, keypad_data);270271matrix_keypad_build_keymap(pdata->keymap_data, row_shift,272input_dev->keycode, input_dev->keybit);273274error = request_irq(keypad_data->irq, omap4_keypad_interrupt,275IRQF_TRIGGER_RISING,276"omap4-keypad", keypad_data);277if (error) {278dev_err(&pdev->dev, "failed to register interrupt\n");279goto err_free_input;280}281282pm_runtime_enable(&pdev->dev);283284error = input_register_device(keypad_data->input);285if (error < 0) {286dev_err(&pdev->dev, "failed to register input device\n");287goto err_pm_disable;288}289290platform_set_drvdata(pdev, keypad_data);291return 0;292293err_pm_disable:294pm_runtime_disable(&pdev->dev);295free_irq(keypad_data->irq, keypad_data);296err_free_input:297input_free_device(input_dev);298err_unmap:299iounmap(keypad_data->base);300err_release_mem:301release_mem_region(res->start, size);302err_free_keypad:303kfree(keypad_data);304return error;305}306307static int __devexit omap4_keypad_remove(struct platform_device *pdev)308{309struct omap4_keypad *keypad_data = platform_get_drvdata(pdev);310struct resource *res;311312free_irq(keypad_data->irq, keypad_data);313314pm_runtime_disable(&pdev->dev);315316input_unregister_device(keypad_data->input);317318iounmap(keypad_data->base);319320res = platform_get_resource(pdev, IORESOURCE_MEM, 0);321release_mem_region(res->start, resource_size(res));322323kfree(keypad_data);324platform_set_drvdata(pdev, NULL);325326return 0;327}328329static struct platform_driver omap4_keypad_driver = {330.probe = omap4_keypad_probe,331.remove = __devexit_p(omap4_keypad_remove),332.driver = {333.name = "omap4-keypad",334.owner = THIS_MODULE,335},336};337338static int __init omap4_keypad_init(void)339{340return platform_driver_register(&omap4_keypad_driver);341}342module_init(omap4_keypad_init);343344static void __exit omap4_keypad_exit(void)345{346platform_driver_unregister(&omap4_keypad_driver);347}348module_exit(omap4_keypad_exit);349350MODULE_AUTHOR("Texas Instruments");351MODULE_DESCRIPTION("OMAP4 Keypad Driver");352MODULE_LICENSE("GPL");353MODULE_ALIAS("platform:omap4-keypad");354355356