diff options
author | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2011-03-17 01:11:25 -0400 |
---|---|---|
committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2011-03-17 02:29:22 -0400 |
commit | 71f80045d48f259ea423bae3c14c2361e010a9ce (patch) | |
tree | 0b5294909c6138af3eb2dff0ea96afa5cfd88ec3 /drivers | |
parent | dacb650f125c7dc7ead9735d081bc078325b6d23 (diff) |
Input: tsc2005 - handle read errors from SPI layer
Tested-by: Aaro Koskinen <aaro.koskinen@nokia.com>
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/input/touchscreen/tsc2005.c | 100 |
1 files changed, 81 insertions, 19 deletions
diff --git a/drivers/input/touchscreen/tsc2005.c b/drivers/input/touchscreen/tsc2005.c index 7a7653390187..5a15919ec4c7 100644 --- a/drivers/input/touchscreen/tsc2005.c +++ b/drivers/input/touchscreen/tsc2005.c | |||
@@ -146,7 +146,7 @@ struct tsc2005 { | |||
146 | void (*set_reset)(bool enable); | 146 | void (*set_reset)(bool enable); |
147 | }; | 147 | }; |
148 | 148 | ||
149 | static void tsc2005_cmd(struct tsc2005 *ts, u8 cmd) | 149 | static int tsc2005_cmd(struct tsc2005 *ts, u8 cmd) |
150 | { | 150 | { |
151 | u8 tx = TSC2005_CMD | TSC2005_CMD_12BIT | cmd; | 151 | u8 tx = TSC2005_CMD | TSC2005_CMD_12BIT | cmd; |
152 | struct spi_transfer xfer = { | 152 | struct spi_transfer xfer = { |
@@ -155,13 +155,22 @@ static void tsc2005_cmd(struct tsc2005 *ts, u8 cmd) | |||
155 | .bits_per_word = 8, | 155 | .bits_per_word = 8, |
156 | }; | 156 | }; |
157 | struct spi_message msg; | 157 | struct spi_message msg; |
158 | int error; | ||
158 | 159 | ||
159 | spi_message_init(&msg); | 160 | spi_message_init(&msg); |
160 | spi_message_add_tail(&xfer, &msg); | 161 | spi_message_add_tail(&xfer, &msg); |
161 | spi_sync(ts->spi, &msg); | 162 | |
163 | error = spi_sync(ts->spi, &msg); | ||
164 | if (error) { | ||
165 | dev_err(&ts->spi->dev, "%s: failed, command: %x, error: %d\n", | ||
166 | __func__, cmd, error); | ||
167 | return error; | ||
168 | } | ||
169 | |||
170 | return 0; | ||
162 | } | 171 | } |
163 | 172 | ||
164 | static void tsc2005_write(struct tsc2005 *ts, u8 reg, u16 value) | 173 | static int tsc2005_write(struct tsc2005 *ts, u8 reg, u16 value) |
165 | { | 174 | { |
166 | u32 tx = ((reg | TSC2005_REG_PND0) << 16) | value; | 175 | u32 tx = ((reg | TSC2005_REG_PND0) << 16) | value; |
167 | struct spi_transfer xfer = { | 176 | struct spi_transfer xfer = { |
@@ -170,10 +179,20 @@ static void tsc2005_write(struct tsc2005 *ts, u8 reg, u16 value) | |||
170 | .bits_per_word = 24, | 179 | .bits_per_word = 24, |
171 | }; | 180 | }; |
172 | struct spi_message msg; | 181 | struct spi_message msg; |
182 | int error; | ||
173 | 183 | ||
174 | spi_message_init(&msg); | 184 | spi_message_init(&msg); |
175 | spi_message_add_tail(&xfer, &msg); | 185 | spi_message_add_tail(&xfer, &msg); |
176 | spi_sync(ts->spi, &msg); | 186 | |
187 | error = spi_sync(ts->spi, &msg); | ||
188 | if (error) { | ||
189 | dev_err(&ts->spi->dev, | ||
190 | "%s: failed, register: %x, value: %x, error: %d\n", | ||
191 | __func__, reg, value, error); | ||
192 | return error; | ||
193 | } | ||
194 | |||
195 | return 0; | ||
177 | } | 196 | } |
178 | 197 | ||
179 | static void tsc2005_setup_read(struct tsc2005_spi_rd *rd, u8 reg, bool last) | 198 | static void tsc2005_setup_read(struct tsc2005_spi_rd *rd, u8 reg, bool last) |
@@ -188,18 +207,23 @@ static void tsc2005_setup_read(struct tsc2005_spi_rd *rd, u8 reg, bool last) | |||
188 | rd->spi_xfer.cs_change = !last; | 207 | rd->spi_xfer.cs_change = !last; |
189 | } | 208 | } |
190 | 209 | ||
191 | static void tsc2005_read(struct tsc2005 *ts, u8 reg, u16 *value) | 210 | static int tsc2005_read(struct tsc2005 *ts, u8 reg, u16 *value) |
192 | { | 211 | { |
193 | struct tsc2005_spi_rd spi_rd; | 212 | struct tsc2005_spi_rd spi_rd; |
194 | struct spi_message msg; | 213 | struct spi_message msg; |
214 | int error; | ||
195 | 215 | ||
196 | tsc2005_setup_read(&spi_rd, reg, true); | 216 | tsc2005_setup_read(&spi_rd, reg, true); |
197 | 217 | ||
198 | spi_message_init(&msg); | 218 | spi_message_init(&msg); |
199 | spi_message_add_tail(&spi_rd.spi_xfer, &msg); | 219 | spi_message_add_tail(&spi_rd.spi_xfer, &msg); |
200 | spi_sync(ts->spi, &msg); | 220 | |
221 | error = spi_sync(ts->spi, &msg); | ||
222 | if (error) | ||
223 | return error; | ||
201 | 224 | ||
202 | *value = spi_rd.spi_rx; | 225 | *value = spi_rd.spi_rx; |
226 | return 0; | ||
203 | } | 227 | } |
204 | 228 | ||
205 | static void tsc2005_update_pen_state(struct tsc2005 *ts, | 229 | static void tsc2005_update_pen_state(struct tsc2005 *ts, |
@@ -232,6 +256,7 @@ static irqreturn_t tsc2005_irq_thread(int irq, void *_ts) | |||
232 | unsigned int pressure; | 256 | unsigned int pressure; |
233 | u32 x, y; | 257 | u32 x, y; |
234 | u32 z1, z2; | 258 | u32 z1, z2; |
259 | int error; | ||
235 | 260 | ||
236 | mutex_lock(&ts->mutex); | 261 | mutex_lock(&ts->mutex); |
237 | 262 | ||
@@ -239,7 +264,10 @@ static irqreturn_t tsc2005_irq_thread(int irq, void *_ts) | |||
239 | goto out; | 264 | goto out; |
240 | 265 | ||
241 | /* read the coordinates */ | 266 | /* read the coordinates */ |
242 | spi_sync(ts->spi, &ts->spi_read_msg); | 267 | error = spi_sync(ts->spi, &ts->spi_read_msg); |
268 | if (unlikely(error)) | ||
269 | goto out; | ||
270 | |||
243 | x = ts->spi_x.spi_rx; | 271 | x = ts->spi_x.spi_rx; |
244 | y = ts->spi_y.spi_rx; | 272 | y = ts->spi_y.spi_rx; |
245 | z1 = ts->spi_z1.spi_rx; | 273 | z1 = ts->spi_z1.spi_rx; |
@@ -392,7 +420,8 @@ static ssize_t tsc2005_selftest_show(struct device *dev, | |||
392 | u16 temp_high; | 420 | u16 temp_high; |
393 | u16 temp_high_orig; | 421 | u16 temp_high_orig; |
394 | u16 temp_high_test; | 422 | u16 temp_high_test; |
395 | unsigned int result; | 423 | bool success = true; |
424 | int error; | ||
396 | 425 | ||
397 | mutex_lock(&ts->mutex); | 426 | mutex_lock(&ts->mutex); |
398 | 427 | ||
@@ -400,34 +429,65 @@ static ssize_t tsc2005_selftest_show(struct device *dev, | |||
400 | * Test TSC2005 communications via temp high register. | 429 | * Test TSC2005 communications via temp high register. |
401 | */ | 430 | */ |
402 | tsc2005_disable(ts); | 431 | tsc2005_disable(ts); |
403 | result = 1; | 432 | |
404 | tsc2005_read(ts, TSC2005_REG_TEMP_HIGH, &temp_high_orig); | 433 | error = tsc2005_read(ts, TSC2005_REG_TEMP_HIGH, &temp_high_orig); |
434 | if (error) { | ||
435 | dev_warn(dev, "selftest failed: read error %d\n", error); | ||
436 | success = false; | ||
437 | goto out; | ||
438 | } | ||
439 | |||
405 | temp_high_test = (temp_high_orig - 1) & MAX_12BIT; | 440 | temp_high_test = (temp_high_orig - 1) & MAX_12BIT; |
406 | tsc2005_write(ts, TSC2005_REG_TEMP_HIGH, temp_high_test); | 441 | |
407 | tsc2005_read(ts, TSC2005_REG_TEMP_HIGH, &temp_high); | 442 | error = tsc2005_write(ts, TSC2005_REG_TEMP_HIGH, temp_high_test); |
443 | if (error) { | ||
444 | dev_warn(dev, "selftest failed: write error %d\n", error); | ||
445 | success = false; | ||
446 | goto out; | ||
447 | } | ||
448 | |||
449 | error = tsc2005_read(ts, TSC2005_REG_TEMP_HIGH, &temp_high); | ||
450 | if (error) { | ||
451 | dev_warn(dev, "selftest failed: read error %d after write\n", | ||
452 | error); | ||
453 | success = false; | ||
454 | goto out; | ||
455 | } | ||
456 | |||
408 | if (temp_high != temp_high_test) { | 457 | if (temp_high != temp_high_test) { |
409 | dev_warn(dev, "selftest failed: %d != %d\n", | 458 | dev_warn(dev, "selftest failed: %d != %d\n", |
410 | temp_high, temp_high_test); | 459 | temp_high, temp_high_test); |
411 | result = 0; | 460 | success = false; |
412 | } | 461 | } |
413 | 462 | ||
414 | /* hardware reset */ | 463 | /* hardware reset */ |
415 | ts->set_reset(false); | 464 | ts->set_reset(false); |
416 | usleep_range(100, 500); /* only 10us required */ | 465 | usleep_range(100, 500); /* only 10us required */ |
417 | ts->set_reset(true); | 466 | ts->set_reset(true); |
418 | tsc2005_enable(ts); | 467 | |
468 | if (!success) | ||
469 | goto out; | ||
419 | 470 | ||
420 | /* test that the reset really happened */ | 471 | /* test that the reset really happened */ |
421 | tsc2005_read(ts, TSC2005_REG_TEMP_HIGH, &temp_high); | 472 | error = tsc2005_read(ts, TSC2005_REG_TEMP_HIGH, &temp_high); |
473 | if (error) { | ||
474 | dev_warn(dev, "selftest failed: read error %d after reset\n", | ||
475 | error); | ||
476 | success = false; | ||
477 | goto out; | ||
478 | } | ||
479 | |||
422 | if (temp_high != temp_high_orig) { | 480 | if (temp_high != temp_high_orig) { |
423 | dev_warn(dev, "selftest failed after reset: %d != %d\n", | 481 | dev_warn(dev, "selftest failed after reset: %d != %d\n", |
424 | temp_high, temp_high_orig); | 482 | temp_high, temp_high_orig); |
425 | result = 0; | 483 | success = false; |
426 | } | 484 | } |
427 | 485 | ||
486 | out: | ||
487 | tsc2005_enable(ts); | ||
428 | mutex_unlock(&ts->mutex); | 488 | mutex_unlock(&ts->mutex); |
429 | 489 | ||
430 | return sprintf(buf, "%u\n", result); | 490 | return sprintf(buf, "%d\n", success); |
431 | } | 491 | } |
432 | 492 | ||
433 | static DEVICE_ATTR(selftest, S_IRUGO, tsc2005_selftest_show, NULL); | 493 | static DEVICE_ATTR(selftest, S_IRUGO, tsc2005_selftest_show, NULL); |
@@ -469,6 +529,7 @@ static void tsc2005_esd_timer(unsigned long data) | |||
469 | static void tsc2005_esd_work(struct work_struct *work) | 529 | static void tsc2005_esd_work(struct work_struct *work) |
470 | { | 530 | { |
471 | struct tsc2005 *ts = container_of(work, struct tsc2005, esd_work); | 531 | struct tsc2005 *ts = container_of(work, struct tsc2005, esd_work); |
532 | int error; | ||
472 | u16 r; | 533 | u16 r; |
473 | 534 | ||
474 | mutex_lock(&ts->mutex); | 535 | mutex_lock(&ts->mutex); |
@@ -480,8 +541,9 @@ static void tsc2005_esd_work(struct work_struct *work) | |||
480 | * If we cannot read our known value from configuration register 0 then | 541 | * If we cannot read our known value from configuration register 0 then |
481 | * reset the controller as if from power-up and start scanning again. | 542 | * reset the controller as if from power-up and start scanning again. |
482 | */ | 543 | */ |
483 | tsc2005_read(ts, TSC2005_REG_CFR0, &r); | 544 | error = tsc2005_read(ts, TSC2005_REG_CFR0, &r); |
484 | if ((r ^ TSC2005_CFR0_INITVALUE) & TSC2005_CFR0_RW_MASK) { | 545 | if (error || |
546 | ((r ^ TSC2005_CFR0_INITVALUE) & TSC2005_CFR0_RW_MASK)) { | ||
485 | dev_info(&ts->spi->dev, "TSC2005 not responding - resetting\n"); | 547 | dev_info(&ts->spi->dev, "TSC2005 not responding - resetting\n"); |
486 | ts->set_reset(false); | 548 | ts->set_reset(false); |
487 | tsc2005_update_pen_state(ts, 0, 0, 0); | 549 | tsc2005_update_pen_state(ts, 0, 0, 0); |