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