--- /dev/null
+From 57f312b955938fc4663f430cb57a71f2414f601b Mon Sep 17 00:00:00 2001
+Date: Sun, 10 Aug 2025 20:05:13 +0200
+Subject: [PATCH] i2c: rtl9300: Fix out-of-bounds bug in rtl9300_i2c_smbus_xfer
+
+The data->block[0] variable comes from user. Without proper check,
+the variable may be very large to cause an out-of-bounds bug.
+
+Fix this bug by checking the value of data->block[0] first.
+
+1. commit 39244cc75482 ("i2c: ismt: Fix an out-of-bounds bug in
+ ismt_access()")
+2. commit 92fbb6d1296f ("i2c: xgene-slimpro: Fix out-of-bounds bug in
+ xgene_slimpro_i2c_xfer()")
+
+Fixes: c366be720235 ("i2c: Add driver for the RTL9300 I2C controller")
+
+diff --git a/drivers/i2c/busses/i2c-rtl9300.c b/drivers/i2c/busses/i2c-rtl9300.c
+index e064e8a4a1f0..568495720810 100644
+--- a/drivers/i2c/busses/i2c-rtl9300.c
++++ b/drivers/i2c/busses/i2c-rtl9300.c
+@@ -281,6 +281,10 @@ static int rtl9300_i2c_smbus_xfer(struct i2c_adapter *adap, u16 addr, unsigned s
+ ret = rtl9300_i2c_reg_addr_set(i2c, command, 1);
+ if (ret)
+ goto out_unlock;
++ if (data->block[0] < 1 || data->block[0] > I2C_SMBUS_BLOCK_MAX) {
++ ret = -EINVAL;
++ goto out_unlock;
++ }
+ ret = rtl9300_i2c_config_xfer(i2c, chan, addr, data->block[0]);
+ if (ret)
+ goto out_unlock;
+--
+2.48.1
+
--- /dev/null
+From d67b740b9edfa46310355e2b68050f79ebf05a4c Mon Sep 17 00:00:00 2001
+Date: Sun, 10 Aug 2025 20:05:14 +0200
+Subject: [PATCH] i2c: rtl9300: Fix multi-byte I2C write
+
+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.
+
+Fixes: c366be720235 ("i2c: Add driver for the RTL9300 I2C controller")
+
+diff --git a/drivers/i2c/busses/i2c-rtl9300.c b/drivers/i2c/busses/i2c-rtl9300.c
+index 568495720810..4a538b266080 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.48.1
+
--- /dev/null
+From ceee7776c010c5f09d30985c9e5223b363a6172a Mon Sep 17 00:00:00 2001
+Date: Sun, 10 Aug 2025 20:05:15 +0200
+Subject: [PATCH] i2c: rtl9300: Increase timeout for transfer polling
+
+The timeout for transfers was only set to 2ms. Because of this relatively
+low limit, 12-byte read operations to the frontend MCU of a RTL8239 POE PSE
+chip cluster was consistently resulting in a timeout.
+
+The original OpenWrt downstream driver [1] was not using any timeout limit
+at all. This is also possible by setting the timeout_us parameter of
+regmap_read_poll_timeout() to 0. But since the driver currently implements
+the ETIMEDOUT error, it is more sensible to increase the timeout in such a
+way that communication with the (quite common) Realtek I2C-connected POE
+management solution is possible.
+
+[1] https://git.openwrt.org/?p=openwrt/openwrt.git;a=blob;f=target/linux/realtek/files-6.12/drivers/i2c/busses/i2c-rtl9300.c;h=c4d973195ef39dc56d6207e665d279745525fcac#l202
+
+Fixes: c366be720235 ("i2c: Add driver for the RTL9300 I2C controller")
+
+diff --git a/drivers/i2c/busses/i2c-rtl9300.c b/drivers/i2c/busses/i2c-rtl9300.c
+index 4a538b266080..4a282d57e2c1 100644
+--- a/drivers/i2c/busses/i2c-rtl9300.c
++++ b/drivers/i2c/busses/i2c-rtl9300.c
+@@ -175,7 +175,7 @@ static int rtl9300_i2c_execute_xfer(struct rtl9300_i2c *i2c, char read_write,
+ return ret;
+
+ ret = regmap_read_poll_timeout(i2c->regmap, i2c->reg_base + RTL9300_I2C_MST_CTRL1,
+- val, !(val & RTL9300_I2C_MST_CTRL1_I2C_TRIG), 100, 2000);
++ val, !(val & RTL9300_I2C_MST_CTRL1_I2C_TRIG), 100, 100000);
+ if (ret)
+ return ret;
+
+--
+2.48.1
+
--- /dev/null
+From 82b350dd8185ce790e61555c436f90b6501af23c Mon Sep 17 00:00:00 2001
+Date: Sun, 10 Aug 2025 20:05:16 +0200
+Subject: [PATCH] i2c: rtl9300: Add missing count byte for SMBus Block Ops
+
+The expected on-wire format of an SMBus Block Write is
+
+ S Addr Wr [A] Comm [A] Count [A] Data [A] Data [A] ... [A] Data [A] P
+
+Everything starting from the Count byte is provided by the I2C subsystem in
+the array data->block. But the driver was skipping the Count byte
+(data->block[0]) when sending it to the RTL93xx I2C controller.
+
+Only the actual data could be seen on the wire:
+
+ S Addr Wr [A] Comm [A] Data [A] Data [A] ... [A] Data [A] P
+
+This wire format is not SMBus Block Write compatible but matches the format
+of an I2C Block Write. Simply adding the count byte to the buffer for the
+I2C controller is enough to fix the transmission.
+
+This also affects read because the I2C controller must receive the count
+byte + $count * data bytes.
+
+Fixes: c366be720235 ("i2c: Add driver for the RTL9300 I2C controller")
+
+diff --git a/drivers/i2c/busses/i2c-rtl9300.c b/drivers/i2c/busses/i2c-rtl9300.c
+index 4a282d57e2c1..cfafe089102a 100644
+--- a/drivers/i2c/busses/i2c-rtl9300.c
++++ b/drivers/i2c/busses/i2c-rtl9300.c
+@@ -285,15 +285,15 @@ static int rtl9300_i2c_smbus_xfer(struct i2c_adapter *adap, u16 addr, unsigned s
+ ret = -EINVAL;
+ goto out_unlock;
+ }
+- ret = rtl9300_i2c_config_xfer(i2c, chan, addr, data->block[0]);
++ ret = rtl9300_i2c_config_xfer(i2c, chan, addr, data->block[0] + 1);
+ if (ret)
+ goto out_unlock;
+ if (read_write == I2C_SMBUS_WRITE) {
+- ret = rtl9300_i2c_write(i2c, &data->block[1], data->block[0]);
++ ret = rtl9300_i2c_write(i2c, &data->block[0], data->block[0] + 1);
+ if (ret)
+ goto out_unlock;
+ }
+- len = data->block[0];
++ len = data->block[0] + 1;
+ break;
+
+ default:
+--
+2.48.1
+
--- /dev/null
+From cd6c956fbc13156bcbcca084b46a8380caebc2a8 Mon Sep 17 00:00:00 2001
+Date: Sun, 31 Aug 2025 10:04:46 +0000
+Subject: [PATCH] i2c: rtl9300: fix channel number bound check
+
+Fix the current check for number of channels (child nodes in the device
+tree). Before, this was:
+
+if (device_get_child_node_count(dev) >= RTL9300_I2C_MUX_NCHAN)
+
+RTL9300_I2C_MUX_NCHAN gives the maximum number of channels so checking
+with '>=' isn't correct because it doesn't allow the last channel
+number. Thus, fix it to:
+
+if (device_get_child_node_count(dev) > RTL9300_I2C_MUX_NCHAN)
+
+Issue occured on a TP-Link TL-ST1008F v2.0 device (8 SFP+ ports) and fix
+is tested there.
+
+Fixes: c366be720235 ("i2c: Add driver for the RTL9300 I2C controller")
+
+diff --git a/drivers/i2c/busses/i2c-rtl9300.c b/drivers/i2c/busses/i2c-rtl9300.c
+index cfafe089102a..1a63790f1957 100644
+--- a/drivers/i2c/busses/i2c-rtl9300.c
++++ b/drivers/i2c/busses/i2c-rtl9300.c
+@@ -353,7 +353,7 @@ static int rtl9300_i2c_probe(struct platform_device *pdev)
+
+ platform_set_drvdata(pdev, i2c);
+
+- if (device_get_child_node_count(dev) >= RTL9300_I2C_MUX_NCHAN)
++ if (device_get_child_node_count(dev) > RTL9300_I2C_MUX_NCHAN)
+ return dev_err_probe(dev, -EINVAL, "Too many channels\n");
+
+ device_for_each_child_node(dev, child) {
+--
+2.48.1
+
--- /dev/null
+From 06418cb5a1a542a003fdb4ad8e76ea542d57cfba Mon Sep 17 00:00:00 2001
+Date: Sun, 31 Aug 2025 10:04:47 +0000
+Subject: [PATCH] i2c: rtl9300: ensure data length is within supported range
+
+Add an explicit check for the xfer length to 'rtl9300_i2c_config_xfer'
+to ensure the data length isn't within the supported range. In
+particular a data length of 0 is not supported by the hardware and
+causes unintended or destructive behaviour.
+
+This limitation becomes obvious when looking at the register
+documentation [1]. 4 bits are reserved for DATA_WIDTH and the value
+of these 4 bits is used as N + 1, allowing a data length range of
+1 <= len <= 16.
+
+Affected by this is the SMBus Quick Operation which works with a data
+length of 0. Passing 0 as the length causes an underflow of the value
+due to:
+
+(len - 1) & 0xf
+
+and effectively specifying a transfer length of 16 via the registers.
+This causes a 16-byte write operation instead of a Quick Write. For
+example, on SFP modules without write-protected EEPROM this soft-bricks
+them by overwriting some initial bytes.
+
+For completeness, also add a quirk for the zero length.
+
+[1] https://svanheule.net/realtek/longan/register/i2c_mst1_ctrl2
+
+Fixes: c366be720235 ("i2c: Add driver for the RTL9300 I2C controller")
+
+diff --git a/drivers/i2c/busses/i2c-rtl9300.c b/drivers/i2c/busses/i2c-rtl9300.c
+index 1a63790f1957..2b3e80aa1bdf 100644
+--- a/drivers/i2c/busses/i2c-rtl9300.c
++++ b/drivers/i2c/busses/i2c-rtl9300.c
+@@ -99,6 +99,9 @@ static int rtl9300_i2c_config_xfer(struct rtl9300_i2c *i2c, struct rtl9300_i2c_c
+ {
+ u32 val, mask;
+
++ if (len < 1 || len > 16)
++ return -EINVAL;
++
+ val = chan->bus_freq << RTL9300_I2C_MST_CTRL2_SCL_FREQ_OFS;
+ mask = RTL9300_I2C_MST_CTRL2_SCL_FREQ_MASK;
+
+@@ -323,7 +326,7 @@ static const struct i2c_algorithm rtl9300_i2c_algo = {
+ };
+
+ static struct i2c_adapter_quirks rtl9300_i2c_quirks = {
+- .flags = I2C_AQ_NO_CLK_STRETCH,
++ .flags = I2C_AQ_NO_CLK_STRETCH | I2C_AQ_NO_ZERO_LEN,
+ .max_read_len = 16,
+ .max_write_len = 16,
+ };
+--
+2.48.1
+
--- /dev/null
+From ede965fd555ac2536cf651893a998dbfd8e57b86 Mon Sep 17 00:00:00 2001
+Date: Sun, 31 Aug 2025 10:04:48 +0000
+Subject: [PATCH] i2c: rtl9300: remove broken SMBus Quick operation support
+
+Remove the SMBus Quick operation from this driver because it is not
+natively supported by the hardware and is wrongly implemented in the
+driver.
+
+The I2C controllers in Realtek RTL9300 and RTL9310 are SMBus-compliant
+but there doesn't seem to be native support for the SMBus Quick
+operation. It is not explicitly mentioned in the documentation but
+looking at the registers which configure an SMBus transaction, one can
+see that the data length cannot be set to 0. This suggests that the
+hardware doesn't allow any SMBus message without data bytes (except for
+those it does on it's own, see SMBus Block Read).
+
+The current implementation of SMBus Quick operation passes a length of
+0 (which is actually invalid). Before the fix of a bug in a previous
+commit, this led to a read operation of 16 bytes from any register (the
+one of a former transaction or any other value.
+
+This caused issues like soft-bricked SFP modules after a simple probe
+with i2cdetect which uses Quick by default. Running this with SFP
+modules whose EEPROM isn't write-protected, some of the initial bytes
+are overwritten because a 16-byte write operation is executed instead of
+a Quick Write. (This temporarily soft-bricked one of my DAC cables.)
+
+Because SMBus Quick operation is obviously not supported on these
+controllers (because a length of 0 cannot be set, even when no register
+address is set), remove that instead of claiming there is support. There
+also shouldn't be any kind of emulated 'Quick' which just does another
+kind of operation in the background. Otherwise, specific issues occur
+in case of a 'Quick' Write which actually writes unknown data to an
+unknown register.
+
+Fixes: c366be720235 ("i2c: Add driver for the RTL9300 I2C controller")
+
+diff --git a/drivers/i2c/busses/i2c-rtl9300.c b/drivers/i2c/busses/i2c-rtl9300.c
+index 2b3e80aa1bdf..9e1f71fed0fe 100644
+--- a/drivers/i2c/busses/i2c-rtl9300.c
++++ b/drivers/i2c/busses/i2c-rtl9300.c
+@@ -225,15 +225,6 @@ static int rtl9300_i2c_smbus_xfer(struct i2c_adapter *adap, u16 addr, unsigned s
+ }
+
+ switch (size) {
+- case I2C_SMBUS_QUICK:
+- ret = rtl9300_i2c_config_xfer(i2c, chan, addr, 0);
+- if (ret)
+- goto out_unlock;
+- ret = rtl9300_i2c_reg_addr_set(i2c, 0, 0);
+- if (ret)
+- goto out_unlock;
+- break;
+-
+ case I2C_SMBUS_BYTE:
+ if (read_write == I2C_SMBUS_WRITE) {
+ ret = rtl9300_i2c_config_xfer(i2c, chan, addr, 0);
+@@ -315,9 +306,9 @@ static int rtl9300_i2c_smbus_xfer(struct i2c_adapter *adap, u16 addr, unsigned s
+
+ static u32 rtl9300_i2c_func(struct i2c_adapter *a)
+ {
+- return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
+- I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
+- I2C_FUNC_SMBUS_BLOCK_DATA;
++ return I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA |
++ I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_BLOCK_DATA |
++ I2C_FUNC_SMBUS_I2C_BLOCK;
+ }
+
+ static const struct i2c_algorithm rtl9300_i2c_algo = {
+--
+2.48.1
+
--- /dev/null
+From 095530512152e6811278de9c30f170f0ac9705eb Mon Sep 17 00:00:00 2001
+Date: Sat, 27 Sep 2025 11:52:16 +0200
+Subject: [PATCH] i2c: rtl9300: Drop unsupported I2C_FUNC_SMBUS_I2C_BLOCK
+
+While applying the patch for commit ede965fd555a ("i2c: rtl9300: remove
+broken SMBus Quick operation support"), a conflict was incorrectly solved
+by adding the I2C_FUNC_SMBUS_I2C_BLOCK feature flag. But the code to handle
+I2C_SMBUS_I2C_BLOCK_DATA requests will be added by a separate commit.
+
+Fixes: ede965fd555a ("i2c: rtl9300: remove broken SMBus Quick operation support")
+
+diff --git a/drivers/i2c/busses/i2c-rtl9300.c b/drivers/i2c/busses/i2c-rtl9300.c
+index 9e1f71fed0fe..af991b28e4f8 100644
+--- a/drivers/i2c/busses/i2c-rtl9300.c
++++ b/drivers/i2c/busses/i2c-rtl9300.c
+@@ -307,8 +307,7 @@ static int rtl9300_i2c_smbus_xfer(struct i2c_adapter *adap, u16 addr, unsigned s
+ static u32 rtl9300_i2c_func(struct i2c_adapter *a)
+ {
+ return I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA |
+- I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_BLOCK_DATA |
+- I2C_FUNC_SMBUS_I2C_BLOCK;
++ I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_BLOCK_DATA;
+ }
+
+ static const struct i2c_algorithm rtl9300_i2c_algo = {
+--
+2.48.1
+
--- /dev/null
+From 415216ae3196e67bdb9515519f219d553bd38d3a Mon Sep 17 00:00:00 2001
+Date: Sat, 27 Sep 2025 11:52:17 +0200
+Subject: [PATCH] i2c: rtl9300: Implement I2C block read and write
+
+It was noticed that the original implementation of SMBus Block Write in the
+driver was actually an I2C Block Write. Both differ only in the Count byte
+before the actual data:
+
+ S Addr Wr [A] Comm [A] Count [A] Data [A] Data [A] ... [A] Data [A] P
+
+The I2C Block Write is just skipping this Count byte and starts directly
+with the data:
+
+ S Addr Wr [A] Comm [A] Data [A] Data [A] ... [A] Data [A] P
+
+The I2C controller of RTL93xx doesn't handle this Count byte special and it
+is simply another one of (16 possible) data bytes. Adding support for the
+I2C Block Write therefore only requires skipping the count byte (0) in
+data->block.
+
+It is similar for reads. The SMBUS Block read is having a Count byte before
+the data:
+
+ S Addr Wr [A] Comm [A]
+ Sr Addr Rd [A] [Count] A [Data] A [Data] A ... A [Data] NA P
+
+And the I2C Block Read is directly starting with the actual data:
+
+ S Addr Wr [A] Comm [A]
+ Sr Addr Rd [A] [Data] A [Data] A ... A [Data] NA P
+
+The I2C controller is also not handling this byte in a special way. It
+simply provides every byte after the Rd marker + Ack as part of the 16 byte
+receive buffer (registers). The content of this buffer just has to be
+copied to the right position in the receive data->block.
+
+
+diff --git a/drivers/i2c/busses/i2c-rtl9300.c b/drivers/i2c/busses/i2c-rtl9300.c
+index af991b28e4f8..9e6232075137 100644
+--- a/drivers/i2c/busses/i2c-rtl9300.c
++++ b/drivers/i2c/busses/i2c-rtl9300.c
+@@ -186,22 +186,32 @@ static int rtl9300_i2c_execute_xfer(struct rtl9300_i2c *i2c, char read_write,
+ return -EIO;
+
+ if (read_write == I2C_SMBUS_READ) {
+- if (size == I2C_SMBUS_BYTE || size == I2C_SMBUS_BYTE_DATA) {
++ switch (size) {
++ case I2C_SMBUS_BYTE:
++ case I2C_SMBUS_BYTE_DATA:
+ ret = regmap_read(i2c->regmap,
+ i2c->reg_base + RTL9300_I2C_MST_DATA_WORD0, &val);
+ if (ret)
+ return ret;
+ data->byte = val & 0xff;
+- } else if (size == I2C_SMBUS_WORD_DATA) {
++ break;
++ case I2C_SMBUS_WORD_DATA:
+ ret = regmap_read(i2c->regmap,
+ i2c->reg_base + RTL9300_I2C_MST_DATA_WORD0, &val);
+ if (ret)
+ return ret;
+ data->word = val & 0xffff;
+- } else {
++ break;
++ case I2C_SMBUS_I2C_BLOCK_DATA:
++ ret = rtl9300_i2c_read(i2c, &data->block[1], len);
++ if (ret)
++ return ret;
++ break;
++ default:
+ ret = rtl9300_i2c_read(i2c, &data->block[0], len);
+ if (ret)
+ return ret;
++ break;
+ }
+ }
+
+@@ -290,6 +300,25 @@ static int rtl9300_i2c_smbus_xfer(struct i2c_adapter *adap, u16 addr, unsigned s
+ len = data->block[0] + 1;
+ break;
+
++ case I2C_SMBUS_I2C_BLOCK_DATA:
++ ret = rtl9300_i2c_reg_addr_set(i2c, command, 1);
++ if (ret)
++ goto out_unlock;
++ if (data->block[0] < 1 || data->block[0] > I2C_SMBUS_BLOCK_MAX) {
++ ret = -EINVAL;
++ goto out_unlock;
++ }
++ ret = rtl9300_i2c_config_xfer(i2c, chan, addr, data->block[0]);
++ if (ret)
++ goto out_unlock;
++ if (read_write == I2C_SMBUS_WRITE) {
++ ret = rtl9300_i2c_write(i2c, &data->block[1], data->block[0]);
++ if (ret)
++ goto out_unlock;
++ }
++ len = data->block[0];
++ break;
++
+ default:
+ dev_err(&adap->dev, "Unsupported transaction %d\n", size);
+ ret = -EOPNOTSUPP;
+@@ -307,7 +336,8 @@ static int rtl9300_i2c_smbus_xfer(struct i2c_adapter *adap, u16 addr, unsigned s
+ static u32 rtl9300_i2c_func(struct i2c_adapter *a)
+ {
+ return I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA |
+- I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_BLOCK_DATA;
++ I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_BLOCK_DATA |
++ I2C_FUNC_SMBUS_I2C_BLOCK;
+ }
+
+ static const struct i2c_algorithm rtl9300_i2c_algo = {
+--
+2.48.1
+
--- /dev/null
+From 5a6ecb27435ef7a67d7bec4543f0c6303f34e8a6 Mon Sep 17 00:00:00 2001
+Date: Sat, 27 Sep 2025 10:19:23 +0000
+Subject: [PATCH] i2c: rtl9300: use regmap fields and API for registers
+
+Adapt the RTL9300 I2C controller driver to use more of the regmap
+API, especially make use of reg_field and regmap_field instead of macros
+to represent registers. Most register operations are performed through
+regmap_field_* API then.
+
+Handle SCL selection using separate chip-specific functions since this
+is already known to differ between the Realtek SoC families in such a
+way that this cannot be properly handled using just a different
+reg_field.
+
+This makes it easier to add support for newer generations or to handle
+differences between specific revisions within a series. Just by
+defining a separate driver data structure with the corresponding
+register field definitions and linking it to a new compatible.
+
+
+diff --git a/drivers/i2c/busses/i2c-rtl9300.c b/drivers/i2c/busses/i2c-rtl9300.c
+index 9e6232075137..8483bab72146 100644
+--- a/drivers/i2c/busses/i2c-rtl9300.c
++++ b/drivers/i2c/busses/i2c-rtl9300.c
+@@ -23,97 +23,117 @@ struct rtl9300_i2c_chan {
+ u8 sda_pin;
+ };
+
++enum rtl9300_i2c_reg_scope {
++ REG_SCOPE_GLOBAL,
++ REG_SCOPE_MASTER,
++};
++
++struct rtl9300_i2c_reg_field {
++ struct reg_field field;
++ enum rtl9300_i2c_reg_scope scope;
++};
++
++enum rtl9300_i2c_reg_fields {
++ F_DATA_WIDTH = 0,
++ F_DEV_ADDR,
++ F_I2C_FAIL,
++ F_I2C_TRIG,
++ F_MEM_ADDR,
++ F_MEM_ADDR_WIDTH,
++ F_RD_MODE,
++ F_RWOP,
++ F_SCL_FREQ,
++ F_SCL_SEL,
++ F_SDA_OUT_SEL,
++ F_SDA_SEL,
++
++ /* keep last */
++ F_NUM_FIELDS
++};
++
++struct rtl9300_i2c_drv_data {
++ struct rtl9300_i2c_reg_field field_desc[F_NUM_FIELDS];
++ int (*select_scl)(struct rtl9300_i2c *i2c, u8 scl);
++ u32 data_reg;
++ u8 max_nchan;
++};
++
+ #define RTL9300_I2C_MUX_NCHAN 8
+
+ struct rtl9300_i2c {
+ struct regmap *regmap;
+ struct device *dev;
+ struct rtl9300_i2c_chan chans[RTL9300_I2C_MUX_NCHAN];
++ struct regmap_field *fields[F_NUM_FIELDS];
+ u32 reg_base;
++ u32 data_reg;
+ u8 sda_pin;
+ struct mutex lock;
+ };
+
+ #define RTL9300_I2C_MST_CTRL1 0x0
+-#define RTL9300_I2C_MST_CTRL1_MEM_ADDR_OFS 8
+-#define RTL9300_I2C_MST_CTRL1_MEM_ADDR_MASK GENMASK(31, 8)
+-#define RTL9300_I2C_MST_CTRL1_SDA_OUT_SEL_OFS 4
+-#define RTL9300_I2C_MST_CTRL1_SDA_OUT_SEL_MASK GENMASK(6, 4)
+-#define RTL9300_I2C_MST_CTRL1_GPIO_SCL_SEL BIT(3)
+-#define RTL9300_I2C_MST_CTRL1_RWOP BIT(2)
+-#define RTL9300_I2C_MST_CTRL1_I2C_FAIL BIT(1)
+-#define RTL9300_I2C_MST_CTRL1_I2C_TRIG BIT(0)
+ #define RTL9300_I2C_MST_CTRL2 0x4
+-#define RTL9300_I2C_MST_CTRL2_RD_MODE BIT(15)
+-#define RTL9300_I2C_MST_CTRL2_DEV_ADDR_OFS 8
+-#define RTL9300_I2C_MST_CTRL2_DEV_ADDR_MASK GENMASK(14, 8)
+-#define RTL9300_I2C_MST_CTRL2_DATA_WIDTH_OFS 4
+-#define RTL9300_I2C_MST_CTRL2_DATA_WIDTH_MASK GENMASK(7, 4)
+-#define RTL9300_I2C_MST_CTRL2_MEM_ADDR_WIDTH_OFS 2
+-#define RTL9300_I2C_MST_CTRL2_MEM_ADDR_WIDTH_MASK GENMASK(3, 2)
+-#define RTL9300_I2C_MST_CTRL2_SCL_FREQ_OFS 0
+-#define RTL9300_I2C_MST_CTRL2_SCL_FREQ_MASK GENMASK(1, 0)
+ #define RTL9300_I2C_MST_DATA_WORD0 0x8
+ #define RTL9300_I2C_MST_DATA_WORD1 0xc
+ #define RTL9300_I2C_MST_DATA_WORD2 0x10
+ #define RTL9300_I2C_MST_DATA_WORD3 0x14
+-
+ #define RTL9300_I2C_MST_GLB_CTRL 0x384
+
+ static int rtl9300_i2c_reg_addr_set(struct rtl9300_i2c *i2c, u32 reg, u16 len)
+ {
+- u32 val, mask;
+ int ret;
+
+- val = len << RTL9300_I2C_MST_CTRL2_MEM_ADDR_WIDTH_OFS;
+- mask = RTL9300_I2C_MST_CTRL2_MEM_ADDR_WIDTH_MASK;
+-
+- ret = regmap_update_bits(i2c->regmap, i2c->reg_base + RTL9300_I2C_MST_CTRL2, mask, val);
++ ret = regmap_field_write(i2c->fields[F_MEM_ADDR_WIDTH], len);
+ if (ret)
+ return ret;
+
+- val = reg << RTL9300_I2C_MST_CTRL1_MEM_ADDR_OFS;
+- mask = RTL9300_I2C_MST_CTRL1_MEM_ADDR_MASK;
++ return regmap_field_write(i2c->fields[F_MEM_ADDR], reg);
++}
+
+- return regmap_update_bits(i2c->regmap, i2c->reg_base + RTL9300_I2C_MST_CTRL1, mask, val);
++static int rtl9300_i2c_select_scl(struct rtl9300_i2c *i2c, u8 scl)
++{
++ return regmap_field_write(i2c->fields[F_SCL_SEL], 1);
+ }
+
+ static int rtl9300_i2c_config_io(struct rtl9300_i2c *i2c, u8 sda_pin)
+ {
++ struct rtl9300_i2c_drv_data *drv_data;
+ int ret;
+- u32 val, mask;
+
+- ret = regmap_update_bits(i2c->regmap, RTL9300_I2C_MST_GLB_CTRL, BIT(sda_pin), BIT(sda_pin));
++ drv_data = (struct rtl9300_i2c_drv_data *)device_get_match_data(i2c->dev);
++
++ ret = regmap_field_update_bits(i2c->fields[F_SDA_SEL], BIT(sda_pin), BIT(sda_pin));
+ if (ret)
+ return ret;
+
+- val = (sda_pin << RTL9300_I2C_MST_CTRL1_SDA_OUT_SEL_OFS) |
+- RTL9300_I2C_MST_CTRL1_GPIO_SCL_SEL;
+- mask = RTL9300_I2C_MST_CTRL1_SDA_OUT_SEL_MASK | RTL9300_I2C_MST_CTRL1_GPIO_SCL_SEL;
++ ret = regmap_field_write(i2c->fields[F_SDA_OUT_SEL], sda_pin);
++ if (ret)
++ return ret;
+
+- return regmap_update_bits(i2c->regmap, i2c->reg_base + RTL9300_I2C_MST_CTRL1, mask, val);
++ return drv_data->select_scl(i2c, 0);
+ }
+
+ static int rtl9300_i2c_config_xfer(struct rtl9300_i2c *i2c, struct rtl9300_i2c_chan *chan,
+ u16 addr, u16 len)
+ {
+- u32 val, mask;
++ int ret;
+
+ if (len < 1 || len > 16)
+ return -EINVAL;
+
+- val = chan->bus_freq << RTL9300_I2C_MST_CTRL2_SCL_FREQ_OFS;
+- mask = RTL9300_I2C_MST_CTRL2_SCL_FREQ_MASK;
+-
+- val |= addr << RTL9300_I2C_MST_CTRL2_DEV_ADDR_OFS;
+- mask |= RTL9300_I2C_MST_CTRL2_DEV_ADDR_MASK;
++ ret = regmap_field_write(i2c->fields[F_SCL_FREQ], chan->bus_freq);
++ if (ret)
++ return ret;
+
+- val |= ((len - 1) & 0xf) << RTL9300_I2C_MST_CTRL2_DATA_WIDTH_OFS;
+- mask |= RTL9300_I2C_MST_CTRL2_DATA_WIDTH_MASK;
++ ret = regmap_field_write(i2c->fields[F_DEV_ADDR], addr);
++ if (ret)
++ return ret;
+
+- mask |= RTL9300_I2C_MST_CTRL2_RD_MODE;
++ ret = regmap_field_write(i2c->fields[F_DATA_WIDTH], (len - 1) & 0xf);
++ if (ret)
++ return ret;
+
+- return regmap_update_bits(i2c->regmap, i2c->reg_base + RTL9300_I2C_MST_CTRL2, mask, val);
++ return regmap_field_write(i2c->fields[F_RD_MODE], 0);
+ }
+
+ static int rtl9300_i2c_read(struct rtl9300_i2c *i2c, u8 *buf, int len)
+@@ -124,8 +144,7 @@ static int rtl9300_i2c_read(struct rtl9300_i2c *i2c, u8 *buf, int len)
+ if (len > 16)
+ return -EIO;
+
+- ret = regmap_bulk_read(i2c->regmap, i2c->reg_base + RTL9300_I2C_MST_DATA_WORD0,
+- vals, ARRAY_SIZE(vals));
++ ret = regmap_bulk_read(i2c->regmap, i2c->data_reg, vals, ARRAY_SIZE(vals));
+ if (ret)
+ return ret;
+
+@@ -152,52 +171,49 @@ static int rtl9300_i2c_write(struct rtl9300_i2c *i2c, u8 *buf, int len)
+ vals[reg] |= buf[i] << shift;
+ }
+
+- return regmap_bulk_write(i2c->regmap, i2c->reg_base + RTL9300_I2C_MST_DATA_WORD0,
+- vals, ARRAY_SIZE(vals));
++ return regmap_bulk_write(i2c->regmap, i2c->data_reg, vals, ARRAY_SIZE(vals));
+ }
+
+ static int rtl9300_i2c_writel(struct rtl9300_i2c *i2c, u32 data)
+ {
+- return regmap_write(i2c->regmap, i2c->reg_base + RTL9300_I2C_MST_DATA_WORD0, data);
++ return regmap_write(i2c->regmap, i2c->data_reg, data);
+ }
+
+ static int rtl9300_i2c_execute_xfer(struct rtl9300_i2c *i2c, char read_write,
+ int size, union i2c_smbus_data *data, int len)
+ {
+- u32 val, mask;
++ u32 val;
+ int ret;
+
+- val = read_write == I2C_SMBUS_WRITE ? RTL9300_I2C_MST_CTRL1_RWOP : 0;
+- mask = RTL9300_I2C_MST_CTRL1_RWOP;
+-
+- val |= RTL9300_I2C_MST_CTRL1_I2C_TRIG;
+- mask |= RTL9300_I2C_MST_CTRL1_I2C_TRIG;
++ ret = regmap_field_write(i2c->fields[F_RWOP], read_write == I2C_SMBUS_WRITE);
++ if (ret)
++ return ret;
+
+- ret = regmap_update_bits(i2c->regmap, i2c->reg_base + RTL9300_I2C_MST_CTRL1, mask, val);
++ ret = regmap_field_write(i2c->fields[F_I2C_TRIG], 1);
+ if (ret)
+ return ret;
+
+- ret = regmap_read_poll_timeout(i2c->regmap, i2c->reg_base + RTL9300_I2C_MST_CTRL1,
+- val, !(val & RTL9300_I2C_MST_CTRL1_I2C_TRIG), 100, 100000);
++ ret = regmap_field_read_poll_timeout(i2c->fields[F_I2C_TRIG], val, !val, 100, 100000);
+ if (ret)
+ return ret;
+
+- if (val & RTL9300_I2C_MST_CTRL1_I2C_FAIL)
++ ret = regmap_field_read(i2c->fields[F_I2C_FAIL], &val);
++ if (ret)
++ return ret;
++ if (val)
+ return -EIO;
+
+ if (read_write == I2C_SMBUS_READ) {
+ switch (size) {
+ case I2C_SMBUS_BYTE:
+ case I2C_SMBUS_BYTE_DATA:
+- ret = regmap_read(i2c->regmap,
+- i2c->reg_base + RTL9300_I2C_MST_DATA_WORD0, &val);
++ ret = regmap_read(i2c->regmap, i2c->data_reg, &val);
+ if (ret)
+ return ret;
+ data->byte = val & 0xff;
+ break;
+ case I2C_SMBUS_WORD_DATA:
+- ret = regmap_read(i2c->regmap,
+- i2c->reg_base + RTL9300_I2C_MST_DATA_WORD0, &val);
++ ret = regmap_read(i2c->regmap, i2c->data_reg, &val);
+ if (ret)
+ return ret;
+ data->word = val & 0xffff;
+@@ -355,9 +371,11 @@ static int rtl9300_i2c_probe(struct platform_device *pdev)
+ {
+ struct device *dev = &pdev->dev;
+ struct rtl9300_i2c *i2c;
++ struct fwnode_handle *child;
++ struct rtl9300_i2c_drv_data *drv_data;
++ struct reg_field fields[F_NUM_FIELDS];
+ u32 clock_freq, sda_pin;
+ int ret, i = 0;
+- struct fwnode_handle *child;
+
+ i2c = devm_kzalloc(dev, sizeof(*i2c), GFP_KERNEL);
+ if (!i2c)
+@@ -376,9 +394,22 @@ static int rtl9300_i2c_probe(struct platform_device *pdev)
+
+ platform_set_drvdata(pdev, i2c);
+
+- if (device_get_child_node_count(dev) > RTL9300_I2C_MUX_NCHAN)
++ drv_data = (struct rtl9300_i2c_drv_data *)device_get_match_data(i2c->dev);
++ if (device_get_child_node_count(dev) > drv_data->max_nchan)
+ return dev_err_probe(dev, -EINVAL, "Too many channels\n");
+
++ i2c->data_reg = i2c->reg_base + drv_data->data_reg;
++ for (i = 0; i < F_NUM_FIELDS; i++) {
++ fields[i] = drv_data->field_desc[i].field;
++ if (drv_data->field_desc[i].scope == REG_SCOPE_MASTER)
++ fields[i].reg += i2c->reg_base;
++ }
++ ret = devm_regmap_field_bulk_alloc(dev, i2c->regmap, i2c->fields,
++ fields, F_NUM_FIELDS);
++ if (ret)
++ return ret;
++
++ i = 0;
+ device_for_each_child_node(dev, child) {
+ struct rtl9300_i2c_chan *chan = &i2c->chans[i];
+ struct i2c_adapter *adap = &chan->adap;
+@@ -395,7 +426,6 @@ static int rtl9300_i2c_probe(struct platform_device *pdev)
+ case I2C_MAX_STANDARD_MODE_FREQ:
+ chan->bus_freq = RTL9300_I2C_STD_FREQ;
+ break;
+-
+ case I2C_MAX_FAST_MODE_FREQ:
+ chan->bus_freq = RTL9300_I2C_FAST_FREQ;
+ break;
+@@ -427,11 +457,37 @@ static int rtl9300_i2c_probe(struct platform_device *pdev)
+ return 0;
+ }
+
++#define GLB_REG_FIELD(reg, msb, lsb) \
++ { .field = REG_FIELD(reg, msb, lsb), .scope = REG_SCOPE_GLOBAL }
++#define MST_REG_FIELD(reg, msb, lsb) \
++ { .field = REG_FIELD(reg, msb, lsb), .scope = REG_SCOPE_MASTER }
++
++static const struct rtl9300_i2c_drv_data rtl9300_i2c_drv_data = {
++ .field_desc = {
++ [F_MEM_ADDR] = MST_REG_FIELD(RTL9300_I2C_MST_CTRL1, 8, 31),
++ [F_SDA_OUT_SEL] = MST_REG_FIELD(RTL9300_I2C_MST_CTRL1, 4, 6),
++ [F_SCL_SEL] = MST_REG_FIELD(RTL9300_I2C_MST_CTRL1, 3, 3),
++ [F_RWOP] = MST_REG_FIELD(RTL9300_I2C_MST_CTRL1, 2, 2),
++ [F_I2C_FAIL] = MST_REG_FIELD(RTL9300_I2C_MST_CTRL1, 1, 1),
++ [F_I2C_TRIG] = MST_REG_FIELD(RTL9300_I2C_MST_CTRL1, 0, 0),
++ [F_RD_MODE] = MST_REG_FIELD(RTL9300_I2C_MST_CTRL2, 15, 15),
++ [F_DEV_ADDR] = MST_REG_FIELD(RTL9300_I2C_MST_CTRL2, 8, 14),
++ [F_DATA_WIDTH] = MST_REG_FIELD(RTL9300_I2C_MST_CTRL2, 4, 7),
++ [F_MEM_ADDR_WIDTH] = MST_REG_FIELD(RTL9300_I2C_MST_CTRL2, 2, 3),
++ [F_SCL_FREQ] = MST_REG_FIELD(RTL9300_I2C_MST_CTRL2, 0, 1),
++ [F_SDA_SEL] = GLB_REG_FIELD(RTL9300_I2C_MST_GLB_CTRL, 0, 7),
++ },
++ .select_scl = rtl9300_i2c_select_scl,
++ .data_reg = RTL9300_I2C_MST_DATA_WORD0,
++ .max_nchan = RTL9300_I2C_MUX_NCHAN,
++};
++
++
+ static const struct of_device_id i2c_rtl9300_dt_ids[] = {
+- { .compatible = "realtek,rtl9301-i2c" },
+- { .compatible = "realtek,rtl9302b-i2c" },
+- { .compatible = "realtek,rtl9302c-i2c" },
+- { .compatible = "realtek,rtl9303-i2c" },
++ { .compatible = "realtek,rtl9301-i2c", .data = (void *) &rtl9300_i2c_drv_data },
++ { .compatible = "realtek,rtl9302b-i2c", .data = (void *) &rtl9300_i2c_drv_data },
++ { .compatible = "realtek,rtl9302c-i2c", .data = (void *) &rtl9300_i2c_drv_data },
++ { .compatible = "realtek,rtl9303-i2c", .data = (void *) &rtl9300_i2c_drv_data },
+ {}
+ };
+ MODULE_DEVICE_TABLE(of, i2c_rtl9300_dt_ids);
+--
+2.48.1
+
--- /dev/null
+From 80f3e37d5e734bbfe891592bb669ceb5e8b314dc Mon Sep 17 00:00:00 2001
+Date: Sat, 27 Sep 2025 10:19:24 +0000
+Subject: [PATCH] dt-bindings: i2c: realtek,rtl9301-i2c: fix wording and typos
+
+Fix wording of binding description to use plural because there is not
+only a single RTL9300 SoC. RTL9300 describes a whole family of Realtek
+SoCs.
+
+Add missing word 'of' in description of reg property.
+
+Change 'SDA pin' to 'SDA line number' because the property must contain
+the SDA (channel) number ranging from 0-7 instead of a real pin number.
+
+
+diff --git a/Documentation/devicetree/bindings/i2c/realtek,rtl9301-i2c.yaml b/Documentation/devicetree/bindings/i2c/realtek,rtl9301-i2c.yaml
+index 69ac5db8b914..274e2ab8b612 100644
+--- a/Documentation/devicetree/bindings/i2c/realtek,rtl9301-i2c.yaml
++++ b/Documentation/devicetree/bindings/i2c/realtek,rtl9301-i2c.yaml
+@@ -10,7 +10,7 @@ maintainers:
+
+ description:
+- The RTL9300 SoC has two I2C controllers. Each of these has an SCL line (which
++ RTL9300 SoCs have two I2C controllers. Each of these has an SCL line (which
+ if not-used for SCL can be a GPIO). There are 8 common SDA lines that can be
+ assigned to either I2C controller.
+
+@@ -27,7 +27,7 @@ properties:
+
+ reg:
+ items:
+- - description: Register offset and size this I2C controller.
++ - description: Register offset and size of this I2C controller.
+
+ "#address-cells":
+ const: 1
+@@ -42,7 +42,7 @@ patternProperties:
+
+ properties:
+ reg:
+- description: The SDA pin associated with the I2C bus.
++ description: The SDA line number associated with the I2C bus.
+ maxItems: 1
+
+ required:
+--
+2.48.1
+
--- /dev/null
+From 8ff3819d7edcd56e4c533b9391a156cd607048fa Mon Sep 17 00:00:00 2001
+Date: Sat, 27 Sep 2025 10:19:25 +0000
+Subject: [PATCH] i2c: rtl9300: rename internal sda_pin to sda_num
+
+Rename the internally used 'sda_pin' to 'sda_num' to make it clear that
+this is NOT the actual pin number of the GPIO pin but rather the logical
+SDA channel number. Although the alternate function SDA_Y is sometimes
+given with the GPIO number, this is not always the case. Thus, avoid any
+confusion or misconfiguration by giving the variable the correct name.
+
+This follows the description change in the devicetree bindings.
+
+
+diff --git a/drivers/i2c/busses/i2c-rtl9300.c b/drivers/i2c/busses/i2c-rtl9300.c
+index 8483bab72146..f9b5ac7670c2 100644
+--- a/drivers/i2c/busses/i2c-rtl9300.c
++++ b/drivers/i2c/busses/i2c-rtl9300.c
+@@ -20,7 +20,7 @@ struct rtl9300_i2c_chan {
+ struct i2c_adapter adap;
+ struct rtl9300_i2c *i2c;
+ enum rtl9300_bus_freq bus_freq;
+- u8 sda_pin;
++ u8 sda_num;
+ };
+
+ enum rtl9300_i2c_reg_scope {
+@@ -67,7 +67,7 @@ struct rtl9300_i2c {
+ struct regmap_field *fields[F_NUM_FIELDS];
+ u32 reg_base;
+ u32 data_reg;
+- u8 sda_pin;
++ u8 sda_num;
+ struct mutex lock;
+ };
+
+@@ -102,11 +102,11 @@ static int rtl9300_i2c_config_io(struct rtl9300_i2c *i2c, u8 sda_pin)
+
+ drv_data = (struct rtl9300_i2c_drv_data *)device_get_match_data(i2c->dev);
+
+- ret = regmap_field_update_bits(i2c->fields[F_SDA_SEL], BIT(sda_pin), BIT(sda_pin));
++ ret = regmap_field_update_bits(i2c->fields[F_SDA_SEL], BIT(sda_num), BIT(sda_num));
+ if (ret)
+ return ret;
+
+- ret = regmap_field_write(i2c->fields[F_SDA_OUT_SEL], sda_pin);
++ ret = regmap_field_write(i2c->fields[F_SDA_OUT_SEL], sda_num);
+ if (ret)
+ return ret;
+
+@@ -243,11 +243,11 @@ static int rtl9300_i2c_smbus_xfer(struct i2c_adapter *adap, u16 addr, unsigned s
+ int len = 0, ret;
+
+ mutex_lock(&i2c->lock);
+- if (chan->sda_pin != i2c->sda_pin) {
++ if (chan->sda_num != i2c->sda_num) {
+ ret = rtl9300_i2c_config_io(i2c, chan->sda_pin);
+ if (ret)
+ goto out_unlock;
+- i2c->sda_pin = chan->sda_pin;
++ i2c->sda_num = chan->sda_num;
+ }
+
+ switch (size) {
+@@ -374,7 +374,7 @@ static int rtl9300_i2c_probe(struct platform_device *pdev)
+ struct fwnode_handle *child;
+ struct rtl9300_i2c_drv_data *drv_data;
+ struct reg_field fields[F_NUM_FIELDS];
+- u32 clock_freq, sda_pin;
++ u32 clock_freq, sda_num;
+ int ret, i = 0;
+
+ i2c = devm_kzalloc(dev, sizeof(*i2c), GFP_KERNEL);
+@@ -414,7 +414,7 @@ static int rtl9300_i2c_probe(struct platform_device *pdev)
+ struct rtl9300_i2c_chan *chan = &i2c->chans[i];
+ struct i2c_adapter *adap = &chan->adap;
+
+- ret = fwnode_property_read_u32(child, "reg", &sda_pin);
++ ret = fwnode_property_read_u32(child, "reg", &sda_num);
+ if (ret)
+ return ret;
+
+@@ -431,11 +431,11 @@ static int rtl9300_i2c_probe(struct platform_device *pdev)
+ break;
+ default:
+ dev_warn(i2c->dev, "SDA%d clock-frequency %d not supported using default\n",
+- sda_pin, clock_freq);
++ sda_num, clock_freq);
+ break;
+ }
+
+- chan->sda_pin = sda_pin;
++ chan->sda_num = sda_num;
+ chan->i2c = i2c;
+ adap = &i2c->chans[i].adap;
+ adap->owner = THIS_MODULE;
+@@ -445,14 +445,14 @@ static int rtl9300_i2c_probe(struct platform_device *pdev)
+ adap->dev.parent = dev;
+ i2c_set_adapdata(adap, chan);
+ adap->dev.of_node = to_of_node(child);
+- snprintf(adap->name, sizeof(adap->name), "%s SDA%d\n", dev_name(dev), sda_pin);
++ snprintf(adap->name, sizeof(adap->name), "%s SDA%d\n", dev_name(dev), sda_num);
+ i++;
+
+ ret = devm_i2c_add_adapter(dev, adap);
+ if (ret)
+ return ret;
+ }
+- i2c->sda_pin = 0xff;
++ i2c->sda_num = 0xff;
+
+ return 0;
+ }
+--
+2.48.1
+
--- /dev/null
+From 6b0549abc8582a81425f89a436def8e28d8d7dce Mon Sep 17 00:00:00 2001
+Date: Sat, 27 Sep 2025 10:19:26 +0000
+Subject: [PATCH] i2c: rtl9300: move setting SCL frequency to config_io
+
+Move the register operation to set the SCL frequency to the
+rtl9300_i2c_config_io function instead of the rtl9300_i2c_config_xfer
+function. This rather belongs there next to selecting the current SDA
+output line.
+
+
+diff --git a/drivers/i2c/busses/i2c-rtl9300.c b/drivers/i2c/busses/i2c-rtl9300.c
+index f9b5ac7670c2..4177cfb77094 100644
+--- a/drivers/i2c/busses/i2c-rtl9300.c
++++ b/drivers/i2c/busses/i2c-rtl9300.c
+@@ -95,18 +95,23 @@ static int rtl9300_i2c_select_scl(struct rtl9300_i2c *i2c, u8 scl)
+ return regmap_field_write(i2c->fields[F_SCL_SEL], 1);
+ }
+
+-static int rtl9300_i2c_config_io(struct rtl9300_i2c *i2c, u8 sda_pin)
++static int rtl9300_i2c_config_io(struct rtl9300_i2c *i2c, struct rtl9300_i2c_chan *chan)
+ {
+ struct rtl9300_i2c_drv_data *drv_data;
+ int ret;
+
+ drv_data = (struct rtl9300_i2c_drv_data *)device_get_match_data(i2c->dev);
+
+- ret = regmap_field_update_bits(i2c->fields[F_SDA_SEL], BIT(sda_num), BIT(sda_num));
++ ret = regmap_field_update_bits(i2c->fields[F_SDA_SEL], BIT(chan->sda_num),
++ BIT(chan->sda_num));
+ if (ret)
+ return ret;
+
+- ret = regmap_field_write(i2c->fields[F_SDA_OUT_SEL], sda_num);
++ ret = regmap_field_write(i2c->fields[F_SDA_OUT_SEL], chan->sda_num);
++ if (ret)
++ return ret;
++
++ ret = regmap_field_write(i2c->fields[F_SCL_FREQ], chan->bus_freq);
+ if (ret)
+ return ret;
+
+@@ -121,10 +126,6 @@ static int rtl9300_i2c_config_xfer(struct rtl9300_i2c *i2c, struct rtl9300_i2c_c
+ if (len < 1 || len > 16)
+ return -EINVAL;
+
+- ret = regmap_field_write(i2c->fields[F_SCL_FREQ], chan->bus_freq);
+- if (ret)
+- return ret;
+-
+ ret = regmap_field_write(i2c->fields[F_DEV_ADDR], addr);
+ if (ret)
+ return ret;
+@@ -244,7 +245,7 @@ static int rtl9300_i2c_smbus_xfer(struct i2c_adapter *adap, u16 addr, unsigned s
+
+ mutex_lock(&i2c->lock);
+ if (chan->sda_num != i2c->sda_num) {
+- ret = rtl9300_i2c_config_io(i2c, chan->sda_pin);
++ ret = rtl9300_i2c_config_io(i2c, chan);
+ if (ret)
+ goto out_unlock;
+ i2c->sda_num = chan->sda_num;
+--
+2.48.1
+
--- /dev/null
+From 0cb24186d0ebd7dd12c070fed9d782bf7c6dfb1e Mon Sep 17 00:00:00 2001
+Date: Sat, 27 Sep 2025 10:19:27 +0000
+Subject: [PATCH] i2c: rtl9300: do not set read mode on every transfer
+
+Move the operation to set the read mode from config_xfer to probe.
+
+The I2C controller of RTL9300 and RTL9310 support a legacy message mode
+for READs with 'Read Address Data' instead of the standard format 'Write
+Address ; Read Data'. There is no way to pass that via smbus_xfer, thus
+there is no point in supported this in the driver and moreover no point
+in setting this on every transaction. Setting this once in the probe
+call is sufficient.
+
+
+diff --git a/drivers/i2c/busses/i2c-rtl9300.c b/drivers/i2c/busses/i2c-rtl9300.c
+index 4177cfb77094..9e3517b09b3d 100644
+--- a/drivers/i2c/busses/i2c-rtl9300.c
++++ b/drivers/i2c/busses/i2c-rtl9300.c
+@@ -130,11 +130,7 @@ static int rtl9300_i2c_config_xfer(struct rtl9300_i2c *i2c, struct rtl9300_i2c_c
+ if (ret)
+ return ret;
+
+- ret = regmap_field_write(i2c->fields[F_DATA_WIDTH], (len - 1) & 0xf);
+- if (ret)
+- return ret;
+-
+- return regmap_field_write(i2c->fields[F_RD_MODE], 0);
++ return regmap_field_write(i2c->fields[F_DATA_WIDTH], (len - 1) & 0xf);
+ }
+
+ static int rtl9300_i2c_read(struct rtl9300_i2c *i2c, u8 *buf, int len)
+@@ -455,6 +451,11 @@ static int rtl9300_i2c_probe(struct platform_device *pdev)
+ }
+ i2c->sda_num = 0xff;
+
++ /* only use standard read format */
++ ret = regmap_field_write(i2c->fields[F_RD_MODE], 0);
++ if (ret)
++ return ret;
++
+ return 0;
+ }
+
+--
+2.48.1
+
--- /dev/null
+From 447dd46f95014eb4ea94f6164963bf23ce05b927 Mon Sep 17 00:00:00 2001
+Date: Sat, 27 Sep 2025 10:19:28 +0000
+Subject: [PATCH] i2c: rtl9300: separate xfer configuration and execution
+
+So far, the rtl9300_i2c_smbus_xfer code is quite a mess with function
+calls distributed over the whole function setting different values in
+different cases. Calls to rtl9300_i2c_config_xfer and
+rtl9300_i2c_reg_addr_set are used in every case-block with varying
+values whose meaning is not instantly obvious. In some cases, there are
+additional calls within these case-blocks doing more things.
+
+This is in general a bad design and especially really bad for
+readability and maintainability because it distributes changes or
+issues to multiple locations due to the same function being called with
+different hardcoded values in different places.
+
+To have a good structure, setting different parameters based on the
+desired operation should not be interleaved with applying these
+parameters to the hardware registers. Or in different words, the
+parameter site should be mixed with the call site.
+
+Thus, separate configuration and execution of an SMBus xfer within
+rtl9300_i2c_smbus_xfer to improve readability and maintainability. Add a
+new 'struct rtl9300_i2c_xfer' to carry the required parameters for an
+xfer which are configured based on the input parameters within a single
+switch-case block, without having any function calls within this block.
+
+The function calls to actually apply these values to the hardware
+registers then appear below in a single place and just operate on the
+passed instance of 'struct rtl9300_i2c_xfer'. These are
+'rtl9300_i2c_prepare_xfer' which combines applying all parameters of the
+xfer to the corresponding register, and 'rtl9300_i2c_do_xfer' which
+actually executes the xfer and does post-processing if needed.
+
+
+diff --git a/drivers/i2c/busses/i2c-rtl9300.c b/drivers/i2c/busses/i2c-rtl9300.c
+index 9e3517b09b3d..fb3ebbd46a18 100644
+--- a/drivers/i2c/busses/i2c-rtl9300.c
++++ b/drivers/i2c/busses/i2c-rtl9300.c
+@@ -8,6 +8,7 @@
+ #include <linux/mutex.h>
+ #include <linux/platform_device.h>
+ #include <linux/regmap.h>
++#include <linux/unaligned.h>
+
+ enum rtl9300_bus_freq {
+ RTL9300_I2C_STD_FREQ,
+@@ -71,6 +72,22 @@ struct rtl9300_i2c {
+ struct mutex lock;
+ };
+
++enum rtl9300_i2c_xfer_type {
++ RTL9300_I2C_XFER_BYTE,
++ RTL9300_I2C_XFER_WORD,
++ RTL9300_I2C_XFER_BLOCK,
++};
++
++struct rtl9300_i2c_xfer {
++ enum rtl9300_i2c_xfer_type type;
++ u16 dev_addr;
++ u8 reg_addr;
++ u8 reg_addr_len;
++ u8 *data;
++ u8 data_len;
++ bool write;
++};
++
+ #define RTL9300_I2C_MST_CTRL1 0x0
+ #define RTL9300_I2C_MST_CTRL2 0x4
+ #define RTL9300_I2C_MST_DATA_WORD0 0x8
+@@ -95,45 +112,37 @@ static int rtl9300_i2c_select_scl(struct rtl9300_i2c *i2c, u8 scl)
+ return regmap_field_write(i2c->fields[F_SCL_SEL], 1);
+ }
+
+-static int rtl9300_i2c_config_io(struct rtl9300_i2c *i2c, struct rtl9300_i2c_chan *chan)
++static int rtl9300_i2c_config_chan(struct rtl9300_i2c *i2c, struct rtl9300_i2c_chan *chan)
+ {
+ struct rtl9300_i2c_drv_data *drv_data;
+ int ret;
+
+- drv_data = (struct rtl9300_i2c_drv_data *)device_get_match_data(i2c->dev);
++ if (i2c->sda_num == chan->sda_num)
++ return 0;
+
+- ret = regmap_field_update_bits(i2c->fields[F_SDA_SEL], BIT(chan->sda_num),
+- BIT(chan->sda_num));
++ ret = regmap_field_write(i2c->fields[F_SCL_FREQ], chan->bus_freq);
+ if (ret)
+ return ret;
+
+- ret = regmap_field_write(i2c->fields[F_SDA_OUT_SEL], chan->sda_num);
++ drv_data = (struct rtl9300_i2c_drv_data *)device_get_match_data(i2c->dev);
++ ret = drv_data->select_scl(i2c, 0);
+ if (ret)
+ return ret;
+
+- ret = regmap_field_write(i2c->fields[F_SCL_FREQ], chan->bus_freq);
++ ret = regmap_field_update_bits(i2c->fields[F_SDA_SEL], BIT(chan->sda_num),
++ BIT(chan->sda_num));
+ if (ret)
+ return ret;
+
+- return drv_data->select_scl(i2c, 0);
+-}
+-
+-static int rtl9300_i2c_config_xfer(struct rtl9300_i2c *i2c, struct rtl9300_i2c_chan *chan,
+- u16 addr, u16 len)
+-{
+- int ret;
+-
+- if (len < 1 || len > 16)
+- return -EINVAL;
+-
+- ret = regmap_field_write(i2c->fields[F_DEV_ADDR], addr);
++ ret = regmap_field_write(i2c->fields[F_SDA_OUT_SEL], chan->sda_num);
+ if (ret)
+ return ret;
+
+- return regmap_field_write(i2c->fields[F_DATA_WIDTH], (len - 1) & 0xf);
++ i2c->sda_num = chan->sda_num;
++ return 0;
+ }
+
+-static int rtl9300_i2c_read(struct rtl9300_i2c *i2c, u8 *buf, int len)
++static int rtl9300_i2c_read(struct rtl9300_i2c *i2c, u8 *buf, u8 len)
+ {
+ u32 vals[4] = {};
+ int i, ret;
+@@ -153,7 +162,7 @@ static int rtl9300_i2c_read(struct rtl9300_i2c *i2c, u8 *buf, int len)
+ return 0;
+ }
+
+-static int rtl9300_i2c_write(struct rtl9300_i2c *i2c, u8 *buf, int len)
++static int rtl9300_i2c_write(struct rtl9300_i2c *i2c, u8 *buf, u8 len)
+ {
+ u32 vals[4] = {};
+ int i;
+@@ -176,16 +185,51 @@ static int rtl9300_i2c_writel(struct rtl9300_i2c *i2c, u32 data)
+ return regmap_write(i2c->regmap, i2c->data_reg, data);
+ }
+
+-static int rtl9300_i2c_execute_xfer(struct rtl9300_i2c *i2c, char read_write,
+- int size, union i2c_smbus_data *data, int len)
++static int rtl9300_i2c_prepare_xfer(struct rtl9300_i2c *i2c, struct rtl9300_i2c_xfer *xfer)
+ {
+- u32 val;
+ int ret;
+
+- ret = regmap_field_write(i2c->fields[F_RWOP], read_write == I2C_SMBUS_WRITE);
++ if (xfer->data_len < 1 || xfer->data_len > 16)
++ return -EINVAL;
++
++ ret = regmap_field_write(i2c->fields[F_DEV_ADDR], xfer->dev_addr);
++ if (ret)
++ return ret;
++
++ ret = rtl9300_i2c_reg_addr_set(i2c, xfer->reg_addr, xfer->reg_addr_len);
++ if (ret)
++ return ret;
++
++ ret = regmap_field_write(i2c->fields[F_RWOP], xfer->write);
++ if (ret)
++ return ret;
++
++ ret = regmap_field_write(i2c->fields[F_DATA_WIDTH], (xfer->data_len - 1) & 0xf);
+ if (ret)
+ return ret;
+
++ if (xfer->write) {
++ switch (xfer->type) {
++ case RTL9300_I2C_XFER_BYTE:
++ ret = rtl9300_i2c_writel(i2c, *xfer->data);
++ break;
++ case RTL9300_I2C_XFER_WORD:
++ ret = rtl9300_i2c_writel(i2c, get_unaligned((const u16 *)xfer->data));
++ break;
++ default:
++ ret = rtl9300_i2c_write(i2c, xfer->data, xfer->data_len);
++ break;
++ }
++ }
++
++ return ret;
++}
++
++static int rtl9300_i2c_do_xfer(struct rtl9300_i2c *i2c, struct rtl9300_i2c_xfer *xfer)
++{
++ u32 val;
++ int ret;
++
+ ret = regmap_field_write(i2c->fields[F_I2C_TRIG], 1);
+ if (ret)
+ return ret;
+@@ -200,28 +244,24 @@ static int rtl9300_i2c_execute_xfer(struct rtl9300_i2c *i2c, char read_write,
+ if (val)
+ return -EIO;
+
+- if (read_write == I2C_SMBUS_READ) {
+- switch (size) {
+- case I2C_SMBUS_BYTE:
+- case I2C_SMBUS_BYTE_DATA:
++ if (!xfer->write) {
++ switch (xfer->type) {
++ case RTL9300_I2C_XFER_BYTE:
+ ret = regmap_read(i2c->regmap, i2c->data_reg, &val);
+ if (ret)
+ return ret;
+- data->byte = val & 0xff;
++
++ *xfer->data = val & 0xff;
+ break;
+- case I2C_SMBUS_WORD_DATA:
++ case RTL9300_I2C_XFER_WORD:
+ ret = regmap_read(i2c->regmap, i2c->data_reg, &val);
+ if (ret)
+ return ret;
+- data->word = val & 0xffff;
+- break;
+- case I2C_SMBUS_I2C_BLOCK_DATA:
+- ret = rtl9300_i2c_read(i2c, &data->block[1], len);
+- if (ret)
+- return ret;
++
++ put_unaligned(val & 0xffff, (u16*)xfer->data);
+ break;
+ default:
+- ret = rtl9300_i2c_read(i2c, &data->block[0], len);
++ ret = rtl9300_i2c_read(i2c, xfer->data, xfer->data_len);
+ if (ret)
+ return ret;
+ break;
+@@ -237,108 +277,62 @@ static int rtl9300_i2c_smbus_xfer(struct i2c_adapter *adap, u16 addr, unsigned s
+ {
+ struct rtl9300_i2c_chan *chan = i2c_get_adapdata(adap);
+ struct rtl9300_i2c *i2c = chan->i2c;
+- int len = 0, ret;
++ struct rtl9300_i2c_xfer xfer = {0};
++ int ret;
++
++ if (addr > 0x7f)
++ return -EINVAL;
+
+ mutex_lock(&i2c->lock);
+- if (chan->sda_num != i2c->sda_num) {
+- ret = rtl9300_i2c_config_io(i2c, chan);
+- if (ret)
+- goto out_unlock;
+- i2c->sda_num = chan->sda_num;
+- }
++
++ ret = rtl9300_i2c_config_chan(i2c, chan);
++ if (ret)
++ goto out_unlock;
++
++ xfer.dev_addr = addr & 0x7f;
++ xfer.write = (read_write == I2C_SMBUS_WRITE);
++ xfer.reg_addr = command;
++ xfer.reg_addr_len = 1;
+
+ switch (size) {
+ case I2C_SMBUS_BYTE:
+- if (read_write == I2C_SMBUS_WRITE) {
+- ret = rtl9300_i2c_config_xfer(i2c, chan, addr, 0);
+- if (ret)
+- goto out_unlock;
+- ret = rtl9300_i2c_reg_addr_set(i2c, command, 1);
+- if (ret)
+- goto out_unlock;
+- } else {
+- ret = rtl9300_i2c_config_xfer(i2c, chan, addr, 1);
+- if (ret)
+- goto out_unlock;
+- ret = rtl9300_i2c_reg_addr_set(i2c, 0, 0);
+- if (ret)
+- goto out_unlock;
+- }
++ xfer.data = (read_write == I2C_SMBUS_READ) ? &data->byte : &command;
++ xfer.data_len = 1;
++ xfer.reg_addr = 0;
++ xfer.reg_addr_len = 0;
++ xfer.type = RTL9300_I2C_XFER_BYTE;
+ break;
+-
+ case I2C_SMBUS_BYTE_DATA:
+- ret = rtl9300_i2c_reg_addr_set(i2c, command, 1);
+- if (ret)
+- goto out_unlock;
+- ret = rtl9300_i2c_config_xfer(i2c, chan, addr, 1);
+- if (ret)
+- goto out_unlock;
+- if (read_write == I2C_SMBUS_WRITE) {
+- ret = rtl9300_i2c_writel(i2c, data->byte);
+- if (ret)
+- goto out_unlock;
+- }
++ xfer.data = &data->byte;
++ xfer.data_len = 1;
++ xfer.type = RTL9300_I2C_XFER_BYTE;
+ break;
+-
+ case I2C_SMBUS_WORD_DATA:
+- ret = rtl9300_i2c_reg_addr_set(i2c, command, 1);
+- if (ret)
+- goto out_unlock;
+- ret = rtl9300_i2c_config_xfer(i2c, chan, addr, 2);
+- if (ret)
+- goto out_unlock;
+- if (read_write == I2C_SMBUS_WRITE) {
+- ret = rtl9300_i2c_writel(i2c, data->word);
+- if (ret)
+- goto out_unlock;
+- }
++ xfer.data = (u8 *)&data->word;
++ xfer.data_len = 2;
++ xfer.type = RTL9300_I2C_XFER_WORD;
+ break;
+-
+ case I2C_SMBUS_BLOCK_DATA:
+- ret = rtl9300_i2c_reg_addr_set(i2c, command, 1);
+- if (ret)
+- goto out_unlock;
+- if (data->block[0] < 1 || data->block[0] > I2C_SMBUS_BLOCK_MAX) {
+- ret = -EINVAL;
+- goto out_unlock;
+- }
+- ret = rtl9300_i2c_config_xfer(i2c, chan, addr, data->block[0] + 1);
+- if (ret)
+- goto out_unlock;
+- if (read_write == I2C_SMBUS_WRITE) {
+- ret = rtl9300_i2c_write(i2c, &data->block[0], data->block[0] + 1);
+- if (ret)
+- goto out_unlock;
+- }
+- len = data->block[0] + 1;
++ xfer.data = &data->block[0];
++ xfer.data_len = data->block[0] + 1;
++ xfer.type = RTL9300_I2C_XFER_BLOCK;
+ break;
+-
+ case I2C_SMBUS_I2C_BLOCK_DATA:
+- ret = rtl9300_i2c_reg_addr_set(i2c, command, 1);
+- if (ret)
+- goto out_unlock;
+- if (data->block[0] < 1 || data->block[0] > I2C_SMBUS_BLOCK_MAX) {
+- ret = -EINVAL;
+- goto out_unlock;
+- }
+- ret = rtl9300_i2c_config_xfer(i2c, chan, addr, data->block[0]);
+- if (ret)
+- goto out_unlock;
+- if (read_write == I2C_SMBUS_WRITE) {
+- ret = rtl9300_i2c_write(i2c, &data->block[1], data->block[0]);
+- if (ret)
+- goto out_unlock;
+- }
+- len = data->block[0];
++ xfer.data = &data->block[1];
++ xfer.data_len = data->block[0];
++ xfer.type = RTL9300_I2C_XFER_BLOCK;
+ break;
+-
+ default:
+ dev_err(&adap->dev, "Unsupported transaction %d\n", size);
+ ret = -EOPNOTSUPP;
+ goto out_unlock;
+ }
+
+- ret = rtl9300_i2c_execute_xfer(i2c, read_write, size, data, len);
++ ret = rtl9300_i2c_prepare_xfer(i2c, &xfer);
++ if (ret)
++ goto out_unlock;
++
++ ret = rtl9300_i2c_do_xfer(i2c, &xfer);
+
+ out_unlock:
+ mutex_unlock(&i2c->lock);
+--
+2.48.1
+
--- /dev/null
+From bcd5f0da57e6c47a884dcad94ad6b0e32cce8705 Mon Sep 17 00:00:00 2001
+Date: Sat, 27 Sep 2025 10:19:29 +0000
+Subject: [PATCH] i2c: rtl9300: use scoped guard instead of explicit
+ lock/unlock
+
+Use the scoped guard infrastructure which unlocks a mutex automatically
+when the guard goes out of scope, instead of explicit lock and unlock.
+This simplifies the code and control flow in rtl9300_i2c_smbus_xfer and
+removes the need of using goto in error cases to unlock before
+returning.
+
+
+diff --git a/drivers/i2c/busses/i2c-rtl9300.c b/drivers/i2c/busses/i2c-rtl9300.c
+index fb3ebbd46a18..c67463228604 100644
+--- a/drivers/i2c/busses/i2c-rtl9300.c
++++ b/drivers/i2c/busses/i2c-rtl9300.c
+@@ -72,6 +72,8 @@ struct rtl9300_i2c {
+ struct mutex lock;
+ };
+
++DEFINE_GUARD(rtl9300_i2c, struct rtl9300_i2c *, mutex_lock(&_T->lock), mutex_unlock(&_T->lock))
++
+ enum rtl9300_i2c_xfer_type {
+ RTL9300_I2C_XFER_BYTE,
+ RTL9300_I2C_XFER_WORD,
+@@ -283,11 +285,11 @@ static int rtl9300_i2c_smbus_xfer(struct i2c_adapter *adap, u16 addr, unsigned s
+ if (addr > 0x7f)
+ return -EINVAL;
+
+- mutex_lock(&i2c->lock);
++ guard(rtl9300_i2c)(i2c);
+
+ ret = rtl9300_i2c_config_chan(i2c, chan);
+ if (ret)
+- goto out_unlock;
++ return ret;
+
+ xfer.dev_addr = addr & 0x7f;
+ xfer.write = (read_write == I2C_SMBUS_WRITE);
+@@ -324,20 +326,14 @@ static int rtl9300_i2c_smbus_xfer(struct i2c_adapter *adap, u16 addr, unsigned s
+ break;
+ default:
+ dev_err(&adap->dev, "Unsupported transaction %d\n", size);
+- ret = -EOPNOTSUPP;
+- goto out_unlock;
++ return -EOPNOTSUPP;
+ }
+
+ ret = rtl9300_i2c_prepare_xfer(i2c, &xfer);
+ if (ret)
+- goto out_unlock;
+-
+- ret = rtl9300_i2c_do_xfer(i2c, &xfer);
+-
+-out_unlock:
+- mutex_unlock(&i2c->lock);
++ return ret;
+
+- return ret;
++ return rtl9300_i2c_do_xfer(i2c, &xfer);
+ }
+
+ static u32 rtl9300_i2c_func(struct i2c_adapter *a)
+--
+2.48.1
+
--- /dev/null
+From 17689aafb793599a862617a127429dd3d6f675c9 Mon Sep 17 00:00:00 2001
+Date: Sat, 27 Sep 2025 10:19:30 +0000
+Subject: [PATCH] dt-bindings: i2c: realtek,rtl9301-i2c: extend for RTL9310
+ support
+
+Adjust the regex for child-node address to account for the fact that
+RTL9310 supports 12 instead of only 8 SDA lines. Also, narrow this per
+variant.
+
+Add a vendor-specific property to explicitly specify the SCL line number
+of the defined I2C controller/master. This is required, in particular
+for RTL9310, to operate on the correct SCL for each controller. Require
+this property to be specified for RTL9310.
+
+Add compatibles for known SoC variants RTL9311, RTL9312 and RTL9313.
+
+
+diff --git a/Documentation/devicetree/bindings/i2c/realtek,rtl9301-i2c.yaml b/Documentation/devicetree/bindings/i2c/realtek,rtl9301-i2c.yaml
+index 274e2ab8b612..17ce39c19ab1 100644
+--- a/Documentation/devicetree/bindings/i2c/realtek,rtl9301-i2c.yaml
++++ b/Documentation/devicetree/bindings/i2c/realtek,rtl9301-i2c.yaml
+@@ -13,6 +13,8 @@ description:
+ RTL9300 SoCs have two I2C controllers. Each of these has an SCL line (which
+ if not-used for SCL can be a GPIO). There are 8 common SDA lines that can be
+ assigned to either I2C controller.
++ RTL9310 SoCs have equal capabilities but support 12 common SDA lines which
++ can be assigned to either I2C controller.
+
+ properties:
+ compatible:
+@@ -23,7 +25,15 @@ properties:
+ - realtek,rtl9302c-i2c
+ - realtek,rtl9303-i2c
+ - const: realtek,rtl9301-i2c
+- - const: realtek,rtl9301-i2c
++ - items:
++ - enum:
++ - realtek,rtl9311-i2c
++ - realtek,rtl9312-i2c
++ - realtek,rtl9313-i2c
++ - const: realtek,rtl9310-i2c
++ - enum:
++ - realtek,rtl9301-i2c
++ - realtek,rtl9310-i2c
+
+ reg:
+ items:
+@@ -35,8 +45,14 @@ properties:
+ "#size-cells":
+ const: 0
+
++ realtek,scl:
++ $ref: /schemas/types.yaml#/definitions/uint32
++ description:
++ The SCL line number of this I2C controller.
++ enum: [ 0, 1 ]
++
+ patternProperties:
+- '^i2c@[0-7]$':
++ '^i2c@[0-9ab]$':
+ $ref: /schemas/i2c/i2c-controller.yaml
+ unevaluatedProperties: false
+
+@@ -48,6 +64,25 @@ patternProperties:
+ required:
+ - reg
+
++
++allOf:
++ - if:
++ properties:
++ compatible:
++ contains:
++ const: realtek,rtl9310-i2c
++ then:
++ required:
++ - realtek,scl
++ - if:
++ properties:
++ compatible:
++ contains:
++ const: realtek,rtl9301-i2c
++ then:
++ patternProperties:
++ '^i2c@[89ab]$': false
++
+ required:
+ - compatible
+ - reg
+--
+2.48.1
+
--- /dev/null
+From 8d43287120ce6437e7a77e735d99137f3fdb3ae9 Mon Sep 17 00:00:00 2001
+Date: Sat, 27 Sep 2025 10:19:31 +0000
+Subject: [PATCH] i2c: rtl9300: add support for RTL9310 I2C controller
+
+Add support for the internal I2C controllers of RTL9310 series based
+SoCs to the driver for RTL9300. Add register definitions, chip-specific
+functions and compatible strings for known RTL9310-based SoCs RTL9311,
+RTL9312 and RTL9313.
+
+Make use of a new device tree property 'realtek,scl' which needs to be
+specified in case both or only the second master is used. This is
+required due how the register layout changed in contrast to RTL9300,
+which has SCL selection in a global register instead of a
+master-specific one.
+
+
+diff --git a/drivers/i2c/busses/i2c-rtl9300.c b/drivers/i2c/busses/i2c-rtl9300.c
+index c67463228604..4723e48cfe18 100644
+--- a/drivers/i2c/busses/i2c-rtl9300.c
++++ b/drivers/i2c/busses/i2c-rtl9300.c
+@@ -60,14 +60,16 @@ struct rtl9300_i2c_drv_data {
+ };
+
+ #define RTL9300_I2C_MUX_NCHAN 8
++#define RTL9310_I2C_MUX_NCHAN 12
+
+ struct rtl9300_i2c {
+ struct regmap *regmap;
+ struct device *dev;
+- struct rtl9300_i2c_chan chans[RTL9300_I2C_MUX_NCHAN];
++ struct rtl9300_i2c_chan chans[RTL9310_I2C_MUX_NCHAN];
+ struct regmap_field *fields[F_NUM_FIELDS];
+ u32 reg_base;
+ u32 data_reg;
++ u8 scl_num;
+ u8 sda_num;
+ struct mutex lock;
+ };
+@@ -98,6 +100,12 @@ struct rtl9300_i2c_xfer {
+ #define RTL9300_I2C_MST_DATA_WORD3 0x14
+ #define RTL9300_I2C_MST_GLB_CTRL 0x384
+
++#define RTL9310_I2C_MST_IF_CTRL 0x1004
++#define RTL9310_I2C_MST_IF_SEL 0x1008
++#define RTL9310_I2C_MST_CTRL 0x0
++#define RTL9310_I2C_MST_MEMADDR_CTRL 0x4
++#define RTL9310_I2C_MST_DATA_CTRL 0x8
++
+ static int rtl9300_i2c_reg_addr_set(struct rtl9300_i2c *i2c, u32 reg, u16 len)
+ {
+ int ret;
+@@ -114,6 +122,11 @@ static int rtl9300_i2c_select_scl(struct rtl9300_i2c *i2c, u8 scl)
+ return regmap_field_write(i2c->fields[F_SCL_SEL], 1);
+ }
+
++static int rtl9310_i2c_select_scl(struct rtl9300_i2c *i2c, u8 scl)
++{
++ return regmap_field_update_bits(i2c->fields[F_SCL_SEL], BIT(scl), BIT(scl));
++}
++
+ static int rtl9300_i2c_config_chan(struct rtl9300_i2c *i2c, struct rtl9300_i2c_chan *chan)
+ {
+ struct rtl9300_i2c_drv_data *drv_data;
+@@ -127,7 +140,7 @@ static int rtl9300_i2c_config_chan(struct rtl9300_i2c *i2c, struct rtl9300_i2c_c
+ return ret;
+
+ drv_data = (struct rtl9300_i2c_drv_data *)device_get_match_data(i2c->dev);
+- ret = drv_data->select_scl(i2c, 0);
++ ret = drv_data->select_scl(i2c, i2c->scl_num);
+ if (ret)
+ return ret;
+
+@@ -361,7 +374,7 @@ static int rtl9300_i2c_probe(struct platform_device *pdev)
+ struct fwnode_handle *child;
+ struct rtl9300_i2c_drv_data *drv_data;
+ struct reg_field fields[F_NUM_FIELDS];
+- u32 clock_freq, sda_num;
++ u32 clock_freq, scl_num, sda_num;
+ int ret, i = 0;
+
+ i2c = devm_kzalloc(dev, sizeof(*i2c), GFP_KERNEL);
+@@ -379,6 +392,11 @@ static int rtl9300_i2c_probe(struct platform_device *pdev)
+ if (ret)
+ return ret;
+
++ ret = device_property_read_u32(dev, "realtek,scl", &scl_num);
++ if (ret || scl_num != 1)
++ scl_num = 0;
++ i2c->scl_num = (u8)scl_num;
++
+ platform_set_drvdata(pdev, i2c);
+
+ drv_data = (struct rtl9300_i2c_drv_data *)device_get_match_data(i2c->dev);
+@@ -474,12 +492,35 @@ static const struct rtl9300_i2c_drv_data rtl9300_i2c_drv_data = {
+ .max_nchan = RTL9300_I2C_MUX_NCHAN,
+ };
+
++static const struct rtl9300_i2c_drv_data rtl9310_i2c_drv_data = {
++ .field_desc = {
++ [F_SCL_SEL] = GLB_REG_FIELD(RTL9310_I2C_MST_IF_SEL, 12, 13),
++ [F_SDA_SEL] = GLB_REG_FIELD(RTL9310_I2C_MST_IF_SEL, 0, 11),
++ [F_SCL_FREQ] = MST_REG_FIELD(RTL9310_I2C_MST_CTRL, 30, 31),
++ [F_DEV_ADDR] = MST_REG_FIELD(RTL9310_I2C_MST_CTRL, 11, 17),
++ [F_SDA_OUT_SEL] = MST_REG_FIELD(RTL9310_I2C_MST_CTRL, 18, 21),
++ [F_MEM_ADDR_WIDTH] = MST_REG_FIELD(RTL9310_I2C_MST_CTRL, 9, 10),
++ [F_DATA_WIDTH] = MST_REG_FIELD(RTL9310_I2C_MST_CTRL, 5, 8),
++ [F_RD_MODE] = MST_REG_FIELD(RTL9310_I2C_MST_CTRL, 4, 4),
++ [F_RWOP] = MST_REG_FIELD(RTL9310_I2C_MST_CTRL, 2, 2),
++ [F_I2C_FAIL] = MST_REG_FIELD(RTL9310_I2C_MST_CTRL, 1, 1),
++ [F_I2C_TRIG] = MST_REG_FIELD(RTL9310_I2C_MST_CTRL, 0, 0),
++ [F_MEM_ADDR] = MST_REG_FIELD(RTL9310_I2C_MST_MEMADDR_CTRL, 0, 23),
++ },
++ .select_scl = rtl9310_i2c_select_scl,
++ .data_reg = RTL9310_I2C_MST_DATA_CTRL,
++ .max_nchan = RTL9310_I2C_MUX_NCHAN,
++};
+
+ static const struct of_device_id i2c_rtl9300_dt_ids[] = {
+ { .compatible = "realtek,rtl9301-i2c", .data = (void *) &rtl9300_i2c_drv_data },
+ { .compatible = "realtek,rtl9302b-i2c", .data = (void *) &rtl9300_i2c_drv_data },
+ { .compatible = "realtek,rtl9302c-i2c", .data = (void *) &rtl9300_i2c_drv_data },
+ { .compatible = "realtek,rtl9303-i2c", .data = (void *) &rtl9300_i2c_drv_data },
++ { .compatible = "realtek,rtl9310-i2c", .data = (void *) &rtl9310_i2c_drv_data },
++ { .compatible = "realtek,rtl9311-i2c", .data = (void *) &rtl9310_i2c_drv_data },
++ { .compatible = "realtek,rtl9312-i2c", .data = (void *) &rtl9310_i2c_drv_data },
++ { .compatible = "realtek,rtl9313-i2c", .data = (void *) &rtl9310_i2c_drv_data },
+ {}
+ };
+ MODULE_DEVICE_TABLE(of, i2c_rtl9300_dt_ids);
+--
+2.48.1
+
+++ /dev/null
-From c8e44fae5b05528d4d672a935c8ace21eed8b8e7 Mon Sep 17 00:00:00 2001
-Date: Sat, 9 Aug 2025 08:40:54 +0200
-Subject: [PATCH 1/5] i2c: rtl9300: Fix out-of-bounds bug in
- rtl9300_i2c_smbus_xfer
-
-The data->block[0] variable comes from user. Without proper check,
-the variable may be very large to cause an out-of-bounds bug.
-
-Fix this bug by checking the value of data->block[0] first.
-
-1. commit 39244cc75482 ("i2c: ismt: Fix an out-of-bounds bug in
- ismt_access()")
-2. commit 92fbb6d1296f ("i2c: xgene-slimpro: Fix out-of-bounds bug in
- xgene_slimpro_i2c_xfer()")
-
-Fixes: c366be720235 ("i2c: Add driver for the RTL9300 I2C controller")
-
---- a/drivers/i2c/busses/i2c-rtl9300.c
-+++ b/drivers/i2c/busses/i2c-rtl9300.c
-@@ -281,6 +281,10 @@ static int rtl9300_i2c_smbus_xfer(struct
- ret = rtl9300_i2c_reg_addr_set(i2c, command, 1);
- if (ret)
- goto out_unlock;
-+ if (data->block[0] < 1 || data->block[0] > I2C_SMBUS_BLOCK_MAX) {
-+ ret = -EINVAL;
-+ goto out_unlock;
-+ }
- ret = rtl9300_i2c_config_xfer(i2c, chan, addr, data->block[0]);
- if (ret)
- goto out_unlock;
+++ /dev/null
-From 0190fed3eed28be78717a0b45d7038c855e83b18 Mon Sep 17 00:00:00 2001
-Date: Sat, 9 Aug 2025 08:40:55 +0200
-Subject: [PATCH 2/5] i2c: rtl9300: Fix multi-byte I2C write
-
-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.
-
-Fixes: c366be720235 ("i2c: Add driver for the RTL9300 I2C controller")
-
---- a/drivers/i2c/busses/i2c-rtl9300.c
-+++ b/drivers/i2c/busses/i2c-rtl9300.c
-@@ -143,10 +143,10 @@ static int rtl9300_i2c_write(struct rtl9
- 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,
+++ /dev/null
-From 1757cce14242192be41819db4bc7b6b707d20b87 Mon Sep 17 00:00:00 2001
-Date: Sat, 9 Aug 2025 08:40:56 +0200
-Subject: [PATCH 3/5] i2c: rtl9300: Increase timeout for transfer polling
-
-The timeout for transfers was only set to 2ms. Because of this relatively
-low limit, 12-byte read operations to the frontend MCU of a RTL8239 POE PSE
-chip cluster was consistently resulting in a timeout.
-
-The original OpenWrt downstream driver [1] was not using any timeout limit
-at all. This is also possible by setting the timeout_us parameter of
-regmap_read_poll_timeout() to 0. But since the driver currently implements
-the ETIMEDOUT error, it is more sensible to increase the timeout in such a
-way that communication with the (quite common) Realtek I2C-connected POE
-management solution is possible.
-
-[1] https://git.openwrt.org/?p=openwrt/openwrt.git;a=blob;f=target/linux/realtek/files-6.12/drivers/i2c/busses/i2c-rtl9300.c;h=c4d973195ef39dc56d6207e665d279745525fcac#l202
-
-Fixes: c366be720235 ("i2c: Add driver for the RTL9300 I2C controller")
-
---- a/drivers/i2c/busses/i2c-rtl9300.c
-+++ b/drivers/i2c/busses/i2c-rtl9300.c
-@@ -175,7 +175,7 @@ static int rtl9300_i2c_execute_xfer(stru
- return ret;
-
- ret = regmap_read_poll_timeout(i2c->regmap, i2c->reg_base + RTL9300_I2C_MST_CTRL1,
-- val, !(val & RTL9300_I2C_MST_CTRL1_I2C_TRIG), 100, 2000);
-+ val, !(val & RTL9300_I2C_MST_CTRL1_I2C_TRIG), 100, 100000);
- if (ret)
- return ret;
-
+++ /dev/null
-From ebc6d39a075f89ab3a7bb3cf011c5fbd555ab82b Mon Sep 17 00:00:00 2001
-Date: Sat, 9 Aug 2025 08:40:57 +0200
-Subject: [PATCH 4/5] i2c: rtl9300: Add missing count byte for SMBus Block Ops
-
-The expected on-wire format of an SMBus Block Write is
-
- S Addr Wr [A] Comm [A] Count [A] Data [A] Data [A] ... [A] Data [A] P
-
-Everything starting from the Count byte is provided by the I2C subsystem in
-the array data->block. But the driver was skipping the Count byte
-(data->block[0]) when sending it to the RTL93xx I2C controller.
-
-Only the actual data could be seen on the wire:
-
- S Addr Wr [A] Comm [A] Data [A] Data [A] ... [A] Data [A] P
-
-This wire format is not SMBus Block Write compatible but matches the format
-of an I2C Block Write. Simply adding the count byte to the buffer for the
-I2C controller is enough to fix the transmission.
-
-This also affects read because the I2C controller must receive the count
-byte + $count * data bytes.
-
-Fixes: c366be720235 ("i2c: Add driver for the RTL9300 I2C controller")
-
---- a/drivers/i2c/busses/i2c-rtl9300.c
-+++ b/drivers/i2c/busses/i2c-rtl9300.c
-@@ -285,15 +285,15 @@ static int rtl9300_i2c_smbus_xfer(struct
- ret = -EINVAL;
- goto out_unlock;
- }
-- ret = rtl9300_i2c_config_xfer(i2c, chan, addr, data->block[0]);
-+ ret = rtl9300_i2c_config_xfer(i2c, chan, addr, data->block[0] + 1);
- if (ret)
- goto out_unlock;
- if (read_write == I2C_SMBUS_WRITE) {
-- ret = rtl9300_i2c_write(i2c, &data->block[1], data->block[0]);
-+ ret = rtl9300_i2c_write(i2c, &data->block[0], data->block[0] + 1);
- if (ret)
- goto out_unlock;
- }
-- len = data->block[0];
-+ len = data->block[0] + 1;
- break;
-
- default:
+++ /dev/null
-From 1bd7526db1cf9d5c6a700c37b8bf915f8ff72c44 Mon Sep 17 00:00:00 2001
-Date: Sat, 9 Aug 2025 08:40:58 +0200
-Subject: [PATCH 5/5] i2c: rtl9300: Implement I2C block read and write
-
-It was noticed that the original implementation of SMBus Block Write in the
-driver was actually an I2C Block Write. Both differ only in the Count byte
-before the actual data:
-
- S Addr Wr [A] Comm [A] Count [A] Data [A] Data [A] ... [A] Data [A] P
-
-The I2C Block Write is just skipping this Count byte and starts directly
-with the data:
-
- S Addr Wr [A] Comm [A] Data [A] Data [A] ... [A] Data [A] P
-
-The I2C controller of RTL93xx doesn't handle this Count byte special and it
-is simply another one of (16 possible) data bytes. Adding support for the
-I2C Block Write therefore only requires skipping the count byte (0) in
-data->block.
-
-It is similar for reads. The SMBUS Block read is having a Count byte before
-the data:
-
- S Addr Wr [A] Comm [A]
- Sr Addr Rd [A] [Count] A [Data] A [Data] A ... A [Data] NA P
-
-And the I2C Block Read is directly starting with the actual data:
-
- S Addr Wr [A] Comm [A]
- Sr Addr Rd [A] [Data] A [Data] A ... A [Data] NA P
-
-The I2C controller is also not handling this byte in a special way. It
-simply provides every byte after the Rd marker + Ack as part of the 16 byte
-receive buffer (registers). The content of this buffer just has to be
-copied to the right position in the receive data->block.
-
-
---- a/drivers/i2c/busses/i2c-rtl9300.c
-+++ b/drivers/i2c/busses/i2c-rtl9300.c
-@@ -183,22 +183,32 @@ static int rtl9300_i2c_execute_xfer(stru
- return -EIO;
-
- if (read_write == I2C_SMBUS_READ) {
-- if (size == I2C_SMBUS_BYTE || size == I2C_SMBUS_BYTE_DATA) {
-+ switch (size) {
-+ case I2C_SMBUS_BYTE:
-+ case I2C_SMBUS_BYTE_DATA:
- ret = regmap_read(i2c->regmap,
- i2c->reg_base + RTL9300_I2C_MST_DATA_WORD0, &val);
- if (ret)
- return ret;
- data->byte = val & 0xff;
-- } else if (size == I2C_SMBUS_WORD_DATA) {
-+ break;
-+ case I2C_SMBUS_WORD_DATA:
- ret = regmap_read(i2c->regmap,
- i2c->reg_base + RTL9300_I2C_MST_DATA_WORD0, &val);
- if (ret)
- return ret;
- data->word = val & 0xffff;
-- } else {
-+ break;
-+ case I2C_SMBUS_I2C_BLOCK_DATA:
-+ ret = rtl9300_i2c_read(i2c, &data->block[1], len);
-+ if (ret)
-+ return ret;
-+ break;
-+ default:
- ret = rtl9300_i2c_read(i2c, &data->block[0], len);
- if (ret)
- return ret;
-+ break;
- }
- }
-
-@@ -296,6 +306,25 @@ static int rtl9300_i2c_smbus_xfer(struct
- len = data->block[0] + 1;
- break;
-
-+ case I2C_SMBUS_I2C_BLOCK_DATA:
-+ ret = rtl9300_i2c_reg_addr_set(i2c, command, 1);
-+ if (ret)
-+ goto out_unlock;
-+ if (data->block[0] < 1 || data->block[0] > I2C_SMBUS_BLOCK_MAX) {
-+ ret = -EINVAL;
-+ goto out_unlock;
-+ }
-+ ret = rtl9300_i2c_config_xfer(i2c, chan, addr, data->block[0]);
-+ if (ret)
-+ goto out_unlock;
-+ if (read_write == I2C_SMBUS_WRITE) {
-+ ret = rtl9300_i2c_write(i2c, &data->block[1], data->block[0]);
-+ if (ret)
-+ goto out_unlock;
-+ }
-+ len = data->block[0];
-+ break;
-+
- default:
- dev_err(&adap->dev, "Unsupported transaction %d\n", size);
- ret = -EOPNOTSUPP;
-@@ -314,6 +343,7 @@ static u32 rtl9300_i2c_func(struct i2c_a
- {
- return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
- I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
-+ I2C_FUNC_SMBUS_READ_I2C_BLOCK | I2C_FUNC_SMBUS_WRITE_I2C_BLOCK |
- I2C_FUNC_SMBUS_BLOCK_DATA;
- }
-
+++ /dev/null
-From 055c2659011812c1abbd879ce80b2342e4d93576 Mon Sep 17 00:00:00 2001
-Date: Mon, 30 Jun 2025 15:37:06 +0000
-Subject: [PATCH v5 01/11] i2c: rtl9300: use regmap fields and API for
- registers
-
-Adapt the RTL9300 I2C controller driver to use more of the regmap
-API, especially make use of reg_field and regmap_field instead of macros
-to represent registers. Most register operations are performed through
-regmap_field_* API then.
-
-Handle SCL selection using separate chip-specific functions since this
-is already known to differ between the Realtek SoC families in such a
-way that this cannot be properly handled using just a different
-reg_field.
-
-This makes it easier to add support for newer generations or to handle
-differences between specific revisions within a series. Just by
-defining a separate driver data structure with the corresponding
-register field definitions and linking it to a new compatible.
-
----
- drivers/i2c/busses/i2c-rtl9300.c | 192 ++++++++++++++++++++-----------
- 1 file changed, 124 insertions(+), 68 deletions(-)
-
---- a/drivers/i2c/busses/i2c-rtl9300.c
-+++ b/drivers/i2c/busses/i2c-rtl9300.c
-@@ -23,94 +23,114 @@ struct rtl9300_i2c_chan {
- u8 sda_pin;
- };
-
-+enum rtl9300_i2c_reg_scope {
-+ REG_SCOPE_GLOBAL,
-+ REG_SCOPE_MASTER,
-+};
-+
-+struct rtl9300_i2c_reg_field {
-+ struct reg_field field;
-+ enum rtl9300_i2c_reg_scope scope;
-+};
-+
-+enum rtl9300_i2c_reg_fields {
-+ F_DATA_WIDTH = 0,
-+ F_DEV_ADDR,
-+ F_I2C_FAIL,
-+ F_I2C_TRIG,
-+ F_MEM_ADDR,
-+ F_MEM_ADDR_WIDTH,
-+ F_RD_MODE,
-+ F_RWOP,
-+ F_SCL_FREQ,
-+ F_SCL_SEL,
-+ F_SDA_OUT_SEL,
-+ F_SDA_SEL,
-+
-+ /* keep last */
-+ F_NUM_FIELDS
-+};
-+
-+struct rtl9300_i2c_drv_data {
-+ struct rtl9300_i2c_reg_field field_desc[F_NUM_FIELDS];
-+ int (*select_scl)(struct rtl9300_i2c *i2c, u8 scl);
-+ u32 data_reg;
-+ u8 max_nchan;
-+};
-+
- #define RTL9300_I2C_MUX_NCHAN 8
-
- struct rtl9300_i2c {
- struct regmap *regmap;
- struct device *dev;
- struct rtl9300_i2c_chan chans[RTL9300_I2C_MUX_NCHAN];
-+ struct regmap_field *fields[F_NUM_FIELDS];
- u32 reg_base;
-+ u32 data_reg;
- u8 sda_pin;
- struct mutex lock;
- };
-
- #define RTL9300_I2C_MST_CTRL1 0x0
--#define RTL9300_I2C_MST_CTRL1_MEM_ADDR_OFS 8
--#define RTL9300_I2C_MST_CTRL1_MEM_ADDR_MASK GENMASK(31, 8)
--#define RTL9300_I2C_MST_CTRL1_SDA_OUT_SEL_OFS 4
--#define RTL9300_I2C_MST_CTRL1_SDA_OUT_SEL_MASK GENMASK(6, 4)
--#define RTL9300_I2C_MST_CTRL1_GPIO_SCL_SEL BIT(3)
--#define RTL9300_I2C_MST_CTRL1_RWOP BIT(2)
--#define RTL9300_I2C_MST_CTRL1_I2C_FAIL BIT(1)
--#define RTL9300_I2C_MST_CTRL1_I2C_TRIG BIT(0)
- #define RTL9300_I2C_MST_CTRL2 0x4
--#define RTL9300_I2C_MST_CTRL2_RD_MODE BIT(15)
--#define RTL9300_I2C_MST_CTRL2_DEV_ADDR_OFS 8
--#define RTL9300_I2C_MST_CTRL2_DEV_ADDR_MASK GENMASK(14, 8)
--#define RTL9300_I2C_MST_CTRL2_DATA_WIDTH_OFS 4
--#define RTL9300_I2C_MST_CTRL2_DATA_WIDTH_MASK GENMASK(7, 4)
--#define RTL9300_I2C_MST_CTRL2_MEM_ADDR_WIDTH_OFS 2
--#define RTL9300_I2C_MST_CTRL2_MEM_ADDR_WIDTH_MASK GENMASK(3, 2)
--#define RTL9300_I2C_MST_CTRL2_SCL_FREQ_OFS 0
--#define RTL9300_I2C_MST_CTRL2_SCL_FREQ_MASK GENMASK(1, 0)
- #define RTL9300_I2C_MST_DATA_WORD0 0x8
- #define RTL9300_I2C_MST_DATA_WORD1 0xc
- #define RTL9300_I2C_MST_DATA_WORD2 0x10
- #define RTL9300_I2C_MST_DATA_WORD3 0x14
--
- #define RTL9300_I2C_MST_GLB_CTRL 0x384
-
- static int rtl9300_i2c_reg_addr_set(struct rtl9300_i2c *i2c, u32 reg, u16 len)
- {
-- u32 val, mask;
- int ret;
-
-- val = len << RTL9300_I2C_MST_CTRL2_MEM_ADDR_WIDTH_OFS;
-- mask = RTL9300_I2C_MST_CTRL2_MEM_ADDR_WIDTH_MASK;
--
-- ret = regmap_update_bits(i2c->regmap, i2c->reg_base + RTL9300_I2C_MST_CTRL2, mask, val);
-+ ret = regmap_field_write(i2c->fields[F_MEM_ADDR_WIDTH], len);
- if (ret)
- return ret;
-
-- val = reg << RTL9300_I2C_MST_CTRL1_MEM_ADDR_OFS;
-- mask = RTL9300_I2C_MST_CTRL1_MEM_ADDR_MASK;
-+ return regmap_field_write(i2c->fields[F_MEM_ADDR], reg);
-+}
-
-- return regmap_update_bits(i2c->regmap, i2c->reg_base + RTL9300_I2C_MST_CTRL1, mask, val);
-+static int rtl9300_i2c_select_scl(struct rtl9300_i2c *i2c, u8 scl)
-+{
-+ return regmap_field_write(i2c->fields[F_SCL_SEL], 1);
- }
-
- static int rtl9300_i2c_config_io(struct rtl9300_i2c *i2c, u8 sda_pin)
- {
-+ struct rtl9300_i2c_drv_data *drv_data;
- int ret;
-- u32 val, mask;
-
-- ret = regmap_update_bits(i2c->regmap, RTL9300_I2C_MST_GLB_CTRL, BIT(sda_pin), BIT(sda_pin));
-+ drv_data = (struct rtl9300_i2c_drv_data *)device_get_match_data(i2c->dev);
-+
-+ ret = regmap_field_update_bits(i2c->fields[F_SDA_SEL], BIT(sda_pin), BIT(sda_pin));
- if (ret)
- return ret;
-
-- val = (sda_pin << RTL9300_I2C_MST_CTRL1_SDA_OUT_SEL_OFS) |
-- RTL9300_I2C_MST_CTRL1_GPIO_SCL_SEL;
-- mask = RTL9300_I2C_MST_CTRL1_SDA_OUT_SEL_MASK | RTL9300_I2C_MST_CTRL1_GPIO_SCL_SEL;
-+ ret = regmap_field_write(i2c->fields[F_SDA_OUT_SEL], sda_pin);
-+ if (ret)
-+ return ret;
-
-- return regmap_update_bits(i2c->regmap, i2c->reg_base + RTL9300_I2C_MST_CTRL1, mask, val);
-+ return drv_data->select_scl(i2c, 0);
- }
-
- static int rtl9300_i2c_config_xfer(struct rtl9300_i2c *i2c, struct rtl9300_i2c_chan *chan,
- u16 addr, u16 len)
- {
-- u32 val, mask;
--
-- val = chan->bus_freq << RTL9300_I2C_MST_CTRL2_SCL_FREQ_OFS;
-- mask = RTL9300_I2C_MST_CTRL2_SCL_FREQ_MASK;
-+ int ret;
-
-- val |= addr << RTL9300_I2C_MST_CTRL2_DEV_ADDR_OFS;
-- mask |= RTL9300_I2C_MST_CTRL2_DEV_ADDR_MASK;
-+ ret = regmap_field_write(i2c->fields[F_SCL_FREQ], chan->bus_freq);
-+ if (ret)
-+ return ret;
-
-- val |= ((len - 1) & 0xf) << RTL9300_I2C_MST_CTRL2_DATA_WIDTH_OFS;
-- mask |= RTL9300_I2C_MST_CTRL2_DATA_WIDTH_MASK;
-+ ret = regmap_field_write(i2c->fields[F_DEV_ADDR], addr);
-+ if (ret)
-+ return ret;
-
-- mask |= RTL9300_I2C_MST_CTRL2_RD_MODE;
-+ ret = regmap_field_write(i2c->fields[F_DATA_WIDTH], (len - 1) & 0xf);
-+ if (ret)
-+ return ret;
-
-- return regmap_update_bits(i2c->regmap, i2c->reg_base + RTL9300_I2C_MST_CTRL2, mask, val);
-+ return regmap_field_write(i2c->fields[F_RD_MODE], 0);
- }
-
- static int rtl9300_i2c_read(struct rtl9300_i2c *i2c, u8 *buf, int len)
-@@ -121,8 +141,7 @@ static int rtl9300_i2c_read(struct rtl93
- if (len > 16)
- return -EIO;
-
-- ret = regmap_bulk_read(i2c->regmap, i2c->reg_base + RTL9300_I2C_MST_DATA_WORD0,
-- vals, ARRAY_SIZE(vals));
-+ ret = regmap_bulk_read(i2c->regmap, i2c->data_reg, vals, ARRAY_SIZE(vals));
- if (ret)
- return ret;
-
-@@ -149,52 +168,49 @@ static int rtl9300_i2c_write(struct rtl9
- vals[reg] |= buf[i] << shift;
- }
-
-- return regmap_bulk_write(i2c->regmap, i2c->reg_base + RTL9300_I2C_MST_DATA_WORD0,
-- vals, ARRAY_SIZE(vals));
-+ return regmap_bulk_write(i2c->regmap, i2c->data_reg, vals, ARRAY_SIZE(vals));
- }
-
- static int rtl9300_i2c_writel(struct rtl9300_i2c *i2c, u32 data)
- {
-- return regmap_write(i2c->regmap, i2c->reg_base + RTL9300_I2C_MST_DATA_WORD0, data);
-+ return regmap_write(i2c->regmap, i2c->data_reg, data);
- }
-
- static int rtl9300_i2c_execute_xfer(struct rtl9300_i2c *i2c, char read_write,
- int size, union i2c_smbus_data *data, int len)
- {
-- u32 val, mask;
-+ u32 val;
- int ret;
-
-- val = read_write == I2C_SMBUS_WRITE ? RTL9300_I2C_MST_CTRL1_RWOP : 0;
-- mask = RTL9300_I2C_MST_CTRL1_RWOP;
--
-- val |= RTL9300_I2C_MST_CTRL1_I2C_TRIG;
-- mask |= RTL9300_I2C_MST_CTRL1_I2C_TRIG;
-+ ret = regmap_field_write(i2c->fields[F_RWOP], read_write == I2C_SMBUS_WRITE);
-+ if (ret)
-+ return ret;
-
-- ret = regmap_update_bits(i2c->regmap, i2c->reg_base + RTL9300_I2C_MST_CTRL1, mask, val);
-+ ret = regmap_field_write(i2c->fields[F_I2C_TRIG], 1);
- if (ret)
- return ret;
-
-- ret = regmap_read_poll_timeout(i2c->regmap, i2c->reg_base + RTL9300_I2C_MST_CTRL1,
-- val, !(val & RTL9300_I2C_MST_CTRL1_I2C_TRIG), 100, 100000);
-+ ret = regmap_field_read_poll_timeout(i2c->fields[F_I2C_TRIG], val, !val, 100, 100000);
- if (ret)
- return ret;
-
-- if (val & RTL9300_I2C_MST_CTRL1_I2C_FAIL)
-+ ret = regmap_field_read(i2c->fields[F_I2C_FAIL], &val);
-+ if (ret)
-+ return ret;
-+ if (val)
- return -EIO;
-
- if (read_write == I2C_SMBUS_READ) {
- switch (size) {
- case I2C_SMBUS_BYTE:
- case I2C_SMBUS_BYTE_DATA:
-- ret = regmap_read(i2c->regmap,
-- i2c->reg_base + RTL9300_I2C_MST_DATA_WORD0, &val);
-+ ret = regmap_read(i2c->regmap, i2c->data_reg, &val);
- if (ret)
- return ret;
- data->byte = val & 0xff;
- break;
- case I2C_SMBUS_WORD_DATA:
-- ret = regmap_read(i2c->regmap,
-- i2c->reg_base + RTL9300_I2C_MST_DATA_WORD0, &val);
-+ ret = regmap_read(i2c->regmap, i2c->data_reg, &val);
- if (ret)
- return ret;
- data->word = val & 0xffff;
-@@ -362,9 +378,11 @@ static int rtl9300_i2c_probe(struct plat
- {
- struct device *dev = &pdev->dev;
- struct rtl9300_i2c *i2c;
-+ struct fwnode_handle *child;
-+ struct rtl9300_i2c_drv_data *drv_data;
-+ struct reg_field fields[F_NUM_FIELDS];
- u32 clock_freq, sda_pin;
- int ret, i = 0;
-- struct fwnode_handle *child;
-
- i2c = devm_kzalloc(dev, sizeof(*i2c), GFP_KERNEL);
- if (!i2c)
-@@ -383,9 +401,22 @@ static int rtl9300_i2c_probe(struct plat
-
- platform_set_drvdata(pdev, i2c);
-
-- if (device_get_child_node_count(dev) >= RTL9300_I2C_MUX_NCHAN)
-+ drv_data = (struct rtl9300_i2c_drv_data *)device_get_match_data(i2c->dev);
-+ if (device_get_child_node_count(dev) >= drv_data->max_nchan)
- return dev_err_probe(dev, -EINVAL, "Too many channels\n");
-
-+ i2c->data_reg = i2c->reg_base + drv_data->data_reg;
-+ for (i = 0; i < F_NUM_FIELDS; i++) {
-+ fields[i] = drv_data->field_desc[i].field;
-+ if (drv_data->field_desc[i].scope == REG_SCOPE_MASTER)
-+ fields[i].reg += i2c->reg_base;
-+ }
-+ ret = devm_regmap_field_bulk_alloc(dev, i2c->regmap, i2c->fields,
-+ fields, F_NUM_FIELDS);
-+ if (ret)
-+ return ret;
-+
-+ i = 0;
- device_for_each_child_node(dev, child) {
- struct rtl9300_i2c_chan *chan = &i2c->chans[i];
- struct i2c_adapter *adap = &chan->adap;
-@@ -402,7 +433,6 @@ static int rtl9300_i2c_probe(struct plat
- case I2C_MAX_STANDARD_MODE_FREQ:
- chan->bus_freq = RTL9300_I2C_STD_FREQ;
- break;
--
- case I2C_MAX_FAST_MODE_FREQ:
- chan->bus_freq = RTL9300_I2C_FAST_FREQ;
- break;
-@@ -434,11 +464,37 @@ static int rtl9300_i2c_probe(struct plat
- return 0;
- }
-
-+#define GLB_REG_FIELD(reg, msb, lsb) \
-+ { .field = REG_FIELD(reg, msb, lsb), .scope = REG_SCOPE_GLOBAL }
-+#define MST_REG_FIELD(reg, msb, lsb) \
-+ { .field = REG_FIELD(reg, msb, lsb), .scope = REG_SCOPE_MASTER }
-+
-+static const struct rtl9300_i2c_drv_data rtl9300_i2c_drv_data = {
-+ .field_desc = {
-+ [F_MEM_ADDR] = MST_REG_FIELD(RTL9300_I2C_MST_CTRL1, 8, 31),
-+ [F_SDA_OUT_SEL] = MST_REG_FIELD(RTL9300_I2C_MST_CTRL1, 4, 6),
-+ [F_SCL_SEL] = MST_REG_FIELD(RTL9300_I2C_MST_CTRL1, 3, 3),
-+ [F_RWOP] = MST_REG_FIELD(RTL9300_I2C_MST_CTRL1, 2, 2),
-+ [F_I2C_FAIL] = MST_REG_FIELD(RTL9300_I2C_MST_CTRL1, 1, 1),
-+ [F_I2C_TRIG] = MST_REG_FIELD(RTL9300_I2C_MST_CTRL1, 0, 0),
-+ [F_RD_MODE] = MST_REG_FIELD(RTL9300_I2C_MST_CTRL2, 15, 15),
-+ [F_DEV_ADDR] = MST_REG_FIELD(RTL9300_I2C_MST_CTRL2, 8, 14),
-+ [F_DATA_WIDTH] = MST_REG_FIELD(RTL9300_I2C_MST_CTRL2, 4, 7),
-+ [F_MEM_ADDR_WIDTH] = MST_REG_FIELD(RTL9300_I2C_MST_CTRL2, 2, 3),
-+ [F_SCL_FREQ] = MST_REG_FIELD(RTL9300_I2C_MST_CTRL2, 0, 1),
-+ [F_SDA_SEL] = GLB_REG_FIELD(RTL9300_I2C_MST_GLB_CTRL, 0, 7),
-+ },
-+ .select_scl = rtl9300_i2c_select_scl,
-+ .data_reg = RTL9300_I2C_MST_DATA_WORD0,
-+ .max_nchan = RTL9300_I2C_MUX_NCHAN,
-+};
-+
-+
- static const struct of_device_id i2c_rtl9300_dt_ids[] = {
-- { .compatible = "realtek,rtl9301-i2c" },
-- { .compatible = "realtek,rtl9302b-i2c" },
-- { .compatible = "realtek,rtl9302c-i2c" },
-- { .compatible = "realtek,rtl9303-i2c" },
-+ { .compatible = "realtek,rtl9301-i2c", .data = (void *) &rtl9300_i2c_drv_data },
-+ { .compatible = "realtek,rtl9302b-i2c", .data = (void *) &rtl9300_i2c_drv_data },
-+ { .compatible = "realtek,rtl9302c-i2c", .data = (void *) &rtl9300_i2c_drv_data },
-+ { .compatible = "realtek,rtl9303-i2c", .data = (void *) &rtl9300_i2c_drv_data },
- {}
- };
- MODULE_DEVICE_TABLE(of, i2c_rtl9300_dt_ids);
+++ /dev/null
-From 571dd5203fcd1b9f78bc3d913492c0e13ad1bb00 Mon Sep 17 00:00:00 2001
-Date: Sat, 9 Aug 2025 19:58:43 +0000
-Subject: [PATCH v5 02/11] i2c: rtl9300: fix channel number bound check
-
-Fix the current check for number of channels (child nodes in the device
-tree). Before, this was:
-
-if (device_get_child_node_count(dev) >= drv_data->max_nchan)
-
-drv_data->max_nchan gives the maximum number of channels so checking
-with '>=' isn't correct because it doesn't allow the last channel
-number. Thus, fix it to:
-
-if (device_get_child_node_count(dev) > drv_data->max_nchan)
-
-Issue occured on a TP-Link TL-ST1008F v2.0 device (8 SFP+ ports) and fix
-is tested there.
-
----
- drivers/i2c/busses/i2c-rtl9300.c | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
---- a/drivers/i2c/busses/i2c-rtl9300.c
-+++ b/drivers/i2c/busses/i2c-rtl9300.c
-@@ -402,7 +402,7 @@ static int rtl9300_i2c_probe(struct plat
- platform_set_drvdata(pdev, i2c);
-
- drv_data = (struct rtl9300_i2c_drv_data *)device_get_match_data(i2c->dev);
-- if (device_get_child_node_count(dev) >= drv_data->max_nchan)
-+ if (device_get_child_node_count(dev) > drv_data->max_nchan)
- return dev_err_probe(dev, -EINVAL, "Too many channels\n");
-
- i2c->data_reg = i2c->reg_base + drv_data->data_reg;
+++ /dev/null
-From 7bb38428b718a281e09b35bc26478600b53300fc Mon Sep 17 00:00:00 2001
-Date: Mon, 4 Aug 2025 10:26:35 +0000
-Subject: [PATCH v5 03/11] dt-bindings: i2c: realtek,rtl9301-i2c: fix wording
- and typos
-
-Fix wording of binding description to use plural because there is not
-only a single RTL9300 SoC. RTL9300 describes a whole family of Realtek
-SoCs.
-
-Add missing word 'of' in description of reg property.
-
-Change 'SDA pin' to 'SDA line number' because the property must contain
-the SDA (channel) number ranging from 0-7 instead of a real pin number.
-
----
- .../devicetree/bindings/i2c/realtek,rtl9301-i2c.yaml | 6 +++---
- 1 file changed, 3 insertions(+), 3 deletions(-)
-
---- a/Documentation/devicetree/bindings/i2c/realtek,rtl9301-i2c.yaml
-+++ b/Documentation/devicetree/bindings/i2c/realtek,rtl9301-i2c.yaml
-@@ -10,7 +10,7 @@ maintainers:
-
- description:
-- The RTL9300 SoC has two I2C controllers. Each of these has an SCL line (which
-+ RTL9300 SoCs have two I2C controllers. Each of these has an SCL line (which
- if not-used for SCL can be a GPIO). There are 8 common SDA lines that can be
- assigned to either I2C controller.
-
-@@ -27,7 +27,7 @@ properties:
-
- reg:
- items:
-- - description: Register offset and size this I2C controller.
-+ - description: Register offset and size of this I2C controller.
-
- "#address-cells":
- const: 1
-@@ -42,7 +42,7 @@ patternProperties:
-
- properties:
- reg:
-- description: The SDA pin associated with the I2C bus.
-+ description: The SDA line number associated with the I2C bus.
- maxItems: 1
-
- required:
+++ /dev/null
-From bccfb0e2a337529d07134e52ca3e2ec1c156bd7d Mon Sep 17 00:00:00 2001
-Date: Mon, 4 Aug 2025 11:27:57 +0000
-Subject: [PATCH v5 04/11] i2c: rtl9300: rename internal sda_pin to sda_num
-
-Rename the internally used 'sda_pin' to 'sda_num' to make it clear that
-this is NOT the actual pin number of the GPIO pin but rather the logical
-SDA channel number. Although the alternate function SDA_Y is sometimes
-given with the GPIO number, this is not always the case. Thus, avoid any
-confusion or misconfiguration by giving the variable the correct name.
-
-This follows the description change in the devicetree bindings.
-
----
- drivers/i2c/busses/i2c-rtl9300.c | 24 ++++++++++++------------
- 1 file changed, 12 insertions(+), 12 deletions(-)
-
---- a/drivers/i2c/busses/i2c-rtl9300.c
-+++ b/drivers/i2c/busses/i2c-rtl9300.c
-@@ -20,7 +20,7 @@ struct rtl9300_i2c_chan {
- struct i2c_adapter adap;
- struct rtl9300_i2c *i2c;
- enum rtl9300_bus_freq bus_freq;
-- u8 sda_pin;
-+ u8 sda_num;
- };
-
- enum rtl9300_i2c_reg_scope {
-@@ -67,7 +67,7 @@ struct rtl9300_i2c {
- struct regmap_field *fields[F_NUM_FIELDS];
- u32 reg_base;
- u32 data_reg;
-- u8 sda_pin;
-+ u8 sda_num;
- struct mutex lock;
- };
-
-@@ -102,11 +102,11 @@ static int rtl9300_i2c_config_io(struct
-
- drv_data = (struct rtl9300_i2c_drv_data *)device_get_match_data(i2c->dev);
-
-- ret = regmap_field_update_bits(i2c->fields[F_SDA_SEL], BIT(sda_pin), BIT(sda_pin));
-+ ret = regmap_field_update_bits(i2c->fields[F_SDA_SEL], BIT(sda_num), BIT(sda_num));
- if (ret)
- return ret;
-
-- ret = regmap_field_write(i2c->fields[F_SDA_OUT_SEL], sda_pin);
-+ ret = regmap_field_write(i2c->fields[F_SDA_OUT_SEL], sda_num);
- if (ret)
- return ret;
-
-@@ -240,11 +240,11 @@ static int rtl9300_i2c_smbus_xfer(struct
- int len = 0, ret;
-
- mutex_lock(&i2c->lock);
-- if (chan->sda_pin != i2c->sda_pin) {
-+ if (chan->sda_num != i2c->sda_num) {
- ret = rtl9300_i2c_config_io(i2c, chan->sda_pin);
- if (ret)
- goto out_unlock;
-- i2c->sda_pin = chan->sda_pin;
-+ i2c->sda_num = chan->sda_num;
- }
-
- switch (size) {
-@@ -381,7 +381,7 @@ static int rtl9300_i2c_probe(struct plat
- struct fwnode_handle *child;
- struct rtl9300_i2c_drv_data *drv_data;
- struct reg_field fields[F_NUM_FIELDS];
-- u32 clock_freq, sda_pin;
-+ u32 clock_freq, sda_num;
- int ret, i = 0;
-
- i2c = devm_kzalloc(dev, sizeof(*i2c), GFP_KERNEL);
-@@ -421,7 +421,7 @@ static int rtl9300_i2c_probe(struct plat
- struct rtl9300_i2c_chan *chan = &i2c->chans[i];
- struct i2c_adapter *adap = &chan->adap;
-
-- ret = fwnode_property_read_u32(child, "reg", &sda_pin);
-+ ret = fwnode_property_read_u32(child, "reg", &sda_num);
- if (ret)
- return ret;
-
-@@ -438,11 +438,11 @@ static int rtl9300_i2c_probe(struct plat
- break;
- default:
- dev_warn(i2c->dev, "SDA%d clock-frequency %d not supported using default\n",
-- sda_pin, clock_freq);
-+ sda_num, clock_freq);
- break;
- }
-
-- chan->sda_pin = sda_pin;
-+ chan->sda_num = sda_num;
- chan->i2c = i2c;
- adap = &i2c->chans[i].adap;
- adap->owner = THIS_MODULE;
-@@ -452,14 +452,14 @@ static int rtl9300_i2c_probe(struct plat
- adap->dev.parent = dev;
- i2c_set_adapdata(adap, chan);
- adap->dev.of_node = to_of_node(child);
-- snprintf(adap->name, sizeof(adap->name), "%s SDA%d\n", dev_name(dev), sda_pin);
-+ snprintf(adap->name, sizeof(adap->name), "%s SDA%d\n", dev_name(dev), sda_num);
- i++;
-
- ret = devm_i2c_add_adapter(dev, adap);
- if (ret)
- return ret;
- }
-- i2c->sda_pin = 0xff;
-+ i2c->sda_num = 0xff;
-
- return 0;
- }
+++ /dev/null
-From 47c2f21b934bda1d4031db8d54e08d52b180d9df Mon Sep 17 00:00:00 2001
-Date: Tue, 5 Aug 2025 09:06:44 +0000
-Subject: [PATCH v5 05/11] i2c: rtl9300: check if xfer length is valid
-
-Add an explicit check for the xfer length to 'rtl9300_i2c_config_xfer'
-to make sure a length < 1 or > 16 isn't accepted. While there shouldn't
-be a length > 16 because this is specified in the i2c_adapter_quirks, a
-length of 0 may be passed. This is problematic, because the code adopts
-this value with
-
-(len - 1) & 0xf
-
-because the corresponding register documentation states that the value
-in the register is always used + 1 ([1]). Obvious reason for this is to
-fit allow xfer length of 16 to be specified in this 4-bit wide register
-field.
-
-Another consequence of this is also, that an xfer length of 0 cannot be
-set. Thus, an explicit check should avoid this. Before, this actually
-led to writing 0xf into the register, probably causing unintended
-behaviour.
-
----
- drivers/i2c/busses/i2c-rtl9300.c | 3 +++
- 1 file changed, 3 insertions(+)
-
---- a/drivers/i2c/busses/i2c-rtl9300.c
-+++ b/drivers/i2c/busses/i2c-rtl9300.c
-@@ -118,6 +118,9 @@ static int rtl9300_i2c_config_xfer(struc
- {
- int ret;
-
-+ if (len < 1 || len > 16)
-+ return -EINVAL;
-+
- ret = regmap_field_write(i2c->fields[F_SCL_FREQ], chan->bus_freq);
- if (ret)
- return ret;
+++ /dev/null
-From cee289c080977752b485dc41a5fbc53c5af93aa9 Mon Sep 17 00:00:00 2001
-Date: Tue, 5 Aug 2025 09:14:51 +0000
-Subject: [PATCH v5 06/11] i2c: rtl9300: remove SMBus Quick operation support
-
-Remove the SMBus Quick operation from this driver because it is not
-natively supported by the hardware and is wrongly implemented in the
-driver.
-
-The I2C controllers in Realtek RTL9300 and RTL9310 are SMBus-compliant
-but there doesn't seem to be native support for the SMBus Quick
-operation. It is not explicitly mentioned in the documentation but
-looking at the registers which configure an SMBus transaction, one can
-see that the data length cannot be set to 0. This suggests that the
-hardware doesn't allow any SMBus message without data bytes (except for
-those it does on it's own, see SMBus Block Read).
-
-The current implementation of SMBus Quick operation passes a length of
-0 (which is actually invalid). Before the fix of a bug in a previous
-commit, this led to a read operation of 16 bytes from any register (the
-one of a former transaction or any other value.
-
-Although there are currently no reports of actual issues this caused.
-However, as an example, i2cdetect by default uses Quick Write operation
-to probe the bus and this may already write anything to some register
-of a device, causing unintended behaviour. This could be the cause of a
-recent brick of one of my DAC cables where there was a checksum mismatch
-of the EEPROM after having run 'i2cdetect -l' before.
-
-Because SMBus Quick operation is obviously not supported on these
-controllers (because a length of 0 cannot be set, even when no register
-address is set), remove that instead of claiming there is support. There
-also shouldn't be any kind of emulated 'Quick' which just does another
-kind of operation in the background. Otherwise, specific issues may
-occur in case of a 'Quick' Write which writes unknown data to an unknown
-register.
-
----
- drivers/i2c/busses/i2c-rtl9300.c | 16 +++-------------
- 1 file changed, 3 insertions(+), 13 deletions(-)
-
---- a/drivers/i2c/busses/i2c-rtl9300.c
-+++ b/drivers/i2c/busses/i2c-rtl9300.c
-@@ -251,15 +251,6 @@ static int rtl9300_i2c_smbus_xfer(struct
- }
-
- switch (size) {
-- case I2C_SMBUS_QUICK:
-- ret = rtl9300_i2c_config_xfer(i2c, chan, addr, 0);
-- if (ret)
-- goto out_unlock;
-- ret = rtl9300_i2c_reg_addr_set(i2c, 0, 0);
-- if (ret)
-- goto out_unlock;
-- break;
--
- case I2C_SMBUS_BYTE:
- if (read_write == I2C_SMBUS_WRITE) {
- ret = rtl9300_i2c_config_xfer(i2c, chan, addr, 0);
-@@ -360,10 +351,9 @@ out_unlock:
-
- static u32 rtl9300_i2c_func(struct i2c_adapter *a)
- {
-- return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
-- I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
-- I2C_FUNC_SMBUS_READ_I2C_BLOCK | I2C_FUNC_SMBUS_WRITE_I2C_BLOCK |
-- I2C_FUNC_SMBUS_BLOCK_DATA;
-+ return I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA |
-+ I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_READ_I2C_BLOCK |
-+ I2C_FUNC_SMBUS_WRITE_I2C_BLOCK | I2C_FUNC_SMBUS_BLOCK_DATA;
- }
-
- static const struct i2c_algorithm rtl9300_i2c_algo = {
+++ /dev/null
-From c64be5e5ee6326ca7ec38dbcc8892254f1da3cf8 Mon Sep 17 00:00:00 2001
-Date: Fri, 1 Aug 2025 21:18:56 +0000
-Subject: [PATCH v5 07/11] i2c: rtl9300: move setting SCL frequency to
- config_io
-
-Move the register operation to set the SCL frequency to the
-rtl9300_i2c_config_io function instead of the rtl9300_i2c_config_xfer
-function. This rather belongs there next to selecting the current SDA
-output line.
-
----
- drivers/i2c/busses/i2c-rtl9300.c | 17 +++++++++--------
- 1 file changed, 9 insertions(+), 8 deletions(-)
-
---- a/drivers/i2c/busses/i2c-rtl9300.c
-+++ b/drivers/i2c/busses/i2c-rtl9300.c
-@@ -95,18 +95,23 @@ static int rtl9300_i2c_select_scl(struct
- return regmap_field_write(i2c->fields[F_SCL_SEL], 1);
- }
-
--static int rtl9300_i2c_config_io(struct rtl9300_i2c *i2c, u8 sda_pin)
-+static int rtl9300_i2c_config_io(struct rtl9300_i2c *i2c, struct rtl9300_i2c_chan *chan)
- {
- struct rtl9300_i2c_drv_data *drv_data;
- int ret;
-
- drv_data = (struct rtl9300_i2c_drv_data *)device_get_match_data(i2c->dev);
-
-- ret = regmap_field_update_bits(i2c->fields[F_SDA_SEL], BIT(sda_num), BIT(sda_num));
-+ ret = regmap_field_update_bits(i2c->fields[F_SDA_SEL], BIT(chan->sda_num),
-+ BIT(chan->sda_num));
- if (ret)
- return ret;
-
-- ret = regmap_field_write(i2c->fields[F_SDA_OUT_SEL], sda_num);
-+ ret = regmap_field_write(i2c->fields[F_SDA_OUT_SEL], chan->sda_num);
-+ if (ret)
-+ return ret;
-+
-+ ret = regmap_field_write(i2c->fields[F_SCL_FREQ], chan->bus_freq);
- if (ret)
- return ret;
-
-@@ -121,10 +126,6 @@ static int rtl9300_i2c_config_xfer(struc
- if (len < 1 || len > 16)
- return -EINVAL;
-
-- ret = regmap_field_write(i2c->fields[F_SCL_FREQ], chan->bus_freq);
-- if (ret)
-- return ret;
--
- ret = regmap_field_write(i2c->fields[F_DEV_ADDR], addr);
- if (ret)
- return ret;
-@@ -244,7 +245,7 @@ static int rtl9300_i2c_smbus_xfer(struct
-
- mutex_lock(&i2c->lock);
- if (chan->sda_num != i2c->sda_num) {
-- ret = rtl9300_i2c_config_io(i2c, chan->sda_pin);
-+ ret = rtl9300_i2c_config_io(i2c, chan);
- if (ret)
- goto out_unlock;
- i2c->sda_num = chan->sda_num;
+++ /dev/null
-From bd70c2680786f3b1def31285ad5114f17e381b3a Mon Sep 17 00:00:00 2001
-Date: Fri, 1 Aug 2025 21:31:09 +0000
-Subject: [PATCH v5 08/11] i2c: rtl9300: do not set read mode on every transfer
-
-Move the operation to set the read mode from config_xfer to probe.
-
-The I2C controller of RTL9300 and RTL9310 support a legacy message mode
-for READs with 'Read Address Data' instead of the standard format 'Write
-Address ; Read Data'. There is no way to pass that via smbus_xfer, thus
-there is no point in supported this in the driver and moreover no point
-in setting this on every transaction. Setting this once in the probe
-call is sufficient.
-
----
- drivers/i2c/busses/i2c-rtl9300.c | 11 ++++++-----
- 1 file changed, 6 insertions(+), 5 deletions(-)
-
---- a/drivers/i2c/busses/i2c-rtl9300.c
-+++ b/drivers/i2c/busses/i2c-rtl9300.c
-@@ -130,11 +130,7 @@ static int rtl9300_i2c_config_xfer(struc
- if (ret)
- return ret;
-
-- ret = regmap_field_write(i2c->fields[F_DATA_WIDTH], (len - 1) & 0xf);
-- if (ret)
-- return ret;
--
-- return regmap_field_write(i2c->fields[F_RD_MODE], 0);
-+ return regmap_field_write(i2c->fields[F_DATA_WIDTH], (len - 1) & 0xf);
- }
-
- static int rtl9300_i2c_read(struct rtl9300_i2c *i2c, u8 *buf, int len)
-@@ -455,6 +451,11 @@ static int rtl9300_i2c_probe(struct plat
- }
- i2c->sda_num = 0xff;
-
-+ /* only use standard read format */
-+ ret = regmap_field_write(i2c->fields[F_RD_MODE], 0);
-+ if (ret)
-+ return ret;
-+
- return 0;
- }
-
+++ /dev/null
-From 9665c6a9812d9e2fa2b86be1fa1fc2b381e6f34e Mon Sep 17 00:00:00 2001
-Date: Mon, 4 Aug 2025 11:31:11 +0000
-Subject: [PATCH v5 09/11] i2c: rtl9300: separate xfer configuration and
- execution
-
-So far, the rtl9300_i2c_smbus_xfer code is quite a mess with function
-calls distributed over the whole function setting different values in
-different cases. Calls to rtl9300_i2c_config_xfer and
-rtl9300_i2c_reg_addr_set are used in every case-block with varying
-values whose meaning is not instantly obvious. In some cases, there are
-additional calls within these case-blocks doing more things.
-
-This is in general a bad design and especially really bad for
-readability and maintainability because it distributes changes or
-issues to multiple locations due to the same function being called with
-different hardcoded values in different places.
-
-To have a good structure, setting different parameters based on the
-desired operation should not be interleaved with applying these
-parameters to the hardware registers. Or in different words, the
-parameter site should be mixed with the call site.
-
-Thus, separate configuration and execution of an SMBus xfer within
-rtl9300_i2c_smbus_xfer to improve readability and maintainability. Add a
-new 'struct rtl9300_i2c_xfer' to carry the required parameters for an
-xfer which are configured based on the input parameters within a single
-switch-case block, without having any function calls within this block.
-
-The function calls to actually apply these values to the hardware
-registers then appear below in a single place and just operate on the
-passed instance of 'struct rtl9300_i2c_xfer'. These are
-'rtl9300_i2c_prepare_xfer' which combines applying all parameters of the
-xfer to the corresponding register, and 'rtl9300_i2c_do_xfer' which
-actually executes the xfer and does post-processing if needed.
-
----
- drivers/i2c/busses/i2c-rtl9300.c | 234 +++++++++++++++----------------
- 1 file changed, 114 insertions(+), 120 deletions(-)
-
---- a/drivers/i2c/busses/i2c-rtl9300.c
-+++ b/drivers/i2c/busses/i2c-rtl9300.c
-@@ -8,6 +8,7 @@
- #include <linux/mutex.h>
- #include <linux/platform_device.h>
- #include <linux/regmap.h>
-+#include <linux/unaligned.h>
-
- enum rtl9300_bus_freq {
- RTL9300_I2C_STD_FREQ,
-@@ -71,6 +72,22 @@ struct rtl9300_i2c {
- struct mutex lock;
- };
-
-+enum rtl9300_i2c_xfer_type {
-+ RTL9300_I2C_XFER_BYTE,
-+ RTL9300_I2C_XFER_WORD,
-+ RTL9300_I2C_XFER_BLOCK,
-+};
-+
-+struct rtl9300_i2c_xfer {
-+ enum rtl9300_i2c_xfer_type type;
-+ u16 dev_addr;
-+ u8 reg_addr;
-+ u8 reg_addr_len;
-+ u8 *data;
-+ u8 data_len;
-+ bool write;
-+};
-+
- #define RTL9300_I2C_MST_CTRL1 0x0
- #define RTL9300_I2C_MST_CTRL2 0x4
- #define RTL9300_I2C_MST_DATA_WORD0 0x8
-@@ -95,45 +112,37 @@ static int rtl9300_i2c_select_scl(struct
- return regmap_field_write(i2c->fields[F_SCL_SEL], 1);
- }
-
--static int rtl9300_i2c_config_io(struct rtl9300_i2c *i2c, struct rtl9300_i2c_chan *chan)
-+static int rtl9300_i2c_config_chan(struct rtl9300_i2c *i2c, struct rtl9300_i2c_chan *chan)
- {
- struct rtl9300_i2c_drv_data *drv_data;
- int ret;
-
-- drv_data = (struct rtl9300_i2c_drv_data *)device_get_match_data(i2c->dev);
-+ if (i2c->sda_num == chan->sda_num)
-+ return 0;
-
-- ret = regmap_field_update_bits(i2c->fields[F_SDA_SEL], BIT(chan->sda_num),
-- BIT(chan->sda_num));
-+ ret = regmap_field_write(i2c->fields[F_SCL_FREQ], chan->bus_freq);
- if (ret)
- return ret;
-
-- ret = regmap_field_write(i2c->fields[F_SDA_OUT_SEL], chan->sda_num);
-+ drv_data = (struct rtl9300_i2c_drv_data *)device_get_match_data(i2c->dev);
-+ ret = drv_data->select_scl(i2c, 0);
- if (ret)
- return ret;
-
-- ret = regmap_field_write(i2c->fields[F_SCL_FREQ], chan->bus_freq);
-+ ret = regmap_field_update_bits(i2c->fields[F_SDA_SEL], BIT(chan->sda_num),
-+ BIT(chan->sda_num));
- if (ret)
- return ret;
-
-- return drv_data->select_scl(i2c, 0);
--}
--
--static int rtl9300_i2c_config_xfer(struct rtl9300_i2c *i2c, struct rtl9300_i2c_chan *chan,
-- u16 addr, u16 len)
--{
-- int ret;
--
-- if (len < 1 || len > 16)
-- return -EINVAL;
--
-- ret = regmap_field_write(i2c->fields[F_DEV_ADDR], addr);
-+ ret = regmap_field_write(i2c->fields[F_SDA_OUT_SEL], chan->sda_num);
- if (ret)
- return ret;
-
-- return regmap_field_write(i2c->fields[F_DATA_WIDTH], (len - 1) & 0xf);
-+ i2c->sda_num = chan->sda_num;
-+ return 0;
- }
-
--static int rtl9300_i2c_read(struct rtl9300_i2c *i2c, u8 *buf, int len)
-+static int rtl9300_i2c_read(struct rtl9300_i2c *i2c, u8 *buf, u8 len)
- {
- u32 vals[4] = {};
- int i, ret;
-@@ -153,7 +162,7 @@ static int rtl9300_i2c_read(struct rtl93
- return 0;
- }
-
--static int rtl9300_i2c_write(struct rtl9300_i2c *i2c, u8 *buf, int len)
-+static int rtl9300_i2c_write(struct rtl9300_i2c *i2c, u8 *buf, u8 len)
- {
- u32 vals[4] = {};
- int i;
-@@ -176,16 +185,51 @@ static int rtl9300_i2c_writel(struct rtl
- return regmap_write(i2c->regmap, i2c->data_reg, data);
- }
-
--static int rtl9300_i2c_execute_xfer(struct rtl9300_i2c *i2c, char read_write,
-- int size, union i2c_smbus_data *data, int len)
-+static int rtl9300_i2c_prepare_xfer(struct rtl9300_i2c *i2c, struct rtl9300_i2c_xfer *xfer)
- {
-- u32 val;
- int ret;
-
-- ret = regmap_field_write(i2c->fields[F_RWOP], read_write == I2C_SMBUS_WRITE);
-+ if (xfer->data_len < 1 || xfer->data_len > 16)
-+ return -EINVAL;
-+
-+ ret = regmap_field_write(i2c->fields[F_DEV_ADDR], xfer->dev_addr);
- if (ret)
- return ret;
-
-+ ret = rtl9300_i2c_reg_addr_set(i2c, xfer->reg_addr, xfer->reg_addr_len);
-+ if (ret)
-+ return ret;
-+
-+ ret = regmap_field_write(i2c->fields[F_RWOP], xfer->write);
-+ if (ret)
-+ return ret;
-+
-+ ret = regmap_field_write(i2c->fields[F_DATA_WIDTH], (xfer->data_len - 1) & 0xf);
-+ if (ret)
-+ return ret;
-+
-+ if (xfer->write) {
-+ switch (xfer->type) {
-+ case RTL9300_I2C_XFER_BYTE:
-+ ret = rtl9300_i2c_writel(i2c, *xfer->data);
-+ break;
-+ case RTL9300_I2C_XFER_WORD:
-+ ret = rtl9300_i2c_writel(i2c, get_unaligned((const u16 *)xfer->data));
-+ break;
-+ default:
-+ ret = rtl9300_i2c_write(i2c, xfer->data, xfer->data_len);
-+ break;
-+ }
-+ }
-+
-+ return ret;
-+}
-+
-+static int rtl9300_i2c_do_xfer(struct rtl9300_i2c *i2c, struct rtl9300_i2c_xfer *xfer)
-+{
-+ u32 val;
-+ int ret;
-+
- ret = regmap_field_write(i2c->fields[F_I2C_TRIG], 1);
- if (ret)
- return ret;
-@@ -200,28 +244,24 @@ static int rtl9300_i2c_execute_xfer(stru
- if (val)
- return -EIO;
-
-- if (read_write == I2C_SMBUS_READ) {
-- switch (size) {
-- case I2C_SMBUS_BYTE:
-- case I2C_SMBUS_BYTE_DATA:
-+ if (!xfer->write) {
-+ switch (xfer->type) {
-+ case RTL9300_I2C_XFER_BYTE:
- ret = regmap_read(i2c->regmap, i2c->data_reg, &val);
- if (ret)
- return ret;
-- data->byte = val & 0xff;
-+
-+ *xfer->data = val & 0xff;
- break;
-- case I2C_SMBUS_WORD_DATA:
-+ case RTL9300_I2C_XFER_WORD:
- ret = regmap_read(i2c->regmap, i2c->data_reg, &val);
- if (ret)
- return ret;
-- data->word = val & 0xffff;
-- break;
-- case I2C_SMBUS_I2C_BLOCK_DATA:
-- ret = rtl9300_i2c_read(i2c, &data->block[1], len);
-- if (ret)
-- return ret;
-+
-+ put_unaligned(val & 0xffff, (u16*)xfer->data);
- break;
- default:
-- ret = rtl9300_i2c_read(i2c, &data->block[0], len);
-+ ret = rtl9300_i2c_read(i2c, xfer->data, xfer->data_len);
- if (ret)
- return ret;
- break;
-@@ -237,108 +277,62 @@ static int rtl9300_i2c_smbus_xfer(struct
- {
- struct rtl9300_i2c_chan *chan = i2c_get_adapdata(adap);
- struct rtl9300_i2c *i2c = chan->i2c;
-- int len = 0, ret;
-+ struct rtl9300_i2c_xfer xfer = {0};
-+ int ret;
-+
-+ if (addr > 0x7f)
-+ return -EINVAL;
-
- mutex_lock(&i2c->lock);
-- if (chan->sda_num != i2c->sda_num) {
-- ret = rtl9300_i2c_config_io(i2c, chan);
-- if (ret)
-- goto out_unlock;
-- i2c->sda_num = chan->sda_num;
-- }
-+
-+ ret = rtl9300_i2c_config_chan(i2c, chan);
-+ if (ret)
-+ goto out_unlock;
-+
-+ xfer.dev_addr = addr & 0x7f;
-+ xfer.write = (read_write == I2C_SMBUS_WRITE);
-+ xfer.reg_addr = command;
-+ xfer.reg_addr_len = 1;
-
- switch (size) {
- case I2C_SMBUS_BYTE:
-- if (read_write == I2C_SMBUS_WRITE) {
-- ret = rtl9300_i2c_config_xfer(i2c, chan, addr, 0);
-- if (ret)
-- goto out_unlock;
-- ret = rtl9300_i2c_reg_addr_set(i2c, command, 1);
-- if (ret)
-- goto out_unlock;
-- } else {
-- ret = rtl9300_i2c_config_xfer(i2c, chan, addr, 1);
-- if (ret)
-- goto out_unlock;
-- ret = rtl9300_i2c_reg_addr_set(i2c, 0, 0);
-- if (ret)
-- goto out_unlock;
-- }
-+ xfer.data = (read_write == I2C_SMBUS_READ) ? &data->byte : &command;
-+ xfer.data_len = 1;
-+ xfer.reg_addr = 0;
-+ xfer.reg_addr_len = 0;
-+ xfer.type = RTL9300_I2C_XFER_BYTE;
- break;
--
- case I2C_SMBUS_BYTE_DATA:
-- ret = rtl9300_i2c_reg_addr_set(i2c, command, 1);
-- if (ret)
-- goto out_unlock;
-- ret = rtl9300_i2c_config_xfer(i2c, chan, addr, 1);
-- if (ret)
-- goto out_unlock;
-- if (read_write == I2C_SMBUS_WRITE) {
-- ret = rtl9300_i2c_writel(i2c, data->byte);
-- if (ret)
-- goto out_unlock;
-- }
-+ xfer.data = &data->byte;
-+ xfer.data_len = 1;
-+ xfer.type = RTL9300_I2C_XFER_BYTE;
- break;
--
- case I2C_SMBUS_WORD_DATA:
-- ret = rtl9300_i2c_reg_addr_set(i2c, command, 1);
-- if (ret)
-- goto out_unlock;
-- ret = rtl9300_i2c_config_xfer(i2c, chan, addr, 2);
-- if (ret)
-- goto out_unlock;
-- if (read_write == I2C_SMBUS_WRITE) {
-- ret = rtl9300_i2c_writel(i2c, data->word);
-- if (ret)
-- goto out_unlock;
-- }
-+ xfer.data = (u8 *)&data->word;
-+ xfer.data_len = 2;
-+ xfer.type = RTL9300_I2C_XFER_WORD;
- break;
--
- case I2C_SMBUS_BLOCK_DATA:
-- ret = rtl9300_i2c_reg_addr_set(i2c, command, 1);
-- if (ret)
-- goto out_unlock;
-- if (data->block[0] < 1 || data->block[0] > I2C_SMBUS_BLOCK_MAX) {
-- ret = -EINVAL;
-- goto out_unlock;
-- }
-- ret = rtl9300_i2c_config_xfer(i2c, chan, addr, data->block[0] + 1);
-- if (ret)
-- goto out_unlock;
-- if (read_write == I2C_SMBUS_WRITE) {
-- ret = rtl9300_i2c_write(i2c, &data->block[0], data->block[0] + 1);
-- if (ret)
-- goto out_unlock;
-- }
-- len = data->block[0] + 1;
-+ xfer.data = &data->block[0];
-+ xfer.data_len = data->block[0] + 1;
-+ xfer.type = RTL9300_I2C_XFER_BLOCK;
- break;
--
- case I2C_SMBUS_I2C_BLOCK_DATA:
-- ret = rtl9300_i2c_reg_addr_set(i2c, command, 1);
-- if (ret)
-- goto out_unlock;
-- if (data->block[0] < 1 || data->block[0] > I2C_SMBUS_BLOCK_MAX) {
-- ret = -EINVAL;
-- goto out_unlock;
-- }
-- ret = rtl9300_i2c_config_xfer(i2c, chan, addr, data->block[0]);
-- if (ret)
-- goto out_unlock;
-- if (read_write == I2C_SMBUS_WRITE) {
-- ret = rtl9300_i2c_write(i2c, &data->block[1], data->block[0]);
-- if (ret)
-- goto out_unlock;
-- }
-- len = data->block[0];
-+ xfer.data = &data->block[1];
-+ xfer.data_len = data->block[0];
-+ xfer.type = RTL9300_I2C_XFER_BLOCK;
- break;
--
- default:
- dev_err(&adap->dev, "Unsupported transaction %d\n", size);
- ret = -EOPNOTSUPP;
- goto out_unlock;
- }
-
-- ret = rtl9300_i2c_execute_xfer(i2c, read_write, size, data, len);
-+ ret = rtl9300_i2c_prepare_xfer(i2c, &xfer);
-+ if (ret)
-+ goto out_unlock;
-+
-+ ret = rtl9300_i2c_do_xfer(i2c, &xfer);
-
- out_unlock:
- mutex_unlock(&i2c->lock);
+++ /dev/null
-From f891af341e32c8a2f2d2d25dd3c424ff47147a31 Mon Sep 17 00:00:00 2001
-Date: Mon, 4 Aug 2025 10:39:39 +0000
-Subject: [PATCH v5 10/11] dt-bindings: i2c: realtek,rtl9301-i2c: extend for
- RTL9310 support
-
-Adjust the regex for child-node address to account for the fact that
-RTL9310 supports 12 instead of only 8 SDA lines. Also, narrow this per
-variant.
-
-Add a vendor-specific property to explicitly specify the
-Realtek-internal ID of the defined I2C controller/master. This is
-required, in particular for RTL9310, to describe the correct I2C
-master. Require this property for RTL9310.
-
-Add compatibles for known SoC variants RTL9311, RTL9312 and RTL9313.
-
----
- .../bindings/i2c/realtek,rtl9301-i2c.yaml | 39 ++++++++++++++++++-
- 1 file changed, 37 insertions(+), 2 deletions(-)
-
---- a/Documentation/devicetree/bindings/i2c/realtek,rtl9301-i2c.yaml
-+++ b/Documentation/devicetree/bindings/i2c/realtek,rtl9301-i2c.yaml
-@@ -13,6 +13,8 @@ description:
- RTL9300 SoCs have two I2C controllers. Each of these has an SCL line (which
- if not-used for SCL can be a GPIO). There are 8 common SDA lines that can be
- assigned to either I2C controller.
-+ RTL9310 SoCs have equal capabilities but support 12 common SDA lines which
-+ can be assigned to either I2C controller.
-
- properties:
- compatible:
-@@ -23,7 +25,15 @@ properties:
- - realtek,rtl9302c-i2c
- - realtek,rtl9303-i2c
- - const: realtek,rtl9301-i2c
-- - const: realtek,rtl9301-i2c
-+ - items:
-+ - enum:
-+ - realtek,rtl9311-i2c
-+ - realtek,rtl9312-i2c
-+ - realtek,rtl9313-i2c
-+ - const: realtek,rtl9310-i2c
-+ - enum:
-+ - realtek,rtl9301-i2c
-+ - realtek,rtl9310-i2c
-
- reg:
- items:
-@@ -35,8 +45,14 @@ properties:
- "#size-cells":
- const: 0
-
-+ realtek,scl:
-+ $ref: /schemas/types.yaml#/definitions/uint32
-+ description:
-+ The SCL line number of this I2C controller.
-+ enum: [ 0, 1 ]
-+
- patternProperties:
-- '^i2c@[0-7]$':
-+ '^i2c@[0-9ab]$':
- $ref: /schemas/i2c/i2c-controller.yaml
- unevaluatedProperties: false
-
-@@ -48,6 +64,25 @@ patternProperties:
- required:
- - reg
-
-+
-+allOf:
-+ - if:
-+ properties:
-+ compatible:
-+ contains:
-+ const: realtek,rtl9310-i2c
-+ then:
-+ required:
-+ - realtek,scl
-+ - if:
-+ properties:
-+ compatible:
-+ contains:
-+ const: realtek,rtl9301-i2c
-+ then:
-+ patternProperties:
-+ '^i2c@[89ab]$': false
-+
- required:
- - compatible
- - reg
+++ /dev/null
-From ceddd84c5f73b17a1d8b9a760d9d5b5b7f0f0519 Mon Sep 17 00:00:00 2001
-Date: Mon, 30 Jun 2025 17:02:32 +0000
-Subject: [PATCH v5 11/11] i2c: rtl9300: add support for RTL9310 I2C controller
-
-Add support for the internal I2C controllers of RTL9310 series based
-SoCs to the driver for RTL9300. Add register definitions, chip-specific
-functions and compatible strings for known RTL9310-based SoCs RTL9311,
-RTL9312 and RTL9313.
-
-Make use of a new device tree property 'realtek,scl' which needs to be
-specified in case both or only the second master is used. This is
-required due how the register layout changed in contrast to RTL9300,
-which has SCL selection in a global register instead of a
-master-specific one.
-
----
- drivers/i2c/busses/i2c-rtl9300.c | 44 ++++++++++++++++++++++++++++++--
- 1 file changed, 42 insertions(+), 2 deletions(-)
-
---- a/drivers/i2c/busses/i2c-rtl9300.c
-+++ b/drivers/i2c/busses/i2c-rtl9300.c
-@@ -60,14 +60,16 @@ struct rtl9300_i2c_drv_data {
- };
-
- #define RTL9300_I2C_MUX_NCHAN 8
-+#define RTL9310_I2C_MUX_NCHAN 12
-
- struct rtl9300_i2c {
- struct regmap *regmap;
- struct device *dev;
-- struct rtl9300_i2c_chan chans[RTL9300_I2C_MUX_NCHAN];
-+ struct rtl9300_i2c_chan chans[RTL9310_I2C_MUX_NCHAN];
- struct regmap_field *fields[F_NUM_FIELDS];
- u32 reg_base;
- u32 data_reg;
-+ u8 scl_num;
- u8 sda_num;
- struct mutex lock;
- };
-@@ -96,6 +98,12 @@ struct rtl9300_i2c_xfer {
- #define RTL9300_I2C_MST_DATA_WORD3 0x14
- #define RTL9300_I2C_MST_GLB_CTRL 0x384
-
-+#define RTL9310_I2C_MST_IF_CTRL 0x1004
-+#define RTL9310_I2C_MST_IF_SEL 0x1008
-+#define RTL9310_I2C_MST_CTRL 0x0
-+#define RTL9310_I2C_MST_MEMADDR_CTRL 0x4
-+#define RTL9310_I2C_MST_DATA_CTRL 0x8
-+
- static int rtl9300_i2c_reg_addr_set(struct rtl9300_i2c *i2c, u32 reg, u16 len)
- {
- int ret;
-@@ -112,6 +120,11 @@ static int rtl9300_i2c_select_scl(struct
- return regmap_field_write(i2c->fields[F_SCL_SEL], 1);
- }
-
-+static int rtl9310_i2c_select_scl(struct rtl9300_i2c *i2c, u8 scl)
-+{
-+ return regmap_field_update_bits(i2c->fields[F_SCL_SEL], BIT(scl), BIT(scl));
-+}
-+
- static int rtl9300_i2c_config_chan(struct rtl9300_i2c *i2c, struct rtl9300_i2c_chan *chan)
- {
- struct rtl9300_i2c_drv_data *drv_data;
-@@ -125,7 +138,7 @@ static int rtl9300_i2c_config_chan(struc
- return ret;
-
- drv_data = (struct rtl9300_i2c_drv_data *)device_get_match_data(i2c->dev);
-- ret = drv_data->select_scl(i2c, 0);
-+ ret = drv_data->select_scl(i2c, i2c->scl_num);
- if (ret)
- return ret;
-
-@@ -383,6 +396,10 @@ static int rtl9300_i2c_probe(struct plat
- if (ret)
- return ret;
-
-+ ret = device_property_read_u8(dev, "realtek,scl", &i2c->scl_num);
-+ if (ret || i2c->scl_num != 1)
-+ i2c->scl_num = 0;
-+
- platform_set_drvdata(pdev, i2c);
-
- drv_data = (struct rtl9300_i2c_drv_data *)device_get_match_data(i2c->dev);
-@@ -478,12 +495,35 @@ static const struct rtl9300_i2c_drv_data
- .max_nchan = RTL9300_I2C_MUX_NCHAN,
- };
-
-+static const struct rtl9300_i2c_drv_data rtl9310_i2c_drv_data = {
-+ .field_desc = {
-+ [F_SCL_SEL] = GLB_REG_FIELD(RTL9310_I2C_MST_IF_SEL, 12, 13),
-+ [F_SDA_SEL] = GLB_REG_FIELD(RTL9310_I2C_MST_IF_SEL, 0, 11),
-+ [F_SCL_FREQ] = MST_REG_FIELD(RTL9310_I2C_MST_CTRL, 30, 31),
-+ [F_DEV_ADDR] = MST_REG_FIELD(RTL9310_I2C_MST_CTRL, 11, 17),
-+ [F_SDA_OUT_SEL] = MST_REG_FIELD(RTL9310_I2C_MST_CTRL, 18, 21),
-+ [F_MEM_ADDR_WIDTH] = MST_REG_FIELD(RTL9310_I2C_MST_CTRL, 9, 10),
-+ [F_DATA_WIDTH] = MST_REG_FIELD(RTL9310_I2C_MST_CTRL, 5, 8),
-+ [F_RD_MODE] = MST_REG_FIELD(RTL9310_I2C_MST_CTRL, 4, 4),
-+ [F_RWOP] = MST_REG_FIELD(RTL9310_I2C_MST_CTRL, 2, 2),
-+ [F_I2C_FAIL] = MST_REG_FIELD(RTL9310_I2C_MST_CTRL, 1, 1),
-+ [F_I2C_TRIG] = MST_REG_FIELD(RTL9310_I2C_MST_CTRL, 0, 0),
-+ [F_MEM_ADDR] = MST_REG_FIELD(RTL9310_I2C_MST_MEMADDR_CTRL, 0, 23),
-+ },
-+ .select_scl = rtl9310_i2c_select_scl,
-+ .data_reg = RTL9310_I2C_MST_DATA_CTRL,
-+ .max_nchan = RTL9310_I2C_MUX_NCHAN,
-+};
-
- static const struct of_device_id i2c_rtl9300_dt_ids[] = {
- { .compatible = "realtek,rtl9301-i2c", .data = (void *) &rtl9300_i2c_drv_data },
- { .compatible = "realtek,rtl9302b-i2c", .data = (void *) &rtl9300_i2c_drv_data },
- { .compatible = "realtek,rtl9302c-i2c", .data = (void *) &rtl9300_i2c_drv_data },
- { .compatible = "realtek,rtl9303-i2c", .data = (void *) &rtl9300_i2c_drv_data },
-+ { .compatible = "realtek,rtl9310-i2c", .data = (void *) &rtl9310_i2c_drv_data },
-+ { .compatible = "realtek,rtl9311-i2c", .data = (void *) &rtl9310_i2c_drv_data },
-+ { .compatible = "realtek,rtl9312-i2c", .data = (void *) &rtl9310_i2c_drv_data },
-+ { .compatible = "realtek,rtl9313-i2c", .data = (void *) &rtl9310_i2c_drv_data },
- {}
- };
- MODULE_DEVICE_TABLE(of, i2c_rtl9300_dt_ids);