From patchwork Fri Oct 9 17:27:05 2009 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: miguel.aguilar@ridgerun.com X-Patchwork-Id: 52769 Received: from bear.ext.ti.com (bear.ext.ti.com [192.94.94.41]) by demeter.kernel.org (8.14.2/8.14.2) with ESMTP id n99HUUYW008197 for ; Fri, 9 Oct 2009 17:30:30 GMT Received: from dlep34.itg.ti.com ([157.170.170.115]) by bear.ext.ti.com (8.13.7/8.13.7) with ESMTP id n99HS8xm006394 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO); Fri, 9 Oct 2009 12:28:10 -0500 Received: from linux.omap.com (localhost [127.0.0.1]) by dlep34.itg.ti.com (8.13.7/8.13.7) with ESMTP id n99HS7EH011392; Fri, 9 Oct 2009 12:28:07 -0500 (CDT) Received: from linux.omap.com (localhost [127.0.0.1]) by linux.omap.com (Postfix) with ESMTP id ADA3880627; Fri, 9 Oct 2009 12:28:07 -0500 (CDT) X-Original-To: davinci-linux-open-source@linux.davincidsp.com Delivered-To: davinci-linux-open-source@linux.davincidsp.com Received: from dflp53.itg.ti.com (dflp53.itg.ti.com [128.247.5.6]) by linux.omap.com (Postfix) with ESMTP id 1566E80626 for ; Fri, 9 Oct 2009 12:28:07 -0500 (CDT) Received: from red.ext.ti.com (localhost [127.0.0.1]) by dflp53.itg.ti.com (8.13.8/8.13.8) with ESMTP id n99HS5Y2001465 for ; Fri, 9 Oct 2009 12:28:05 -0500 (CDT) Received: from mail129-va3-R.bigfish.com (mail-va3.bigfish.com [216.32.180.113]) by red.ext.ti.com (8.13.7/8.13.7) with ESMTP id n99HS125021579 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=FAIL) for ; Fri, 9 Oct 2009 12:28:03 -0500 Received: from mail129-va3 (localhost.localdomain [127.0.0.1]) by mail129-va3-R.bigfish.com (Postfix) with ESMTP id 25DAA19784FF for ; Fri, 9 Oct 2009 17:28:01 +0000 (UTC) X-SpamScore: 2 X-BigFish: vps2(zz18c1J655Na4b1oc8kzz1202hzzz2fh6bh2a8h66h) X-Spam-TCS-SCL: 5:0 X-FB-SS: 5, X-MS-Exchange-Organization-Antispam-Report: OrigIP: 74.208.67.6; Service: EHS Received: by mail129-va3 (MessageSwitch) id 1255109244180691_6618; Fri, 9 Oct 2009 17:27:24 +0000 (UCT) Received: from VA3EHSMHS001.bigfish.com (unknown [10.7.14.240]) by mail129-va3.bigfish.com (Postfix) with ESMTP id D02E11260117; Fri, 9 Oct 2009 17:27:07 +0000 (UTC) Received: from mail.navvo.net (74.208.67.6) by VA3EHSMHS001.bigfish.com (10.7.99.11) with Microsoft SMTP Server (TLS) id 14.0.482.32; Fri, 9 Oct 2009 17:27:05 +0000 Received: from [201.198.127.70] (helo=localhost.localdomain) by mail.navvo.net with esmtpa (Exim 4.63) (envelope-from ) id 1MwJF8-0001ty-D6; Fri, 09 Oct 2009 12:27:04 -0500 From: To: davinci-linux-open-source@linux.davincidsp.com, nsnehaprabha@ti.com, linux-input@vger.kernel.org Date: Fri, 9 Oct 2009 11:27:05 -0600 Message-ID: <1255109225-6833-1-git-send-email-miguel.aguilar@ridgerun.com> X-Mailer: git-send-email 1.6.0.4 X-SA-Exim-Connect-IP: 201.198.127.70 X-SA-Exim-Mail-From: miguel.aguilar@ridgerun.com X-Spam-Checker-Version: SpamAssassin 3.1.7-deb (2006-10-05) on mail.navvo.net X-Spam-Level: X-Spam-Status: No, score=-3.3 required=5.0 tests=ALL_TRUSTED,AWL,BAYES_00, NO_REAL_NAME autolearn=ham version=3.1.7-deb X-SA-Exim-Version: 4.2.1 (built Tue, 09 Jan 2007 17:23:22 +0000) X-SA-Exim-Scanned: Yes (on mail.navvo.net) MIME-Version: 1.0 X-Reverse-DNS: mail.navvo.net Cc: santiago.nunez@ridgerun.com, todd.fischer@ridgerun.com, clark.becker@ridgerun.com, Miguel Aguilar Subject: [PATCH v4 1/2] Input: DaVinci Keypad Driver X-BeenThere: davinci-linux-open-source@linux.davincidsp.com X-Mailman-Version: 2.1.4 Precedence: list List-Id: davinci-linux-open-source.linux.davincidsp.com List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: davinci-linux-open-source-bounces@linux.davincidsp.com Errors-To: davinci-linux-open-source-bounces@linux.davincidsp.com diff --git a/arch/arm/mach-davinci/include/mach/keyscan.h b/arch/arm/mach-davinci/include/mach/keyscan.h new file mode 100644 index 0000000..dd84361 --- /dev/null +++ b/arch/arm/mach-davinci/include/mach/keyscan.h @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2009 Texas Instruments, Inc + * + * Author: Miguel Aguilar + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#ifndef DAVINCI_KEYSCAN_H +#define DAVINCI_KEYSCAN_H + +#include + +/* Base of key scan register bank */ +#define DM365_KEYSCAN_BASE (0x01C69400) + +enum davinci_matrix_types { + DAVINCI_KEYSCAN_MATRIX_4X4, + DAVINCI_KEYSCAN_MATRIX_5X3, +}; + +struct davinci_ks_platform_data { + unsigned short *keymap; + u32 keymapsize; + u8 rep:1; + u8 strobe; + u8 interval; + u8 matrix_type; +}; + +#endif + diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index ee98b1b..b7668ed 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -423,4 +423,14 @@ config KEYBOARD_W90P910 To compile this driver as a module, choose M here: the module will be called w90p910_keypad. +config KEYBOARD_DAVINCI + tristate "TI DaVinci Key Scan" + depends on ARCH_DAVINCI_DM365 + help + Say Y to enable keypad module support for the TI DaVinci + platforms (DM365). + + To compile this driver as a module, choose M here: the + module will be called davinci_keyscan. + endif diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index babad5e..bd49fed 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile @@ -37,3 +37,4 @@ obj-$(CONFIG_KEYBOARD_TOSA) += tosakbd.o obj-$(CONFIG_KEYBOARD_TWL4030) += twl4030_keypad.o obj-$(CONFIG_KEYBOARD_XTKBD) += xtkbd.o obj-$(CONFIG_KEYBOARD_W90P910) += w90p910_keypad.o +obj-$(CONFIG_KEYBOARD_DAVINCI) += davinci_keyscan.o diff --git a/drivers/input/keyboard/davinci_keyscan.c b/drivers/input/keyboard/davinci_keyscan.c new file mode 100644 index 0000000..7b7424b --- /dev/null +++ b/drivers/input/keyboard/davinci_keyscan.c @@ -0,0 +1,338 @@ +/* + * DaVinci Key Scan Driver for TI platforms + * + * Copyright (C) 2009 Texas Instruments, Inc + * + * Author: Miguel Aguilar + * + * Intial Code: Sandeep Paulraj + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +/* Key scan registers */ +#define DAVINCI_KEYSCAN_KEYCTRL 0x0000 +#define DAVINCI_KEYSCAN_INTENA 0x0004 +#define DAVINCI_KEYSCAN_INTFLAG 0x0008 +#define DAVINCI_KEYSCAN_INTCLR 0x000c +#define DAVINCI_KEYSCAN_STRBWIDTH 0x0010 +#define DAVINCI_KEYSCAN_INTERVAL 0x0014 +#define DAVINCI_KEYSCAN_CONTTIME 0x0018 +#define DAVINCI_KEYSCAN_CURRENTST 0x001c +#define DAVINCI_KEYSCAN_PREVSTATE 0x0020 +#define DAVINCI_KEYSCAN_EMUCTRL 0x0024 +#define DAVINCI_KEYSCAN_IODFTCTRL 0x002c + +/* Key Control Register (KEYCTRL) */ +#define DAVINCI_KEYSCAN_KEYEN 0x00000001 +#define DAVINCI_KEYSCAN_PREVMODE 0x00000002 +#define DAVINCI_KEYSCAN_CHATOFF 0x00000004 +#define DAVINCI_KEYSCAN_AUTODET 0x00000008 +#define DAVINCI_KEYSCAN_SCANMODE 0x00000010 +#define DAVINCI_KEYSCAN_OUTTYPE 0x00000020 + +/* Masks for the interrupts */ +#define DAVINCI_KEYSCAN_INT_CONT 0x00000008 +#define DAVINCI_KEYSCAN_INT_OFF 0x00000004 +#define DAVINCI_KEYSCAN_INT_ON 0x00000002 +#define DAVINCI_KEYSCAN_INT_CHANGE 0x00000001 +#define DAVINCI_KEYSCAN_INT_ALL 0x0000000f + +struct davinci_ks { + struct input_dev *input; + struct davinci_ks_platform_data *pdata; + int irq; + void __iomem *base; + resource_size_t pbase; + size_t base_size; + unsigned short keymap[]; +}; + +static void davinci_ks_write(struct davinci_ks *davinci_ks, u32 val, u32 addr) +{ + u32 base = (u32)davinci_ks->base; + + __raw_writel(val,(u32 *)(base + addr)); +} + +static u32 davinci_ks_read(struct davinci_ks *davinci_ks, u32 addr) +{ + u32 base = (u32)davinci_ks->base; + + return __raw_readl((u32 *)(base + addr)); +} + +/* Initializing the kp Module */ +static int davinci_ks_initialize(struct davinci_ks *davinci_ks) +{ + struct device *dev = &davinci_ks->input->dev; + u8 strobe = davinci_ks->pdata->strobe; + u8 interval = davinci_ks->pdata->interval; + u8 matrix_type = davinci_ks->pdata->matrix_type; + + /* Enable all interrupts */ + davinci_ks_write(davinci_ks, DAVINCI_KEYSCAN_INT_ALL, DAVINCI_KEYSCAN_INTENA); + + /* Clear interrupts if any */ + davinci_ks_write(davinci_ks, DAVINCI_KEYSCAN_INT_ALL, DAVINCI_KEYSCAN_INTCLR); + + /* Setup the scan period = strobe + interval */ + davinci_ks_write(davinci_ks, strobe, DAVINCI_KEYSCAN_STRBWIDTH); + davinci_ks_write(davinci_ks, interval, DAVINCI_KEYSCAN_INTERVAL); + davinci_ks_write(davinci_ks, 0x01, DAVINCI_KEYSCAN_CONTTIME); + + /* Define matrix type */ + if ((matrix_type != 0) && (matrix_type != 1)) { + dev_err(dev->parent, "wrong matrix type\n"); + return -EINVAL; + } + + /* Enable key scan module and set matrix type */ + davinci_ks_write(davinci_ks, DAVINCI_KEYSCAN_AUTODET | DAVINCI_KEYSCAN_KEYEN + | (matrix_type << 6), DAVINCI_KEYSCAN_KEYCTRL); + + return 0; +} + +static irqreturn_t davinci_ks_interrupt(int irq, void *dev_id) +{ + struct davinci_ks *davinci_ks = dev_id; + struct device *dev = &davinci_ks->input->dev; + unsigned short *keymap = davinci_ks->keymap; + int keymapsize = davinci_ks->pdata->keymapsize; + u32 prev_status, new_status, changed; + bool release; + int keycode = KEY_UNKNOWN; + int i; + + /* Disable interrupt */ + davinci_ks_write(davinci_ks, 0x0, DAVINCI_KEYSCAN_INTENA); + + /* Reading previous and new status of the key scan */ + prev_status = davinci_ks_read(davinci_ks, DAVINCI_KEYSCAN_PREVSTATE); + new_status = davinci_ks_read(davinci_ks, DAVINCI_KEYSCAN_CURRENTST); + + changed = prev_status ^ new_status; + + if(changed) { + /* + * It goes go through all bits in changed to ensure + * that no key changes are being missed + */ + for(i = 0 ; i < keymapsize; i++) { + if((changed>>i) & 0x1) { + keycode = keymap[i]; + release = (new_status >> i) & 0x1; + dev_dbg(dev->parent, "key %d %s\n", keycode, + release ? "released" : "pressed"); + input_report_key(davinci_ks->input, keycode, + !release); + input_sync(davinci_ks->input); + } + } + /* Clearing interrupt */ + davinci_ks_write(davinci_ks, DAVINCI_KEYSCAN_INT_ALL, + DAVINCI_KEYSCAN_INTCLR); + } + + /* Enable interrupts */ + davinci_ks_write(davinci_ks, 0x1, DAVINCI_KEYSCAN_INTENA); + + return IRQ_HANDLED; +} + +static int __init davinci_ks_probe(struct platform_device *pdev) +{ + struct davinci_ks *davinci_ks; + struct input_dev *key_dev; + struct resource *res, *mem; + struct device * dev = &pdev->dev; + struct davinci_ks_platform_data *pdata = pdev->dev.platform_data; + int ret, i; + + if (!pdata->keymap) { + dev_dbg(dev, "no keymap from pdata\n"); + return -EINVAL; + } + + davinci_ks = kzalloc(sizeof(struct davinci_ks) + + sizeof(unsigned short) * pdata->keymapsize, GFP_KERNEL); + if(!davinci_ks) { + dev_dbg(dev, "could not allocate memory for private data\n"); + return -ENOMEM; + } + + memcpy(davinci_ks->keymap, pdata->keymap, + sizeof(unsigned short) * pdata->keymapsize); + + key_dev = input_allocate_device(); + if (!key_dev) { + dev_dbg(dev, "could not allocate input device\n"); + ret = -ENOMEM; + goto fail1; + } + + platform_set_drvdata(pdev, davinci_ks); + + davinci_ks->input = key_dev; + + davinci_ks->irq = platform_get_irq(pdev, 0); + if (davinci_ks->irq < 0) { + dev_err(dev, "no key scan irq\n"); + ret = davinci_ks->irq; + goto fail2; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + dev_err(dev, "no mem resource\n"); + ret = -EINVAL; + goto fail2; + } + + davinci_ks->pbase = res->start; + davinci_ks->base_size = resource_size(res); + + mem = request_mem_region(davinci_ks->pbase, davinci_ks->base_size, pdev->name); + if (!mem) { + dev_err(dev, "key scan registers at %08x are not free\n", + davinci_ks->pbase); + ret = -EBUSY; + goto fail2; + } + + davinci_ks->base = ioremap(davinci_ks->pbase, davinci_ks->base_size); + if (!davinci_ks->base) { + dev_err(dev, "can't ioremap MEM resource.\n"); + ret = -ENOMEM; + goto fail3; + } + + /* Enable auto repeat feature of Linux input subsystem */ + if (pdata->rep) + __set_bit(EV_REP, key_dev->evbit); + + /* Setup input device */ + __set_bit(EV_KEY, key_dev->evbit); + + /* Setup the platform data */ + davinci_ks->pdata = pdata; + + for (i = 0; i < davinci_ks->pdata->keymapsize; i++) + __set_bit(davinci_ks->pdata->keymap[i], key_dev->keybit); + + key_dev->name = "davinci_keyscan"; + key_dev->phys = "davinci_keyscan/input0"; + key_dev->dev.parent = &pdev->dev; + key_dev->id.bustype = BUS_HOST; + key_dev->id.vendor = 0x0001; + key_dev->id.product = 0x0001; + key_dev->id.version = 0x0001; + key_dev->keycode = davinci_ks->keymap; + key_dev->keycodesize = sizeof(davinci_ks->keymap[0]); + key_dev->keycodemax = davinci_ks->pdata->keymapsize; + + ret = input_register_device(davinci_ks->input); + if (ret < 0) { + dev_err(dev, "unable to register davinci key scan device\n"); + goto fail4; + } + + ret = request_irq(davinci_ks->irq, davinci_ks_interrupt, IRQF_DISABLED, + "davinci_keyscan", davinci_ks); + if (ret < 0) { + dev_err(dev, "unable to register davinci key scan interrupt\n"); + goto fail5; + } + + ret = davinci_ks_initialize(davinci_ks); + if (ret < 0) { + dev_err(dev, "unable to initialize davinci key scan device\n"); + goto fail5; + } + + return 0; + +fail5: + input_unregister_device(davinci_ks->input); + key_dev = NULL; +fail4: + iounmap(davinci_ks->base); +fail3: + release_mem_region(davinci_ks->pbase, davinci_ks->base_size); +fail2: + input_free_device(key_dev); +fail1: + kfree(davinci_ks); + + return ret; +} + +static int __devexit davinci_ks_remove(struct platform_device *pdev) +{ + struct davinci_ks *davinci_ks = platform_get_drvdata(pdev); + + free_irq(davinci_ks->irq, davinci_ks); + + input_unregister_device(davinci_ks->input); + + iounmap(davinci_ks->base); + release_mem_region(davinci_ks->pbase, davinci_ks->base_size); + + platform_set_drvdata(pdev, NULL); + + kfree(davinci_ks); + + return 0; +} + +static struct platform_driver davinci_ks_driver = { + .driver = { + .name = "davinci_keyscan", + .owner = THIS_MODULE, + }, + .remove = __devexit_p(davinci_ks_remove), +}; + +static int __init davinci_ks_init(void) +{ + return platform_driver_probe(&davinci_ks_driver, davinci_ks_probe); +} +module_init(davinci_ks_init); + +static void __exit davinci_ks_exit(void) +{ + platform_driver_unregister(&davinci_ks_driver); +} +module_exit(davinci_ks_exit); + +MODULE_AUTHOR("Miguel Aguilar"); +MODULE_DESCRIPTION("Texas Instruments DaVinci Key Scan Driver"); +MODULE_LICENSE("GPL");