0001
0002
0003
0004
0005
0006
0007
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
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
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
0055
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
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
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
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
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
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");