diff options
Diffstat (limited to 'target/linux/ubicom32/files/arch/ubicom32/mach-common/ubicom32input.c')
-rw-r--r-- | target/linux/ubicom32/files/arch/ubicom32/mach-common/ubicom32input.c | 265 |
1 files changed, 265 insertions, 0 deletions
diff --git a/target/linux/ubicom32/files/arch/ubicom32/mach-common/ubicom32input.c b/target/linux/ubicom32/files/arch/ubicom32/mach-common/ubicom32input.c new file mode 100644 index 000000000..e2e0c24d6 --- /dev/null +++ b/target/linux/ubicom32/files/arch/ubicom32/mach-common/ubicom32input.c @@ -0,0 +1,265 @@ +/* + * arch/ubicom32/mach-common/ubicom32input.c + * Ubicom32 Input driver + * + * based on gpio-keys + * + * (C) Copyright 2009, Ubicom, Inc. + * + * This file is part of the Ubicom32 Linux Kernel Port. + * + * The Ubicom32 Linux Kernel Port 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. + * + * The Ubicom32 Linux Kernel Port 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 the Ubicom32 Linux Kernel Port. If not, + * see <http://www.gnu.org/licenses/>. + * + * Ubicom32 implementation derived from (with many thanks): + * arch/m68knommu + * arch/blackfin + * arch/parisc + * + * + * TODO: add groups for inputs which can be sampled together (i.e. I2C) + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <linux/input.h> +#include <linux/input-polldev.h> +#include <linux/delay.h> +#include <linux/gpio.h> + +#include <asm/ubicom32input.h> + +struct ubicom32input_data { + struct ubicom32input_platform_data *pdata; + + struct input_polled_dev *poll_dev; + + /* + * collection of previous states for buttons + */ + u8 prev_state[0]; +}; + +/* + * ubicom32input_poll + */ +static void ubicom32input_poll(struct input_polled_dev *dev) +{ + struct ubicom32input_data *ud = + (struct ubicom32input_data *)dev->private; + struct ubicom32input_platform_data *pdata = ud->pdata; + struct input_dev *id = dev->input; + int i; + int sync_needed = 0; + + for (i = 0; i < pdata->nbuttons; i++) { + const struct ubicom32input_button *ub = &pdata->buttons[i]; + int state = 0; + + int val = gpio_get_value(ub->gpio); + + /* + * Check to see if the state changed from the last time we + * looked + */ + if (val == ud->prev_state[i]) { + continue; + } + + /* + * The state has changed, determine if we are "up" or "down" + */ + ud->prev_state[i] = val; + + if ((!val && ub->active_low) || (val && !ub->active_low)) { + state = 1; + } + + input_event(id, ub->type, ub->code, state); + sync_needed = 1; + } + + if (sync_needed) { + input_sync(id); + } +} + +/* + * ubicom32input_probe + */ +static int __devinit ubicom32input_probe(struct platform_device *pdev) +{ + int i; + struct ubicom32input_data *ud; + struct input_polled_dev *poll_dev; + struct input_dev *input_dev; + struct ubicom32input_platform_data *pdata; + int ret; + + pdata = pdev->dev.platform_data; + if (!pdata) { + return -EINVAL; + } + + ud = kzalloc(sizeof(struct ubicom32input_data) + + pdata->nbuttons, GFP_KERNEL); + if (!ud) { + return -ENOMEM; + } + ud->pdata = pdata; + + poll_dev = input_allocate_polled_device(); + if (!poll_dev) { + ret = -ENOMEM; + goto fail; + } + + platform_set_drvdata(pdev, ud); + + ud->poll_dev = poll_dev; + poll_dev->private = ud; + poll_dev->poll = ubicom32input_poll; + + /* + * Set the poll interval requested, default to 50 msec + */ + if (pdata->poll_interval) { + poll_dev->poll_interval = pdata->poll_interval; + } else { + poll_dev->poll_interval = 50; + } + + /* + * Setup the input device + */ + input_dev = poll_dev->input; + input_dev->name = pdata->name ? pdata->name : "Ubicom32 Input"; + input_dev->phys = "ubicom32input/input0"; + input_dev->dev.parent = &pdev->dev; + input_dev->id.bustype = BUS_HOST; + + /* + * Reserve the GPIOs + */ + for (i = 0; i < pdata->nbuttons; i++) { + const struct ubicom32input_button *ub = &pdata->buttons[i]; + + ret = gpio_request(ub->gpio, + ub->desc ? ub->desc : "ubicom32input"); + if (ret < 0) { + pr_err("ubicom32input: failed to request " + "GPIO %d ret=%d\n", ub->gpio, ret); + goto fail2; + } + + ret = gpio_direction_input(ub->gpio); + if (ret < 0) { + pr_err("ubicom32input: failed to set " + "GPIO %d to input ret=%d\n", ub->gpio, ret); + goto fail2; + } + + /* + * Set the previous state to the non-active stae + */ + ud->prev_state[i] = ub->active_low; + + input_set_capability(input_dev, + ub->type ? ub->type : EV_KEY, ub->code); + } + + /* + * Register + */ + ret = input_register_polled_device(ud->poll_dev); + if (ret) { + goto fail2; + } + + return 0; + +fail2: + /* + * release the GPIOs we have already requested. + */ + while (--i >= 0) { + gpio_free(pdata->buttons[i].gpio); + } + +fail: + printk(KERN_ERR "Ubicom32Input: Failed to register driver %d", ret); + platform_set_drvdata(pdev, NULL); + input_free_polled_device(poll_dev); + kfree(ud); + return ret; +} + +/* + * ubicom32input_remove + */ +static int __devexit ubicom32input_remove(struct platform_device *dev) +{ + struct ubicom32input_data *ud = + (struct ubicom32input_data *)platform_get_drvdata(dev); + int i; + + /* + * Free the GPIOs + */ + for (i = 0; i < ud->pdata->nbuttons; i++) { + gpio_free(ud->pdata->buttons[i].gpio); + } + + platform_set_drvdata(dev, NULL); + input_unregister_polled_device(ud->poll_dev); + input_free_polled_device(ud->poll_dev); + + kfree(ud); + + return 0; +} + +static struct platform_driver ubicom32input_driver = { + .driver = { + .name = "ubicom32input", + .owner = THIS_MODULE, + }, + .probe = ubicom32input_probe, + .remove = __devexit_p(ubicom32input_remove), +}; + +/* + * ubicom32input_init + */ +static int __devinit ubicom32input_init(void) +{ + return platform_driver_register(&ubicom32input_driver); +} + +/* + * ubicom32input_exit + */ +static void __exit ubicom32input_exit(void) +{ + platform_driver_unregister(&ubicom32input_driver); +} + +module_init(ubicom32input_init); +module_exit(ubicom32input_exit); + +MODULE_AUTHOR("Pat Tjin <pattjin@ubicom.com>"); +MODULE_DESCRIPTION("Ubicom32 Input Driver"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:ubicom32-input"); |