diff options
author | David Brownell <dbrownell@users.sourceforge.net> | 2007-01-18 00:45:48 -0500 |
---|---|---|
committer | Dmitry Torokhov <dtor@insightbb.com> | 2007-01-18 00:45:48 -0500 |
commit | 2c8dc071517ec2843869024dc82be2e246f41064 (patch) | |
tree | 793c90a34b40d9f2a60eb108387b1077ec9e8861 /drivers/input/touchscreen | |
parent | 15e3589e59c35ed33823dda3d38ad171222b83b4 (diff) |
Input: ads7846 - be more compatible with the hwmon framework
- Hook up to hwmon
* show sensor attributes only if hwmon is present
* ... and the board's reference voltage is known
* otherwise be just a touchscreen
- Report voltages per hwmon convention
* measure in millivolts
* voltages are named in[0-8]_input (ugh)
* for 7846 chips, properly range-adjust vBATT/in1_input
Battery measurements help during recharge monitoring. On OSK/Mistral,
the measured voltage agreed with a multimeter to several decimal places.
Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
Diffstat (limited to 'drivers/input/touchscreen')
-rw-r--r-- | drivers/input/touchscreen/Kconfig | 9 | ||||
-rw-r--r-- | drivers/input/touchscreen/ads7846.c | 306 |
2 files changed, 224 insertions, 91 deletions
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig index 29ca0ab0acb8..4bec8422821f 100644 --- a/drivers/input/touchscreen/Kconfig +++ b/drivers/input/touchscreen/Kconfig | |||
@@ -12,13 +12,18 @@ menuconfig INPUT_TOUCHSCREEN | |||
12 | if INPUT_TOUCHSCREEN | 12 | if INPUT_TOUCHSCREEN |
13 | 13 | ||
14 | config TOUCHSCREEN_ADS7846 | 14 | config TOUCHSCREEN_ADS7846 |
15 | tristate "ADS 7846 based touchscreens" | 15 | tristate "ADS 7846/7843 based touchscreens" |
16 | depends on SPI_MASTER | 16 | depends on SPI_MASTER |
17 | depends on HWMON = n || HWMON | ||
17 | help | 18 | help |
18 | Say Y here if you have a touchscreen interface using the | 19 | Say Y here if you have a touchscreen interface using the |
19 | ADS7846 controller, and your board-specific initialization | 20 | ADS7846 or ADS7843 controller, and your board-specific setup |
20 | code includes that in its table of SPI devices. | 21 | code includes that in its table of SPI devices. |
21 | 22 | ||
23 | If HWMON is selected, and the driver is told the reference voltage | ||
24 | on your board, you will also get hwmon interfaces for the voltage | ||
25 | (and on ads7846, temperature) sensors of this chip. | ||
26 | |||
22 | If unsure, say N (but it's safe to say "Y"). | 27 | If unsure, say N (but it's safe to say "Y"). |
23 | 28 | ||
24 | To compile this driver as a module, choose M here: the | 29 | To compile this driver as a module, choose M here: the |
diff --git a/drivers/input/touchscreen/ads7846.c b/drivers/input/touchscreen/ads7846.c index b18c63a3f2ab..cd251efda410 100644 --- a/drivers/input/touchscreen/ads7846.c +++ b/drivers/input/touchscreen/ads7846.c | |||
@@ -17,8 +17,9 @@ | |||
17 | * it under the terms of the GNU General Public License version 2 as | 17 | * it under the terms of the GNU General Public License version 2 as |
18 | * published by the Free Software Foundation. | 18 | * published by the Free Software Foundation. |
19 | */ | 19 | */ |
20 | #include <linux/device.h> | 20 | #include <linux/hwmon.h> |
21 | #include <linux/init.h> | 21 | #include <linux/init.h> |
22 | #include <linux/err.h> | ||
22 | #include <linux/delay.h> | 23 | #include <linux/delay.h> |
23 | #include <linux/input.h> | 24 | #include <linux/input.h> |
24 | #include <linux/interrupt.h> | 25 | #include <linux/interrupt.h> |
@@ -69,7 +70,7 @@ struct ts_event { | |||
69 | u16 x; | 70 | u16 x; |
70 | u16 y; | 71 | u16 y; |
71 | u16 z1, z2; | 72 | u16 z1, z2; |
72 | int ignore; | 73 | int ignore; |
73 | }; | 74 | }; |
74 | 75 | ||
75 | struct ads7846 { | 76 | struct ads7846 { |
@@ -77,7 +78,12 @@ struct ads7846 { | |||
77 | char phys[32]; | 78 | char phys[32]; |
78 | 79 | ||
79 | struct spi_device *spi; | 80 | struct spi_device *spi; |
81 | |||
82 | #if defined(CONFIG_HWMON) || defined(CONFIG_HWMON_MODULE) | ||
80 | struct attribute_group *attr_group; | 83 | struct attribute_group *attr_group; |
84 | struct class_device *hwmon; | ||
85 | #endif | ||
86 | |||
81 | u16 model; | 87 | u16 model; |
82 | u16 vref_delay_usecs; | 88 | u16 vref_delay_usecs; |
83 | u16 x_plate_ohms; | 89 | u16 x_plate_ohms; |
@@ -170,7 +176,12 @@ struct ads7846 { | |||
170 | 176 | ||
171 | /* | 177 | /* |
172 | * Non-touchscreen sensors only use single-ended conversions. | 178 | * Non-touchscreen sensors only use single-ended conversions. |
179 | * The range is GND..vREF. The ads7843 and ads7835 must use external vREF; | ||
180 | * ads7846 lets that pin be unconnected, to use internal vREF. | ||
173 | */ | 181 | */ |
182 | static unsigned vREF_mV; | ||
183 | module_param(vREF_mV, uint, 0); | ||
184 | MODULE_PARM_DESC(vREF_mV, "external vREF voltage, in milliVolts"); | ||
174 | 185 | ||
175 | struct ser_req { | 186 | struct ser_req { |
176 | u8 ref_on; | 187 | u8 ref_on; |
@@ -198,50 +209,55 @@ static int ads7846_read12_ser(struct device *dev, unsigned command) | |||
198 | struct ser_req *req = kzalloc(sizeof *req, GFP_KERNEL); | 209 | struct ser_req *req = kzalloc(sizeof *req, GFP_KERNEL); |
199 | int status; | 210 | int status; |
200 | int sample; | 211 | int sample; |
201 | int i; | 212 | int use_internal; |
202 | 213 | ||
203 | if (!req) | 214 | if (!req) |
204 | return -ENOMEM; | 215 | return -ENOMEM; |
205 | 216 | ||
206 | spi_message_init(&req->msg); | 217 | spi_message_init(&req->msg); |
207 | 218 | ||
208 | /* activate reference, so it has time to settle; */ | 219 | /* FIXME boards with ads7846 might use external vref instead ... */ |
209 | req->ref_on = REF_ON; | 220 | use_internal = (ts->model == 7846); |
210 | req->xfer[0].tx_buf = &req->ref_on; | 221 | |
211 | req->xfer[0].len = 1; | 222 | /* maybe turn on internal vREF, and let it settle */ |
212 | req->xfer[1].rx_buf = &req->scratch; | 223 | if (use_internal) { |
213 | req->xfer[1].len = 2; | 224 | req->ref_on = REF_ON; |
214 | 225 | req->xfer[0].tx_buf = &req->ref_on; | |
215 | /* | 226 | req->xfer[0].len = 1; |
216 | * for external VREF, 0 usec (and assume it's always on); | 227 | spi_message_add_tail(&req->xfer[0], &req->msg); |
217 | * for 1uF, use 800 usec; | 228 | |
218 | * no cap, 100 usec. | 229 | req->xfer[1].rx_buf = &req->scratch; |
219 | */ | 230 | req->xfer[1].len = 2; |
220 | req->xfer[1].delay_usecs = ts->vref_delay_usecs; | 231 | |
232 | /* for 1uF, settle for 800 usec; no cap, 100 usec. */ | ||
233 | req->xfer[1].delay_usecs = ts->vref_delay_usecs; | ||
234 | spi_message_add_tail(&req->xfer[1], &req->msg); | ||
235 | } | ||
221 | 236 | ||
222 | /* take sample */ | 237 | /* take sample */ |
223 | req->command = (u8) command; | 238 | req->command = (u8) command; |
224 | req->xfer[2].tx_buf = &req->command; | 239 | req->xfer[2].tx_buf = &req->command; |
225 | req->xfer[2].len = 1; | 240 | req->xfer[2].len = 1; |
241 | spi_message_add_tail(&req->xfer[2], &req->msg); | ||
242 | |||
226 | req->xfer[3].rx_buf = &req->sample; | 243 | req->xfer[3].rx_buf = &req->sample; |
227 | req->xfer[3].len = 2; | 244 | req->xfer[3].len = 2; |
245 | spi_message_add_tail(&req->xfer[3], &req->msg); | ||
228 | 246 | ||
229 | /* REVISIT: take a few more samples, and compare ... */ | 247 | /* REVISIT: take a few more samples, and compare ... */ |
230 | 248 | ||
231 | /* turn off reference */ | 249 | /* maybe off internal vREF */ |
232 | req->ref_off = REF_OFF; | 250 | if (use_internal) { |
233 | req->xfer[4].tx_buf = &req->ref_off; | 251 | req->ref_off = REF_OFF; |
234 | req->xfer[4].len = 1; | 252 | req->xfer[4].tx_buf = &req->ref_off; |
235 | req->xfer[5].rx_buf = &req->scratch; | 253 | req->xfer[4].len = 1; |
236 | req->xfer[5].len = 2; | 254 | spi_message_add_tail(&req->xfer[4], &req->msg); |
237 | 255 | ||
238 | CS_CHANGE(req->xfer[5]); | 256 | req->xfer[5].rx_buf = &req->scratch; |
239 | 257 | req->xfer[5].len = 2; | |
240 | /* group all the transfers together, so we can't interfere with | 258 | CS_CHANGE(req->xfer[5]); |
241 | * reading touchscreen state; disable penirq while sampling | 259 | spi_message_add_tail(&req->xfer[5], &req->msg); |
242 | */ | 260 | } |
243 | for (i = 0; i < 6; i++) | ||
244 | spi_message_add_tail(&req->xfer[i], &req->msg); | ||
245 | 261 | ||
246 | ts->irq_disabled = 1; | 262 | ts->irq_disabled = 1; |
247 | disable_irq(spi->irq); | 263 | disable_irq(spi->irq); |
@@ -261,25 +277,173 @@ static int ads7846_read12_ser(struct device *dev, unsigned command) | |||
261 | return status ? status : sample; | 277 | return status ? status : sample; |
262 | } | 278 | } |
263 | 279 | ||
264 | #define SHOW(name) static ssize_t \ | 280 | #if defined(CONFIG_HWMON) || defined(CONFIG_HWMON_MODULE) |
281 | |||
282 | #define SHOW(name, var, adjust) static ssize_t \ | ||
265 | name ## _show(struct device *dev, struct device_attribute *attr, char *buf) \ | 283 | name ## _show(struct device *dev, struct device_attribute *attr, char *buf) \ |
266 | { \ | 284 | { \ |
285 | struct ads7846 *ts = dev_get_drvdata(dev); \ | ||
267 | ssize_t v = ads7846_read12_ser(dev, \ | 286 | ssize_t v = ads7846_read12_ser(dev, \ |
268 | READ_12BIT_SER(name) | ADS_PD10_ALL_ON); \ | 287 | READ_12BIT_SER(var) | ADS_PD10_ALL_ON); \ |
269 | if (v < 0) \ | 288 | if (v < 0) \ |
270 | return v; \ | 289 | return v; \ |
271 | return sprintf(buf, "%u\n", (unsigned) v); \ | 290 | return sprintf(buf, "%u\n", adjust(ts, v)); \ |
272 | } \ | 291 | } \ |
273 | static DEVICE_ATTR(name, S_IRUGO, name ## _show, NULL); | 292 | static DEVICE_ATTR(name, S_IRUGO, name ## _show, NULL); |
274 | 293 | ||
275 | SHOW(temp0) | 294 | |
276 | SHOW(temp1) | 295 | /* Sysfs conventions report temperatures in millidegrees Celcius. |
277 | SHOW(vaux) | 296 | * ADS7846 could use the low-accuracy two-sample scheme, but can't do the high |
278 | SHOW(vbatt) | 297 | * accuracy scheme without calibration data. For now we won't try either; |
298 | * userspace sees raw sensor values, and must scale/calibrate appropriately. | ||
299 | */ | ||
300 | static inline unsigned null_adjust(struct ads7846 *ts, ssize_t v) | ||
301 | { | ||
302 | return v; | ||
303 | } | ||
304 | |||
305 | SHOW(temp0, temp0, null_adjust) /* temp1_input */ | ||
306 | SHOW(temp1, temp1, null_adjust) /* temp2_input */ | ||
307 | |||
308 | |||
309 | /* sysfs conventions report voltages in millivolts. We can convert voltages | ||
310 | * if we know vREF. userspace may need to scale vAUX to match the board's | ||
311 | * external resistors; we assume that vBATT only uses the internal ones. | ||
312 | */ | ||
313 | static inline unsigned vaux_adjust(struct ads7846 *ts, ssize_t v) | ||
314 | { | ||
315 | unsigned retval = v; | ||
316 | |||
317 | /* external resistors may scale vAUX into 0..vREF */ | ||
318 | retval *= vREF_mV; | ||
319 | retval = retval >> 12; | ||
320 | return retval; | ||
321 | } | ||
322 | |||
323 | static inline unsigned vbatt_adjust(struct ads7846 *ts, ssize_t v) | ||
324 | { | ||
325 | unsigned retval = vaux_adjust(ts, v); | ||
326 | |||
327 | /* ads7846 has a resistor ladder to scale this signal down */ | ||
328 | if (ts->model == 7846) | ||
329 | retval *= 4; | ||
330 | return retval; | ||
331 | } | ||
332 | |||
333 | SHOW(in0_input, vaux, vaux_adjust) | ||
334 | SHOW(in1_input, vbatt, vbatt_adjust) | ||
335 | |||
336 | |||
337 | static struct attribute *ads7846_attributes[] = { | ||
338 | &dev_attr_temp0.attr, | ||
339 | &dev_attr_temp1.attr, | ||
340 | &dev_attr_in0_input.attr, | ||
341 | &dev_attr_in1_input.attr, | ||
342 | NULL, | ||
343 | }; | ||
344 | |||
345 | static struct attribute_group ads7846_attr_group = { | ||
346 | .attrs = ads7846_attributes, | ||
347 | }; | ||
348 | |||
349 | static struct attribute *ads7843_attributes[] = { | ||
350 | &dev_attr_in0_input.attr, | ||
351 | &dev_attr_in1_input.attr, | ||
352 | NULL, | ||
353 | }; | ||
354 | |||
355 | static struct attribute_group ads7843_attr_group = { | ||
356 | .attrs = ads7843_attributes, | ||
357 | }; | ||
358 | |||
359 | static struct attribute *ads7845_attributes[] = { | ||
360 | &dev_attr_in0_input.attr, | ||
361 | NULL, | ||
362 | }; | ||
363 | |||
364 | static struct attribute_group ads7845_attr_group = { | ||
365 | .attrs = ads7845_attributes, | ||
366 | }; | ||
367 | |||
368 | static int ads784x_hwmon_register(struct spi_device *spi, struct ads7846 *ts) | ||
369 | { | ||
370 | struct class_device *hwmon; | ||
371 | int err; | ||
372 | |||
373 | /* hwmon sensors need a reference voltage */ | ||
374 | switch (ts->model) { | ||
375 | case 7846: | ||
376 | if (!vREF_mV) { | ||
377 | dev_dbg(&spi->dev, "assuming 2.5V internal vREF\n"); | ||
378 | vREF_mV = 2500; | ||
379 | } | ||
380 | break; | ||
381 | case 7845: | ||
382 | case 7843: | ||
383 | if (!vREF_mV) { | ||
384 | dev_warn(&spi->dev, | ||
385 | "external vREF for ADS%d not specified\n", | ||
386 | ts->model); | ||
387 | return 0; | ||
388 | } | ||
389 | break; | ||
390 | } | ||
391 | |||
392 | /* different chips have different sensor groups */ | ||
393 | switch (ts->model) { | ||
394 | case 7846: | ||
395 | ts->attr_group = &ads7846_attr_group; | ||
396 | break; | ||
397 | case 7845: | ||
398 | ts->attr_group = &ads7845_attr_group; | ||
399 | break; | ||
400 | case 7843: | ||
401 | ts->attr_group = &ads7843_attr_group; | ||
402 | break; | ||
403 | default: | ||
404 | dev_dbg(&spi->dev, "ADS%d not recognized\n", ts->model); | ||
405 | return 0; | ||
406 | } | ||
407 | |||
408 | err = sysfs_create_group(&spi->dev.kobj, ts->attr_group); | ||
409 | if (err) | ||
410 | return err; | ||
411 | |||
412 | hwmon = hwmon_device_register(&spi->dev); | ||
413 | if (IS_ERR(hwmon)) { | ||
414 | sysfs_remove_group(&spi->dev.kobj, ts->attr_group); | ||
415 | return PTR_ERR(hwmon); | ||
416 | } | ||
417 | |||
418 | ts->hwmon = hwmon; | ||
419 | return 0; | ||
420 | } | ||
421 | |||
422 | static void ads784x_hwmon_unregister(struct spi_device *spi, | ||
423 | struct ads7846 *ts) | ||
424 | { | ||
425 | if (ts->hwmon) { | ||
426 | sysfs_remove_group(&spi->dev.kobj, ts->attr_group); | ||
427 | hwmon_device_unregister(ts->hwmon); | ||
428 | } | ||
429 | } | ||
430 | |||
431 | #else | ||
432 | static inline int ads784x_hwmon_register(struct spi_device *spi, | ||
433 | struct ads7846 *ts) | ||
434 | { | ||
435 | return 0; | ||
436 | } | ||
437 | |||
438 | static inline void ads784x_hwmon_unregister(struct spi_device *spi, | ||
439 | struct ads7846 *ts) | ||
440 | { | ||
441 | } | ||
442 | #endif | ||
279 | 443 | ||
280 | static int is_pen_down(struct device *dev) | 444 | static int is_pen_down(struct device *dev) |
281 | { | 445 | { |
282 | struct ads7846 *ts = dev_get_drvdata(dev); | 446 | struct ads7846 *ts = dev_get_drvdata(dev); |
283 | 447 | ||
284 | return ts->pendown; | 448 | return ts->pendown; |
285 | } | 449 | } |
@@ -323,46 +487,14 @@ static ssize_t ads7846_disable_store(struct device *dev, | |||
323 | 487 | ||
324 | static DEVICE_ATTR(disable, 0664, ads7846_disable_show, ads7846_disable_store); | 488 | static DEVICE_ATTR(disable, 0664, ads7846_disable_show, ads7846_disable_store); |
325 | 489 | ||
326 | static struct attribute *ads7846_attributes[] = { | 490 | static struct attribute *ads784x_attributes[] = { |
327 | &dev_attr_temp0.attr, | ||
328 | &dev_attr_temp1.attr, | ||
329 | &dev_attr_vbatt.attr, | ||
330 | &dev_attr_vaux.attr, | ||
331 | &dev_attr_pen_down.attr, | ||
332 | &dev_attr_disable.attr, | ||
333 | NULL, | ||
334 | }; | ||
335 | |||
336 | static struct attribute_group ads7846_attr_group = { | ||
337 | .attrs = ads7846_attributes, | ||
338 | }; | ||
339 | |||
340 | /* | ||
341 | * ads7843/7845 don't have temperature sensors, and | ||
342 | * use the other sensors a bit differently too | ||
343 | */ | ||
344 | |||
345 | static struct attribute *ads7843_attributes[] = { | ||
346 | &dev_attr_vbatt.attr, | ||
347 | &dev_attr_vaux.attr, | ||
348 | &dev_attr_pen_down.attr, | 491 | &dev_attr_pen_down.attr, |
349 | &dev_attr_disable.attr, | 492 | &dev_attr_disable.attr, |
350 | NULL, | 493 | NULL, |
351 | }; | 494 | }; |
352 | 495 | ||
353 | static struct attribute_group ads7843_attr_group = { | 496 | static struct attribute_group ads784x_attr_group = { |
354 | .attrs = ads7843_attributes, | 497 | .attrs = ads784x_attributes, |
355 | }; | ||
356 | |||
357 | static struct attribute *ads7845_attributes[] = { | ||
358 | &dev_attr_vaux.attr, | ||
359 | &dev_attr_pen_down.attr, | ||
360 | &dev_attr_disable.attr, | ||
361 | NULL, | ||
362 | }; | ||
363 | |||
364 | static struct attribute_group ads7845_attr_group = { | ||
365 | .attrs = ads7845_attributes, | ||
366 | }; | 498 | }; |
367 | 499 | ||
368 | /*--------------------------------------------------------------------------*/ | 500 | /*--------------------------------------------------------------------------*/ |
@@ -886,28 +1018,21 @@ static int __devinit ads7846_probe(struct spi_device *spi) | |||
886 | goto err_cleanup_filter; | 1018 | goto err_cleanup_filter; |
887 | } | 1019 | } |
888 | 1020 | ||
1021 | err = ads784x_hwmon_register(spi, ts); | ||
1022 | if (err) | ||
1023 | goto err_free_irq; | ||
1024 | |||
889 | dev_info(&spi->dev, "touchscreen, irq %d\n", spi->irq); | 1025 | dev_info(&spi->dev, "touchscreen, irq %d\n", spi->irq); |
890 | 1026 | ||
891 | /* take a first sample, leaving nPENIRQ active; avoid | 1027 | /* take a first sample, leaving nPENIRQ active and vREF off; avoid |
892 | * the touchscreen, in case it's not connected. | 1028 | * the touchscreen, in case it's not connected. |
893 | */ | 1029 | */ |
894 | (void) ads7846_read12_ser(&spi->dev, | 1030 | (void) ads7846_read12_ser(&spi->dev, |
895 | READ_12BIT_SER(vaux) | ADS_PD10_ALL_ON); | 1031 | READ_12BIT_SER(vaux) | ADS_PD10_ALL_ON); |
896 | 1032 | ||
897 | switch (ts->model) { | 1033 | err = sysfs_create_group(&spi->dev.kobj, &ads784x_attr_group); |
898 | case 7846: | ||
899 | ts->attr_group = &ads7846_attr_group; | ||
900 | break; | ||
901 | case 7845: | ||
902 | ts->attr_group = &ads7845_attr_group; | ||
903 | break; | ||
904 | default: | ||
905 | ts->attr_group = &ads7843_attr_group; | ||
906 | break; | ||
907 | } | ||
908 | err = sysfs_create_group(&spi->dev.kobj, ts->attr_group); | ||
909 | if (err) | 1034 | if (err) |
910 | goto err_free_irq; | 1035 | goto err_remove_hwmon; |
911 | 1036 | ||
912 | err = input_register_device(input_dev); | 1037 | err = input_register_device(input_dev); |
913 | if (err) | 1038 | if (err) |
@@ -916,7 +1041,9 @@ static int __devinit ads7846_probe(struct spi_device *spi) | |||
916 | return 0; | 1041 | return 0; |
917 | 1042 | ||
918 | err_remove_attr_group: | 1043 | err_remove_attr_group: |
919 | sysfs_remove_group(&spi->dev.kobj, ts->attr_group); | 1044 | sysfs_remove_group(&spi->dev.kobj, &ads784x_attr_group); |
1045 | err_remove_hwmon: | ||
1046 | ads784x_hwmon_unregister(spi, ts); | ||
920 | err_free_irq: | 1047 | err_free_irq: |
921 | free_irq(spi->irq, ts); | 1048 | free_irq(spi->irq, ts); |
922 | err_cleanup_filter: | 1049 | err_cleanup_filter: |
@@ -932,11 +1059,12 @@ static int __devexit ads7846_remove(struct spi_device *spi) | |||
932 | { | 1059 | { |
933 | struct ads7846 *ts = dev_get_drvdata(&spi->dev); | 1060 | struct ads7846 *ts = dev_get_drvdata(&spi->dev); |
934 | 1061 | ||
1062 | ads784x_hwmon_unregister(spi, ts); | ||
935 | input_unregister_device(ts->input); | 1063 | input_unregister_device(ts->input); |
936 | 1064 | ||
937 | ads7846_suspend(spi, PMSG_SUSPEND); | 1065 | ads7846_suspend(spi, PMSG_SUSPEND); |
938 | 1066 | ||
939 | sysfs_remove_group(&spi->dev.kobj, ts->attr_group); | 1067 | sysfs_remove_group(&spi->dev.kobj, &ads784x_attr_group); |
940 | 1068 | ||
941 | free_irq(ts->spi->irq, ts); | 1069 | free_irq(ts->spi->irq, ts); |
942 | /* suspend left the IRQ disabled */ | 1070 | /* suspend left the IRQ disabled */ |