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/interrupt.h>
  12#include <linux/input.h>
  13#include <linux/platform_device.h>
  14#include <linux/io.h>
  15#include <linux/slab.h>
  16
  17#include <linux/platform_data/keyboard-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 pxa930_rotary_probe(struct platform_device *pdev)
  85{
  86        struct pxa930_rotary_platform_data *pdata =
  87                        dev_get_platdata(&pdev->dev);
  88        struct pxa930_rotary *r;
  89        struct input_dev *input_dev;
  90        struct resource *res;
  91        int irq;
  92        int err;
  93
  94        irq = platform_get_irq(pdev, 0);
  95        if (irq < 0) {
  96                dev_err(&pdev->dev, "no irq for rotary controller\n");
  97                return -ENXIO;
  98        }
  99
 100        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 101        if (!res) {
 102                dev_err(&pdev->dev, "no I/O memory defined\n");
 103                return -ENXIO;
 104        }
 105
 106        if (!pdata) {
 107                dev_err(&pdev->dev, "no platform data defined\n");
 108                return -EINVAL;
 109        }
 110
 111        r = kzalloc(sizeof(struct pxa930_rotary), GFP_KERNEL);
 112        if (!r)
 113                return -ENOMEM;
 114
 115        r->mmio_base = ioremap_nocache(res->start, resource_size(res));
 116        if (r->mmio_base == NULL) {
 117                dev_err(&pdev->dev, "failed to remap IO memory\n");
 118                err = -ENXIO;
 119                goto failed_free;
 120        }
 121
 122        r->pdata = pdata;
 123        platform_set_drvdata(pdev, r);
 124
 125        /* allocate and register the input device */
 126        input_dev = input_allocate_device();
 127        if (!input_dev) {
 128                dev_err(&pdev->dev, "failed to allocate input device\n");
 129                err = -ENOMEM;
 130                goto failed_free_io;
 131        }
 132
 133        input_dev->name = pdev->name;
 134        input_dev->id.bustype = BUS_HOST;
 135        input_dev->open = pxa930_rotary_open;
 136        input_dev->close = pxa930_rotary_close;
 137        input_dev->dev.parent = &pdev->dev;
 138
 139        if (pdata->up_key && pdata->down_key) {
 140                __set_bit(pdata->up_key, input_dev->keybit);
 141                __set_bit(pdata->down_key, input_dev->keybit);
 142                __set_bit(EV_KEY, input_dev->evbit);
 143        } else {
 144                __set_bit(pdata->rel_code, input_dev->relbit);
 145                __set_bit(EV_REL, input_dev->evbit);
 146        }
 147
 148        r->input_dev = input_dev;
 149        input_set_drvdata(input_dev, r);
 150
 151        err = request_irq(irq, rotary_irq, 0,
 152                        "enhanced rotary", r);
 153        if (err) {
 154                dev_err(&pdev->dev, "failed to request IRQ\n");
 155                goto failed_free_input;
 156        }
 157
 158        err = input_register_device(input_dev);
 159        if (err) {
 160                dev_err(&pdev->dev, "failed to register input device\n");
 161                goto failed_free_irq;
 162        }
 163
 164        return 0;
 165
 166failed_free_irq:
 167        free_irq(irq, r);
 168failed_free_input:
 169        input_free_device(input_dev);
 170failed_free_io:
 171        iounmap(r->mmio_base);
 172failed_free:
 173        kfree(r);
 174        return err;
 175}
 176
 177static int pxa930_rotary_remove(struct platform_device *pdev)
 178{
 179        struct pxa930_rotary *r = platform_get_drvdata(pdev);
 180
 181        free_irq(platform_get_irq(pdev, 0), r);
 182        input_unregister_device(r->input_dev);
 183        iounmap(r->mmio_base);
 184        kfree(r);
 185
 186        return 0;
 187}
 188
 189static struct platform_driver pxa930_rotary_driver = {
 190        .driver         = {
 191                .name   = "pxa930-rotary",
 192        },
 193        .probe          = pxa930_rotary_probe,
 194        .remove         = pxa930_rotary_remove,
 195};
 196module_platform_driver(pxa930_rotary_driver);
 197
 198MODULE_LICENSE("GPL");
 199MODULE_DESCRIPTION("Driver for PXA93x Enhanced Rotary Controller");
 200MODULE_AUTHOR("Yao Yong <yaoyong@marvell.com>");
 201