diff mbox

[U-Boot] rockchip: i2c: fix >32 byte reads

Message ID 1501760891-13982-1-git-send-email-w.egorov@phytec.de
State Accepted
Commit 5deaa530280fda91b8fef632c62c94e7bfd89561
Delegated to: Philipp Tomsich
Headers show

Commit Message

Wadim Egorov Aug. 3, 2017, 11:48 a.m. UTC
The hw can read up to 32 bytes at a time. If we need
more than one chunk, we have to enter the plain RX mode.

Signed-off-by: Wadim Egorov <w.egorov@phytec.de>
---
 drivers/i2c/rk_i2c.c | 19 ++++++++++++++++---
 1 file changed, 16 insertions(+), 3 deletions(-)

Comments

Philipp Tomsich Aug. 4, 2017, 10:43 p.m. UTC | #1
> The hw can read up to 32 bytes at a time. If we need
> more than one chunk, we have to enter the plain RX mode.
> 
> Signed-off-by: Wadim Egorov <w.egorov@phytec.de>
> ---
>  drivers/i2c/rk_i2c.c | 19 ++++++++++++++++---
>  1 file changed, 16 insertions(+), 3 deletions(-)
> 

Acked-by: Philipp Tomsich <philipp.tomsich@theobroma-systems.com>
Philipp Tomsich Aug. 18, 2017, 1:06 p.m. UTC | #2
On Thu, 3 Aug 2017, Wadim Egorov wrote:

> The hw can read up to 32 bytes at a time. If we need
> more than one chunk, we have to enter the plain RX mode.

Why does this need to be in 'plain RX' mode for more than 32 bytes?
What happens if someone wants to write/transmit more than 32 bytes?

