linux-i2c.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Sven Eckelmann <sven@narfation.org>
To: Chris Packham <chris.packham@alliedtelesis.co.nz>,
	 Andi Shyti <andi.shyti@kernel.org>
Cc: linux-i2c@vger.kernel.org, linux-kernel@vger.kernel.org,
	 Jonas Jelonek <jelonek.jonas@gmail.com>,
	 Harshal Gohel <hg@simonwunderlich.de>,
	 Simon Wunderlich <sw@simonwunderlich.de>,
	 Sven Eckelmann <sven@narfation.org>,
	stable@vger.kernel.org
Subject: [PATCH i2c-host-fixes v5 2/5] i2c: rtl9300: Fix multi-byte I2C write
Date: Sun, 10 Aug 2025 20:05:14 +0200	[thread overview]
Message-ID: <20250810-i2c-rtl9300-multi-byte-v5-2-cd9dca0db722@narfation.org> (raw)
In-Reply-To: <20250810-i2c-rtl9300-multi-byte-v5-0-cd9dca0db722@narfation.org>

From: Harshal Gohel <hg@simonwunderlich.de>

The RTL93xx I2C controller has 4 32 bit registers to store the bytes for
the upcoming I2C transmission. The first byte is stored in the
least-significant byte of the first register. And the last byte in the most
significant byte of the last register. A map of the transferred bytes to
their order in the registers is:

reg 0: 0x04_03_02_01
reg 1: 0x08_07_06_05
reg 2: 0x0c_0b_0a_09
reg 3: 0x10_0f_0e_0d

The i2c_read() function basically demonstrates how the hardware would pick
up bytes from this register set. But the i2c_write() function was just
pushing bytes one after another to the least significant byte of a register
AFTER shifting the last one to the next more significant byte position.

If you would then have tried to send a buffer with numbers 1-11 using
i2c_write(), you would have ended up with following register content:

reg 0: 0x01_02_03_04
reg 1: 0x05_06_07_08
reg 2: 0x00_09_0a_0b
reg 3: 0x00_00_00_00

On the wire, you would then have seen:

  Sr Addr Wr [A] 04 A 03 A 02 A 01 A 08 A 07 A 06 A 05 A 0b A 0a A 09 A P

But the correct data transmission was expected to be

  Sr Addr Wr [A] 01 A 02 A 03 A 04 A 05 A 06 A 07 A 08 A 09 A 0a A 0b A P

Because of this multi-byte ordering problem, only single byte i2c_write()
operations were executed correctly (on the wire).

By shifting the byte directly to the correct end position in the register,
it is possible to avoid this incorrect byte ordering and fix multi-byte
transmissions.

The second initialization (to 0) of vals was also be dropped because this
array is initialized to 0 on the stack by using `= {};`. This makes the
fix a lot more readable.

Cc: <stable@vger.kernel.org>
Fixes: c366be720235 ("i2c: Add driver for the RTL9300 I2C controller")
Signed-off-by: Harshal Gohel <hg@simonwunderlich.de>
Co-developed-by: Sven Eckelmann <sven@narfation.org>
Signed-off-by: Sven Eckelmann <sven@narfation.org>
Reviewed-by: Chris Packham <chris.packham@alliedtelesis.co.nz>
Tested-by: Chris Packham <chris.packham@alliedtelesis.co.nz>
---
 drivers/i2c/busses/i2c-rtl9300.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/drivers/i2c/busses/i2c-rtl9300.c b/drivers/i2c/busses/i2c-rtl9300.c
index 568495720810b373c4fa3b31d3f4cdec7c64b5f9..4a538b2660802c98e1a51d2ed782e154f2a5d1a0 100644
--- a/drivers/i2c/busses/i2c-rtl9300.c
+++ b/drivers/i2c/busses/i2c-rtl9300.c
@@ -143,10 +143,10 @@ static int rtl9300_i2c_write(struct rtl9300_i2c *i2c, u8 *buf, int len)
 		return -EIO;
 
 	for (i = 0; i < len; i++) {
-		if (i % 4 == 0)
-			vals[i/4] = 0;
-		vals[i/4] <<= 8;
-		vals[i/4] |= buf[i];
+		unsigned int shift = (i % 4) * 8;
+		unsigned int reg = i / 4;
+
+		vals[reg] |= buf[i] << shift;
 	}
 
 	return regmap_bulk_write(i2c->regmap, i2c->reg_base + RTL9300_I2C_MST_DATA_WORD0,

-- 
2.47.2


  parent reply	other threads:[~2025-08-10 18:06 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-08-10 18:05 [PATCH v5 0/5] i2c: rtl9300: Fix multi-byte I2C operations Sven Eckelmann
2025-08-10 18:05 ` [PATCH v5 1/5] i2c: rtl9300: Fix out-of-bounds bug in rtl9300_i2c_smbus_xfer Sven Eckelmann
2025-08-10 18:05 ` Sven Eckelmann [this message]
2025-08-10 18:05 ` [PATCH i2c-host-fixes v5 3/5] i2c: rtl9300: Increase timeout for transfer polling Sven Eckelmann
2025-08-10 18:05 ` [PATCH i2c-host-fixes v5 4/5] i2c: rtl9300: Add missing count byte for SMBus Block Ops Sven Eckelmann
2025-08-10 18:05 ` [PATCH i2c-host v5 5/5] i2c: rtl9300: Implement I2C block read and write Sven Eckelmann
2025-08-14  7:20   ` Jonas Jelonek
2025-08-19 21:28 ` [PATCH v5 0/5] i2c: rtl9300: Fix multi-byte I2C operations Andi Shyti

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=20250810-i2c-rtl9300-multi-byte-v5-2-cd9dca0db722@narfation.org \
    --to=sven@narfation.org \
    --cc=andi.shyti@kernel.org \
    --cc=chris.packham@alliedtelesis.co.nz \
    --cc=hg@simonwunderlich.de \
    --cc=jelonek.jonas@gmail.com \
    --cc=linux-i2c@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=stable@vger.kernel.org \
    --cc=sw@simonwunderlich.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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).