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