Commit 5515ae11 authored by Kamlakant Patel's avatar Kamlakant Patel Committed by Wolfram Sang
Browse files

i2c: xlp9xx: Handle I2C_M_RECV_LEN in msg->flags



The driver needs to handle the flag I2C_M_RECV_LEN during receive to
support SMBus emulation.

Update receive logic to handle the case where the length is received
as the first byte of a transaction.

Also update the code to handle I2C_CLIENT_PEC, which is set when the
client sends a packet error checking code byte.

Signed-off-by: default avatarJayachandran C <jnair@caviumnetworks.com>
Signed-off-by: default avatarKamlakant Patel <kamlakant.patel@cavium.com>
Reviewed-by: default avatarMika Westerberg <mika.westerberg@linux.intel.com>
Signed-off-by: default avatarWolfram Sang <wsa@the-dreams.de>
parent c347b8fc
Loading
Loading
Loading
Loading
+27 −3
Original line number Diff line number Diff line
@@ -82,6 +82,8 @@ struct xlp9xx_i2c_dev {
	struct completion msg_complete;
	int irq;
	bool msg_read;
	bool len_recv;
	bool client_pec;
	u32 __iomem *base;
	u32 msg_buf_remaining;
	u32 msg_len;
@@ -143,10 +145,25 @@ static void xlp9xx_i2c_fill_tx_fifo(struct xlp9xx_i2c_dev *priv)
static void xlp9xx_i2c_drain_rx_fifo(struct xlp9xx_i2c_dev *priv)
{
	u32 len, i;
	u8 *buf = priv->msg_buf;
	u8 rlen, *buf = priv->msg_buf;

	len = xlp9xx_read_i2c_reg(priv, XLP9XX_I2C_FIFOWCNT) &
				  XLP9XX_I2C_FIFO_WCNT_MASK;
	if (!len)
		return;
	if (priv->len_recv) {
		/* read length byte */
		rlen = xlp9xx_read_i2c_reg(priv, XLP9XX_I2C_MRXFIFO);
		*buf++ = rlen;
		len--;
		if (priv->client_pec)
			++rlen;
		/* update remaining bytes and message length */
		priv->msg_buf_remaining = rlen;
		priv->msg_len = rlen + 1;
		priv->len_recv = false;
	}

	len = min(priv->msg_buf_remaining, len);
	for (i = 0; i < len; i++, buf++)
		*buf = xlp9xx_read_i2c_reg(priv, XLP9XX_I2C_MRXFIFO);
@@ -230,7 +247,7 @@ static int xlp9xx_i2c_xfer_msg(struct xlp9xx_i2c_dev *priv, struct i2c_msg *msg,
			       int last_msg)
{
	unsigned long timeleft;
	u32 intr_mask, cmd, val;
	u32 intr_mask, cmd, val, len;

	priv->msg_buf = msg->buf;
	priv->msg_buf_remaining = priv->msg_len = msg->len;
@@ -263,9 +280,13 @@ static int xlp9xx_i2c_xfer_msg(struct xlp9xx_i2c_dev *priv, struct i2c_msg *msg,
	else
		val &= ~XLP9XX_I2C_CTRL_ADDMODE;

	priv->len_recv = msg->flags & I2C_M_RECV_LEN;
	len = priv->len_recv ? XLP9XX_I2C_FIFO_SIZE : msg->len;
	priv->client_pec = msg->flags & I2C_CLIENT_PEC;

	/* set data length to be transferred */
	val = (val & ~XLP9XX_I2C_CTRL_MCTLEN_MASK) |
	      (msg->len << XLP9XX_I2C_CTRL_MCTLEN_SHIFT);
	      (len << XLP9XX_I2C_CTRL_MCTLEN_SHIFT);
	xlp9xx_write_i2c_reg(priv, XLP9XX_I2C_CTRL, val);

	/* fill fifo during tx */
@@ -312,6 +333,9 @@ static int xlp9xx_i2c_xfer_msg(struct xlp9xx_i2c_dev *priv, struct i2c_msg *msg,
		return -ETIMEDOUT;
	}

	/* update msg->len with actual received length */
	if (msg->flags & I2C_M_RECV_LEN)
		msg->len = priv->msg_len;
	return 0;
}