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

Commit fd110ccc authored by Gilad Avidov's avatar Gilad Avidov Committed by Ankit Gupta
Browse files

i2c-msm-v2: Add support for block mode



Add support for block mode for transfers that are
larger then fifo size. This mode will be useful
when I2C instance cannot use BAM pipes for such
transfer size due to ownership conflicts with
other EEs. I2C Driver can be configured either
to use Block or BAM mode for such transfer
size.

Change-Id: Ia3ab87f4dee011f9ac9d437b78cc762573540ae0
Signed-off-by: default avatarGilad Avidov <gavidov@codeaurora.org>
Signed-off-by: default avatarAnkit Gupta <ankgupta@codeaurora.org>
parent 512fe370
Loading
Loading
Loading
Loading
+488 −72
Original line number Diff line number Diff line
@@ -47,7 +47,7 @@ static const enum msm_i2_debug_level DEFAULT_DBG_LVL = MSM_ERR;

/* string table for enum i2c_msm_xfer_mode_id */
static const char * const i2c_msm_mode_str_tbl[] = {
	"FIFO", "BAM", "None",
	"FIFO", "BLOCK", "BAM", "None",
};

/* string table for qup_io_modes register */
@@ -58,14 +58,17 @@ static const char * const i2c_msm_qup_mode_str_tbl[] = {
/* from enum i2c_msm_xfer_mode_id to qup_io_modes register values */
static const u32 i2c_msm_mode_to_reg_tbl[] = {
	0x0, /* map I2C_MSM_XFER_MODE_FIFO -> binary 00 */
	0x1, /* map I2C_MSM_XFER_MODE_BLOCK -> binary 01 */
	0x3  /* map I2C_MSM_XFER_MODE_BAM -> binary 11 */
};

/* Forward declarations */
static bool i2c_msm_xfer_next_buf(struct i2c_msm_ctrl *ctrl);
static int  i2c_msm_xfer_wait_for_completion(struct i2c_msm_ctrl *ctrl);
static int i2c_msm_xfer_wait_for_completion(struct i2c_msm_ctrl *ctrl,
						struct completion *complete);
static int  i2c_msm_bam_xfer(struct i2c_msm_ctrl *ctrl);
static int  i2c_msm_fifo_xfer(struct i2c_msm_ctrl *ctrl);
static int  i2c_msm_blk_xfer(struct i2c_msm_ctrl *ctrl);
static void i2c_msm_pm_resume_adptr(struct i2c_msm_ctrl *ctrl);
static void i2c_msm_pm_suspend_adptr(struct i2c_msm_ctrl *ctrl);
static int  i2c_msm_qup_init(struct i2c_msm_ctrl *ctrl);
@@ -99,6 +102,20 @@ static void i2c_msm_fifo_set_struct(struct i2c_msm_ctrl *ctrl,
					(struct i2c_msm_xfer_mode *) fifo;
}

static struct i2c_msm_xfer_mode_blk *i2c_msm_blk_get_struct(
						struct i2c_msm_ctrl *ctrl)
{
	return (struct i2c_msm_xfer_mode_blk *)
				ctrl->ver.xfer_mode[I2C_MSM_XFER_MODE_BLOCK];
}

static void i2c_msm_blk_set_struct(struct i2c_msm_ctrl *ctrl,
					  struct i2c_msm_xfer_mode_blk *blk)
{
	ctrl->ver.xfer_mode[I2C_MSM_XFER_MODE_BLOCK] =
					(struct i2c_msm_xfer_mode *) blk;
}

static const char * const i2c_msm_mini_core_str_tbl[] = {
	"null", "SPI", "I2C", "reserved",
};
@@ -245,7 +262,7 @@ static struct i2c_msm_qup_reg_fld i2c_msm_qup_op_fields_map[] = {
	{ "MX_OUT_DN",      10,   1},
	{ "MX_IN_DN",       11,   1},
	{ "OUT_BLK_WR",     12,   1},
	{ "IN_BLK_WR",      13,   1},
	{ "IN_BLK_RD",      13,   1},
	{ "DONE_TGL",       14,   1},
	{ "NWD",            15,   1},
	{ NULL,              0,   1},
@@ -352,6 +369,8 @@ static const struct i2c_msm_qup_reg_dump i2c_msm_qup_reg_dump_map[] = {
{QUP_OUT_FIFO_CNT,       "QUP_OUT_CNT" },
{QUP_MX_READ_COUNT,      "MX_RD_CNT"   },
{QUP_MX_WRITE_COUNT,     "MX_WR_CNT"   },
{QUP_MX_INPUT_COUNT,     "MX_IN_CNT"   },
{QUP_MX_OUTPUT_COUNT,    "MX_OUT_CNT"  },
{0,                       NULL         },
};

@@ -947,8 +966,10 @@ i2c_msm_qup_xfer_init_reset_state(struct i2c_msm_ctrl *ctrl)
{
	struct i2c_msm_xfer *xfer = &ctrl->xfer;
	void __iomem * const base = ctrl->rsrcs.base;
	u32  in_cnt        = 0;
	u32  out_cnt       = 0;
	u32  mx_rd_cnt     = 0;
	u32  mx_wr_cnt     = 0;
	u32  mx_in_cnt     = 0;
	u32  mx_out_cnt    = 0;
	u32  no_input      = 0;
	u32  no_output     = 0;
	u32  input_mode    = i2c_msm_mode_to_reg_tbl[xfer->mode_id] << 12;
@@ -956,7 +977,8 @@ i2c_msm_qup_xfer_init_reset_state(struct i2c_msm_ctrl *ctrl)
	u32  config_reg;
	u32  io_modes_reg;
	u32  op_mask;

	u32  rx_cnt = 0;
	u32  tx_cnt = 0;
	/*
	 * BAM mode:
	 * 1. QUP_MX_*_COUNT must be zero in all cases.
@@ -966,20 +988,32 @@ i2c_msm_qup_xfer_init_reset_state(struct i2c_msm_ctrl *ctrl)
	 * 2. QUP_MX_READ_COUNT and QUP_MX_WRITE_COUNT reflect true count
	 * 3. QUP_NO_INPUT and QUP_NO_OUPUT are set according to counts
	 */
	if (xfer->mode_id == I2C_MSM_XFER_MODE_FIFO) {
		in_cnt        = xfer->rx_cnt  + xfer->rx_ovrhd_cnt;
		out_cnt       = xfer->tx_cnt + xfer->tx_ovrhd_cnt;
		no_input      = in_cnt  ? 0 : QUP_NO_INPUT;
		no_output     = out_cnt ? 0 : QUP_NO_OUPUT;
	if (xfer->mode_id != I2C_MSM_XFER_MODE_BAM) {
		rx_cnt   = xfer->rx_cnt + xfer->rx_ovrhd_cnt;
		tx_cnt   = xfer->tx_cnt + xfer->tx_ovrhd_cnt;
		no_input = rx_cnt  ? 0 : QUP_NO_INPUT;

		switch (xfer->mode_id) {
		case I2C_MSM_XFER_MODE_FIFO:
			mx_rd_cnt  = rx_cnt;
			mx_wr_cnt  = tx_cnt;
			break;
		case I2C_MSM_XFER_MODE_BLOCK:
			mx_in_cnt  = rx_cnt;
			mx_out_cnt = tx_cnt;
			break;
		default:
			break;
		}
	}

	/* BAM counter initializers */
	writel_relaxed(0, base + QUP_MX_INPUT_COUNT);
	writel_relaxed(0, base + QUP_MX_OUTPUT_COUNT);
	/* init BAM/BLOCK modes counter */
	writel_relaxed(mx_in_cnt,  base + QUP_MX_INPUT_COUNT);
	writel_relaxed(mx_out_cnt, base + QUP_MX_OUTPUT_COUNT);

	/* FIFO counter initializers */
	writel_relaxed(in_cnt , base + QUP_MX_READ_COUNT);
	writel_relaxed(out_cnt, base + QUP_MX_WRITE_COUNT);
	/* int FIFO mode counter */
	writel_relaxed(mx_rd_cnt, base + QUP_MX_READ_COUNT);
	writel_relaxed(mx_wr_cnt, base + QUP_MX_WRITE_COUNT);

	/*
	 * Set QUP mini-core to I2C tags ver-2
@@ -1275,7 +1309,7 @@ static int i2c_msm_fifo_xfer_process(struct i2c_msm_ctrl *ctrl)
		return ret;

	/* wait for input done interrupt */
	ret = i2c_msm_xfer_wait_for_completion(ctrl);
	ret = i2c_msm_xfer_wait_for_completion(ctrl, &ctrl->xfer.complete);
	if (ret < 0)
		return ret;

@@ -1338,6 +1372,348 @@ static int i2c_msm_fifo_create_struct(struct i2c_msm_ctrl *ctrl)
	return 0;
}

static void i2c_msm_blk_teardown(struct i2c_msm_ctrl *ctrl) {}

static void i2c_msm_blk_destroy_struct(struct i2c_msm_ctrl *ctrl)
{
	struct i2c_msm_xfer_mode_blk *blk = i2c_msm_blk_get_struct(ctrl);
	kfree(blk->tx_cache);
	kfree(blk->rx_cache);
	kfree(blk);
	i2c_msm_blk_set_struct(ctrl, NULL);
}

/*
 * i2c_msm_blk_create_struct: Allocate memory and initialize blk structure
 *
 * @return 0 on success or error code
 */
static int i2c_msm_blk_create_struct(struct i2c_msm_ctrl *ctrl)
{
	u32 reg_data;
	int ret;
	struct i2c_msm_xfer_mode_blk *blk = kmalloc(sizeof(*blk), GFP_KERNEL);
	if (!blk) {
		dev_err(ctrl->dev,
		"error on allocating memory for block mode. malloc(size:%zu)\n",
		 sizeof(*blk));
		return -ENOMEM;
	}

	reg_data = readl_relaxed(ctrl->rsrcs.base + QUP_IO_MODES);

	*blk = (struct i2c_msm_xfer_mode_blk) {
		.in_blk_sz  = i2c_msm_reg_io_modes_in_blk_sz(reg_data),
		.out_blk_sz = i2c_msm_reg_io_modes_out_blk_sz(reg_data),
		.ops = (struct i2c_msm_xfer_mode) {
			.xfer     = i2c_msm_blk_xfer,
			.teardown = i2c_msm_blk_teardown,
		},
	};

	blk->tx_cache = kmalloc(blk->out_blk_sz, GFP_KERNEL);
	if (!blk->tx_cache) {
		dev_err(ctrl->dev,
		"error on allocating memory for block tx_cache. malloc(size:%zu)\n",
		 blk->out_blk_sz);
		ret = -ENOMEM;
		goto out_buf_err;
	}

	blk->rx_cache = kmalloc(blk->in_blk_sz, GFP_KERNEL);
	if (!blk->tx_cache) {
		dev_err(ctrl->dev,
		"error on allocating memory for block tx_cache. malloc(size:%zu)\n",
		 blk->out_blk_sz);
		ret = -ENOMEM;
		goto in_buf_err;
	}

	i2c_msm_blk_set_struct(ctrl, blk);
	return 0;

in_buf_err:
	kfree(blk->tx_cache);
out_buf_err:
	kfree(blk);

	return ret;
}

/*
 * i2c_msm_blk_wr_flush: flushes internal cached block to FIFO
 *
 * @return 0 on success or error code
 */
static int i2c_msm_blk_wr_flush(struct i2c_msm_ctrl *ctrl)
{
	int byte_num;
	int ret = 0;
	struct i2c_msm_xfer_mode_blk *blk = i2c_msm_blk_get_struct(ctrl);
	u32 *buf_u32_ptr;

	if (!blk->tx_cache_idx)
		return 0;

	/* if no blocks availble wait for interrupt */
	ret = i2c_msm_xfer_wait_for_completion(ctrl, &blk->wait_tx_blk);
	if (ret)
		return ret;

	/*
	 * pause the controller until we finish loading the block in order to
	 * avoid race conditions
	 */
	ret = i2c_msm_qup_state_set(ctrl, QUP_STATE_PAUSE);
	if (ret < 0)
		return ret;
	i2c_msm_dbg(ctrl, MSM_DBG, "OUT-BLK:%*phC", blk->tx_cache_idx,
							blk->tx_cache);

	for (byte_num = 0; byte_num < blk->tx_cache_idx;
						byte_num += sizeof(u32)) {
		buf_u32_ptr = (u32 *) (blk->tx_cache + byte_num);
		writel_relaxed(*buf_u32_ptr,
					ctrl->rsrcs.base + QUP_OUT_FIFO_BASE);
		*buf_u32_ptr = 0;
	}

	/* now cache is empty */
	blk->tx_cache_idx = 0;
	ret = i2c_msm_qup_state_set(ctrl, QUP_STATE_RUN);
	if (ret < 0)
		return ret;

	return ret;
}

/*
 * i2c_msm_blk_wr_buf:
 *
 * @len buf size (in bytes)
 * @return number of bytes from buf which have been processed (written to
 *         FIFO or kept in out buffer and will be written later)
 */
static size_t
i2c_msm_blk_wr_buf(struct i2c_msm_ctrl *ctrl, const u8 *buf, size_t len)
{
	struct i2c_msm_xfer_mode_blk *blk = i2c_msm_blk_get_struct(ctrl);
	int byte_num;
	int ret = 0;

	for (byte_num = 0; byte_num < len; ++byte_num, ++buf) {
		blk->tx_cache[blk->tx_cache_idx] = *buf;
		++blk->tx_cache_idx;

		/* flush cached buffer to HW FIFO when full */
		if (blk->tx_cache_idx == blk->out_blk_sz) {
			ret = i2c_msm_blk_wr_flush(ctrl);
			if (ret)
				return ret;
		}
	}
	return byte_num;
}

/*
 * i2c_msm_blk_xfer_wr_tag: buffered writing the tag of current buf
 * @return zero on success
 */
static int i2c_msm_blk_xfer_wr_tag(struct i2c_msm_ctrl *ctrl)
{
	struct i2c_msm_xfer_buf *buf = &ctrl->xfer.cur_buf;
	size_t len = 0;

	if (!buf->out_tag.len)
		return 0;

	len = i2c_msm_blk_wr_buf(ctrl, (u8 *) &buf->out_tag.val,
							buf->out_tag.len);
	if (len != buf->out_tag.len)
		return -EFAULT;

	buf->out_tag = (struct i2c_msm_tag) {0};
	return 0;
}

/*
 * i2c_msm_blk_wr_xfer_buf: writes ctrl->xfer.cur_buf to HW
 *
 * @return zero on success
 */
static int i2c_msm_blk_wr_xfer_buf(struct i2c_msm_ctrl *ctrl)
{
	struct i2c_msm_xfer_buf *buf  = &ctrl->xfer.cur_buf;
	size_t len;
	size_t buf_has_bc = buf->len - buf->byte_idx;
	int ret;
	ret = i2c_msm_blk_xfer_wr_tag(ctrl);
	if (ret)
		return ret;

	len = i2c_msm_blk_wr_buf(ctrl, i2c_msm_buf_to_ptr(buf), buf_has_bc);
	if (len < buf_has_bc)
		return -EFAULT;

	buf->byte_idx += len;
	buf->prcsed_bc = len;
	return 0;
}

/*
 * i2c_msm_blk_rd_blk: read a block from HW FIFO to internal cache
 *
 * @return number of bytes read or negative error value
 * @need_bc number of bytes that we need
 *
 * uses internal counter to keep track of number of available blocks. When
 * zero, waits for interrupt.
 */
static int i2c_msm_blk_rd_blk(struct i2c_msm_ctrl *ctrl, size_t need_bc)
{
	int byte_num;
	int ret = 0;
	struct i2c_msm_xfer_mode_blk *blk = i2c_msm_blk_get_struct(ctrl);
	u32 *cache_ptr = (u32 *) blk->rx_cache;
	int read_bc    = min_t(size_t, blk->in_blk_sz, need_bc);

	/* wait for block avialble interrupt */
	ret = i2c_msm_xfer_wait_for_completion(ctrl, &blk->wait_rx_blk);
	if (ret)
		return ret;

	/* Read block from HW to cache */
	for (byte_num = 0; byte_num < blk->in_blk_sz;
					byte_num += sizeof(u32)) {
		if (byte_num < read_bc) {
			*cache_ptr = readl_relaxed(ctrl->rsrcs.base +
							QUP_IN_FIFO_BASE);
			++cache_ptr;
		}
	}
	blk->rx_cache_idx = 0;
	return read_bc;
}

/*
 * i2c_msm_blk_rd_xfer_buf: fill in ctrl->xfer.cur_buf from HW
 *
 * @return zero on success
 */
static int i2c_msm_blk_rd_xfer_buf(struct i2c_msm_ctrl *ctrl)
{
	struct i2c_msm_xfer_mode_blk *blk = i2c_msm_blk_get_struct(ctrl);
	struct i2c_msm_xfer_buf *buf      = &ctrl->xfer.cur_buf;
	size_t bc_to_prcs                 = buf->len - buf->byte_idx;
	struct i2c_msg *msg               = ctrl->xfer.msgs + buf->msg_idx;
	size_t copy_bc;         /* number of bytes to copy to user's buffer */
	int cache_avail_bc;
	int    ret = 0;

	/* write tag to out FIFO */
	ret = i2c_msm_blk_xfer_wr_tag(ctrl);
	if (ret)
		return ret;
	i2c_msm_blk_wr_flush(ctrl);

	while (bc_to_prcs || buf->in_tag.len) {
		cache_avail_bc = i2c_msm_blk_rd_blk(ctrl,
						bc_to_prcs + buf->in_tag.len);

		i2c_msm_dbg(ctrl, MSM_DBG, "IN-BLK:%*phC\n", cache_avail_bc,
					blk->rx_cache + blk->rx_cache_idx);

		if (cache_avail_bc < 0)
			return cache_avail_bc;

		/* discard tag from input FIFO */
		if (buf->in_tag.len) {
			size_t discard_bc = min_t(size_t, cache_avail_bc,
							buf->in_tag.len);
			blk->rx_cache_idx += discard_bc;
			buf->in_tag.len   -= discard_bc;
			cache_avail_bc    -= discard_bc;
		}

		/* copy bytes from cached block to user's buffer */
		copy_bc = min_t(size_t, cache_avail_bc, bc_to_prcs);
		memcpy(msg->buf + buf->byte_idx,
			blk->rx_cache + blk->rx_cache_idx, copy_bc);

		blk->rx_cache_idx += copy_bc;
		bc_to_prcs        -= copy_bc;
		buf->byte_idx     += copy_bc;
	}
	return ret;
}

/*
 * i2c_msm_blk_xfer: process transfer using block mode
 */
static int i2c_msm_blk_xfer(struct i2c_msm_ctrl *ctrl)
{
	int ret = 0;
	struct i2c_msm_xfer_buf      *buf = &ctrl->xfer.cur_buf;
	struct i2c_msm_xfer_mode_blk *blk = i2c_msm_blk_get_struct(ctrl);

	if (!blk) {
		ret = i2c_msm_blk_create_struct(ctrl);
		if (ret)
			return ret;
		blk = i2c_msm_blk_get_struct(ctrl);
	}

	init_completion(&blk->wait_rx_blk);
	init_completion(&blk->wait_tx_blk);

	/* tx_cnt > 0 always */
	blk->complete_mask = QUP_MAX_OUTPUT_DONE_FLAG;
	if (&ctrl->xfer.rx_cnt)
		blk->complete_mask |= QUP_MAX_INPUT_DONE_FLAG;

	/* initialize block mode for new transfer */
	blk->tx_cache_idx = 0;
	blk->rx_cache_idx = 0;

	ret = i2c_msm_qup_state_set(ctrl, QUP_STATE_RESET);
	if (ret < 0)
		return ret;

	/* program qup registers */
	i2c_msm_qup_xfer_init_reset_state(ctrl);

	ret = i2c_msm_qup_state_set(ctrl, QUP_STATE_RUN);
	if (ret < 0)
		return ret;

	/* program qup registers which must be set *after* reset */
	i2c_msm_qup_xfer_init_run_state(ctrl);

	while (i2c_msm_xfer_next_buf(ctrl)) {
		if (buf->is_rx) {
			ret = i2c_msm_blk_rd_xfer_buf(ctrl);
			if (ret)
				return ret;
			/*
			* SW workaround to wait for extra interrupt from
			* hardware for last block in block mode for read
			*/
			if (buf->is_last) {
				ret = i2c_msm_xfer_wait_for_completion(ctrl,
							&blk->wait_rx_blk);
				if (!ret)
					complete(&ctrl->xfer.complete);
			}
		} else {
			ret = i2c_msm_blk_wr_xfer_buf(ctrl);
			if (ret)
				return ret;
		}
	}
	i2c_msm_blk_wr_flush(ctrl);
	return i2c_msm_xfer_wait_for_completion(ctrl, &ctrl->xfer.complete);
}

/*
 * i2c_msm_bam_xfer_prepare: map DMA buffers, and create tags.
 * @return zero on success or negative error value
@@ -1562,7 +1938,7 @@ static int i2c_msm_bam_xfer_process(struct i2c_msm_ctrl *ctrl)
		}
	}

	ret = i2c_msm_xfer_wait_for_completion(ctrl);
	ret = i2c_msm_xfer_wait_for_completion(ctrl, &ctrl->xfer.complete);

bam_xfer_end:
	return ret;
@@ -2248,6 +2624,8 @@ static irqreturn_t i2c_msm_qup_isr(int irq, void *devid)
{
	struct i2c_msm_ctrl *ctrl = devid;
	void __iomem        *base = ctrl->rsrcs.base;
	struct i2c_msm_xfer *xfer = &ctrl->xfer;
	struct i2c_msm_xfer_mode_blk *blk;
	u32  i2c_status      = 0;
	u32  err_flags       = 0;
	u32  qup_op          = 0;
@@ -2279,6 +2657,21 @@ static irqreturn_t i2c_msm_qup_isr(int irq, void *devid)
	    "IRQ MASTER_STATUS:0x%08x ERROR_FLAGS:0x%08x OPERATIONAL:0x%08x\n",
	    i2c_status, err_flags, qup_op);

	/* clear interrupts fields */
	clr_flds = i2c_status & QUP_MSTR_STTS_ERR_MASK;
	if (clr_flds)
		writel_relaxed(clr_flds, base + QUP_I2C_STATUS);

	clr_flds = err_flags & QUP_ERR_FLGS_MASK;
	if (clr_flds)
		writel_relaxed(clr_flds,  base + QUP_ERROR_FLAGS);

	clr_flds = qup_op & (QUP_OUTPUT_SERVICE_FLAG | QUP_INPUT_SERVICE_FLAG);

	if (clr_flds)
		writel_relaxed(clr_flds, base + QUP_OPERATIONAL);
	mb();

	if (err_flags & QUP_ERR_FLGS_MASK) {
		dump_details    = true;
		signal_complete = true;
@@ -2307,32 +2700,58 @@ static irqreturn_t i2c_msm_qup_isr(int irq, void *devid)
			ctrl->xfer.err |= I2C_MSM_ERR_BUS_ERR;
	}

	blk = i2c_msm_blk_get_struct(ctrl);

	if (xfer->mode_id == I2C_MSM_XFER_MODE_BLOCK) {
		/*For Block Mode */
		/* block ready for writing */
		if (qup_op & QUP_OUTPUT_SERVICE_FLAG) {
			log_event = true;
			if (qup_op & QUP_OUT_BLOCK_WRITE_REQ)
				complete(&blk->wait_tx_blk);

			if ((qup_op & blk->complete_mask)
					== blk->complete_mask) {
				log_event       = true;
				signal_complete = true;
			}
		}
		/* block ready for reading */
		if (qup_op & QUP_INPUT_SERVICE_FLAG) {
			log_event = true;
			complete(&blk->wait_rx_blk);
		}
	} else {
		/* for FIFO/BAM Mode*/
		if (qup_op & QUP_MAX_INPUT_DONE_FLAG) {
			log_event = true;
			/*
		 * If last transaction is an input then the entire transfer
		 * is done
			 * If last transaction is an input then the entire
			 * transfer is done
			 */
			if (ctrl->xfer.last_is_rx)
				signal_complete = true;
		}
		/*
	 * Ideally, would like to check QUP_MAX_OUTPUT_DONE_FLAG. However,
	 * QUP_MAX_OUTPUT_DONE_FLAG is lagging behind QUP_OUTPUT_SERVICE_FLAG.
	 * The only reason for QUP_OUTPUT_SERVICE_FLAG to be set in FIFO mode
	 * is QUP_MAX_OUTPUT_DONE_FLAG condition.
	 * The code checking here QUP_OUTPUT_SERVICE_FLAG and assumes that
		 * Ideally, would like to check QUP_MAX_OUTPUT_DONE_FLAG.
		 * However, QUP_MAX_OUTPUT_DONE_FLAG is lagging behind
		 * QUP_OUTPUT_SERVICE_FLAG. The only reason for
		 * QUP_OUTPUT_SERVICE_FLAG to be set in FIFO mode is
		 * QUP_MAX_OUTPUT_DONE_FLAG condition. The code checking
		 * here QUP_OUTPUT_SERVICE_FLAG and assumes that
		 * QUP_MAX_OUTPUT_DONE_FLAG.
		 */
	if (qup_op & (QUP_OUTPUT_SERVICE_FLAG | QUP_MAX_OUTPUT_DONE_FLAG)) {
		if (qup_op & (QUP_OUTPUT_SERVICE_FLAG |
						QUP_MAX_OUTPUT_DONE_FLAG)) {
			log_event = true;
			/*
		 * If last transaction is an output then the entire transfer
		 * is done
			 * If last transaction is an output then the
			 * entire transfer is done
			 */
			if (!ctrl->xfer.last_is_rx)
				signal_complete = true;
		}
	}

	if (dump_details && (ctrl->dbgfs.dbg_lvl >= MSM_DBG)) {
		dev_info(ctrl->dev,
@@ -2341,18 +2760,6 @@ static irqreturn_t i2c_msm_qup_isr(int irq, void *devid)
		i2c_msm_dbg_qup_reg_dump(ctrl);
	}

	clr_flds = i2c_status & QUP_MSTR_STTS_ERR_MASK;
	if (clr_flds)
		writel_relaxed(clr_flds, base + QUP_I2C_STATUS);

	clr_flds = err_flags & QUP_ERR_FLGS_MASK;
	if (clr_flds)
		writel_relaxed(clr_flds,  base + QUP_ERROR_FLAGS);

	clr_flds = qup_op & (QUP_OUTPUT_SERVICE_FLAG | QUP_INPUT_SERVICE_FLAG);
	if (clr_flds)
		writel_relaxed(clr_flds,     base + QUP_OPERATIONAL);

isr_end:
	if (dump_details || log_event || (ctrl->dbgfs.dbg_lvl >= MSM_DBG))
		i2c_msm_prof_evnt_add(ctrl, MSM_PROF,
@@ -2393,6 +2800,10 @@ static int i2c_msm_qup_create_struct(struct i2c_msm_ctrl *ctrl)
	if (ret)
		i2c_msm_bam_destroy_struct(ctrl);

	ret = i2c_msm_blk_create_struct(ctrl);
	if (ret)
		return ret;

	return ret;
}

@@ -2400,6 +2811,7 @@ static void i2c_msm_qup_destroy_struct(struct i2c_msm_ctrl *ctrl)
{
	i2c_msm_fifo_destroy_struct(ctrl);
	i2c_msm_bam_destroy_struct(ctrl);
	i2c_msm_blk_destroy_struct(ctrl);
}

static int i2c_msm_qup_init(struct i2c_msm_ctrl *ctrl)
@@ -2528,7 +2940,8 @@ static int i2c_msm_qup_post_xfer(struct i2c_msm_ctrl *ctrl, int err)
	return err;
}

static void i2c_msm_qup_choose_mode(struct i2c_msm_ctrl *ctrl)
static enum i2c_msm_xfer_mode_id
i2c_msm_qup_choose_mode(struct i2c_msm_ctrl *ctrl)
{
	struct i2c_msm_xfer_mode_fifo *fifo;
	struct i2c_msm_xfer           *xfer = &ctrl->xfer;
@@ -2538,13 +2951,16 @@ static void i2c_msm_qup_choose_mode(struct i2c_msm_ctrl *ctrl)
	fifo = i2c_msm_fifo_get_struct(ctrl);

	if (ctrl->dbgfs.force_xfer_mode != I2C_MSM_XFER_MODE_NONE)
		xfer->mode_id = ctrl->dbgfs.force_xfer_mode;
	else if (((rx_cnt_sum < fifo->input_fifo_sz) &&
		  (tx_cnt_sum < fifo->output_fifo_sz)) ||
						ctrl->rsrcs.disable_dma)
		xfer->mode_id = I2C_MSM_XFER_MODE_FIFO;
	else
		xfer->mode_id = I2C_MSM_XFER_MODE_BAM;
		return ctrl->dbgfs.force_xfer_mode;

	if (((rx_cnt_sum < fifo->input_fifo_sz) &&
		(tx_cnt_sum < fifo->output_fifo_sz)))
		return I2C_MSM_XFER_MODE_FIFO;

	if (ctrl->rsrcs.disable_dma)
		return I2C_MSM_XFER_MODE_BLOCK;

	return I2C_MSM_XFER_MODE_BAM;
}

/*
@@ -2616,14 +3032,14 @@ static void i2c_msm_xfer_calc_timeout(struct i2c_msm_ctrl *ctrl)
	ctrl->xfer.timeout = usecs_to_jiffies(xfer_max_usec);
}

static int i2c_msm_xfer_wait_for_completion(struct i2c_msm_ctrl *ctrl)
static int i2c_msm_xfer_wait_for_completion(struct i2c_msm_ctrl *ctrl,
						struct completion *complete)
{
	struct i2c_msm_xfer *xfer = &ctrl->xfer;
	long  time_left;
	int   ret = 0;

	time_left = wait_for_completion_timeout(&xfer->complete,
							xfer->timeout);
	time_left = wait_for_completion_timeout(complete, xfer->timeout);
	if (!time_left) {
		i2c_msm_prof_evnt_add(ctrl, MSM_ERR, i2c_msm_prof_dump_cmplt_fl,
					xfer->timeout, time_left, 0);
@@ -2818,7 +3234,7 @@ i2c_msm_frmwrk_xfer(struct i2c_adapter *adap, struct i2c_msg msgs[], int num)

	i2c_msm_xfer_scan(ctrl);
	i2c_msm_xfer_calc_timeout(ctrl);
	(*ctrl->ver.choose_mode)(ctrl);
	xfer->mode_id = (*ctrl->ver.choose_mode)(ctrl);

	i2c_msm_prof_evnt_add(ctrl, MSM_PROF, i2c_msm_prof_dump_scan_sum,
		((xfer->rx_cnt & 0xff) | ((xfer->rx_ovrhd_cnt & 0xff) << 16)),
+32 −1
Original line number Diff line number Diff line
@@ -17,6 +17,8 @@
#ifndef _I2C_MSM_V2_H
#define _I2C_MSM_V2_H

#include <linux/bitops.h>

enum msm_i2_debug_level {
	MSM_ERR,	/* Error messages only. Always on */
	MSM_PROF,	/* High level events. Use for profiling */
@@ -100,6 +102,8 @@ enum {
	QUP_INPUT_SERVICE_FLAG   = 1U << 9,
	QUP_MAX_OUTPUT_DONE_FLAG = 1U << 10,
	QUP_MAX_INPUT_DONE_FLAG  = 1U << 11,
	QUP_OUT_BLOCK_WRITE_REQ  = BIT(12),
	QUP_IN_BLOCK_READ_REQ    = BIT(13),
};

/* Register:QUP_OPERATIONAL_MASK fields */
@@ -426,9 +430,36 @@ struct i2c_msm_xfer_mode_fifo {
	int                      out_buf_idx;
};

/* i2c_msm_xfer_mode_blk: operations and state of Block mode
 *
 * @in_blk_sz size of input/rx block
 * @out_blk_sz size of output/tx block
 * @tx_cache internal buffer to store tx data
 * @rx_cache internal buffer to store rx data
 * @rx_cache_idx points to the next unread index in rx cache
 * @tx_cache_idx points to the next unwritten index in tx cache
 * @wait_rx_blk completion object to wait on for end of blk rx transfer.
 * @wait_tx_blk completion object to wait on for end of blk tx transfer.
 * @complete_mask applied to QUP_OPERATIONAL to determine when blk
 *  xfer is complete.
 */
struct i2c_msm_xfer_mode_blk {
	struct i2c_msm_xfer_mode ops;
	size_t                   in_blk_sz;
	size_t                   out_blk_sz;
	u8                       *tx_cache;
	u8                       *rx_cache;
	int                      rx_cache_idx;
	int                      tx_cache_idx;
	struct completion        wait_rx_blk;
	struct completion        wait_tx_blk;
	u32                      complete_mask;
};

/* INPUT_MODE and OUTPUT_MODE filds of QUP_IO_MODES register */
enum i2c_msm_xfer_mode_id {
	I2C_MSM_XFER_MODE_FIFO,
	I2C_MSM_XFER_MODE_BLOCK,
	I2C_MSM_XFER_MODE_BAM,
	I2C_MSM_XFER_MODE_NONE, /* keep last as a counter */
};
@@ -460,7 +491,7 @@ struct i2c_msm_ctrl_ver {
	int			  (*reset)      (struct i2c_msm_ctrl *);
	int			  (*init_rsrcs) (struct platform_device *,
						 struct i2c_msm_ctrl *);
	void			  (*choose_mode)(struct i2c_msm_ctrl *);
	enum i2c_msm_xfer_mode_id (*choose_mode)(struct i2c_msm_ctrl *);
	int			  (*post_xfer)  (struct i2c_msm_ctrl *,
								int err);