From patchwork Wed Nov 24 21:59:30 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ben Gardiner X-Patchwork-Id: 354442 Received: from devils.ext.ti.com (devils.ext.ti.com [198.47.26.153]) by demeter1.kernel.org (8.14.4/8.14.3) with ESMTP id oAOM2Equ013349 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=OK) for ; Wed, 24 Nov 2010 22:06:22 GMT Received: from dlep33.itg.ti.com ([157.170.170.112]) by devils.ext.ti.com (8.13.7/8.13.7) with ESMTP id oAOLxjTE031091 (version=TLSv1/SSLv3 cipher=DHE-RSA-AES256-SHA bits=256 verify=NO); Wed, 24 Nov 2010 15:59:45 -0600 Received: from linux.omap.com (localhost [127.0.0.1]) by dlep33.itg.ti.com (8.13.7/8.13.7) with ESMTP id oAOLxiTp028228; Wed, 24 Nov 2010 15:59:44 -0600 (CST) Received: from linux.omap.com (localhost [127.0.0.1]) by linux.omap.com (Postfix) with ESMTP id 9767E8062B; Wed, 24 Nov 2010 15:59:43 -0600 (CST) X-Original-To: davinci-linux-open-source@linux.davincidsp.com Delivered-To: davinci-linux-open-source@linux.davincidsp.com Received: from dflp51.itg.ti.com (dflp51.itg.ti.com [128.247.22.94]) by linux.omap.com (Postfix) with ESMTP id DECED80626 for ; Wed, 24 Nov 2010 15:59:40 -0600 (CST) Received: from neches.ext.ti.com (localhost [127.0.0.1]) by dflp51.itg.ti.com (8.13.7/8.13.7) with ESMTP id oAOLxeYd019707 for ; Wed, 24 Nov 2010 15:59:40 -0600 (CST) Received: from psmtp.com (na3sys009amx165.postini.com [74.125.149.91]) by neches.ext.ti.com (8.13.7/8.13.7) with SMTP id oAOLxdiS030703 for ; Wed, 24 Nov 2010 15:59:39 -0600 Received: from source ([74.125.149.67]) by na3sys009amx165.postini.com ([74.125.148.10]) with SMTP; Wed, 24 Nov 2010 13:59:40 PST Received: from source ([209.85.213.179]) by na3sys009aob101.postini.com ([74.125.148.12]) with SMTP ID DSNKTO2Ky5Jpk6/l+E4z6b8OPGT+ryh7vgrS@postini.com; Wed, 24 Nov 2010 13:59:39 PST Received: by mail-yx0-f179.google.com with SMTP id 7so61069yxs.10 for ; Wed, 24 Nov 2010 13:59:39 -0800 (PST) Received: by 10.151.41.7 with SMTP id t7mr1718649ybj.343.1290635979339; Wed, 24 Nov 2010 13:59:39 -0800 (PST) Received: from localhost.localdomain ([206.191.47.130]) by mx.google.com with ESMTPS id m65sm5072712yha.23.2010.11.24.13.59.38 (version=TLSv1/SSLv3 cipher=RC4-MD5); Wed, 24 Nov 2010 13:59:38 -0800 (PST) From: Ben Gardiner To: Kevin Hilman , davinci-linux-open-source@linux.davincidsp.com, linux-input@vger.kernel.org, Dmitry Torokhov Subject: [PATCH v5 1/5] [WIP] input: add input driver for polled GPIO buttons Date: Wed, 24 Nov 2010 16:59:30 -0500 Message-Id: <91c06463c8c2afb543c8a22350d2ddd8bf2ef71f.1290635422.git.bengardiner@nanometrics.ca> X-Mailer: git-send-email 1.7.0.4 In-Reply-To: References: X-pstn-neptune: 0/0/0.00/0 X-pstn-levels: (S:85.07643/99.90000 CV:99.9000 FC:95.5390 LC:95.5390 R:95.9108 P:95.9108 M:88.1613 C:98.6951 ) X-pstn-settings: 2 (0.5000:0.0750) s cv GT3 gt2 gt1 r p m c X-pstn-addresses: from [db-null] Cc: Mike Frysinger , Dmitry Torokhov , linux-kernel@vger.kernel.org, Gabor Juhos , Paul Mundt , Chris Cordahi X-BeenThere: davinci-linux-open-source@linux.davincidsp.com X-Mailman-Version: 2.1.12 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: davinci-linux-open-source-bounces@linux.davincidsp.com Errors-To: davinci-linux-open-source-bounces@linux.davincidsp.com X-Greylist: Sender succeeded STARTTLS authentication, not delayed by milter-greylist-4.2.3 (demeter1.kernel.org [140.211.167.41]); Wed, 24 Nov 2010 22:08:39 +0000 (UTC) diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig index b8c51b9..9648ff4 100644 --- a/drivers/input/keyboard/Kconfig +++ b/drivers/input/keyboard/Kconfig @@ -485,4 +485,20 @@ config KEYBOARD_W90P910 To compile this driver as a module, choose M here: the module will be called w90p910_keypad. +config KEYBOARD_GPIO_POLLED + tristate "Polled GPIO buttons" + depends on GENERIC_GPIO + select INPUT_POLLDEV + help + This driver implements support for buttons connected + to GPIO pins of various CPUs (and some other chips). + + Say Y here if your device has buttons connected + directly to such GPIO pins. Your board-specific + setup logic must also provide a platform device, + with configuration data saying which GPIOs are used. + + To compile this driver as a module, choose M here: the + module will be called gpio-buttons. + endif diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile index a34452e..e6da817 100644 --- a/drivers/input/keyboard/Makefile +++ b/drivers/input/keyboard/Makefile @@ -44,3 +44,5 @@ obj-$(CONFIG_KEYBOARD_TNETV107X) += tnetv107x-keypad.o obj-$(CONFIG_KEYBOARD_TWL4030) += twl4030_keypad.o obj-$(CONFIG_KEYBOARD_XTKBD) += xtkbd.o obj-$(CONFIG_KEYBOARD_W90P910) += w90p910_keypad.o +obj-$(CONFIG_KEYBOARD_GPIO_POLLED) += gpio_keys_polled.o + diff --git a/drivers/input/keyboard/gpio_keys_polled.c b/drivers/input/keyboard/gpio_keys_polled.c new file mode 100644 index 0000000..390ed93 --- /dev/null +++ b/drivers/input/keyboard/gpio_keys_polled.c @@ -0,0 +1,240 @@ +/* + * Driver for buttons on GPIO lines not capable of generating interrupts + * + * Copyright (C) 2007-2010 Gabor Juhos + * Copyright (C) 2010 Nuno Goncalves + * + * This file was based on: /drivers/input/misc/cobalt_btns.c + * Copyright (C) 2007 Yoichi Yuasa + * + * also was based on: /drivers/input/keyboard/gpio_keys.c + * Copyright 2005 Phil Blundell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define DRV_NAME "gpio-keys-polled" + +struct gpio_keys_button_data { + int last_state; + int count; + int can_sleep; +}; + +struct gpio_keys_polled_dev { + struct input_polled_dev *poll_dev; + struct gpio_keys_polled_platform_data *pdata; + struct gpio_keys_button_data *data; +}; + +static void gpio_keys_polled_check_state(struct input_dev *input, + struct gpio_keys_button *button, + struct gpio_keys_button_data *bdata) +{ + int state; + + if (bdata->can_sleep) + state = !!gpio_get_value_cansleep(button->gpio); + else + state = !!gpio_get_value(button->gpio); + + if (state != bdata->last_state) { + unsigned int type = button->type ?: EV_KEY; + + input_event(input, type, button->code, + !!(state ^ button->active_low)); + input_sync(input); + bdata->count = 0; + bdata->last_state = state; + } +} + +static void gpio_keys_polled_poll(struct input_polled_dev *dev) +{ + struct gpio_keys_polled_dev *bdev = dev->private; + struct gpio_keys_polled_platform_data *pdata = bdev->pdata; + struct input_dev *input = dev->input; + int i, threshold; + + for (i = 0; i < bdev->pdata->nbuttons; i++) { + struct gpio_keys_button *button = &pdata->buttons[i]; + struct gpio_keys_button_data *bdata = &bdev->data[i]; + + threshold = round_up(button->debounce_interval, + pdata->poll_interval) / + pdata->poll_interval; + if (bdata->count < threshold) + bdata->count++; + else + gpio_keys_polled_check_state(input, button, bdata); + + } +} + +static int __devinit gpio_keys_polled_probe(struct platform_device *pdev) +{ + struct gpio_keys_polled_platform_data *pdata = pdev->dev.platform_data; + struct device *dev = &pdev->dev; + struct gpio_keys_polled_dev *bdev; + struct input_polled_dev *poll_dev; + struct input_dev *input; + int error; + int i; + + if (!pdata) + return -ENXIO; + + bdev = kzalloc(sizeof(struct gpio_keys_polled_dev) + + pdata->nbuttons * sizeof(struct gpio_keys_button_data), + GFP_KERNEL); + if (!bdev) { + dev_err(dev, "no memory for private data\n"); + return -ENOMEM; + } + + bdev->data = (struct gpio_keys_button_data *) &bdev[1]; + + poll_dev = input_allocate_polled_device(); + if (!poll_dev) { + dev_err(dev, "no memory for polled device\n"); + error = -ENOMEM; + goto err_free_bdev; + } + + poll_dev->private = bdev; + poll_dev->poll = gpio_keys_polled_poll; + poll_dev->poll_interval = pdata->poll_interval; + + input = poll_dev->input; + + input->evbit[0] = BIT(EV_KEY); + input->name = pdev->name; + input->phys = DRV_NAME"/input0"; + input->dev.parent = &pdev->dev; + + input->id.bustype = BUS_HOST; + input->id.vendor = 0x0001; + input->id.product = 0x0001; + input->id.version = 0x0100; + + for (i = 0; i < pdata->nbuttons; i++) { + struct gpio_keys_button *button = &pdata->buttons[i]; + unsigned int gpio = button->gpio; + unsigned int type = button->type ?: EV_KEY; + + if (button->wakeup) { + dev_err(dev, DRV_NAME " does not support wakeup\n"); + goto err_free_gpio; + } + + error = gpio_request(gpio, + button->desc ? button->desc : DRV_NAME); + if (error) { + dev_err(dev, "unable to claim gpio %u, err=%d\n", + gpio, error); + goto err_free_gpio; + } + + error = gpio_direction_input(gpio); + if (error) { + dev_err(dev, + "unable to set direction on gpio %u, err=%d\n", + gpio, error); + goto err_free_gpio; + } + + bdev->data[i].can_sleep = gpio_cansleep(gpio); + bdev->data[i].last_state = -1; + + input_set_capability(input, type, button->code); + } + + bdev->poll_dev = poll_dev; + bdev->pdata = pdata; + platform_set_drvdata(pdev, bdev); + + error = input_register_polled_device(poll_dev); + if (error) { + dev_err(dev, "unable to register polled device, err=%d\n", + error); + goto err_free_gpio; + } + + /* report initial state of the buttons */ + for (i = 0; i < pdata->nbuttons; i++) + gpio_keys_polled_check_state(input, &pdata->buttons[i], + &bdev->data[i]); + + return 0; + +err_free_gpio: + for (i = i - 1; i >= 0; i--) + gpio_free(pdata->buttons[i].gpio); + + input_free_polled_device(poll_dev); + +err_free_bdev: + kfree(bdev); + + platform_set_drvdata(pdev, NULL); + return error; +} + +static int __devexit gpio_keys_polled_remove(struct platform_device *pdev) +{ + struct gpio_keys_polled_dev *bdev = platform_get_drvdata(pdev); + struct gpio_keys_polled_platform_data *pdata = bdev->pdata; + int i; + + input_unregister_polled_device(bdev->poll_dev); + + for (i = 0; i < pdata->nbuttons; i++) + gpio_free(pdata->buttons[i].gpio); + + input_free_polled_device(bdev->poll_dev); + + kfree(bdev); + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static struct platform_driver gpio_keys_polled_driver = { + .probe = gpio_keys_polled_probe, + .remove = __devexit_p(gpio_keys_polled_remove), + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + }, +}; + +static int __init gpio_keys_polled_init(void) +{ + return platform_driver_register(&gpio_keys_polled_driver); +} + +static void __exit gpio_keys_polled_exit(void) +{ + platform_driver_unregister(&gpio_keys_polled_driver); +} + +module_init(gpio_keys_polled_init); +module_exit(gpio_keys_polled_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Gabor Juhos "); +MODULE_DESCRIPTION("Polled GPIO Buttons driver"); diff --git a/include/linux/gpio_keys_polled.h b/include/linux/gpio_keys_polled.h new file mode 100644 index 0000000..bf7f94a --- /dev/null +++ b/include/linux/gpio_keys_polled.h @@ -0,0 +1,26 @@ +/* + * Definitions for the GPIO buttons interface driver + * + * Copyright (C) 2007-2010 Gabor Juhos + * + * This file was based on: /include/linux/gpio_keys.h + * The original gpio_keys.h seems not to have a license. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#ifndef _GPIO_BUTTONS_H_ +#define _GPIO_BUTTONS_H_ + +#include + +struct gpio_keys_polled_platform_data { + struct gpio_keys_button *buttons; + int nbuttons; /* number of buttons */ + int poll_interval; /* polling interval in msecs*/ +}; + +#endif /* _GPIO_BUTTONS_H_ */