Input: tsc2005 - handle read errors from SPI layer
authorDmitry Torokhov <dmitry.torokhov@gmail.com>
Thu, 17 Mar 2011 05:11:25 +0000 (22:11 -0700)
committerDmitry Torokhov <dmitry.torokhov@gmail.com>
Thu, 17 Mar 2011 06:29:22 +0000 (23:29 -0700)
Tested-by: Aaro Koskinen <aaro.koskinen@nokia.com>
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
drivers/input/touchscreen/tsc2005.c

index 7a76533901870bcc7c99e3824597d5abdb783471..5a15919ec4c75b77db52df4a7bfbb74a5d92b690 100644 (file)
@@ -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);