All of lore.kernel.org
 help / color / mirror / Atom feed
From: qiangming.xia <qiangming.xia@mediatek.com>
To: <wsa@the-dreams.de>, Wolfram Sang <wsa+renesas@sang-engineering.com>
Cc: devicetree@vger.kernel.org, srv_heupstream@mediatek.com,
	linux-kernel@vger.kernel.org, linux-mediatek@lists.infradead.org,
	linux-i2c@vger.kernel.org, linux-arm-kernel@lists.infradead.org
Subject: Re: [PATCH] i2c: mediatek: Add i2c support for continuous mode
Date: Mon, 15 Jun 2020 18:40:29 +0800	[thread overview]
Message-ID: <1592217629.22302.4.camel@mbjsdccf07> (raw)
In-Reply-To: <20200508071809.10187-1-qiangming.xia@mediatek.com>

Hi, Wolfram
	Do you have time to take a look at this? Thanks.

On Fri, 2020-05-08 at 15:18 +0800, Qiangming Xia wrote:
> From: "qiangming.xia" <qiangming.xia@mediatek.com>
> 
>     Mediatek i2c controller support for continuous mode, it allow to
> transfer multiple write messages once. It combined by
> 'S + addr(wr) + data + Sr + ... + Sr + S + addr(wr) + data + P'.
>     Some slave devices need write many offset ranges, and the offset range
> maybe not continuous. For example, need write 'offset_0', 'offset_3' and
> 'offset_5' 10 bytes, total 30 bytes. Slave device driver usually execute
> three times i2c transfer API,i.e,i2c_transfer() to complete it, however,
> it can use once transfer to finish it by continuous mode.
> 
> Signed-off-by: Qiangming Xia <qiangming.xia@mediatek.com>
> Feature: I2C
> ---
>  drivers/i2c/busses/i2c-mt65xx.c | 74 ++++++++++++++++++++++++++++++++-
>  1 file changed, 72 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/i2c/busses/i2c-mt65xx.c b/drivers/i2c/busses/i2c-mt65xx.c
> index 2152ec5f535c..9bab8c44ad58 100644
> --- a/drivers/i2c/busses/i2c-mt65xx.c
> +++ b/drivers/i2c/busses/i2c-mt65xx.c
> @@ -98,6 +98,7 @@ enum mtk_trans_op {
>  	I2C_MASTER_WR = 1,
>  	I2C_MASTER_RD,
>  	I2C_MASTER_WRRD,
> +	I2C_MASTER_CONTINUOUS_WR,
>  };
>  
>  enum I2C_REGS_OFFSET {
> @@ -619,6 +620,9 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs,
>  					    OFFSET_TRANSFER_LEN);
>  		}
>  		mtk_i2c_writew(i2c, I2C_WRRD_TRANAC_VALUE, OFFSET_TRANSAC_LEN);
> +	} else if (i2c->op == I2C_MASTER_CONTINUOUS_WR) {
> +		mtk_i2c_writew(i2c, msgs->len / num, OFFSET_TRANSFER_LEN);
> +		mtk_i2c_writew(i2c, num, OFFSET_TRANSAC_LEN);
>  	} else {
>  		mtk_i2c_writew(i2c, msgs->len, OFFSET_TRANSFER_LEN);
>  		mtk_i2c_writew(i2c, num, OFFSET_TRANSAC_LEN);
> @@ -671,7 +675,7 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs,
>  
>  		writel((u32)wpaddr, i2c->pdmabase + OFFSET_TX_MEM_ADDR);
>  		writel(msgs->len, i2c->pdmabase + OFFSET_TX_LEN);
> -	} else {
> +	} else if (i2c->op == I2C_MASTER_WRRD) {
>  		writel(I2C_DMA_CLR_FLAG, i2c->pdmabase + OFFSET_INT_FLAG);
>  		writel(I2C_DMA_CLR_FLAG, i2c->pdmabase + OFFSET_CON);
>  
> @@ -722,6 +726,24 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs,
>  		writel((u32)rpaddr, i2c->pdmabase + OFFSET_RX_MEM_ADDR);
>  		writel(msgs->len, i2c->pdmabase + OFFSET_TX_LEN);
>  		writel((msgs + 1)->len, i2c->pdmabase + OFFSET_RX_LEN);
> +	} else if (i2c->op == I2C_MASTER_CONTINUOUS_WR) {
> +		writel(I2C_DMA_INT_FLAG_NONE, i2c->pdmabase + OFFSET_INT_FLAG);
> +		writel(I2C_DMA_CON_TX, i2c->pdmabase + OFFSET_CON);
> +
> +		wpaddr = dma_map_single(i2c->dev, msgs->buf,
> +					msgs->len, DMA_TO_DEVICE);
> +		if (dma_mapping_error(i2c->dev, wpaddr)) {
> +			kfree(msgs->buf);
> +			return -ENOMEM;
> +		}
> +
> +		if (i2c->dev_comp->support_33bits) {
> +			reg_4g_mode = mtk_i2c_set_4g_mode(wpaddr);
> +			writel(reg_4g_mode, i2c->pdmabase + OFFSET_TX_4G_MODE);
> +		}
> +
> +		writel((u32)wpaddr, i2c->pdmabase + OFFSET_TX_MEM_ADDR);
> +		writel(msgs->len, i2c->pdmabase + OFFSET_TX_LEN);
>  	}
>  
>  	writel(I2C_DMA_START_EN, i2c->pdmabase + OFFSET_EN);
> @@ -752,7 +774,7 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs,
>  				 msgs->len, DMA_FROM_DEVICE);
>  
>  		i2c_put_dma_safe_msg_buf(dma_rd_buf, msgs, true);
> -	} else {
> +	} else if (i2c->op == I2C_MASTER_WRRD) {
>  		dma_unmap_single(i2c->dev, wpaddr, msgs->len,
>  				 DMA_TO_DEVICE);
>  		dma_unmap_single(i2c->dev, rpaddr, (msgs + 1)->len,
> @@ -760,6 +782,11 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs,
>  
>  		i2c_put_dma_safe_msg_buf(dma_wr_buf, msgs, true);
>  		i2c_put_dma_safe_msg_buf(dma_rd_buf, (msgs + 1), true);
> +	} else if (i2c->op == I2C_MASTER_CONTINUOUS_WR) {
> +		dma_unmap_single(i2c->dev, wpaddr,
> +				 msgs->len, DMA_TO_DEVICE);
> +
> +		kfree(msgs->buf);
>  	}
>  
>  	if (ret == 0) {
> @@ -783,6 +810,9 @@ static int mtk_i2c_transfer(struct i2c_adapter *adap,
>  	int ret;
>  	int left_num = num;
>  	struct mtk_i2c *i2c = i2c_get_adapdata(adap);
> +	struct i2c_msg multi_msg[1];
> +	u8 *dma_multi_wr_buf;
> +	int j;
>  
>  	ret = mtk_i2c_clock_enable(i2c);
>  	if (ret)
> @@ -798,6 +828,46 @@ static int mtk_i2c_transfer(struct i2c_adapter *adap,
>  		}
>  	}
>  
> +	if (num > 1) {
> +		for (int i = 0; i < num - 1; i++) {
> +			if (!(msgs[i].flags & I2C_M_RD) &&
> +				!(msgs[i+1].flags & I2C_M_RD) &&
> +					(msgs[i].addr == msgs[i+1].addr) &&
> +						(msgs[i].len ==
> +							msgs[i+1].len)) {
> +				continue;
> +			} else
> +				break;
> +		}
> +		if (i >= num - 1) {
> +			i2c->op = I2C_MASTER_CONTINUOUS_WR;
> +			j = 0;
> +			dma_multi_wr_buf = kzalloc(msgs->len * num, GFP_KERNEL);
> +			if (!dma_multi_wr_buf) {
> +				ret =  -ENOMEM;
> +				goto err_exit;
> +			}
> +			multi_msg->addr  = msgs->addr;
> +			multi_msg->len   = msgs->len * num;
> +			multi_msg->buf   = dma_multi_wr_buf;
> +			multi_msg->flags  = 0;
> +			while (j < num) {
> +				memcpy(dma_multi_wr_buf + msgs->len * j,
> +							msgs->buf, msgs->len);
> +				j++;
> +				msgs++;
> +			}
> +
> +			i2c->ignore_restart_irq = false;
> +			ret = mtk_i2c_do_transfer(i2c, multi_msg, num, 0);
> +			if (ret < 0)
> +				goto err_exit;
> +			ret = num;
> +				goto err_exit;
> +
> +		}
> +	}
> +
>  	if (i2c->auto_restart && num >= 2 && i2c->speed_hz > MAX_FS_MODE_SPEED)
>  		/* ignore the first restart irq after the master code,
>  		 * otherwise the first transfer will be discarded.

_______________________________________________
Linux-mediatek mailing list
Linux-mediatek@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-mediatek

WARNING: multiple messages have this Message-ID (diff)
From: qiangming.xia <qiangming.xia@mediatek.com>
To: <wsa@the-dreams.de>, Wolfram Sang <wsa+renesas@sang-engineering.com>
Cc: devicetree@vger.kernel.org, srv_heupstream@mediatek.com,
	linux-kernel@vger.kernel.org, linux-mediatek@lists.infradead.org,
	linux-i2c@vger.kernel.org, linux-arm-kernel@lists.infradead.org
Subject: Re: [PATCH] i2c: mediatek: Add i2c support for continuous mode
Date: Mon, 15 Jun 2020 18:40:29 +0800	[thread overview]
Message-ID: <1592217629.22302.4.camel@mbjsdccf07> (raw)
In-Reply-To: <20200508071809.10187-1-qiangming.xia@mediatek.com>

Hi, Wolfram
	Do you have time to take a look at this? Thanks.

On Fri, 2020-05-08 at 15:18 +0800, Qiangming Xia wrote:
> From: "qiangming.xia" <qiangming.xia@mediatek.com>
> 
>     Mediatek i2c controller support for continuous mode, it allow to
> transfer multiple write messages once. It combined by
> 'S + addr(wr) + data + Sr + ... + Sr + S + addr(wr) + data + P'.
>     Some slave devices need write many offset ranges, and the offset range
> maybe not continuous. For example, need write 'offset_0', 'offset_3' and
> 'offset_5' 10 bytes, total 30 bytes. Slave device driver usually execute
> three times i2c transfer API,i.e,i2c_transfer() to complete it, however,
> it can use once transfer to finish it by continuous mode.
> 
> Signed-off-by: Qiangming Xia <qiangming.xia@mediatek.com>
> Feature: I2C
> ---
>  drivers/i2c/busses/i2c-mt65xx.c | 74 ++++++++++++++++++++++++++++++++-
>  1 file changed, 72 insertions(+), 2 deletions(-)
> 
> diff --git a/drivers/i2c/busses/i2c-mt65xx.c b/drivers/i2c/busses/i2c-mt65xx.c
> index 2152ec5f535c..9bab8c44ad58 100644
> --- a/drivers/i2c/busses/i2c-mt65xx.c
> +++ b/drivers/i2c/busses/i2c-mt65xx.c
> @@ -98,6 +98,7 @@ enum mtk_trans_op {
>  	I2C_MASTER_WR = 1,
>  	I2C_MASTER_RD,
>  	I2C_MASTER_WRRD,
> +	I2C_MASTER_CONTINUOUS_WR,
>  };
>  
>  enum I2C_REGS_OFFSET {
> @@ -619,6 +620,9 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs,
>  					    OFFSET_TRANSFER_LEN);
>  		}
>  		mtk_i2c_writew(i2c, I2C_WRRD_TRANAC_VALUE, OFFSET_TRANSAC_LEN);
> +	} else if (i2c->op == I2C_MASTER_CONTINUOUS_WR) {
> +		mtk_i2c_writew(i2c, msgs->len / num, OFFSET_TRANSFER_LEN);
> +		mtk_i2c_writew(i2c, num, OFFSET_TRANSAC_LEN);
>  	} else {
>  		mtk_i2c_writew(i2c, msgs->len, OFFSET_TRANSFER_LEN);
>  		mtk_i2c_writew(i2c, num, OFFSET_TRANSAC_LEN);
> @@ -671,7 +675,7 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs,
>  
>  		writel((u32)wpaddr, i2c->pdmabase + OFFSET_TX_MEM_ADDR);
>  		writel(msgs->len, i2c->pdmabase + OFFSET_TX_LEN);
> -	} else {
> +	} else if (i2c->op == I2C_MASTER_WRRD) {
>  		writel(I2C_DMA_CLR_FLAG, i2c->pdmabase + OFFSET_INT_FLAG);
>  		writel(I2C_DMA_CLR_FLAG, i2c->pdmabase + OFFSET_CON);
>  
> @@ -722,6 +726,24 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs,
>  		writel((u32)rpaddr, i2c->pdmabase + OFFSET_RX_MEM_ADDR);
>  		writel(msgs->len, i2c->pdmabase + OFFSET_TX_LEN);
>  		writel((msgs + 1)->len, i2c->pdmabase + OFFSET_RX_LEN);
> +	} else if (i2c->op == I2C_MASTER_CONTINUOUS_WR) {
> +		writel(I2C_DMA_INT_FLAG_NONE, i2c->pdmabase + OFFSET_INT_FLAG);
> +		writel(I2C_DMA_CON_TX, i2c->pdmabase + OFFSET_CON);
> +
> +		wpaddr = dma_map_single(i2c->dev, msgs->buf,
> +					msgs->len, DMA_TO_DEVICE);
> +		if (dma_mapping_error(i2c->dev, wpaddr)) {
> +			kfree(msgs->buf);
> +			return -ENOMEM;
> +		}
> +
> +		if (i2c->dev_comp->support_33bits) {
> +			reg_4g_mode = mtk_i2c_set_4g_mode(wpaddr);
> +			writel(reg_4g_mode, i2c->pdmabase + OFFSET_TX_4G_MODE);
> +		}
> +
> +		writel((u32)wpaddr, i2c->pdmabase + OFFSET_TX_MEM_ADDR);
> +		writel(msgs->len, i2c->pdmabase + OFFSET_TX_LEN);
>  	}
>  
>  	writel(I2C_DMA_START_EN, i2c->pdmabase + OFFSET_EN);
> @@ -752,7 +774,7 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs,
>  				 msgs->len, DMA_FROM_DEVICE);
>  
>  		i2c_put_dma_safe_msg_buf(dma_rd_buf, msgs, true);
> -	} else {
> +	} else if (i2c->op == I2C_MASTER_WRRD) {
>  		dma_unmap_single(i2c->dev, wpaddr, msgs->len,
>  				 DMA_TO_DEVICE);
>  		dma_unmap_single(i2c->dev, rpaddr, (msgs + 1)->len,
> @@ -760,6 +782,11 @@ static int mtk_i2c_do_transfer(struct mtk_i2c *i2c, struct i2c_msg *msgs,
>  
>  		i2c_put_dma_safe_msg_buf(dma_wr_buf, msgs, true);
>  		i2c_put_dma_safe_msg_buf(dma_rd_buf, (msgs + 1), true);
> +	} else if (i2c->op == I2C_MASTER_CONTINUOUS_WR) {
> +		dma_unmap_single(i2c->dev, wpaddr,
> +				 msgs->len, DMA_TO_DEVICE);
> +
> +		kfree(msgs->buf);
>  	}
>  
>  	if (ret == 0) {
> @@ -783,6 +810,9 @@ static int mtk_i2c_transfer(struct i2c_adapter *adap,
>  	int ret;
>  	int left_num = num;
>  	struct mtk_i2c *i2c = i2c_get_adapdata(adap);
> +	struct i2c_msg multi_msg[1];
> +	u8 *dma_multi_wr_buf;
> +	int j;
>  
>  	ret = mtk_i2c_clock_enable(i2c);
>  	if (ret)
> @@ -798,6 +828,46 @@ static int mtk_i2c_transfer(struct i2c_adapter *adap,
>  		}
>  	}
>  
> +	if (num > 1) {
> +		for (int i = 0; i < num - 1; i++) {
> +			if (!(msgs[i].flags & I2C_M_RD) &&
> +				!(msgs[i+1].flags & I2C_M_RD) &&
> +					(msgs[i].addr == msgs[i+1].addr) &&
> +						(msgs[i].len ==
> +							msgs[i+1].len)) {
> +				continue;
> +			} else
> +				break;
> +		}
> +		if (i >= num - 1) {
> +			i2c->op = I2C_MASTER_CONTINUOUS_WR;
> +			j = 0;
> +			dma_multi_wr_buf = kzalloc(msgs->len * num, GFP_KERNEL);
> +			if (!dma_multi_wr_buf) {
> +				ret =  -ENOMEM;
> +				goto err_exit;
> +			}
> +			multi_msg->addr  = msgs->addr;
> +			multi_msg->len   = msgs->len * num;
> +			multi_msg->buf   = dma_multi_wr_buf;
> +			multi_msg->flags  = 0;
> +			while (j < num) {
> +				memcpy(dma_multi_wr_buf + msgs->len * j,
> +							msgs->buf, msgs->len);
> +				j++;
> +				msgs++;
> +			}
> +
> +			i2c->ignore_restart_irq = false;
> +			ret = mtk_i2c_do_transfer(i2c, multi_msg, num, 0);
> +			if (ret < 0)
> +				goto err_exit;
> +			ret = num;
> +				goto err_exit;
> +
> +		}
> +	}
> +
>  	if (i2c->auto_restart && num >= 2 && i2c->speed_hz > MAX_FS_MODE_SPEED)
>  		/* ignore the first restart irq after the master code,
>  		 * otherwise the first transfer will be discarded.

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

  reply	other threads:[~2020-06-15 10:41 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2020-05-08  7:18 [PATCH] i2c: mediatek: Add i2c support for continuous mode Qiangming Xia
2020-05-08  7:18 ` Qiangming Xia
2020-06-15 10:40 ` qiangming.xia [this message]
2020-06-15 10:40   ` qiangming.xia
2020-06-15 12:46   ` Wolfram Sang
2020-06-15 12:46     ` Wolfram Sang
2020-06-15 12:46     ` Wolfram Sang

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1592217629.22302.4.camel@mbjsdccf07 \
    --to=qiangming.xia@mediatek.com \
    --cc=devicetree@vger.kernel.org \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-i2c@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-mediatek@lists.infradead.org \
    --cc=srv_heupstream@mediatek.com \
    --cc=wsa+renesas@sang-engineering.com \
    --cc=wsa@the-dreams.de \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.