2 * Copyright (c) 2008 Nuvoton technology corporation.
4 * Wan ZongShun <mcuos.com@gmail.com>
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.
12 #include <linux/delay.h>
13 #include <linux/module.h>
14 #include <linux/platform_device.h>
16 #include <linux/clk.h>
17 #include <linux/input.h>
18 #include <linux/interrupt.h>
19 #include <linux/slab.h>
21 /* ADC controller bit defines */
22 #define ADC_DELAY 0xf00
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 */
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 */
50 struct input_dev
*input
;
51 struct timer_list timer
;
59 static void w90p910_report_event(struct w90p910_ts
*w90p910_ts
, bool down
)
61 struct input_dev
*dev
= w90p910_ts
->input
;
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));
70 input_report_key(dev
, BTN_TOUCH
, down
);
74 static void w90p910_prepare_x_reading(struct w90p910_ts
*w90p910_ts
)
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
);
84 w90p910_ts
->state
= TS_WAIT_X_COORD
;
87 static void w90p910_prepare_y_reading(struct w90p910_ts
*w90p910_ts
)
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
);
97 w90p910_ts
->state
= TS_WAIT_Y_COORD
;
100 static void w90p910_prepare_next_packet(struct w90p910_ts
*w90p910_ts
)
102 unsigned long ctlreg
;
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
);
109 w90p910_ts
->state
= TS_WAIT_NEW_PACKET
;
112 static irqreturn_t
w90p910_ts_interrupt(int irq
, void *dev_id
)
114 struct w90p910_ts
*w90p910_ts
= dev_id
;
117 spin_lock_irqsave(&w90p910_ts
->lock
, flags
);
119 switch (w90p910_ts
->state
) {
120 case TS_WAIT_NEW_PACKET
:
122 * The controller only generates interrupts when pen
125 del_timer(&w90p910_ts
->timer
);
126 w90p910_prepare_x_reading(w90p910_ts
);
130 case TS_WAIT_X_COORD
:
131 w90p910_prepare_y_reading(w90p910_ts
);
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));
144 spin_unlock_irqrestore(&w90p910_ts
->lock
, flags
);
149 static void w90p910_check_pen_up(unsigned long data
)
151 struct w90p910_ts
*w90p910_ts
= (struct w90p910_ts
*) data
;
154 spin_lock_irqsave(&w90p910_ts
->lock
, flags
);
156 if (w90p910_ts
->state
== TS_WAIT_NEW_PACKET
&&
157 !(__raw_readl(w90p910_ts
->ts_reg
+ 0x04) & ADC_DOWN
)) {
159 w90p910_report_event(w90p910_ts
, false);
162 spin_unlock_irqrestore(&w90p910_ts
->lock
, flags
);
165 static int w90p910_open(struct input_dev
*dev
)
167 struct w90p910_ts
*w90p910_ts
= input_get_drvdata(dev
);
170 /* enable the ADC clock */
171 clk_enable(w90p910_ts
->clk
);
173 __raw_writel(ADC_RST1
, w90p910_ts
->ts_reg
);
175 __raw_writel(ADC_RST0
, w90p910_ts
->ts_reg
);
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);
183 w90p910_ts
->state
= TS_WAIT_NEW_PACKET
;
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
);
194 static void w90p910_close(struct input_dev
*dev
)
196 struct w90p910_ts
*w90p910_ts
= input_get_drvdata(dev
);
199 /* disable trigger mode */
201 spin_lock_irq(&w90p910_ts
->lock
);
203 w90p910_ts
->state
= TS_IDLE
;
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
);
209 spin_unlock_irq(&w90p910_ts
->lock
);
211 /* Now that interrupts are shut off we can safely delete timer */
212 del_timer_sync(&w90p910_ts
->timer
);
214 /* stop the ADC clock */
215 clk_disable(w90p910_ts
->clk
);
218 static int w90x900ts_probe(struct platform_device
*pdev
)
220 struct w90p910_ts
*w90p910_ts
;
221 struct input_dev
*input_dev
;
222 struct resource
*res
;
225 w90p910_ts
= kzalloc(sizeof(struct w90p910_ts
), GFP_KERNEL
);
226 input_dev
= input_allocate_device();
227 if (!w90p910_ts
|| !input_dev
) {
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
);
238 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
244 if (!request_mem_region(res
->start
, resource_size(res
),
250 w90p910_ts
->ts_reg
= ioremap(res
->start
, resource_size(res
));
251 if (!w90p910_ts
->ts_reg
) {
256 w90p910_ts
->clk
= clk_get(&pdev
->dev
, NULL
);
257 if (IS_ERR(w90p910_ts
->clk
)) {
258 err
= PTR_ERR(w90p910_ts
->clk
);
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
;
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
);
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);
278 input_set_drvdata(input_dev
, w90p910_ts
);
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
)) {
287 err
= input_register_device(w90p910_ts
->input
);
291 platform_set_drvdata(pdev
, w90p910_ts
);
295 fail5
: free_irq(w90p910_ts
->irq_num
, w90p910_ts
);
296 fail4
: clk_put(w90p910_ts
->clk
);
297 fail3
: iounmap(w90p910_ts
->ts_reg
);
298 fail2
: release_mem_region(res
->start
, resource_size(res
));
299 fail1
: input_free_device(input_dev
);
304 static int w90x900ts_remove(struct platform_device
*pdev
)
306 struct w90p910_ts
*w90p910_ts
= platform_get_drvdata(pdev
);
307 struct resource
*res
;
309 free_irq(w90p910_ts
->irq_num
, w90p910_ts
);
310 del_timer_sync(&w90p910_ts
->timer
);
311 iounmap(w90p910_ts
->ts_reg
);
313 clk_put(w90p910_ts
->clk
);
315 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
316 release_mem_region(res
->start
, resource_size(res
));
318 input_unregister_device(w90p910_ts
->input
);
324 static struct platform_driver w90x900ts_driver
= {
325 .probe
= w90x900ts_probe
,
326 .remove
= w90x900ts_remove
,
331 module_platform_driver(w90x900ts_driver
);
333 MODULE_AUTHOR("Wan ZongShun <mcuos.com@gmail.com>");
334 MODULE_DESCRIPTION("w90p910 touch screen driver!");
335 MODULE_LICENSE("GPL");
336 MODULE_ALIAS("platform:nuc900-ts");