Commit | Line | Data |
---|---|---|
1b8be32e RP |
1 | /* |
2 | * TI Touch Screen driver | |
3 | * | |
4 | * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com/ | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or | |
7 | * modify it under the terms of the GNU General Public License as | |
8 | * published by the Free Software Foundation version 2. | |
9 | * | |
10 | * This program is distributed "as is" WITHOUT ANY WARRANTY of any | |
11 | * kind, whether express or implied; without even the implied warranty | |
12 | * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
13 | * GNU General Public License for more details. | |
14 | */ | |
15 | ||
16 | ||
1b8be32e RP |
17 | #include <linux/kernel.h> |
18 | #include <linux/err.h> | |
19 | #include <linux/module.h> | |
20 | #include <linux/input.h> | |
21 | #include <linux/slab.h> | |
22 | #include <linux/interrupt.h> | |
23 | #include <linux/clk.h> | |
24 | #include <linux/platform_device.h> | |
25 | #include <linux/io.h> | |
1b8be32e | 26 | #include <linux/delay.h> |
0396310b PR |
27 | #include <linux/of.h> |
28 | #include <linux/of_device.h> | |
1b8be32e | 29 | |
2b99bafa | 30 | #include <linux/mfd/ti_am335x_tscadc.h> |
33f5cc60 PR |
31 | |
32 | #define ADCFSM_STEPID 0x10 | |
1b8be32e | 33 | #define SEQ_SETTLE 275 |
1b8be32e | 34 | #define MAX_12BIT ((1 << 12) - 1) |
1b8be32e | 35 | |
bb76dc09 PR |
36 | static const int config_pins[] = { |
37 | STEPCONFIG_XPP, | |
38 | STEPCONFIG_XNN, | |
39 | STEPCONFIG_YPP, | |
40 | STEPCONFIG_YNN, | |
41 | }; | |
42 | ||
55c04de5 | 43 | struct titsc { |
1b8be32e | 44 | struct input_dev *input; |
2b99bafa | 45 | struct ti_tscadc_dev *mfd_tscadc; |
1b8be32e RP |
46 | unsigned int irq; |
47 | unsigned int wires; | |
48 | unsigned int x_plate_resistance; | |
49 | bool pen_down; | |
0396310b | 50 | int coordinate_readouts; |
bb76dc09 PR |
51 | u32 config_inp[4]; |
52 | u32 bit_xp, bit_xn, bit_yp, bit_yn; | |
53 | u32 inp_xp, inp_xn, inp_yp, inp_yn; | |
baee5399 | 54 | u32 step_mask; |
1b8be32e RP |
55 | }; |
56 | ||
55c04de5 | 57 | static unsigned int titsc_readl(struct titsc *ts, unsigned int reg) |
1b8be32e | 58 | { |
2b99bafa | 59 | return readl(ts->mfd_tscadc->tscadc_base + reg); |
1b8be32e RP |
60 | } |
61 | ||
55c04de5 | 62 | static void titsc_writel(struct titsc *tsc, unsigned int reg, |
1b8be32e RP |
63 | unsigned int val) |
64 | { | |
2b99bafa | 65 | writel(val, tsc->mfd_tscadc->tscadc_base + reg); |
1b8be32e RP |
66 | } |
67 | ||
bb76dc09 PR |
68 | static int titsc_config_wires(struct titsc *ts_dev) |
69 | { | |
70 | u32 analog_line[4]; | |
71 | u32 wire_order[4]; | |
72 | int i, bit_cfg; | |
73 | ||
74 | for (i = 0; i < 4; i++) { | |
75 | /* | |
76 | * Get the order in which TSC wires are attached | |
77 | * w.r.t. each of the analog input lines on the EVM. | |
78 | */ | |
79 | analog_line[i] = (ts_dev->config_inp[i] & 0xF0) >> 4; | |
80 | wire_order[i] = ts_dev->config_inp[i] & 0x0F; | |
81 | if (WARN_ON(analog_line[i] > 7)) | |
82 | return -EINVAL; | |
83 | if (WARN_ON(wire_order[i] > ARRAY_SIZE(config_pins))) | |
84 | return -EINVAL; | |
85 | } | |
86 | ||
87 | for (i = 0; i < 4; i++) { | |
88 | int an_line; | |
89 | int wi_order; | |
90 | ||
91 | an_line = analog_line[i]; | |
92 | wi_order = wire_order[i]; | |
93 | bit_cfg = config_pins[wi_order]; | |
94 | if (bit_cfg == 0) | |
95 | return -EINVAL; | |
96 | switch (wi_order) { | |
97 | case 0: | |
98 | ts_dev->bit_xp = bit_cfg; | |
99 | ts_dev->inp_xp = an_line; | |
100 | break; | |
101 | ||
102 | case 1: | |
103 | ts_dev->bit_xn = bit_cfg; | |
104 | ts_dev->inp_xn = an_line; | |
105 | break; | |
106 | ||
107 | case 2: | |
108 | ts_dev->bit_yp = bit_cfg; | |
109 | ts_dev->inp_yp = an_line; | |
110 | break; | |
111 | case 3: | |
112 | ts_dev->bit_yn = bit_cfg; | |
113 | ts_dev->inp_yn = an_line; | |
114 | break; | |
115 | } | |
116 | } | |
117 | return 0; | |
118 | } | |
119 | ||
55c04de5 | 120 | static void titsc_step_config(struct titsc *ts_dev) |
1b8be32e RP |
121 | { |
122 | unsigned int config; | |
8c896308 SAS |
123 | int i; |
124 | int end_step; | |
125 | u32 stepenable; | |
1b8be32e RP |
126 | |
127 | config = STEPCONFIG_MODE_HWSYNC | | |
bb76dc09 | 128 | STEPCONFIG_AVG_16 | ts_dev->bit_xp; |
1b8be32e RP |
129 | switch (ts_dev->wires) { |
130 | case 4: | |
bb76dc09 | 131 | config |= STEPCONFIG_INP(ts_dev->inp_yp) | ts_dev->bit_xn; |
1b8be32e RP |
132 | break; |
133 | case 5: | |
bb76dc09 PR |
134 | config |= ts_dev->bit_yn | |
135 | STEPCONFIG_INP_AN4 | ts_dev->bit_xn | | |
136 | ts_dev->bit_yp; | |
1b8be32e RP |
137 | break; |
138 | case 8: | |
bb76dc09 | 139 | config |= STEPCONFIG_INP(ts_dev->inp_yp) | ts_dev->bit_xn; |
1b8be32e RP |
140 | break; |
141 | } | |
142 | ||
8c896308 SAS |
143 | /* 1 … coordinate_readouts is for X */ |
144 | end_step = ts_dev->coordinate_readouts; | |
145 | for (i = 0; i < end_step; i++) { | |
55c04de5 PR |
146 | titsc_writel(ts_dev, REG_STEPCONFIG(i), config); |
147 | titsc_writel(ts_dev, REG_STEPDELAY(i), STEPCONFIG_OPENDLY); | |
1b8be32e RP |
148 | } |
149 | ||
150 | config = 0; | |
151 | config = STEPCONFIG_MODE_HWSYNC | | |
bb76dc09 | 152 | STEPCONFIG_AVG_16 | ts_dev->bit_yn | |
8c896308 | 153 | STEPCONFIG_INM_ADCREFM; |
1b8be32e RP |
154 | switch (ts_dev->wires) { |
155 | case 4: | |
bb76dc09 | 156 | config |= ts_dev->bit_yp | STEPCONFIG_INP(ts_dev->inp_xp); |
1b8be32e RP |
157 | break; |
158 | case 5: | |
bb76dc09 PR |
159 | config |= ts_dev->bit_xp | STEPCONFIG_INP_AN4 | |
160 | ts_dev->bit_xn | ts_dev->bit_yp; | |
1b8be32e RP |
161 | break; |
162 | case 8: | |
bb76dc09 | 163 | config |= ts_dev->bit_yp | STEPCONFIG_INP(ts_dev->inp_xp); |
1b8be32e RP |
164 | break; |
165 | } | |
166 | ||
8c896308 SAS |
167 | /* coordinate_readouts … coordinate_readouts * 2 is for Y */ |
168 | end_step = ts_dev->coordinate_readouts * 2; | |
169 | for (i = ts_dev->coordinate_readouts; i < end_step; i++) { | |
55c04de5 PR |
170 | titsc_writel(ts_dev, REG_STEPCONFIG(i), config); |
171 | titsc_writel(ts_dev, REG_STEPDELAY(i), STEPCONFIG_OPENDLY); | |
1b8be32e RP |
172 | } |
173 | ||
1b8be32e | 174 | /* Charge step configuration */ |
bb76dc09 | 175 | config = ts_dev->bit_xp | ts_dev->bit_yn | |
33f5cc60 | 176 | STEPCHARGE_RFP_XPUL | STEPCHARGE_RFM_XNUR | |
bb76dc09 | 177 | STEPCHARGE_INM_AN1 | STEPCHARGE_INP(ts_dev->inp_yp); |
1b8be32e | 178 | |
55c04de5 PR |
179 | titsc_writel(ts_dev, REG_CHARGECONFIG, config); |
180 | titsc_writel(ts_dev, REG_CHARGEDELAY, CHARGEDLY_OPENDLY); | |
1b8be32e | 181 | |
8c896308 | 182 | /* coordinate_readouts * 2 … coordinate_readouts * 2 + 2 is for Z */ |
1b8be32e | 183 | config = STEPCONFIG_MODE_HWSYNC | |
bb76dc09 PR |
184 | STEPCONFIG_AVG_16 | ts_dev->bit_yp | |
185 | ts_dev->bit_xn | STEPCONFIG_INM_ADCREFM | | |
186 | STEPCONFIG_INP(ts_dev->inp_xp); | |
8c896308 SAS |
187 | titsc_writel(ts_dev, REG_STEPCONFIG(end_step), config); |
188 | titsc_writel(ts_dev, REG_STEPDELAY(end_step), | |
d1fb5743 | 189 | STEPCONFIG_OPENDLY); |
1b8be32e | 190 | |
8c896308 SAS |
191 | end_step++; |
192 | config |= STEPCONFIG_INP(ts_dev->inp_yn); | |
193 | titsc_writel(ts_dev, REG_STEPCONFIG(end_step), config); | |
194 | titsc_writel(ts_dev, REG_STEPDELAY(end_step), | |
d1fb5743 | 195 | STEPCONFIG_OPENDLY); |
1b8be32e | 196 | |
abeccee4 | 197 | /* The steps1 … end and bit 0 for TS_Charge */ |
8c896308 | 198 | stepenable = (1 << (end_step + 2)) - 1; |
baee5399 | 199 | ts_dev->step_mask = stepenable; |
7e170c6e | 200 | am335x_tsc_se_set_cache(ts_dev->mfd_tscadc, ts_dev->step_mask); |
1b8be32e RP |
201 | } |
202 | ||
55c04de5 | 203 | static void titsc_read_coordinates(struct titsc *ts_dev, |
8c896308 | 204 | u32 *x, u32 *y, u32 *z1, u32 *z2) |
1b8be32e | 205 | { |
55c04de5 | 206 | unsigned int fifocount = titsc_readl(ts_dev, REG_FIFO0CNT); |
1b8be32e RP |
207 | unsigned int prev_val_x = ~0, prev_val_y = ~0; |
208 | unsigned int prev_diff_x = ~0, prev_diff_y = ~0; | |
209 | unsigned int read, diff; | |
2b99bafa | 210 | unsigned int i, channel; |
8c896308 | 211 | unsigned int creads = ts_dev->coordinate_readouts; |
1b8be32e | 212 | |
8c896308 SAS |
213 | *z1 = *z2 = 0; |
214 | if (fifocount % (creads * 2 + 2)) | |
215 | fifocount -= fifocount % (creads * 2 + 2); | |
1b8be32e RP |
216 | /* |
217 | * Delta filter is used to remove large variations in sampled | |
218 | * values from ADC. The filter tries to predict where the next | |
219 | * coordinate could be. This is done by taking a previous | |
220 | * coordinate and subtracting it form current one. Further the | |
221 | * algorithm compares the difference with that of a present value, | |
222 | * if true the value is reported to the sub system. | |
223 | */ | |
8c896308 | 224 | for (i = 0; i < fifocount; i++) { |
2b99bafa | 225 | read = titsc_readl(ts_dev, REG_FIFO0); |
8c896308 SAS |
226 | |
227 | channel = (read & 0xf0000) >> 16; | |
228 | read &= 0xfff; | |
229 | if (channel < creads) { | |
2b99bafa PR |
230 | diff = abs(read - prev_val_x); |
231 | if (diff < prev_diff_x) { | |
232 | prev_diff_x = diff; | |
233 | *x = read; | |
234 | } | |
235 | prev_val_x = read; | |
1b8be32e | 236 | |
8c896308 | 237 | } else if (channel < creads * 2) { |
2b99bafa PR |
238 | diff = abs(read - prev_val_y); |
239 | if (diff < prev_diff_y) { | |
240 | prev_diff_y = diff; | |
241 | *y = read; | |
242 | } | |
243 | prev_val_y = read; | |
8c896308 SAS |
244 | |
245 | } else if (channel < creads * 2 + 1) { | |
246 | *z1 = read; | |
247 | ||
248 | } else if (channel < creads * 2 + 2) { | |
249 | *z2 = read; | |
1b8be32e | 250 | } |
1b8be32e RP |
251 | } |
252 | } | |
253 | ||
55c04de5 | 254 | static irqreturn_t titsc_irq(int irq, void *dev) |
1b8be32e | 255 | { |
55c04de5 | 256 | struct titsc *ts_dev = dev; |
1b8be32e RP |
257 | struct input_dev *input_dev = ts_dev->input; |
258 | unsigned int status, irqclr = 0; | |
259 | unsigned int x = 0, y = 0; | |
260 | unsigned int z1, z2, z; | |
261 | unsigned int fsm; | |
262 | ||
55c04de5 | 263 | status = titsc_readl(ts_dev, REG_IRQSTATUS); |
baee5399 ZL |
264 | /* |
265 | * ADC and touchscreen share the IRQ line. | |
266 | * FIFO1 interrupts are used by ADC. Handle FIFO0 IRQs here only | |
267 | */ | |
30af55f9 | 268 | if (status & IRQENB_FIFO0THRES) { |
1b8be32e | 269 | |
8c896308 | 270 | titsc_read_coordinates(ts_dev, &x, &y, &z1, &z2); |
2b99bafa | 271 | |
1b8be32e RP |
272 | if (ts_dev->pen_down && z1 != 0 && z2 != 0) { |
273 | /* | |
274 | * Calculate pressure using formula | |
275 | * Resistance(touch) = x plate resistance * | |
276 | * x postion/4096 * ((z2 / z1) - 1) | |
277 | */ | |
8c896308 | 278 | z = z1 - z2; |
1b8be32e RP |
279 | z *= x; |
280 | z *= ts_dev->x_plate_resistance; | |
8c896308 | 281 | z /= z2; |
1b8be32e RP |
282 | z = (z + 2047) >> 12; |
283 | ||
284 | if (z <= MAX_12BIT) { | |
285 | input_report_abs(input_dev, ABS_X, x); | |
286 | input_report_abs(input_dev, ABS_Y, y); | |
287 | input_report_abs(input_dev, ABS_PRESSURE, z); | |
288 | input_report_key(input_dev, BTN_TOUCH, 1); | |
289 | input_sync(input_dev); | |
290 | } | |
291 | } | |
30af55f9 | 292 | irqclr |= IRQENB_FIFO0THRES; |
1b8be32e RP |
293 | } |
294 | ||
295 | /* | |
296 | * Time for sequencer to settle, to read | |
297 | * correct state of the sequencer. | |
298 | */ | |
299 | udelay(SEQ_SETTLE); | |
300 | ||
55c04de5 | 301 | status = titsc_readl(ts_dev, REG_RAWIRQSTATUS); |
1b8be32e RP |
302 | if (status & IRQENB_PENUP) { |
303 | /* Pen up event */ | |
55c04de5 | 304 | fsm = titsc_readl(ts_dev, REG_ADCFSM); |
1b8be32e RP |
305 | if (fsm == ADCFSM_STEPID) { |
306 | ts_dev->pen_down = false; | |
307 | input_report_key(input_dev, BTN_TOUCH, 0); | |
308 | input_report_abs(input_dev, ABS_PRESSURE, 0); | |
309 | input_sync(input_dev); | |
310 | } else { | |
311 | ts_dev->pen_down = true; | |
312 | } | |
313 | irqclr |= IRQENB_PENUP; | |
314 | } | |
315 | ||
00789e5d SAS |
316 | if (status & IRQENB_HW_PEN) { |
317 | ||
318 | titsc_writel(ts_dev, REG_IRQWAKEUP, 0x00); | |
319 | titsc_writel(ts_dev, REG_IRQCLR, IRQENB_HW_PEN); | |
320 | } | |
1b8be32e | 321 | |
9a28b883 SAS |
322 | if (irqclr) { |
323 | titsc_writel(ts_dev, REG_IRQSTATUS, irqclr); | |
7e170c6e | 324 | am335x_tsc_se_set_cache(ts_dev->mfd_tscadc, ts_dev->step_mask); |
9a28b883 SAS |
325 | return IRQ_HANDLED; |
326 | } | |
327 | return IRQ_NONE; | |
1b8be32e RP |
328 | } |
329 | ||
0396310b PR |
330 | static int titsc_parse_dt(struct platform_device *pdev, |
331 | struct titsc *ts_dev) | |
332 | { | |
333 | struct device_node *node = pdev->dev.of_node; | |
334 | int err; | |
335 | ||
336 | if (!node) | |
337 | return -EINVAL; | |
338 | ||
339 | err = of_property_read_u32(node, "ti,wires", &ts_dev->wires); | |
340 | if (err < 0) | |
341 | return err; | |
342 | switch (ts_dev->wires) { | |
343 | case 4: | |
344 | case 5: | |
345 | case 8: | |
346 | break; | |
347 | default: | |
348 | return -EINVAL; | |
349 | } | |
350 | ||
351 | err = of_property_read_u32(node, "ti,x-plate-resistance", | |
352 | &ts_dev->x_plate_resistance); | |
353 | if (err < 0) | |
354 | return err; | |
355 | ||
c9aeb249 FB |
356 | /* |
357 | * Try with the new binding first. If it fails, try again with | |
358 | * bogus, miss-spelled version. | |
359 | */ | |
360 | err = of_property_read_u32(node, "ti,coordinate-readouts", | |
0396310b | 361 | &ts_dev->coordinate_readouts); |
31972f6e FB |
362 | if (err < 0) { |
363 | dev_warn(&pdev->dev, "please use 'ti,coordinate-readouts' instead\n"); | |
c9aeb249 FB |
364 | err = of_property_read_u32(node, "ti,coordiante-readouts", |
365 | &ts_dev->coordinate_readouts); | |
31972f6e FB |
366 | } |
367 | ||
0396310b PR |
368 | if (err < 0) |
369 | return err; | |
370 | ||
371 | return of_property_read_u32_array(node, "ti,wire-config", | |
372 | ts_dev->config_inp, ARRAY_SIZE(ts_dev->config_inp)); | |
1b8be32e RP |
373 | } |
374 | ||
375 | /* | |
376 | * The functions for inserting/removing driver as a module. | |
377 | */ | |
378 | ||
31564cbd | 379 | static int titsc_probe(struct platform_device *pdev) |
1b8be32e | 380 | { |
55c04de5 | 381 | struct titsc *ts_dev; |
1b8be32e | 382 | struct input_dev *input_dev; |
a9bce1b0 | 383 | struct ti_tscadc_dev *tscadc_dev = ti_tscadc_dev_get(pdev); |
1b8be32e | 384 | int err; |
1b8be32e | 385 | |
1b8be32e | 386 | /* Allocate memory for device */ |
55c04de5 | 387 | ts_dev = kzalloc(sizeof(struct titsc), GFP_KERNEL); |
1b8be32e RP |
388 | input_dev = input_allocate_device(); |
389 | if (!ts_dev || !input_dev) { | |
390 | dev_err(&pdev->dev, "failed to allocate memory.\n"); | |
391 | err = -ENOMEM; | |
392 | goto err_free_mem; | |
393 | } | |
394 | ||
2b99bafa PR |
395 | tscadc_dev->tsc = ts_dev; |
396 | ts_dev->mfd_tscadc = tscadc_dev; | |
1b8be32e | 397 | ts_dev->input = input_dev; |
2b99bafa | 398 | ts_dev->irq = tscadc_dev->irq; |
0396310b | 399 | |
b9194fdf | 400 | err = titsc_parse_dt(pdev, ts_dev); |
0396310b PR |
401 | if (err) { |
402 | dev_err(&pdev->dev, "Could not find valid DT data.\n"); | |
403 | goto err_free_mem; | |
404 | } | |
1b8be32e | 405 | |
55c04de5 | 406 | err = request_irq(ts_dev->irq, titsc_irq, |
baee5399 | 407 | IRQF_SHARED, pdev->dev.driver->name, ts_dev); |
1b8be32e RP |
408 | if (err) { |
409 | dev_err(&pdev->dev, "failed to allocate irq.\n"); | |
2b99bafa | 410 | goto err_free_mem; |
1b8be32e | 411 | } |
1b8be32e | 412 | |
55c04de5 | 413 | titsc_writel(ts_dev, REG_IRQENABLE, IRQENB_FIFO0THRES); |
bb76dc09 PR |
414 | err = titsc_config_wires(ts_dev); |
415 | if (err) { | |
416 | dev_err(&pdev->dev, "wrong i/p wire configuration\n"); | |
417 | goto err_free_irq; | |
418 | } | |
55c04de5 | 419 | titsc_step_config(ts_dev); |
8c896308 SAS |
420 | titsc_writel(ts_dev, REG_FIFO0THR, |
421 | ts_dev->coordinate_readouts * 2 + 2 - 1); | |
1b8be32e | 422 | |
2b99bafa | 423 | input_dev->name = "ti-tsc"; |
1b8be32e RP |
424 | input_dev->dev.parent = &pdev->dev; |
425 | ||
426 | input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS); | |
427 | input_dev->keybit[BIT_WORD(BTN_TOUCH)] = BIT_MASK(BTN_TOUCH); | |
428 | ||
429 | input_set_abs_params(input_dev, ABS_X, 0, MAX_12BIT, 0, 0); | |
430 | input_set_abs_params(input_dev, ABS_Y, 0, MAX_12BIT, 0, 0); | |
431 | input_set_abs_params(input_dev, ABS_PRESSURE, 0, MAX_12BIT, 0, 0); | |
432 | ||
433 | /* register to the input system */ | |
434 | err = input_register_device(input_dev); | |
435 | if (err) | |
2b99bafa | 436 | goto err_free_irq; |
1b8be32e RP |
437 | |
438 | platform_set_drvdata(pdev, ts_dev); | |
439 | return 0; | |
440 | ||
1b8be32e RP |
441 | err_free_irq: |
442 | free_irq(ts_dev->irq, ts_dev); | |
1b8be32e RP |
443 | err_free_mem: |
444 | input_free_device(input_dev); | |
445 | kfree(ts_dev); | |
446 | return err; | |
447 | } | |
448 | ||
31564cbd | 449 | static int titsc_remove(struct platform_device *pdev) |
1b8be32e | 450 | { |
a9bce1b0 SAS |
451 | struct titsc *ts_dev = platform_get_drvdata(pdev); |
452 | u32 steps; | |
1b8be32e RP |
453 | |
454 | free_irq(ts_dev->irq, ts_dev); | |
455 | ||
abeccee4 | 456 | /* total steps followed by the enable mask */ |
0396310b | 457 | steps = 2 * ts_dev->coordinate_readouts + 2; |
abeccee4 PR |
458 | steps = (1 << steps) - 1; |
459 | am335x_tsc_se_clr(ts_dev->mfd_tscadc, steps); | |
460 | ||
1b8be32e RP |
461 | input_unregister_device(ts_dev->input); |
462 | ||
2b99bafa PR |
463 | kfree(ts_dev); |
464 | return 0; | |
465 | } | |
1b8be32e | 466 | |
2b99bafa PR |
467 | #ifdef CONFIG_PM |
468 | static int titsc_suspend(struct device *dev) | |
469 | { | |
a9bce1b0 SAS |
470 | struct titsc *ts_dev = dev_get_drvdata(dev); |
471 | struct ti_tscadc_dev *tscadc_dev; | |
2b99bafa PR |
472 | unsigned int idle; |
473 | ||
a9bce1b0 | 474 | tscadc_dev = ti_tscadc_dev_get(to_platform_device(dev)); |
2b99bafa PR |
475 | if (device_may_wakeup(tscadc_dev->dev)) { |
476 | idle = titsc_readl(ts_dev, REG_IRQENABLE); | |
477 | titsc_writel(ts_dev, REG_IRQENABLE, | |
478 | (idle | IRQENB_HW_PEN)); | |
479 | titsc_writel(ts_dev, REG_IRQWAKEUP, IRQWKUP_ENB); | |
480 | } | |
481 | return 0; | |
482 | } | |
1b8be32e | 483 | |
2b99bafa PR |
484 | static int titsc_resume(struct device *dev) |
485 | { | |
a9bce1b0 SAS |
486 | struct titsc *ts_dev = dev_get_drvdata(dev); |
487 | struct ti_tscadc_dev *tscadc_dev; | |
1b8be32e | 488 | |
a9bce1b0 | 489 | tscadc_dev = ti_tscadc_dev_get(to_platform_device(dev)); |
2b99bafa PR |
490 | if (device_may_wakeup(tscadc_dev->dev)) { |
491 | titsc_writel(ts_dev, REG_IRQWAKEUP, | |
492 | 0x00); | |
493 | titsc_writel(ts_dev, REG_IRQCLR, IRQENB_HW_PEN); | |
494 | } | |
495 | titsc_step_config(ts_dev); | |
496 | titsc_writel(ts_dev, REG_FIFO0THR, | |
8c896308 | 497 | ts_dev->coordinate_readouts * 2 + 2 - 1); |
1b8be32e RP |
498 | return 0; |
499 | } | |
500 | ||
2b99bafa PR |
501 | static const struct dev_pm_ops titsc_pm_ops = { |
502 | .suspend = titsc_suspend, | |
503 | .resume = titsc_resume, | |
504 | }; | |
505 | #define TITSC_PM_OPS (&titsc_pm_ops) | |
506 | #else | |
507 | #define TITSC_PM_OPS NULL | |
508 | #endif | |
509 | ||
0396310b PR |
510 | static const struct of_device_id ti_tsc_dt_ids[] = { |
511 | { .compatible = "ti,am3359-tsc", }, | |
512 | { } | |
513 | }; | |
514 | MODULE_DEVICE_TABLE(of, ti_tsc_dt_ids); | |
515 | ||
1b8be32e | 516 | static struct platform_driver ti_tsc_driver = { |
55c04de5 | 517 | .probe = titsc_probe, |
31564cbd | 518 | .remove = titsc_remove, |
1b8be32e | 519 | .driver = { |
5f184e63 | 520 | .name = "TI-am335x-tsc", |
2b99bafa | 521 | .pm = TITSC_PM_OPS, |
8e6146bf | 522 | .of_match_table = ti_tsc_dt_ids, |
1b8be32e RP |
523 | }, |
524 | }; | |
525 | module_platform_driver(ti_tsc_driver); | |
526 | ||
527 | MODULE_DESCRIPTION("TI touchscreen controller driver"); | |
528 | MODULE_AUTHOR("Rachna Patil <rachna@ti.com>"); | |
529 | MODULE_LICENSE("GPL"); |