Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * PXA930 track ball mouse driver
0004  *
0005  * Copyright (C) 2007 Marvell International Ltd.
0006  * 2008-02-28: Yong Yao <yaoyong@marvell.com>
0007  *             initial version
0008  */
0009 
0010 #include <linux/input.h>
0011 #include <linux/interrupt.h>
0012 #include <linux/module.h>
0013 #include <linux/platform_device.h>
0014 #include <linux/delay.h>
0015 #include <linux/io.h>
0016 #include <linux/slab.h>
0017 
0018 #include <linux/platform_data/mouse-pxa930_trkball.h>
0019 
0020 /* Trackball Controller Register Definitions */
0021 #define TBCR        (0x000C)
0022 #define TBCNTR      (0x0010)
0023 #define TBSBC       (0x0014)
0024 
0025 #define TBCR_TBRST  (1 << 1)
0026 #define TBCR_TBSB   (1 << 10)
0027 
0028 #define TBCR_Y_FLT(n)   (((n) & 0xf) << 6)
0029 #define TBCR_X_FLT(n)   (((n) & 0xf) << 2)
0030 
0031 #define TBCNTR_YM(n)    (((n) >> 24) & 0xff)
0032 #define TBCNTR_YP(n)    (((n) >> 16) & 0xff)
0033 #define TBCNTR_XM(n)    (((n) >> 8) & 0xff)
0034 #define TBCNTR_XP(n)    ((n) & 0xff)
0035 
0036 #define TBSBC_TBSBC (0x1)
0037 
0038 struct pxa930_trkball {
0039     struct pxa930_trkball_platform_data *pdata;
0040 
0041     /* Memory Mapped Register */
0042     struct resource *mem;
0043     void __iomem *mmio_base;
0044 
0045     struct input_dev *input;
0046 };
0047 
0048 static irqreturn_t pxa930_trkball_interrupt(int irq, void *dev_id)
0049 {
0050     struct pxa930_trkball *trkball = dev_id;
0051     struct input_dev *input = trkball->input;
0052     int tbcntr, x, y;
0053 
0054     /* According to the spec software must read TBCNTR twice:
0055      * if the read value is the same, the reading is valid
0056      */
0057     tbcntr = __raw_readl(trkball->mmio_base + TBCNTR);
0058 
0059     if (tbcntr == __raw_readl(trkball->mmio_base + TBCNTR)) {
0060         x = (TBCNTR_XP(tbcntr) - TBCNTR_XM(tbcntr)) / 2;
0061         y = (TBCNTR_YP(tbcntr) - TBCNTR_YM(tbcntr)) / 2;
0062 
0063         input_report_rel(input, REL_X, x);
0064         input_report_rel(input, REL_Y, y);
0065         input_sync(input);
0066     }
0067 
0068     __raw_writel(TBSBC_TBSBC, trkball->mmio_base + TBSBC);
0069     __raw_writel(0, trkball->mmio_base + TBSBC);
0070 
0071     return IRQ_HANDLED;
0072 }
0073 
0074 /* For TBCR, we need to wait for a while to make sure it has been modified. */
0075 static int write_tbcr(struct pxa930_trkball *trkball, int v)
0076 {
0077     int i = 100;
0078 
0079     __raw_writel(v, trkball->mmio_base + TBCR);
0080 
0081     while (--i) {
0082         if (__raw_readl(trkball->mmio_base + TBCR) == v)
0083             break;
0084         msleep(1);
0085     }
0086 
0087     if (i == 0) {
0088         pr_err("%s: timed out writing TBCR(%x)!\n", __func__, v);
0089         return -ETIMEDOUT;
0090     }
0091 
0092     return 0;
0093 }
0094 
0095 static void pxa930_trkball_config(struct pxa930_trkball *trkball)
0096 {
0097     uint32_t tbcr;
0098 
0099     /* According to spec, need to write the filters of x,y to 0xf first! */
0100     tbcr = __raw_readl(trkball->mmio_base + TBCR);
0101     write_tbcr(trkball, tbcr | TBCR_X_FLT(0xf) | TBCR_Y_FLT(0xf));
0102     write_tbcr(trkball, TBCR_X_FLT(trkball->pdata->x_filter) |
0103                 TBCR_Y_FLT(trkball->pdata->y_filter));
0104 
0105     /* According to spec, set TBCR_TBRST first, before clearing it! */
0106     tbcr = __raw_readl(trkball->mmio_base + TBCR);
0107     write_tbcr(trkball, tbcr | TBCR_TBRST);
0108     write_tbcr(trkball, tbcr & ~TBCR_TBRST);
0109 
0110     __raw_writel(TBSBC_TBSBC, trkball->mmio_base + TBSBC);
0111     __raw_writel(0, trkball->mmio_base + TBSBC);
0112 
0113     pr_debug("%s: final TBCR=%x!\n", __func__,
0114          __raw_readl(trkball->mmio_base + TBCR));
0115 }
0116 
0117 static int pxa930_trkball_open(struct input_dev *dev)
0118 {
0119     struct pxa930_trkball *trkball = input_get_drvdata(dev);
0120 
0121     pxa930_trkball_config(trkball);
0122 
0123     return 0;
0124 }
0125 
0126 static void pxa930_trkball_disable(struct pxa930_trkball *trkball)
0127 {
0128     uint32_t tbcr = __raw_readl(trkball->mmio_base + TBCR);
0129 
0130     /* Held in reset, gate the 32-KHz input clock off */
0131     write_tbcr(trkball, tbcr | TBCR_TBRST);
0132 }
0133 
0134 static void pxa930_trkball_close(struct input_dev *dev)
0135 {
0136     struct pxa930_trkball *trkball = input_get_drvdata(dev);
0137 
0138     pxa930_trkball_disable(trkball);
0139 }
0140 
0141 static int pxa930_trkball_probe(struct platform_device *pdev)
0142 {
0143     struct pxa930_trkball *trkball;
0144     struct input_dev *input;
0145     struct resource *res;
0146     int irq, error;
0147 
0148     irq = platform_get_irq(pdev, 0);
0149     if (irq < 0)
0150         return -ENXIO;
0151 
0152     res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
0153     if (!res) {
0154         dev_err(&pdev->dev, "failed to get register memory\n");
0155         return -ENXIO;
0156     }
0157 
0158     trkball = kzalloc(sizeof(struct pxa930_trkball), GFP_KERNEL);
0159     if (!trkball)
0160         return -ENOMEM;
0161 
0162     trkball->pdata = dev_get_platdata(&pdev->dev);
0163     if (!trkball->pdata) {
0164         dev_err(&pdev->dev, "no platform data defined\n");
0165         error = -EINVAL;
0166         goto failed;
0167     }
0168 
0169     trkball->mmio_base = ioremap(res->start, resource_size(res));
0170     if (!trkball->mmio_base) {
0171         dev_err(&pdev->dev, "failed to ioremap registers\n");
0172         error = -ENXIO;
0173         goto failed;
0174     }
0175 
0176     /* held the module in reset, will be enabled in open() */
0177     pxa930_trkball_disable(trkball);
0178 
0179     error = request_irq(irq, pxa930_trkball_interrupt, 0,
0180                 pdev->name, trkball);
0181     if (error) {
0182         dev_err(&pdev->dev, "failed to request irq: %d\n", error);
0183         goto failed_free_io;
0184     }
0185 
0186     platform_set_drvdata(pdev, trkball);
0187 
0188     input = input_allocate_device();
0189     if (!input) {
0190         dev_err(&pdev->dev, "failed to allocate input device\n");
0191         error = -ENOMEM;
0192         goto failed_free_irq;
0193     }
0194 
0195     input->name = pdev->name;
0196     input->id.bustype = BUS_HOST;
0197     input->open = pxa930_trkball_open;
0198     input->close = pxa930_trkball_close;
0199     input->dev.parent = &pdev->dev;
0200     input_set_drvdata(input, trkball);
0201 
0202     trkball->input = input;
0203 
0204     input_set_capability(input, EV_REL, REL_X);
0205     input_set_capability(input, EV_REL, REL_Y);
0206 
0207     error = input_register_device(input);
0208     if (error) {
0209         dev_err(&pdev->dev, "unable to register input device\n");
0210         goto failed_free_input;
0211     }
0212 
0213     return 0;
0214 
0215 failed_free_input:
0216     input_free_device(input);
0217 failed_free_irq:
0218     free_irq(irq, trkball);
0219 failed_free_io:
0220     iounmap(trkball->mmio_base);
0221 failed:
0222     kfree(trkball);
0223     return error;
0224 }
0225 
0226 static int pxa930_trkball_remove(struct platform_device *pdev)
0227 {
0228     struct pxa930_trkball *trkball = platform_get_drvdata(pdev);
0229     int irq = platform_get_irq(pdev, 0);
0230 
0231     input_unregister_device(trkball->input);
0232     free_irq(irq, trkball);
0233     iounmap(trkball->mmio_base);
0234     kfree(trkball);
0235 
0236     return 0;
0237 }
0238 
0239 static struct platform_driver pxa930_trkball_driver = {
0240     .driver     = {
0241         .name   = "pxa930-trkball",
0242     },
0243     .probe      = pxa930_trkball_probe,
0244     .remove     = pxa930_trkball_remove,
0245 };
0246 module_platform_driver(pxa930_trkball_driver);
0247 
0248 MODULE_AUTHOR("Yong Yao <yaoyong@marvell.com>");
0249 MODULE_DESCRIPTION("PXA930 Trackball Mouse Driver");
0250 MODULE_LICENSE("GPL");