linux/drivers/input/misc/cobalt_btns.c
<<
>>
Prefs
   1/*
   2 *  Cobalt button interface driver.
   3 *
   4 *  Copyright (C) 2007-2008  Yoichi Yuasa <yuasa@linux-mips.org>
   5 *
   6 *  This program is free software; you can redistribute it and/or modify
   7 *  it under the terms of the GNU General Public License as published by
   8 *  the Free Software Foundation; either version 2 of the License, or
   9 *  (at your option) any later version.
  10 *
  11 *  This program is distributed in the hope that it will be useful,
  12 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14 *  GNU General Public License for more details.
  15 *
  16 *  You should have received a copy of the GNU General Public License
  17 *  along with this program; if not, write to the Free Software
  18 *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  19 */
  20#include <linux/init.h>
  21#include <linux/input-polldev.h>
  22#include <linux/ioport.h>
  23#include <linux/module.h>
  24#include <linux/platform_device.h>
  25
  26#define BUTTONS_POLL_INTERVAL   30      /* msec */
  27#define BUTTONS_COUNT_THRESHOLD 3
  28#define BUTTONS_STATUS_MASK     0xfe000000
  29
  30static const unsigned short cobalt_map[] = {
  31        KEY_RESERVED,
  32        KEY_RESTART,
  33        KEY_LEFT,
  34        KEY_UP,
  35        KEY_DOWN,
  36        KEY_RIGHT,
  37        KEY_ENTER,
  38        KEY_SELECT
  39};
  40
  41struct buttons_dev {
  42        struct input_polled_dev *poll_dev;
  43        unsigned short keymap[ARRAY_SIZE(cobalt_map)];
  44        int count[ARRAY_SIZE(cobalt_map)];
  45        void __iomem *reg;
  46};
  47
  48static void handle_buttons(struct input_polled_dev *dev)
  49{
  50        struct buttons_dev *bdev = dev->private;
  51        struct input_dev *input = dev->input;
  52        uint32_t status;
  53        int i;
  54
  55        status = ~readl(bdev->reg) >> 24;
  56
  57        for (i = 0; i < ARRAY_SIZE(bdev->keymap); i++) {
  58                if (status & (1U << i)) {
  59                        if (++bdev->count[i] == BUTTONS_COUNT_THRESHOLD) {
  60                                input_event(input, EV_MSC, MSC_SCAN, i);
  61                                input_report_key(input, bdev->keymap[i], 1);
  62                                input_sync(input);
  63                        }
  64                } else {
  65                        if (bdev->count[i] >= BUTTONS_COUNT_THRESHOLD) {
  66                                input_event(input, EV_MSC, MSC_SCAN, i);
  67                                input_report_key(input, bdev->keymap[i], 0);
  68                                input_sync(input);
  69                        }
  70                        bdev->count[i] = 0;
  71                }
  72        }
  73}
  74
  75static int __devinit cobalt_buttons_probe(struct platform_device *pdev)
  76{
  77        struct buttons_dev *bdev;
  78        struct input_polled_dev *poll_dev;
  79        struct input_dev *input;
  80        struct resource *res;
  81        int error, i;
  82
  83        bdev = kzalloc(sizeof(struct buttons_dev), GFP_KERNEL);
  84        poll_dev = input_allocate_polled_device();
  85        if (!bdev || !poll_dev) {
  86                error = -ENOMEM;
  87                goto err_free_mem;
  88        }
  89
  90        memcpy(bdev->keymap, cobalt_map, sizeof(bdev->keymap));
  91
  92        poll_dev->private = bdev;
  93        poll_dev->poll = handle_buttons;
  94        poll_dev->poll_interval = BUTTONS_POLL_INTERVAL;
  95
  96        input = poll_dev->input;
  97        input->name = "Cobalt buttons";
  98        input->phys = "cobalt/input0";
  99        input->id.bustype = BUS_HOST;
 100        input->dev.parent = &pdev->dev;
 101
 102        input->keycode = bdev->keymap;
 103        input->keycodemax = ARRAY_SIZE(bdev->keymap);
 104        input->keycodesize = sizeof(unsigned short);
 105
 106        input_set_capability(input, EV_MSC, MSC_SCAN);
 107        __set_bit(EV_KEY, input->evbit);
 108        for (i = 0; i < ARRAY_SIZE(cobalt_map); i++)
 109                __set_bit(bdev->keymap[i], input->keybit);
 110        __clear_bit(KEY_RESERVED, input->keybit);
 111
 112        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 113        if (!res) {
 114                error = -EBUSY;
 115                goto err_free_mem;
 116        }
 117
 118        bdev->poll_dev = poll_dev;
 119        bdev->reg = ioremap(res->start, resource_size(res));
 120        dev_set_drvdata(&pdev->dev, bdev);
 121
 122        error = input_register_polled_device(poll_dev);
 123        if (error)
 124                goto err_iounmap;
 125
 126        return 0;
 127
 128 err_iounmap:
 129        iounmap(bdev->reg);
 130 err_free_mem:
 131        input_free_polled_device(poll_dev);
 132        kfree(bdev);
 133        dev_set_drvdata(&pdev->dev, NULL);
 134        return error;
 135}
 136
 137static int __devexit cobalt_buttons_remove(struct platform_device *pdev)
 138{
 139        struct device *dev = &pdev->dev;
 140        struct buttons_dev *bdev = dev_get_drvdata(dev);
 141
 142        input_unregister_polled_device(bdev->poll_dev);
 143        input_free_polled_device(bdev->poll_dev);
 144        iounmap(bdev->reg);
 145        kfree(bdev);
 146        dev_set_drvdata(dev, NULL);
 147
 148        return 0;
 149}
 150
 151MODULE_AUTHOR("Yoichi Yuasa <yuasa@linux-mips.org>");
 152MODULE_DESCRIPTION("Cobalt button interface driver");
 153MODULE_LICENSE("GPL");
 154/* work with hotplug and coldplug */
 155MODULE_ALIAS("platform:Cobalt buttons");
 156
 157static struct platform_driver cobalt_buttons_driver = {
 158        .probe  = cobalt_buttons_probe,
 159        .remove = __devexit_p(cobalt_buttons_remove),
 160        .driver = {
 161                .name   = "Cobalt buttons",
 162                .owner  = THIS_MODULE,
 163        },
 164};
 165
 166static int __init cobalt_buttons_init(void)
 167{
 168        return platform_driver_register(&cobalt_buttons_driver);
 169}
 170
 171static void __exit cobalt_buttons_exit(void)
 172{
 173        platform_driver_unregister(&cobalt_buttons_driver);
 174}
 175
 176module_init(cobalt_buttons_init);
 177module_exit(cobalt_buttons_exit);
 178