linux/drivers/input/mouse/pxa930_trkball.c
<<
>>
Prefs
   1/*
   2 * PXA930 track ball mouse driver
   3 *
   4 * Copyright (C) 2007 Marvell International Ltd.
   5 * 2008-02-28: Yong Yao <yaoyong@marvell.com>
   6 *             initial version
   7 *
   8 *  This program is free software; you can redistribute it and/or modify
   9 *  it under the terms of the GNU General Public License version 2 as
  10 *  published by the Free Software Foundation.
  11 */
  12
  13#include <linux/init.h>
  14#include <linux/input.h>
  15#include <linux/version.h>
  16#include <linux/interrupt.h>
  17#include <linux/module.h>
  18#include <linux/platform_device.h>
  19#include <linux/delay.h>
  20#include <linux/io.h>
  21#include <linux/slab.h>
  22
  23#include <mach/hardware.h>
  24#include <mach/pxa930_trkball.h>
  25
  26/* Trackball Controller Register Definitions */
  27#define TBCR            (0x000C)
  28#define TBCNTR          (0x0010)
  29#define TBSBC           (0x0014)
  30
  31#define TBCR_TBRST      (1 << 1)
  32#define TBCR_TBSB       (1 << 10)
  33
  34#define TBCR_Y_FLT(n)   (((n) & 0xf) << 6)
  35#define TBCR_X_FLT(n)   (((n) & 0xf) << 2)
  36
  37#define TBCNTR_YM(n)    (((n) >> 24) & 0xff)
  38#define TBCNTR_YP(n)    (((n) >> 16) & 0xff)
  39#define TBCNTR_XM(n)    (((n) >> 8) & 0xff)
  40#define TBCNTR_XP(n)    ((n) & 0xff)
  41
  42#define TBSBC_TBSBC     (0x1)
  43
  44struct pxa930_trkball {
  45        struct pxa930_trkball_platform_data *pdata;
  46
  47        /* Memory Mapped Register */
  48        struct resource *mem;
  49        void __iomem *mmio_base;
  50
  51        struct input_dev *input;
  52};
  53
  54static irqreturn_t pxa930_trkball_interrupt(int irq, void *dev_id)
  55{
  56        struct pxa930_trkball *trkball = dev_id;
  57        struct input_dev *input = trkball->input;
  58        int tbcntr, x, y;
  59
  60        /* According to the spec software must read TBCNTR twice:
  61         * if the read value is the same, the reading is valid
  62         */
  63        tbcntr = __raw_readl(trkball->mmio_base + TBCNTR);
  64
  65        if (tbcntr == __raw_readl(trkball->mmio_base + TBCNTR)) {
  66                x = (TBCNTR_XP(tbcntr) - TBCNTR_XM(tbcntr)) / 2;
  67                y = (TBCNTR_YP(tbcntr) - TBCNTR_YM(tbcntr)) / 2;
  68
  69                input_report_rel(input, REL_X, x);
  70                input_report_rel(input, REL_Y, y);
  71                input_sync(input);
  72        }
  73
  74        __raw_writel(TBSBC_TBSBC, trkball->mmio_base + TBSBC);
  75        __raw_writel(0, trkball->mmio_base + TBSBC);
  76
  77        return IRQ_HANDLED;
  78}
  79
  80/* For TBCR, we need to wait for a while to make sure it has been modified. */
  81static int write_tbcr(struct pxa930_trkball *trkball, int v)
  82{
  83        int i = 100;
  84
  85        __raw_writel(v, trkball->mmio_base + TBCR);
  86
  87        while (--i) {
  88                if (__raw_readl(trkball->mmio_base + TBCR) == v)
  89                        break;
  90                msleep(1);
  91        }
  92
  93        if (i == 0) {
  94                pr_err("%s: timed out writing TBCR(%x)!\n", __func__, v);
  95                return -ETIMEDOUT;
  96        }
  97
  98        return 0;
  99}
 100
 101static void pxa930_trkball_config(struct pxa930_trkball *trkball)
 102{
 103        uint32_t tbcr;
 104
 105        /* According to spec, need to write the filters of x,y to 0xf first! */
 106        tbcr = __raw_readl(trkball->mmio_base + TBCR);
 107        write_tbcr(trkball, tbcr | TBCR_X_FLT(0xf) | TBCR_Y_FLT(0xf));
 108        write_tbcr(trkball, TBCR_X_FLT(trkball->pdata->x_filter) |
 109                            TBCR_Y_FLT(trkball->pdata->y_filter));
 110
 111        /* According to spec, set TBCR_TBRST first, before clearing it! */
 112        tbcr = __raw_readl(trkball->mmio_base + TBCR);
 113        write_tbcr(trkball, tbcr | TBCR_TBRST);
 114        write_tbcr(trkball, tbcr & ~TBCR_TBRST);
 115
 116        __raw_writel(TBSBC_TBSBC, trkball->mmio_base + TBSBC);
 117        __raw_writel(0, trkball->mmio_base + TBSBC);
 118
 119        pr_debug("%s: final TBCR=%x!\n", __func__,
 120                 __raw_readl(trkball->mmio_base + TBCR));
 121}
 122
 123static int pxa930_trkball_open(struct input_dev *dev)
 124{
 125        struct pxa930_trkball *trkball = input_get_drvdata(dev);
 126
 127        pxa930_trkball_config(trkball);
 128
 129        return 0;
 130}
 131
 132static void pxa930_trkball_disable(struct pxa930_trkball *trkball)
 133{
 134        uint32_t tbcr = __raw_readl(trkball->mmio_base + TBCR);
 135
 136        /* Held in reset, gate the 32-KHz input clock off */
 137        write_tbcr(trkball, tbcr | TBCR_TBRST);
 138}
 139
 140static void pxa930_trkball_close(struct input_dev *dev)
 141{
 142        struct pxa930_trkball *trkball = input_get_drvdata(dev);
 143
 144        pxa930_trkball_disable(trkball);
 145}
 146
 147static int __devinit pxa930_trkball_probe(struct platform_device *pdev)
 148{
 149        struct pxa930_trkball *trkball;
 150        struct input_dev *input;
 151        struct resource *res;
 152        int irq, error;
 153
 154        irq = platform_get_irq(pdev, 0);
 155        if (irq < 0) {
 156                dev_err(&pdev->dev, "failed to get trkball irq\n");
 157                return -ENXIO;
 158        }
 159
 160        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
 161        if (!res) {
 162                dev_err(&pdev->dev, "failed to get register memory\n");
 163                return -ENXIO;
 164        }
 165
 166        trkball = kzalloc(sizeof(struct pxa930_trkball), GFP_KERNEL);
 167        if (!trkball)
 168                return -ENOMEM;
 169
 170        trkball->pdata = pdev->dev.platform_data;
 171        if (!trkball->pdata) {
 172                dev_err(&pdev->dev, "no platform data defined\n");
 173                error = -EINVAL;
 174                goto failed;
 175        }
 176
 177        trkball->mmio_base = ioremap_nocache(res->start, resource_size(res));
 178        if (!trkball->mmio_base) {
 179                dev_err(&pdev->dev, "failed to ioremap registers\n");
 180                error = -ENXIO;
 181                goto failed;
 182        }
 183
 184        /* held the module in reset, will be enabled in open() */
 185        pxa930_trkball_disable(trkball);
 186
 187        error = request_irq(irq, pxa930_trkball_interrupt, IRQF_DISABLED,
 188                            pdev->name, trkball);
 189        if (error) {
 190                dev_err(&pdev->dev, "failed to request irq: %d\n", error);
 191                goto failed_free_io;
 192        }
 193
 194        platform_set_drvdata(pdev, trkball);
 195
 196        input = input_allocate_device();
 197        if (!input) {
 198                dev_err(&pdev->dev, "failed to allocate input device\n");
 199                error = -ENOMEM;
 200                goto failed_free_irq;
 201        }
 202
 203        input->name = pdev->name;
 204        input->id.bustype = BUS_HOST;
 205        input->open = pxa930_trkball_open;
 206        input->close = pxa930_trkball_close;
 207        input->dev.parent = &pdev->dev;
 208        input_set_drvdata(input, trkball);
 209
 210        trkball->input = input;
 211
 212        input_set_capability(input, EV_REL, REL_X);
 213        input_set_capability(input, EV_REL, REL_Y);
 214
 215        error = input_register_device(input);
 216        if (error) {
 217                dev_err(&pdev->dev, "unable to register input device\n");
 218                goto failed_free_input;
 219        }
 220
 221        return 0;
 222
 223failed_free_input:
 224        input_free_device(input);
 225failed_free_irq:
 226        free_irq(irq, trkball);
 227failed_free_io:
 228        iounmap(trkball->mmio_base);
 229failed:
 230        kfree(trkball);
 231        return error;
 232}
 233
 234static int __devexit pxa930_trkball_remove(struct platform_device *pdev)
 235{
 236        struct pxa930_trkball *trkball = platform_get_drvdata(pdev);
 237        int irq = platform_get_irq(pdev, 0);
 238
 239        input_unregister_device(trkball->input);
 240        free_irq(irq, trkball);
 241        iounmap(trkball->mmio_base);
 242        kfree(trkball);
 243
 244        return 0;
 245}
 246
 247static struct platform_driver pxa930_trkball_driver = {
 248        .driver         = {
 249                .name   = "pxa930-trkball",
 250        },
 251        .probe          = pxa930_trkball_probe,
 252        .remove         = __devexit_p(pxa930_trkball_remove),
 253};
 254
 255static int __init pxa930_trkball_init(void)
 256{
 257        return platform_driver_register(&pxa930_trkball_driver);
 258}
 259
 260static void __exit pxa930_trkball_exit(void)
 261{
 262        platform_driver_unregister(&pxa930_trkball_driver);
 263}
 264
 265module_init(pxa930_trkball_init);
 266module_exit(pxa930_trkball_exit);
 267
 268MODULE_AUTHOR("Yong Yao <yaoyong@marvell.com>");
 269MODULE_DESCRIPTION("PXA930 Trackball Mouse Driver");
 270MODULE_LICENSE("GPL");
 271