>
> Signed-off-by: Wadim Egorov <w.egorov@phytec.de>
> Acked-by: Philipp Tomsich <philipp.tomsich@theobroma-systems.com>
> ---
> drivers/i2c/rk_i2c.c | 19 ++++++++++++++++---
> 1 file changed, 16 insertions(+), 3 deletions(-)
>
> diff --git a/drivers/i2c/rk_i2c.c b/drivers/i2c/rk_i2c.c
> index 8bc045a..68e6653 100644
> --- a/drivers/i2c/rk_i2c.c
> +++ b/drivers/i2c/rk_i2c.c
> @@ -164,6 +164,7 @@ static int rk_i2c_read(struct rk_i2c *i2c, uchar chip, uint reg, uint r_len,
> 	uint rxdata;
> 	uint i, j;
> 	int err;
> +	bool snd_chunk = false;
>
> 	debug("rk_i2c_read: chip = %d, reg = %d, r_len = %d, b_len = %d\n",
> 	      chip, reg, r_len, b_len);
> @@ -184,15 +185,26 @@ static int rk_i2c_read(struct rk_i2c *i2c, uchar chip, uint reg, uint r_len,
>
> 	while (bytes_remain_len) {
> 		if (bytes_remain_len > RK_I2C_FIFO_SIZE) {
> -			con = I2C_CON_EN | I2C_CON_MOD(I2C_MODE_TRX);
> +			con = I2C_CON_EN;
> 			bytes_xferred = 32;
> 		} else {
> -			con = I2C_CON_EN | I2C_CON_MOD(I2C_MODE_TRX) |
> -				I2C_CON_LASTACK;
> +			/*
> +			 * The hw can read up to 32 bytes at a time. If we need
> +			 * more than one chunk, send an ACK after the last byte.
> +			 */
> +			con = I2C_CON_EN | I2C_CON_LASTACK;
> 			bytes_xferred = bytes_remain_len;
> 		}
> 		words_xferred = DIV_ROUND_UP(bytes_xferred, 4);
>
> +		/*
> +		 * make sure we are in plain RX mode if we read a second chunk
> +		 */
> +		if (snd_chunk)
> +			con |= I2C_CON_MOD(I2C_MODE_RX);
> +		else
> +			con |= I2C_CON_MOD(I2C_MODE_TRX);
> +
> 		writel(con, &regs->con);
> 		writel(bytes_xferred, &regs->mrxcnt);
> 		writel(I2C_MBRFIEN | I2C_NAKRCVIEN, &regs->ien);
> @@ -227,6 +239,7 @@ static int rk_i2c_read(struct rk_i2c *i2c, uchar chip, uint reg, uint r_len,
> 		}
>
> 		bytes_remain_len -= bytes_xferred;
> +		snd_chunk = true;
> 		debug("I2C Read bytes_remain_len %d\n", bytes_remain_len);
> 	}
>
>
Wadim Egorov Aug. 21, 2017, 8:32 a.m. UTC | #3
Am 18.08.2017 um 15:06 schrieb Philipp Tomsich:
>
>
> On Thu, 3 Aug 2017, Wadim Egorov wrote:
>
>> The hw can read up to 32 bytes at a time. If we need
>> more than one chunk, we have to enter the plain RX mode.
>
> Why does this need to be in 'plain RX' mode for more than 32 bytes?

I am not sure why this need to be in receive only mode. Probably that's
how the I2C controller operates for more than 32 byte reads?
Anyway, if you try to read more than 32 bytes from an EEPROM, you will
get RXDATA registers filled with 0xFF in the following chunks without
this patch.
Same thing is done in the linux i2c driver:
 
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/i2c/busses/i2c-rk3x.c?h=v4.13-rc5#n322


> What happens if someone wants to write/transmit more than 32 bytes?

Should not affect other operations as the I2C_CON_MODE will be set
properly in rk_i2c_read/write() calls before every I2C transmit.

Regards,
Wadim

>
>>
>> Signed-off-by: Wadim Egorov <w.egorov@phytec.de>
>> Acked-by: Philipp Tomsich <philipp.tomsich@theobroma-systems.com>
>> ---
>> drivers/i2c/rk_i2c.c | 19 ++++++++++++++++---
>> 1 file changed, 16 insertions(+), 3 deletions(-)
>>
>> diff --git a/drivers/i2c/rk_i2c.c b/drivers/i2c/rk_i2c.c
>> index 8bc045a..68e6653 100644
>> --- a/drivers/i2c/rk_i2c.c
>> +++ b/drivers/i2c/rk_i2c.c
>> @@ -164,6 +164,7 @@ static int rk_i2c_read(struct rk_i2c *i2c, uchar
>> chip, uint reg, uint r_len,
>>     uint rxdata;
>>     uint i, j;
>>     int err;
>> +    bool snd_chunk = false;
>>
>>     debug("rk_i2c_read: chip = %d, reg = %d, r_len = %d, b_len = %d\n",
>>           chip, reg, r_len, b_len);
>> @@ -184,15 +185,26 @@ static int rk_i2c_read(struct rk_i2c *i2c,
>> uchar chip, uint reg, uint r_len,
>>
>>     while (bytes_remain_len) {
>>         if (bytes_remain_len > RK_I2C_FIFO_SIZE) {
>> -            con = I2C_CON_EN | I2C_CON_MOD(I2C_MODE_TRX);
>> +            con = I2C_CON_EN;
>>             bytes_xferred = 32;
>>         } else {
>> -            con = I2C_CON_EN | I2C_CON_MOD(I2C_MODE_TRX) |
>> -                I2C_CON_LASTACK;
>> +            /*
>> +             * The hw can read up to 32 bytes at a time. If we need
>> +             * more than one chunk, send an ACK after the last byte.
>> +             */
>> +            con = I2C_CON_EN | I2C_CON_LASTACK;
>>             bytes_xferred = bytes_remain_len;
>>         }
>>         words_xferred = DIV_ROUND_UP(bytes_xferred, 4);
>>
>> +        /*
>> +         * make sure we are in plain RX mode if we read a second chunk
>> +         */
>> +        if (snd_chunk)
>> +            con |= I2C_CON_MOD(I2C_MODE_RX);
>> +        else
>> +            con |= I2C_CON_MOD(I2C_MODE_TRX);
>> +
>>         writel(con, &regs->con);
>>         writel(bytes_xferred, &regs->mrxcnt);
>>         writel(I2C_MBRFIEN | I2C_NAKRCVIEN, &regs->ien);
>> @@ -227,6 +239,7 @@ static int rk_i2c_read(struct rk_i2c *i2c, uchar
>> chip, uint reg, uint r_len,
>>         }
>>
>>         bytes_remain_len -= bytes_xferred;
>> +        snd_chunk = true;
>>         debug("I2C Read bytes_remain_len %d\n", bytes_remain_len);
>>     }
>>
>>
Philipp Tomsich Sept. 5, 2017, 9:10 a.m. UTC | #4
> The hw can read up to 32 bytes at a time. If we need
> more than one chunk, we have to enter the plain RX mode.
> 
> Signed-off-by: Wadim Egorov <w.egorov@phytec.de>
> Acked-by: Philipp Tomsich <philipp.tomsich@theobroma-systems.com>
> ---
>  drivers/i2c/rk_i2c.c | 19 ++++++++++++++++---
>  1 file changed, 16 insertions(+), 3 deletions(-)
> 

Reviewed-by: Philipp Tomsich <philipp.tomsich@theobroma-systems.com>
Philipp Tomsich Sept. 5, 2017, 9:26 a.m. UTC | #5
> The hw can read up to 32 bytes at a time. If we need
> more than one chunk, we have to enter the plain RX mode.
> 
> Signed-off-by: Wadim Egorov <w.egorov@phytec.de>
> Acked-by: Philipp Tomsich <philipp.tomsich@theobroma-systems.com>
> Reviewed-by: Philipp Tomsich <philipp.tomsich@theobroma-systems.com>
> ---
>  drivers/i2c/rk_i2c.c | 19 ++++++++++++++++---
>  1 file changed, 16 insertions(+), 3 deletions(-)
> 

Applied to u-boot-rockchip, thanks!
diff mbox

Patch

diff --git a/drivers/i2c/rk_i2c.c b/drivers/i2c/rk_i2c.c
index 8bc045a..68e6653 100644
--- a/drivers/i2c/rk_i2c.c
+++ b/drivers/i2c/rk_i2c.c
@@ -164,6 +164,7 @@  static int rk_i2c_read(struct rk_i2c *i2c, uchar chip, uint reg, uint r_len,
 	uint rxdata;
 	uint i, j;
 	int err;
+	bool snd_chunk = false;
 
 	debug("rk_i2c_read: chip = %d, reg = %d, r_len = %d, b_len = %d\n",
 	      chip, reg, r_len, b_len);
@@ -184,15 +185,26 @@  static int rk_i2c_read(struct rk_i2c *i2c, uchar chip, uint reg, uint r_len,
 
 	while (bytes_remain_len) {
 		if (bytes_remain_len > RK_I2C_FIFO_SIZE) {
-			con = I2C_CON_EN | I2C_CON_MOD(I2C_MODE_TRX);
+			con = I2C_CON_EN;
 			bytes_xferred = 32;
 		} else {
-			con = I2C_CON_EN | I2C_CON_MOD(I2C_MODE_TRX) |
-				I2C_CON_LASTACK;
+			/*
+			 * The hw can read up to 32 bytes at a time. If we need
+			 * more than one chunk, send an ACK after the last byte.
+			 */
+			con = I2C_CON_EN | I2C_CON_LASTACK;
 			bytes_xferred = bytes_remain_len;
 		}
 		words_xferred = DIV_ROUND_UP(bytes_xferred, 4);
 
+		/*
+		 * make sure we are in plain RX mode if we read a second chunk
+		 */
+		if (snd_chunk)
+			con |= I2C_CON_MOD(I2C_MODE_RX);
+		else
+			con |= I2C_CON_MOD(I2C_MODE_TRX);
+
 		writel(con, &regs->con);
 		writel(bytes_xferred, &regs->mrxcnt);
 		writel(I2C_MBRFIEN | I2C_NAKRCVIEN, &regs->ien);
@@ -227,6 +239,7 @@  static int rk_i2c_read(struct rk_i2c *i2c, uchar chip, uint reg, uint r_len,
 		}
 
 		bytes_remain_len -= bytes_xferred;
+		snd_chunk = true;
 		debug("I2C Read bytes_remain_len %d\n", bytes_remain_len);
 	}