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