Commit 5a5e277b authored by Lori Hikichi's avatar Lori Hikichi Committed by Wolfram Sang
Browse files

i2c: iproc: Add i2c repeated start capability



Enable handling of i2c repeated start. The current code
handles a multi msg i2c transfer as separate i2c bus
transactions. This change will now handle this case
using the i2c repeated start protocol. The number of msgs
in a transfer is limited to two, and must be a write
followed by a read.

Signed-off-by: default avatarLori Hikichi <lori.hikichi@broadcom.com>
Signed-off-by: default avatarRayagonda Kokatanur <rayagonda.kokatanur@broadcom.com>
Signed-off-by: default avatarIcarus Chau <icarus.chau@broadcom.com>
Signed-off-by: default avatarRay Jui <ray.jui@broadcom.com>
Signed-off-by: default avatarShivaraj Shetty <sshetty1@broadcom.com>
Signed-off-by: default avatarWolfram Sang <wsa@the-dreams.de>
parent 9af43384
Loading
Loading
Loading
Loading
+50 −13
Original line number Diff line number Diff line
@@ -81,6 +81,7 @@
#define M_CMD_PROTOCOL_MASK          0xf
#define M_CMD_PROTOCOL_BLK_WR        0x7
#define M_CMD_PROTOCOL_BLK_RD        0x8
#define M_CMD_PROTOCOL_PROCESS       0xa
#define M_CMD_PEC_SHIFT              8
#define M_CMD_RD_CNT_SHIFT           0
#define M_CMD_RD_CNT_MASK            0xff
@@ -675,13 +676,20 @@ static int bcm_iproc_i2c_xfer_wait(struct bcm_iproc_i2c_dev *iproc_i2c,
	return 0;
}

static int bcm_iproc_i2c_xfer_single_msg(struct bcm_iproc_i2c_dev *iproc_i2c,
					 struct i2c_msg *msg)
/*
 * If 'process_call' is true, then this is a multi-msg transfer that requires
 * a repeated start between the messages.
 * More specifically, it must be a write (reg) followed by a read (data).
 * The i2c quirks are set to enforce this rule.
 */
static int bcm_iproc_i2c_xfer_internal(struct bcm_iproc_i2c_dev *iproc_i2c,
					struct i2c_msg *msgs, bool process_call)
{
	int i;
	u8 addr;
	u32 val, tmp, val_intr_en;
	unsigned int tx_bytes;
	struct i2c_msg *msg = &msgs[0];

	/* check if bus is busy */
	if (!!(iproc_i2c_rd_reg(iproc_i2c,
@@ -707,14 +715,29 @@ static int bcm_iproc_i2c_xfer_single_msg(struct bcm_iproc_i2c_dev *iproc_i2c,
			val = msg->buf[i];

			/* mark the last byte */
			if (i == msg->len - 1)
				val |= BIT(M_TX_WR_STATUS_SHIFT);
			if (!process_call && (i == msg->len - 1))
				val |= 1 << M_TX_WR_STATUS_SHIFT;

			iproc_i2c_wr_reg(iproc_i2c, M_TX_OFFSET, val);
		}
		iproc_i2c->tx_bytes = tx_bytes;
	}

	/* Process the read message if this is process call */
	if (process_call) {
		msg++;
		iproc_i2c->msg = msg;  /* point to second msg */

		/*
		 * The last byte to be sent out should be a slave
		 * address with read operation
		 */
		addr = i2c_8bit_addr_from_msg(msg);
		/* mark it the last byte out */
		val = addr | (1 << M_TX_WR_STATUS_SHIFT);
		iproc_i2c_wr_reg(iproc_i2c, M_TX_OFFSET, val);
	}

	/* mark as incomplete before starting the transaction */
	if (iproc_i2c->irq)
		reinit_completion(&iproc_i2c->done);
@@ -733,7 +756,7 @@ static int bcm_iproc_i2c_xfer_single_msg(struct bcm_iproc_i2c_dev *iproc_i2c,
	 * underrun interrupt, which will be triggerred when the TX FIFO is
	 * empty. When that happens we can then pump more data into the FIFO
	 */
	if (!(msg->flags & I2C_M_RD) &&
	if (!process_call && !(msg->flags & I2C_M_RD) &&
	    msg->len > iproc_i2c->tx_bytes)
		val_intr_en |= BIT(IE_M_TX_UNDERRUN_SHIFT);

@@ -743,6 +766,8 @@ static int bcm_iproc_i2c_xfer_single_msg(struct bcm_iproc_i2c_dev *iproc_i2c,
	 */
	val = BIT(M_CMD_START_BUSY_SHIFT);
	if (msg->flags & I2C_M_RD) {
		u32 protocol;

		iproc_i2c->rx_bytes = 0;
		if (msg->len > M_RX_FIFO_MAX_THLD_VALUE)
			iproc_i2c->thld_bytes = M_RX_FIFO_THLD_VALUE;
@@ -758,7 +783,10 @@ static int bcm_iproc_i2c_xfer_single_msg(struct bcm_iproc_i2c_dev *iproc_i2c,
		/* enable the RX threshold interrupt */
		val_intr_en |= BIT(IE_M_RX_THLD_SHIFT);

		val |= (M_CMD_PROTOCOL_BLK_RD << M_CMD_PROTOCOL_SHIFT) |
		protocol = process_call ?
				M_CMD_PROTOCOL_PROCESS : M_CMD_PROTOCOL_BLK_RD;

		val |= (protocol << M_CMD_PROTOCOL_SHIFT) |
		       (msg->len << M_CMD_RD_CNT_SHIFT);
	} else {
		val |= (M_CMD_PROTOCOL_BLK_WR << M_CMD_PROTOCOL_SHIFT);
@@ -774,16 +802,23 @@ static int bcm_iproc_i2c_xfer(struct i2c_adapter *adapter,
			      struct i2c_msg msgs[], int num)
{
	struct bcm_iproc_i2c_dev *iproc_i2c = i2c_get_adapdata(adapter);
	int ret, i;
	bool process_call = false;
	int ret;

	/* go through all messages */
	for (i = 0; i < num; i++) {
		ret = bcm_iproc_i2c_xfer_single_msg(iproc_i2c, &msgs[i]);
	if (num == 2) {
		/* Repeated start, use process call */
		process_call = true;
		if (msgs[1].flags & I2C_M_NOSTART) {
			dev_err(iproc_i2c->device, "Invalid repeated start\n");
			return -EOPNOTSUPP;
		}
	}

	ret = bcm_iproc_i2c_xfer_internal(iproc_i2c, msgs, process_call);
	if (ret) {
		dev_dbg(iproc_i2c->device, "xfer failed\n");
		return ret;
	}
	}

	return num;
}
@@ -809,6 +844,8 @@ static struct i2c_algorithm bcm_iproc_algo = {
};

static const struct i2c_adapter_quirks bcm_iproc_i2c_quirks = {
	.flags = I2C_AQ_COMB_WRITE_THEN_READ,
	.max_comb_1st_msg_len = M_TX_RX_FIFO_SIZE,
	.max_read_len = M_RX_MAX_READ_LEN,
};