1/*
2 * Copyright (c) 2008 Nuvoton technology corporation.
3 *
4 * Wan ZongShun <mcuos.com@gmail.com>
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation;version 2 of the License.
9 *
10 */
11
12#include <linux/delay.h>
13#include <linux/module.h>
14#include <linux/platform_device.h>
15#include <linux/io.h>
16#include <linux/clk.h>
17#include <linux/input.h>
18#include <linux/interrupt.h>
19#include <linux/slab.h>
20
21/* ADC controller bit defines */
22#define ADC_DELAY	0xf00
23#define ADC_DOWN	0x01
24#define ADC_TSC_Y	(0x01 << 8)
25#define ADC_TSC_X	(0x00 << 8)
26#define TSC_FOURWIRE	(~(0x03 << 1))
27#define ADC_CLK_EN	(0x01 << 28)	/* ADC clock enable */
28#define ADC_READ_CON	(0x01 << 12)
29#define ADC_CONV	(0x01 << 13)
30#define ADC_SEMIAUTO	(0x01 << 14)
31#define ADC_WAITTRIG	(0x03 << 14)
32#define ADC_RST1	(0x01 << 16)
33#define ADC_RST0	(0x00 << 16)
34#define ADC_EN		(0x01 << 17)
35#define ADC_INT		(0x01 << 18)
36#define WT_INT		(0x01 << 20)
37#define ADC_INT_EN	(0x01 << 21)
38#define LVD_INT_EN	(0x01 << 22)
39#define WT_INT_EN	(0x01 << 23)
40#define ADC_DIV		(0x04 << 1)	/* div = 6 */
41
42enum ts_state {
43	TS_WAIT_NEW_PACKET,	/* We are waiting next touch report */
44	TS_WAIT_X_COORD,	/* We are waiting for ADC to report X coord */
45	TS_WAIT_Y_COORD,	/* We are waiting for ADC to report Y coord */
46	TS_IDLE,		/* Input device is closed, don't do anything */
47};
48
49struct w90p910_ts {
50	struct input_dev *input;
51	struct timer_list timer;
52	struct clk *clk;
53	int irq_num;
54	void __iomem *ts_reg;
55	spinlock_t lock;
56	enum ts_state state;
57};
58
59static void w90p910_report_event(struct w90p910_ts *w90p910_ts, bool down)
60{
61	struct input_dev *dev = w90p910_ts->input;
62
63	if (down) {
64		input_report_abs(dev, ABS_X,
65				 __raw_readl(w90p910_ts->ts_reg + 0x0c));
66		input_report_abs(dev, ABS_Y,
67				 __raw_readl(w90p910_ts->ts_reg + 0x10));
68	}
69
70	input_report_key(dev, BTN_TOUCH, down);
71	input_sync(dev);
72}
73
74static void w90p910_prepare_x_reading(struct w90p910_ts *w90p910_ts)
75{
76	unsigned long ctlreg;
77
78	__raw_writel(ADC_TSC_X, w90p910_ts->ts_reg + 0x04);
79	ctlreg = __raw_readl(w90p910_ts->ts_reg);
80	ctlreg &= ~(ADC_WAITTRIG | WT_INT | WT_INT_EN);
81	ctlreg |= ADC_SEMIAUTO | ADC_INT_EN | ADC_CONV;
82	__raw_writel(ctlreg, w90p910_ts->ts_reg);
83
84	w90p910_ts->state = TS_WAIT_X_COORD;
85}
86
87static void w90p910_prepare_y_reading(struct w90p910_ts *w90p910_ts)
88{
89	unsigned long ctlreg;
90
91	__raw_writel(ADC_TSC_Y, w90p910_ts->ts_reg + 0x04);
92	ctlreg = __raw_readl(w90p910_ts->ts_reg);
93	ctlreg &= ~(ADC_WAITTRIG | ADC_INT | WT_INT_EN);
94	ctlreg |= ADC_SEMIAUTO | ADC_INT_EN | ADC_CONV;
95	__raw_writel(ctlreg, w90p910_ts->ts_reg);
96
97	w90p910_ts->state = TS_WAIT_Y_COORD;
98}
99
100static void w90p910_prepare_next_packet(struct w90p910_ts *w90p910_ts)
101{
102	unsigned long ctlreg;
103
104	ctlreg = __raw_readl(w90p910_ts->ts_reg);
105	ctlreg &= ~(ADC_INT | ADC_INT_EN | ADC_SEMIAUTO | ADC_CONV);
106	ctlreg |= ADC_WAITTRIG | WT_INT_EN;
107	__raw_writel(ctlreg, w90p910_ts->ts_reg);
108
109	w90p910_ts->state = TS_WAIT_NEW_PACKET;
110}
111
112static irqreturn_t w90p910_ts_interrupt(int irq, void *dev_id)
113{
114	struct w90p910_ts *w90p910_ts = dev_id;
115	unsigned long flags;
116
117	spin_lock_irqsave(&w90p910_ts->lock, flags);
118
119	switch (w90p910_ts->state) {
120	case TS_WAIT_NEW_PACKET:
121		/*
122		 * The controller only generates interrupts when pen
123		 * is down.
124		 */
125		del_timer(&w90p910_ts->timer);
126		w90p910_prepare_x_reading(w90p910_ts);
127		break;
128
129
130	case TS_WAIT_X_COORD:
131		w90p910_prepare_y_reading(w90p910_ts);
132		break;
133
134	case TS_WAIT_Y_COORD:
135		w90p910_report_event(w90p910_ts, true);
136		w90p910_prepare_next_packet(w90p910_ts);
137		mod_timer(&w90p910_ts->timer, jiffies + msecs_to_jiffies(100));
138		break;
139
140	case TS_IDLE:
141		break;
142	}
143
144	spin_unlock_irqrestore(&w90p910_ts->lock, flags);
145
146	return IRQ_HANDLED;
147}
148
149static void w90p910_check_pen_up(unsigned long data)
150{
151	struct w90p910_ts *w90p910_ts = (struct w90p910_ts *) data;
152	unsigned long flags;
153
154	spin_lock_irqsave(&w90p910_ts->lock, flags);
155
156	if (w90p910_ts->state == TS_WAIT_NEW_PACKET &&
157	    !(__raw_readl(w90p910_ts->ts_reg + 0x04) & ADC_DOWN)) {
158
159		w90p910_report_event(w90p910_ts, false);
160	}
161
162	spin_unlock_irqrestore(&w90p910_ts->lock, flags);
163}
164
165static int w90p910_open(struct input_dev *dev)
166{
167	struct w90p910_ts *w90p910_ts = input_get_drvdata(dev);
168	unsigned long val;
169
170	/* enable the ADC clock */
171	clk_enable(w90p910_ts->clk);
172
173	__raw_writel(ADC_RST1, w90p910_ts->ts_reg);
174	msleep(1);
175	__raw_writel(ADC_RST0, w90p910_ts->ts_reg);
176	msleep(1);
177
178	/* set delay and screen type */
179	val = __raw_readl(w90p910_ts->ts_reg + 0x04);
180	__raw_writel(val & TSC_FOURWIRE, w90p910_ts->ts_reg + 0x04);
181	__raw_writel(ADC_DELAY, w90p910_ts->ts_reg + 0x08);
182
183	w90p910_ts->state = TS_WAIT_NEW_PACKET;
184	wmb();
185
186	/* set trigger mode */
187	val = __raw_readl(w90p910_ts->ts_reg);
188	val |= ADC_WAITTRIG | ADC_DIV | ADC_EN | WT_INT_EN;
189	__raw_writel(val, w90p910_ts->ts_reg);
190
191	return 0;
192}
193
194static void w90p910_close(struct input_dev *dev)
195{
196	struct w90p910_ts *w90p910_ts = input_get_drvdata(dev);
197	unsigned long val;
198
199	/* disable trigger mode */
200
201	spin_lock_irq(&w90p910_ts->lock);
202
203	w90p910_ts->state = TS_IDLE;
204
205	val = __raw_readl(w90p910_ts->ts_reg);
206	val &= ~(ADC_WAITTRIG | ADC_DIV | ADC_EN | WT_INT_EN | ADC_INT_EN);
207	__raw_writel(val, w90p910_ts->ts_reg);
208
209	spin_unlock_irq(&w90p910_ts->lock);
210
211	/* Now that interrupts are shut off we can safely delete timer */
212	del_timer_sync(&w90p910_ts->timer);
213
214	/* stop the ADC clock */
215	clk_disable(w90p910_ts->clk);
216}
217
218static int __devinit w90x900ts_probe(struct platform_device *pdev)
219{
220	struct w90p910_ts *w90p910_ts;
221	struct input_dev *input_dev;
222	struct resource *res;
223	int err;
224
225	w90p910_ts = kzalloc(sizeof(struct w90p910_ts), GFP_KERNEL);
226	input_dev = input_allocate_device();
227	if (!w90p910_ts || !input_dev) {
228		err = -ENOMEM;
229		goto fail1;
230	}
231
232	w90p910_ts->input = input_dev;
233	w90p910_ts->state = TS_IDLE;
234	spin_lock_init(&w90p910_ts->lock);
235	setup_timer(&w90p910_ts->timer, w90p910_check_pen_up,
236		    (unsigned long)w90p910_ts);
237
238	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
239	if (!res) {
240		err = -ENXIO;
241		goto fail1;
242	}
243
244	if (!request_mem_region(res->start, resource_size(res),
245				pdev->name)) {
246		err = -EBUSY;
247		goto fail1;
248	}
249
250	w90p910_ts->ts_reg = ioremap(res->start, resource_size(res));
251	if (!w90p910_ts->ts_reg) {
252		err = -ENOMEM;
253		goto fail2;
254	}
255
256	w90p910_ts->clk = clk_get(&pdev->dev, NULL);
257	if (IS_ERR(w90p910_ts->clk)) {
258		err = PTR_ERR(w90p910_ts->clk);
259		goto fail3;
260	}
261
262	input_dev->name = "W90P910 TouchScreen";
263	input_dev->phys = "w90p910ts/event0";
264	input_dev->id.bustype = BUS_HOST;
265	input_dev->id.vendor  = 0x0005;
266	input_dev->id.product = 0x0001;
267	input_dev->id.version = 0x0100;
268	input_dev->dev.parent = &pdev->dev;
269	input_dev->open = w90p910_open;
270	input_dev->close = w90p910_close;
271
272	input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
273	input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH);
274
275	input_set_abs_params(input_dev, ABS_X, 0, 0x400, 0, 0);
276	input_set_abs_params(input_dev, ABS_Y, 0, 0x400, 0, 0);
277
278	input_set_drvdata(input_dev, w90p910_ts);
279
280	w90p910_ts->irq_num = platform_get_irq(pdev, 0);
281	if (request_irq(w90p910_ts->irq_num, w90p910_ts_interrupt,
282			0, "w90p910ts", w90p910_ts)) {
283		err = -EBUSY;
284		goto fail4;
285	}
286
287	err = input_register_device(w90p910_ts->input);
288	if (err)
289		goto fail5;
290
291	platform_set_drvdata(pdev, w90p910_ts);
292
293	return 0;
294
295fail5:	free_irq(w90p910_ts->irq_num, w90p910_ts);
296fail4:	clk_put(w90p910_ts->clk);
297fail3:	iounmap(w90p910_ts->ts_reg);
298fail2:	release_mem_region(res->start, resource_size(res));
299fail1:	input_free_device(input_dev);
300	kfree(w90p910_ts);
301	return err;
302}
303
304static int __devexit w90x900ts_remove(struct platform_device *pdev)
305{
306	struct w90p910_ts *w90p910_ts = platform_get_drvdata(pdev);
307	struct resource *res;
308
309	free_irq(w90p910_ts->irq_num, w90p910_ts);
310	del_timer_sync(&w90p910_ts->timer);
311	iounmap(w90p910_ts->ts_reg);
312
313	clk_put(w90p910_ts->clk);
314
315	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
316	release_mem_region(res->start, resource_size(res));
317
318	input_unregister_device(w90p910_ts->input);
319	kfree(w90p910_ts);
320
321	platform_set_drvdata(pdev, NULL);
322
323	return 0;
324}
325
326static struct platform_driver w90x900ts_driver = {
327	.probe		= w90x900ts_probe,
328	.remove		= __devexit_p(w90x900ts_remove),
329	.driver		= {
330		.name	= "nuc900-ts",
331		.owner	= THIS_MODULE,
332	},
333};
334module_platform_driver(w90x900ts_driver);
335
336MODULE_AUTHOR("Wan ZongShun <mcuos.com@gmail.com>");
337MODULE_DESCRIPTION("w90p910 touch screen driver!");
338MODULE_LICENSE("GPL");
339MODULE_ALIAS("platform:nuc900-ts");
340