Commit 093f70c1 authored by Wolfram Sang's avatar Wolfram Sang
Browse files

i2c: rcar: fix NACK handling when being a target



When this controller is a target, the NACK handling had two issues.
First, the return value from the backend was not checked on the initial
WRITE_REQUESTED. So, the driver missed to send a NACK in this case.
Also, the NACK always arrives one byte late on the bus, even in the
WRITE_RECEIVED case. This seems to be a HW issue. We should then not
rely on the backend to correctly NACK the superfluous byte as well. Fix
both issues by introducing a flag which gets set whenever the backend
requests a NACK and keep sending it until we get a STOP condition.

Fixes: de20d185 ("i2c: rcar: add slave support")
Signed-off-by: default avatarWolfram Sang <wsa+renesas@sang-engineering.com>
parent 385f2dbb
Loading
Loading
Loading
Loading
+15 −5
Original line number Diff line number Diff line
@@ -130,6 +130,8 @@
#define ID_P_PM_BLOCKED		BIT(31)
#define ID_P_MASK		GENMASK(31, 27)

#define ID_SLAVE_NACK		BIT(0)

enum rcar_i2c_type {
	I2C_RCAR_GEN1,
	I2C_RCAR_GEN2,
@@ -166,6 +168,7 @@ struct rcar_i2c_priv {
	int irq;

	struct i2c_client *host_notify_client;
	u8 slave_flags;
};

#define rcar_i2c_priv_to_dev(p)		((p)->adap.dev.parent)
@@ -655,6 +658,7 @@ static bool rcar_i2c_slave_irq(struct rcar_i2c_priv *priv)
{
	u32 ssr_raw, ssr_filtered;
	u8 value;
	int ret;

	ssr_raw = rcar_i2c_read(priv, ICSSR) & 0xff;
	ssr_filtered = ssr_raw & rcar_i2c_read(priv, ICSIER);
@@ -670,7 +674,10 @@ static bool rcar_i2c_slave_irq(struct rcar_i2c_priv *priv)
			rcar_i2c_write(priv, ICRXTX, value);
			rcar_i2c_write(priv, ICSIER, SDE | SSR | SAR);
		} else {
			i2c_slave_event(priv->slave, I2C_SLAVE_WRITE_REQUESTED, &value);
			ret = i2c_slave_event(priv->slave, I2C_SLAVE_WRITE_REQUESTED, &value);
			if (ret)
				priv->slave_flags |= ID_SLAVE_NACK;

			rcar_i2c_read(priv, ICRXTX);	/* dummy read */
			rcar_i2c_write(priv, ICSIER, SDR | SSR | SAR);
		}
@@ -683,18 +690,21 @@ static bool rcar_i2c_slave_irq(struct rcar_i2c_priv *priv)
	if (ssr_filtered & SSR) {
		i2c_slave_event(priv->slave, I2C_SLAVE_STOP, &value);
		rcar_i2c_write(priv, ICSCR, SIE | SDBS); /* clear our NACK */
		priv->slave_flags &= ~ID_SLAVE_NACK;
		rcar_i2c_write(priv, ICSIER, SAR);
		rcar_i2c_write(priv, ICSSR, ~SSR & 0xff);
	}

	/* master wants to write to us */
	if (ssr_filtered & SDR) {
		int ret;

		value = rcar_i2c_read(priv, ICRXTX);
		ret = i2c_slave_event(priv->slave, I2C_SLAVE_WRITE_RECEIVED, &value);
		/* Send NACK in case of error */
		rcar_i2c_write(priv, ICSCR, SIE | SDBS | (ret < 0 ? FNA : 0));
		if (ret)
			priv->slave_flags |= ID_SLAVE_NACK;

		/* Send NACK in case of error, but it will come 1 byte late :( */
		rcar_i2c_write(priv, ICSCR, SIE | SDBS |
			       (priv->slave_flags & ID_SLAVE_NACK ? FNA : 0));
		rcar_i2c_write(priv, ICSSR, ~SDR & 0xff);
	}