linux/drivers/input/keyboard/pxa930_rotary.c
<<
>>
Prefs
   1/*
   2 * Driver for the enhanced rotary controller on pxa930 and pxa935
   3 *
   4 * This program is free software; you can redistribute it and/or modify
   5 * it under the terms of the GNU General Public License version 2 as
   6 * published by the Free Software Foundation.
   7 */
   8
   9#include <linux/kernel.h>
  10#include <linux/module.h>
  11#include <linux/init.h>
  12#include <linux/interrupt.h>
  13#include <linux/input.h>
  14#include <linux/platform_device.h>
  15#include <linux/io.h>
  16
  17#include <mach/pxa930_rotary.h>
  18
  19#define SBCR    (0x04)
  20#define ERCR    (0x0c)
  21
  22#define SBCR_ERSB       (1 << 5)
  23
  24struct pxa930_rotary {
  25        struct input_dev        *input_dev;
  26        void __iomem            *mmio_base;
  27        int                     last_ercr;
  28
  29        struct pxa930_rotary_platform_data *pdata;
  30};
  31
  32static void clear_sbcr(struct pxa930_rotary *r)
  33{
  34        uint32_t sbcr = __raw_readl(r->mmio_base + SBCR);
  35
  36        __raw_writel(sbcr | SBCR_ERSB, r->mmio_base + SBCR);
  37        __raw_writel(sbcr & ~SBCR_ERSB, r->mmio_base + SBCR);
  38}
  39
  40static irqreturn_t rotary_irq(int irq, void *dev_id)
  41{
  42        struct pxa930_rotary *r = dev_id;
  43        struct pxa930_rotary_platform_data *pdata = r->pdata;
  44        int ercr, delta, key;
  45
  46        ercr = __raw_readl(r->mmio_base + ERCR) & 0xf;
  47        clear_sbcr(r);
  48
  49        delta = ercr - r->last_ercr;
  50        if (delta == 0)
  51                return IRQ_HANDLED;
  52
  53        r->last_ercr = ercr;
  54
  55        if (pdata->up_key && pdata->down_key) {
  56                key = (delta > 0) ? pdata->up_key : pdata->down_key;
  57                input_report_key(r->input_dev, key, 1);
  58                input_sync(r->input_dev);
  59                input_report_key(r->input_dev, key, 0);
  60        } else
  61                input_report_rel(r->input_dev, pdata->rel_code, delta);
  62
  63        input_sync(r->input_dev);
  64
  65        return IRQ_HANDLED;
  66}
  67
  68static int pxa930_rotary_open(struct input_dev *dev)
  69{
  70        struct pxa930_rotary *r = input_get_drvdata(dev);
  71
  72        clear_sbcr(r);
  73
  74        return 0;
  75}
  76
  77static void pxa930_rotary_close(struct input_dev *dev)
  78{
  79        struct pxa930_rotary *r = input_get_drvdata(dev);
  80
  81        clear_sbcr(r);
  82}
  83
  84static int __devinit pxa930_rotary_probe(struct platform_device *pdev)
  85{
  86        struct pxa930_rotary_platform_data *pdata = pdev->dev.platform_data;
  87        struct pxa930_rotary *r;
  88        struct input_dev *input_dev;
  89        struct resource *res;
  90        int irq;
  91        int err;
  92
  93        irq = platform_get_irq(pdev, 0);
  94        if (irq < 0) {
  95                dev_err(&pdev->dev, "no irq for rotary controller\n");
  96                return -ENXIO;
  97        }
  98
  99        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 100        if (!res) {
 101                dev_err(&pdev->dev, "no I/O memory defined\n");
 102                return -ENXIO;
 103        }
 104
 105        if (!pdata) {
 106                dev_err(&pdev->dev, "no platform data defined\n");
 107                return -EINVAL;
 108        }
 109
 110        r = kzalloc(sizeof(struct pxa930_rotary), GFP_KERNEL);
 111        if (!r)
 112                return -ENOMEM;
 113
 114        r->mmio_base = ioremap_nocache(res->start, resource_size(res));
 115        if (r->mmio_base == NULL) {
 116                dev_err(&pdev->dev, "failed to remap IO memory\n");
 117                err = -ENXIO;
 118                goto failed_free;
 119        }
 120
 121        r->pdata = pdata;
 122        platform_set_drvdata(pdev, r);
 123
 124        /* allocate and register the input device */
 125        input_dev = input_allocate_device();
 126        if (!input_dev) {
 127                dev_err(&pdev->dev, "failed to allocate input device\n");
 128                err = -ENOMEM;
 129                goto failed_free_io;
 130        }
 131
 132        input_dev->name = pdev->name;
 133        input_dev->id.bustype = BUS_HOST;
 134        input_dev->open = pxa930_rotary_open;
 135        input_dev->close = pxa930_rotary_close;
 136        input_dev->dev.parent = &pdev->dev;
 137
 138        if (pdata->up_key && pdata->down_key) {
 139                __set_bit(pdata->up_key, input_dev->keybit);
 140                __set_bit(pdata->down_key, input_dev->keybit);
 141                __set_bit(EV_KEY, input_dev->evbit);
 142        } else {
 143                __set_bit(pdata->rel_code, input_dev->relbit);
 144                __set_bit(EV_REL, input_dev->evbit);
 145        }
 146
 147        r->input_dev = input_dev;
 148        input_set_drvdata(input_dev, r);
 149
 150        err = request_irq(irq, rotary_irq, IRQF_DISABLED,
 151                        "enhanced rotary", r);
 152        if (err) {
 153                dev_err(&pdev->dev, "failed to request IRQ\n");
 154                goto failed_free_input;
 155        }
 156
 157        err = input_register_device(input_dev);
 158        if (err) {
 159                dev_err(&pdev->dev, "failed to register input device\n");
 160                goto failed_free_irq;
 161        }
 162
 163        return 0;
 164
 165failed_free_irq:
 166        free_irq(irq, r);
 167failed_free_input:
 168        input_free_device(input_dev);
 169failed_free_io:
 170        iounmap(r->mmio_base);
 171failed_free:
 172        kfree(r);
 173        return err;
 174}
 175
 176static int __devexit pxa930_rotary_remove(struct platform_device *pdev)
 177{
 178        struct pxa930_rotary *r = platform_get_drvdata(pdev);
 179
 180        free_irq(platform_get_irq(pdev, 0), r);
 181        input_unregister_device(r->input_dev);
 182        iounmap(r->mmio_base);
 183        platform_set_drvdata(pdev, NULL);
 184        kfree(r);
 185
 186        return 0;
 187}
 188
 189static struct platform_driver pxa930_rotary_driver = {
 190        .driver         = {
 191                .name   = "pxa930-rotary",
 192                .owner  = THIS_MODULE,
 193        },
 194        .probe          = pxa930_rotary_probe,
 195        .remove         = __devexit_p(pxa930_rotary_remove),
 196};
 197
 198static int __init pxa930_rotary_init(void)
 199{
 200        return platform_driver_register(&pxa930_rotary_driver);
 201}
 202module_init(pxa930_rotary_init);
 203
 204static void __exit pxa930_rotary_exit(void)
 205{
 206        platform_driver_unregister(&pxa930_rotary_driver);
 207}
 208module_exit(pxa930_rotary_exit);
 209
 210MODULE_LICENSE("GPL");
 211MODULE_DESCRIPTION("Driver for PXA93x Enhanced Rotary Controller");
 212MODULE_AUTHOR("Yao Yong <yaoyong@marvell.com>");
 213