Path: blob/master/drivers/input/keyboard/davinci_keyscan.c
15111 views
/*1* DaVinci Key Scan Driver for TI platforms2*3* Copyright (C) 2009 Texas Instruments, Inc4*5* Author: Miguel Aguilar <[email protected]>6*7* Initial Code: Sandeep Paulraj <[email protected]>8*9* This program is free software; you can redistribute it and/or modify10* it under the terms of the GNU General Public License as published by11* the Free Software Foundation; either version 2 of the License, or12* (at your option) any later version.13*14* This program is distributed in the hope that it will be useful,15* but WITHOUT ANY WARRANTY; without even the implied warranty of16* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the17* GNU General Public License for more details.18*19* You should have received a copy of the GNU General Public License20* along with this program; if not, write to the Free Software21* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA22*/23#include <linux/module.h>24#include <linux/init.h>25#include <linux/interrupt.h>26#include <linux/types.h>27#include <linux/input.h>28#include <linux/kernel.h>29#include <linux/delay.h>30#include <linux/platform_device.h>31#include <linux/errno.h>32#include <linux/slab.h>3334#include <asm/irq.h>3536#include <mach/hardware.h>37#include <mach/irqs.h>38#include <mach/keyscan.h>3940/* Key scan registers */41#define DAVINCI_KEYSCAN_KEYCTRL 0x000042#define DAVINCI_KEYSCAN_INTENA 0x000443#define DAVINCI_KEYSCAN_INTFLAG 0x000844#define DAVINCI_KEYSCAN_INTCLR 0x000c45#define DAVINCI_KEYSCAN_STRBWIDTH 0x001046#define DAVINCI_KEYSCAN_INTERVAL 0x001447#define DAVINCI_KEYSCAN_CONTTIME 0x001848#define DAVINCI_KEYSCAN_CURRENTST 0x001c49#define DAVINCI_KEYSCAN_PREVSTATE 0x002050#define DAVINCI_KEYSCAN_EMUCTRL 0x002451#define DAVINCI_KEYSCAN_IODFTCTRL 0x002c5253/* Key Control Register (KEYCTRL) */54#define DAVINCI_KEYSCAN_KEYEN 0x0000000155#define DAVINCI_KEYSCAN_PREVMODE 0x0000000256#define DAVINCI_KEYSCAN_CHATOFF 0x0000000457#define DAVINCI_KEYSCAN_AUTODET 0x0000000858#define DAVINCI_KEYSCAN_SCANMODE 0x0000001059#define DAVINCI_KEYSCAN_OUTTYPE 0x000000206061/* Masks for the interrupts */62#define DAVINCI_KEYSCAN_INT_CONT 0x0000000863#define DAVINCI_KEYSCAN_INT_OFF 0x0000000464#define DAVINCI_KEYSCAN_INT_ON 0x0000000265#define DAVINCI_KEYSCAN_INT_CHANGE 0x0000000166#define DAVINCI_KEYSCAN_INT_ALL 0x0000000f6768struct davinci_ks {69struct input_dev *input;70struct davinci_ks_platform_data *pdata;71int irq;72void __iomem *base;73resource_size_t pbase;74size_t base_size;75unsigned short keymap[];76};7778/* Initializing the kp Module */79static int __init davinci_ks_initialize(struct davinci_ks *davinci_ks)80{81struct device *dev = &davinci_ks->input->dev;82struct davinci_ks_platform_data *pdata = davinci_ks->pdata;83u32 matrix_ctrl;8485/* Enable all interrupts */86__raw_writel(DAVINCI_KEYSCAN_INT_ALL,87davinci_ks->base + DAVINCI_KEYSCAN_INTENA);8889/* Clear interrupts if any */90__raw_writel(DAVINCI_KEYSCAN_INT_ALL,91davinci_ks->base + DAVINCI_KEYSCAN_INTCLR);9293/* Setup the scan period = strobe + interval */94__raw_writel(pdata->strobe,95davinci_ks->base + DAVINCI_KEYSCAN_STRBWIDTH);96__raw_writel(pdata->interval,97davinci_ks->base + DAVINCI_KEYSCAN_INTERVAL);98__raw_writel(0x01,99davinci_ks->base + DAVINCI_KEYSCAN_CONTTIME);100101/* Define matrix type */102switch (pdata->matrix_type) {103case DAVINCI_KEYSCAN_MATRIX_4X4:104matrix_ctrl = 0;105break;106case DAVINCI_KEYSCAN_MATRIX_5X3:107matrix_ctrl = (1 << 6);108break;109default:110dev_err(dev->parent, "wrong matrix type\n");111return -EINVAL;112}113114/* Enable key scan module and set matrix type */115__raw_writel(DAVINCI_KEYSCAN_AUTODET | DAVINCI_KEYSCAN_KEYEN |116matrix_ctrl, davinci_ks->base + DAVINCI_KEYSCAN_KEYCTRL);117118return 0;119}120121static irqreturn_t davinci_ks_interrupt(int irq, void *dev_id)122{123struct davinci_ks *davinci_ks = dev_id;124struct device *dev = &davinci_ks->input->dev;125unsigned short *keymap = davinci_ks->keymap;126int keymapsize = davinci_ks->pdata->keymapsize;127u32 prev_status, new_status, changed;128bool release;129int keycode = KEY_UNKNOWN;130int i;131132/* Disable interrupt */133__raw_writel(0x0, davinci_ks->base + DAVINCI_KEYSCAN_INTENA);134135/* Reading previous and new status of the key scan */136prev_status = __raw_readl(davinci_ks->base + DAVINCI_KEYSCAN_PREVSTATE);137new_status = __raw_readl(davinci_ks->base + DAVINCI_KEYSCAN_CURRENTST);138139changed = prev_status ^ new_status;140141if (changed) {142/*143* It goes through all bits in 'changed' to ensure144* that no key changes are being missed145*/146for (i = 0 ; i < keymapsize; i++) {147if ((changed>>i) & 0x1) {148keycode = keymap[i];149release = (new_status >> i) & 0x1;150dev_dbg(dev->parent, "key %d %s\n", keycode,151release ? "released" : "pressed");152input_report_key(davinci_ks->input, keycode,153!release);154input_sync(davinci_ks->input);155}156}157/* Clearing interrupt */158__raw_writel(DAVINCI_KEYSCAN_INT_ALL,159davinci_ks->base + DAVINCI_KEYSCAN_INTCLR);160}161162/* Enable interrupts */163__raw_writel(0x1, davinci_ks->base + DAVINCI_KEYSCAN_INTENA);164165return IRQ_HANDLED;166}167168static int __init davinci_ks_probe(struct platform_device *pdev)169{170struct davinci_ks *davinci_ks;171struct input_dev *key_dev;172struct resource *res, *mem;173struct device *dev = &pdev->dev;174struct davinci_ks_platform_data *pdata = pdev->dev.platform_data;175int error, i;176177if (pdata->device_enable) {178error = pdata->device_enable(dev);179if (error < 0) {180dev_dbg(dev, "device enable function failed\n");181return error;182}183}184185if (!pdata->keymap) {186dev_dbg(dev, "no keymap from pdata\n");187return -EINVAL;188}189190davinci_ks = kzalloc(sizeof(struct davinci_ks) +191sizeof(unsigned short) * pdata->keymapsize, GFP_KERNEL);192if (!davinci_ks) {193dev_dbg(dev, "could not allocate memory for private data\n");194return -ENOMEM;195}196197memcpy(davinci_ks->keymap, pdata->keymap,198sizeof(unsigned short) * pdata->keymapsize);199200key_dev = input_allocate_device();201if (!key_dev) {202dev_dbg(dev, "could not allocate input device\n");203error = -ENOMEM;204goto fail1;205}206207davinci_ks->input = key_dev;208209davinci_ks->irq = platform_get_irq(pdev, 0);210if (davinci_ks->irq < 0) {211dev_err(dev, "no key scan irq\n");212error = davinci_ks->irq;213goto fail2;214}215216res = platform_get_resource(pdev, IORESOURCE_MEM, 0);217if (!res) {218dev_err(dev, "no mem resource\n");219error = -EINVAL;220goto fail2;221}222223davinci_ks->pbase = res->start;224davinci_ks->base_size = resource_size(res);225226mem = request_mem_region(davinci_ks->pbase, davinci_ks->base_size,227pdev->name);228if (!mem) {229dev_err(dev, "key scan registers at %08x are not free\n",230davinci_ks->pbase);231error = -EBUSY;232goto fail2;233}234235davinci_ks->base = ioremap(davinci_ks->pbase, davinci_ks->base_size);236if (!davinci_ks->base) {237dev_err(dev, "can't ioremap MEM resource.\n");238error = -ENOMEM;239goto fail3;240}241242/* Enable auto repeat feature of Linux input subsystem */243if (pdata->rep)244__set_bit(EV_REP, key_dev->evbit);245246/* Setup input device */247__set_bit(EV_KEY, key_dev->evbit);248249/* Setup the platform data */250davinci_ks->pdata = pdata;251252for (i = 0; i < davinci_ks->pdata->keymapsize; i++)253__set_bit(davinci_ks->pdata->keymap[i], key_dev->keybit);254255key_dev->name = "davinci_keyscan";256key_dev->phys = "davinci_keyscan/input0";257key_dev->dev.parent = &pdev->dev;258key_dev->id.bustype = BUS_HOST;259key_dev->id.vendor = 0x0001;260key_dev->id.product = 0x0001;261key_dev->id.version = 0x0001;262key_dev->keycode = davinci_ks->keymap;263key_dev->keycodesize = sizeof(davinci_ks->keymap[0]);264key_dev->keycodemax = davinci_ks->pdata->keymapsize;265266error = input_register_device(davinci_ks->input);267if (error < 0) {268dev_err(dev, "unable to register davinci key scan device\n");269goto fail4;270}271272error = request_irq(davinci_ks->irq, davinci_ks_interrupt,273IRQF_DISABLED, pdev->name, davinci_ks);274if (error < 0) {275dev_err(dev, "unable to register davinci key scan interrupt\n");276goto fail5;277}278279error = davinci_ks_initialize(davinci_ks);280if (error < 0) {281dev_err(dev, "unable to initialize davinci key scan device\n");282goto fail6;283}284285platform_set_drvdata(pdev, davinci_ks);286return 0;287288fail6:289free_irq(davinci_ks->irq, davinci_ks);290fail5:291input_unregister_device(davinci_ks->input);292key_dev = NULL;293fail4:294iounmap(davinci_ks->base);295fail3:296release_mem_region(davinci_ks->pbase, davinci_ks->base_size);297fail2:298input_free_device(key_dev);299fail1:300kfree(davinci_ks);301302return error;303}304305static int __devexit davinci_ks_remove(struct platform_device *pdev)306{307struct davinci_ks *davinci_ks = platform_get_drvdata(pdev);308309free_irq(davinci_ks->irq, davinci_ks);310311input_unregister_device(davinci_ks->input);312313iounmap(davinci_ks->base);314release_mem_region(davinci_ks->pbase, davinci_ks->base_size);315316platform_set_drvdata(pdev, NULL);317318kfree(davinci_ks);319320return 0;321}322323static struct platform_driver davinci_ks_driver = {324.driver = {325.name = "davinci_keyscan",326.owner = THIS_MODULE,327},328.remove = __devexit_p(davinci_ks_remove),329};330331static int __init davinci_ks_init(void)332{333return platform_driver_probe(&davinci_ks_driver, davinci_ks_probe);334}335module_init(davinci_ks_init);336337static void __exit davinci_ks_exit(void)338{339platform_driver_unregister(&davinci_ks_driver);340}341module_exit(davinci_ks_exit);342343MODULE_AUTHOR("Miguel Aguilar");344MODULE_DESCRIPTION("Texas Instruments DaVinci Key Scan Driver");345MODULE_LICENSE("GPL");346347348