Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 71f80045 authored by Dmitry Torokhov's avatar Dmitry Torokhov
Browse files

Input: tsc2005 - handle read errors from SPI layer

parent dacb650f
Loading
Loading
Loading
Loading
+81 −19
Original line number Diff line number Diff line
@@ -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);