Commit 3b5f7f10 authored by Michael Wu's avatar Michael Wu Committed by Wolfram Sang
Browse files

i2c: designware: slave should do WRITE_REQUESTED before WRITE_RECEIVED



Sometimes we would get the following flow when doing an i2cset:

0x1 STATUS SLAVE_ACTIVITY=0x1 : RAW_INTR_STAT=0x514 : INTR_STAT=0x4
I2C_SLAVE_WRITE_RECEIVED
0x1 STATUS SLAVE_ACTIVITY=0x0 : RAW_INTR_STAT=0x714 : INTR_STAT=0x204
I2C_SLAVE_WRITE_REQUESTED
I2C_SLAVE_WRITE_RECEIVED

Documentation/i2c/slave-interface.rst says that I2C_SLAVE_WRITE_REQUESTED,
which is mandatory, should be sent while the data did not arrive yet. It
means in a write-request I2C_SLAVE_WRITE_REQUESTED should be reported
before any I2C_SLAVE_WRITE_RECEIVED.

By the way, I2C_SLAVE_STOP didn't be reported in the above case because
DW_IC_INTR_STAT was not 0x200.

dev->status can be used to record the current state, especially Designware
I2C controller has no interrupts to identify a write-request. This patch
makes not only I2C_SLAVE_WRITE_REQUESTED been reported first when
IC_INTR_RX_FULL is rising and dev->status isn't STATUS_WRITE_IN_PROGRESS
but also I2C_SLAVE_STOP been reported when a STOP condition is received.

Signed-off-by: default avatarMichael Wu <michael.wu@vatics.com>
Acked-by: default avatarJarkko Nikula <jarkko.nikula@linux.intel.com>
Signed-off-by: default avatarWolfram Sang <wsa@kernel.org>
parent 66b92313
Loading
Loading
Loading
Loading
+18 −27
Original line number Diff line number Diff line
@@ -172,26 +172,25 @@ static int i2c_dw_irq_handler_slave(struct dw_i2c_dev *dev)
		"%#x STATUS SLAVE_ACTIVITY=%#x : RAW_INTR_STAT=%#x : INTR_STAT=%#x\n",
		enabled, slave_activity, raw_stat, stat);

	if ((stat & DW_IC_INTR_RX_FULL) && (stat & DW_IC_INTR_STOP_DET))
		i2c_slave_event(dev->slave, I2C_SLAVE_WRITE_REQUESTED, &val);

	if (stat & DW_IC_INTR_RD_REQ) {
		if (slave_activity) {
	if (stat & DW_IC_INTR_RX_FULL) {
		if (dev->status != STATUS_WRITE_IN_PROGRESS) {
			dev->status = STATUS_WRITE_IN_PROGRESS;
			i2c_slave_event(dev->slave, I2C_SLAVE_WRITE_REQUESTED,
					&val);
		}

		regmap_read(dev->map, DW_IC_DATA_CMD, &tmp);
		val = tmp;

				if (!i2c_slave_event(dev->slave,
						     I2C_SLAVE_WRITE_RECEIVED,
						     &val)) {
					dev_vdbg(dev->dev, "Byte %X acked!",
						 val);
		if (!i2c_slave_event(dev->slave, I2C_SLAVE_WRITE_RECEIVED,
				     &val))
			dev_vdbg(dev->dev, "Byte %X acked!", val);
	}

	if (stat & DW_IC_INTR_RD_REQ) {
		if (slave_activity) {
			regmap_read(dev->map, DW_IC_CLR_RD_REQ, &tmp);
			} else {
				regmap_read(dev->map, DW_IC_CLR_RD_REQ, &tmp);
				regmap_read(dev->map, DW_IC_CLR_RX_UNDER, &tmp);
			}

			dev->status = STATUS_READ_IN_PROGRESS;
			if (!i2c_slave_event(dev->slave,
					     I2C_SLAVE_READ_REQUESTED,
					     &val))
@@ -203,18 +202,10 @@ static int i2c_dw_irq_handler_slave(struct dw_i2c_dev *dev)
		if (!i2c_slave_event(dev->slave, I2C_SLAVE_READ_PROCESSED,
				     &val))
			regmap_read(dev->map, DW_IC_CLR_RX_DONE, &tmp);

		i2c_slave_event(dev->slave, I2C_SLAVE_STOP, &val);
		return 1;
	}

	if (stat & DW_IC_INTR_RX_FULL) {
		regmap_read(dev->map, DW_IC_DATA_CMD, &tmp);
		val = tmp;
		if (!i2c_slave_event(dev->slave, I2C_SLAVE_WRITE_RECEIVED,
				     &val))
			dev_vdbg(dev->dev, "Byte %X acked!", val);
	} else {
	if (stat & DW_IC_INTR_STOP_DET) {
		dev->status = STATUS_IDLE;
		i2c_slave_event(dev->slave, I2C_SLAVE_STOP, &val);
	}