From 71f80045d48f259ea423bae3c14c2361e010a9ce Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Wed, 16 Mar 2011 22:11:25 -0700 Subject: [PATCH] Input: tsc2005 - handle read errors from SPI layer Tested-by: Aaro Koskinen Signed-off-by: Dmitry Torokhov --- drivers/input/touchscreen/tsc2005.c | 100 ++++++++++++++++++++++------ 1 file changed, 81 insertions(+), 19 deletions(-) diff --git a/drivers/input/touchscreen/tsc2005.c b/drivers/input/touchscreen/tsc2005.c index 7a765339018..5a15919ec4c 100644 --- a/drivers/input/touchscreen/tsc2005.c +++ b/drivers/input/touchscreen/tsc2005.c @@ -146,7 +146,7 @@ struct tsc2005 { void (*set_reset)(bool enable); }; -static void tsc2005_cmd(struct tsc2005 *ts, u8 cmd) +static int tsc2005_cmd(struct tsc2005 *ts, u8 cmd) { u8 tx = TSC2005_CMD | TSC2005_CMD_12BIT | cmd; struct spi_transfer xfer = { @@ -155,13 +155,22 @@ static void tsc2005_cmd(struct tsc2005 *ts, u8 cmd) .bits_per_word = 8, }; struct spi_message msg; + int error; spi_message_init(&msg); spi_message_add_tail(&xfer, &msg); - spi_sync(ts->spi, &msg); + + error = spi_sync(ts->spi, &msg); + if (error) { + dev_err(&ts->spi->dev, "%s: failed, command: %x, error: %d\n", + __func__, cmd, error); + return error; + } + + return 0; } -static void tsc2005_write(struct tsc2005 *ts, u8 reg, u16 value) +static int tsc2005_write(struct tsc2005 *ts, u8 reg, u16 value) { u32 tx = ((reg | TSC2005_REG_PND0) << 16) | value; struct spi_transfer xfer = { @@ -170,10 +179,20 @@ static void tsc2005_write(struct tsc2005 *ts, u8 reg, u16 value) .bits_per_word = 24, }; struct spi_message msg; + int error; spi_message_init(&msg); spi_message_add_tail(&xfer, &msg); - spi_sync(ts->spi, &msg); + + error = spi_sync(ts->spi, &msg); + if (error) { + dev_err(&ts->spi->dev, + "%s: failed, register: %x, value: %x, error: %d\n", + __func__, reg, value, error); + return error; + } + + return 0; } 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) rd->spi_xfer.cs_change = !last; } -static void tsc2005_read(struct tsc2005 *ts, u8 reg, u16 *value) +static int tsc2005_read(struct tsc2005 *ts, u8 reg, u16 *value) { struct tsc2005_spi_rd spi_rd; struct spi_message msg; + int error; tsc2005_setup_read(&spi_rd, reg, true); spi_message_init(&msg); spi_message_add_tail(&spi_rd.spi_xfer, &msg); - spi_sync(ts->spi, &msg); + + error = spi_sync(ts->spi, &msg); + if (error) + return error; *value = spi_rd.spi_rx; + return 0; } static void tsc2005_update_pen_state(struct tsc2005 *ts, @@ -232,6 +256,7 @@ static irqreturn_t tsc2005_irq_thread(int irq, void *_ts) unsigned int pressure; u32 x, y; u32 z1, z2; + int error; mutex_lock(&ts->mutex); @@ -239,7 +264,10 @@ static irqreturn_t tsc2005_irq_thread(int irq, void *_ts) goto out; /* read the coordinates */ - spi_sync(ts->spi, &ts->spi_read_msg); + error = spi_sync(ts->spi, &ts->spi_read_msg); + if (unlikely(error)) + goto out; + x = ts->spi_x.spi_rx; y = ts->spi_y.spi_rx; z1 = ts->spi_z1.spi_rx; @@ -392,7 +420,8 @@ static ssize_t tsc2005_selftest_show(struct device *dev, u16 temp_high; u16 temp_high_orig; u16 temp_high_test; - unsigned int result; + bool success = true; + int error; mutex_lock(&ts->mutex); @@ -400,34 +429,65 @@ static ssize_t tsc2005_selftest_show(struct device *dev, * Test TSC2005 communications via temp high register. */ tsc2005_disable(ts); - result = 1; - tsc2005_read(ts, TSC2005_REG_TEMP_HIGH, &temp_high_orig); + + error = tsc2005_read(ts, TSC2005_REG_TEMP_HIGH, &temp_high_orig); + if (error) { + dev_warn(dev, "selftest failed: read error %d\n", error); + success = false; + goto out; + } + temp_high_test = (temp_high_orig - 1) & MAX_12BIT; - tsc2005_write(ts, TSC2005_REG_TEMP_HIGH, temp_high_test); - tsc2005_read(ts, TSC2005_REG_TEMP_HIGH, &temp_high); + + error = tsc2005_write(ts, TSC2005_REG_TEMP_HIGH, temp_high_test); + if (error) { + dev_warn(dev, "selftest failed: write error %d\n", error); + success = false; + goto out; + } + + error = tsc2005_read(ts, TSC2005_REG_TEMP_HIGH, &temp_high); + if (error) { + dev_warn(dev, "selftest failed: read error %d after write\n", + error); + success = false; + goto out; + } + if (temp_high != temp_high_test) { dev_warn(dev, "selftest failed: %d != %d\n", temp_high, temp_high_test); - result = 0; + success = false; } /* hardware reset */ ts->set_reset(false); usleep_range(100, 500); /* only 10us required */ ts->set_reset(true); - tsc2005_enable(ts); + + if (!success) + goto out; /* test that the reset really happened */ - tsc2005_read(ts, TSC2005_REG_TEMP_HIGH, &temp_high); + error = tsc2005_read(ts, TSC2005_REG_TEMP_HIGH, &temp_high); + if (error) { + dev_warn(dev, "selftest failed: read error %d after reset\n", + error); + success = false; + goto out; + } + if (temp_high != temp_high_orig) { dev_warn(dev, "selftest failed after reset: %d != %d\n", temp_high, temp_high_orig); - result = 0; + success = false; } +out: + tsc2005_enable(ts); mutex_unlock(&ts->mutex); - return sprintf(buf, "%u\n", result); + return sprintf(buf, "%d\n", success); } static DEVICE_ATTR(selftest, S_IRUGO, tsc2005_selftest_show, NULL); @@ -469,6 +529,7 @@ static void tsc2005_esd_timer(unsigned long data) static void tsc2005_esd_work(struct work_struct *work) { struct tsc2005 *ts = container_of(work, struct tsc2005, esd_work); + int error; u16 r; mutex_lock(&ts->mutex); @@ -480,8 +541,9 @@ static void tsc2005_esd_work(struct work_struct *work) * If we cannot read our known value from configuration register 0 then * reset the controller as if from power-up and start scanning again. */ - tsc2005_read(ts, TSC2005_REG_CFR0, &r); - if ((r ^ TSC2005_CFR0_INITVALUE) & TSC2005_CFR0_RW_MASK) { + error = tsc2005_read(ts, TSC2005_REG_CFR0, &r); + if (error || + ((r ^ TSC2005_CFR0_INITVALUE) & TSC2005_CFR0_RW_MASK)) { dev_info(&ts->spi->dev, "TSC2005 not responding - resetting\n"); ts->set_reset(false); tsc2005_update_pen_state(ts, 0, 0, 0); -- 2.41.0