-From linux-i2c Fri Feb 27 11:11:34 2026
-From: Jan Kantert <jan-kernel () kantert ! net>
-Date: Fri, 27 Feb 2026 11:11:34 +0000
-To: linux-i2c
-Subject: [PATCH] i2c: rtl9300: add support for 50 kHz and 2.5 MHz bus speeds
-Message-Id: <20260227111134.2163701-1-jan-kernel () kantert ! net>
-X-MARC-Message: https://marc.info/?l=linux-i2c&m=177219358420283
+From 879766b58ea5cba79ff5fe46f062ed8e05e715aa Mon Sep 17 00:00:00 2001
+From: Jan Kantert <jan-kernel@kantert.net>
+Date: Fri, 27 Feb 2026 12:11:34 +0100
+Subject: i2c: rtl9300: add support for 50 kHz and 2.5 MHz bus speeds
Some SFP modules on certain switches (for example the ONTi ONT-S508CL-8S and
XikeStor SKS8300-8X) exhibit unreliable I2C communication at the currently
RTL9300 to improve compatibility with these devices.
Signed-off-by: Jan Kantert <jan-kernel@kantert.net>
+Reviewed-by: Chris Packham <chris.packham@alliedtelesis.co.nz>
+Signed-off-by: Andi Shyti <andi.shyti@kernel.org>
+Link: https://lore.kernel.org/r/20260227111134.2163701-1-jan-kernel@kantert.net
---
drivers/i2c/busses/i2c-rtl9300.c | 16 ++++++++++++++--
1 file changed, 14 insertions(+), 2 deletions(-)
--- /dev/null
+From 4c53b2eb4f18102c36d4bcaf8c604a1825701ffb Mon Sep 17 00:00:00 2001
+From: Rustam Adilov <adilov@disroot.org>
+Date: Wed, 1 Apr 2026 23:06:41 +0500
+Subject: i2c: rtl9300: split data_reg into read and write reg
+
+In RTL9607C i2c controller, there are 2 separate registers for reads
+and writes as opposed the combined 1 on rtl9300 and rtl9310.
+
+In preparation for RTL9607C support, split it up into rd_reg and wd_reg
+properties and change the i2c read and write functions accordingly.
+
+Signed-off-by: Rustam Adilov <adilov@disroot.org>
+Reviewed-by: Chris Packham <chris.packham@alliedtelesis.co.nz>
+Signed-off-by: Andi Shyti <andi.shyti@kernel.org>
+Link: https://lore.kernel.org/r/20260401180648.337834-2-adilov@disroot.org
+---
+ drivers/i2c/busses/i2c-rtl9300.c | 25 +++++++++++++++----------
+ 1 file changed, 15 insertions(+), 10 deletions(-)
+
+--- a/drivers/i2c/busses/i2c-rtl9300.c
++++ b/drivers/i2c/busses/i2c-rtl9300.c
+@@ -61,7 +61,8 @@ enum rtl9300_i2c_reg_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;
++ u32 rd_reg;
++ u32 wd_reg;
+ u8 max_nchan;
+ };
+
+@@ -74,7 +75,8 @@ struct rtl9300_i2c {
+ struct rtl9300_i2c_chan chans[RTL9310_I2C_MUX_NCHAN];
+ struct regmap_field *fields[F_NUM_FIELDS];
+ u32 reg_base;
+- u32 data_reg;
++ u32 rd_reg;
++ u32 wd_reg;
+ u8 scl_num;
+ u8 sda_num;
+ struct mutex lock;
+@@ -171,7 +173,7 @@ static int rtl9300_i2c_read(struct rtl93
+ if (len > 16)
+ return -EIO;
+
+- ret = regmap_bulk_read(i2c->regmap, i2c->data_reg, vals, ARRAY_SIZE(vals));
++ ret = regmap_bulk_read(i2c->regmap, i2c->rd_reg, vals, ARRAY_SIZE(vals));
+ if (ret)
+ return ret;
+
+@@ -198,12 +200,12 @@ static int rtl9300_i2c_write(struct rtl9
+ vals[reg] |= buf[i] << shift;
+ }
+
+- return regmap_bulk_write(i2c->regmap, i2c->data_reg, vals, ARRAY_SIZE(vals));
++ return regmap_bulk_write(i2c->regmap, i2c->wd_reg, vals, ARRAY_SIZE(vals));
+ }
+
+ static int rtl9300_i2c_writel(struct rtl9300_i2c *i2c, u32 data)
+ {
+- return regmap_write(i2c->regmap, i2c->data_reg, data);
++ return regmap_write(i2c->regmap, i2c->wd_reg, data);
+ }
+
+ static int rtl9300_i2c_prepare_xfer(struct rtl9300_i2c *i2c, struct rtl9300_i2c_xfer *xfer)
+@@ -268,14 +270,14 @@ static int rtl9300_i2c_do_xfer(struct rt
+ if (!xfer->write) {
+ switch (xfer->type) {
+ case RTL9300_I2C_XFER_BYTE:
+- ret = regmap_read(i2c->regmap, i2c->data_reg, &val);
++ ret = regmap_read(i2c->regmap, i2c->rd_reg, &val);
+ if (ret)
+ return ret;
+
+ *xfer->data = val & 0xff;
+ break;
+ case RTL9300_I2C_XFER_WORD:
+- ret = regmap_read(i2c->regmap, i2c->data_reg, &val);
++ ret = regmap_read(i2c->regmap, i2c->rd_reg, &val);
+ if (ret)
+ return ret;
+
+@@ -408,7 +410,8 @@ static int rtl9300_i2c_probe(struct plat
+ 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;
++ i2c->rd_reg = i2c->reg_base + drv_data->rd_reg;
++ i2c->wd_reg = i2c->reg_base + drv_data->wd_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)
+@@ -499,7 +502,8 @@ static const struct rtl9300_i2c_drv_data
+ [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,
++ .rd_reg = RTL9300_I2C_MST_DATA_WORD0,
++ .wd_reg = RTL9300_I2C_MST_DATA_WORD0,
+ .max_nchan = RTL9300_I2C_MUX_NCHAN,
+ };
+
+@@ -519,7 +523,8 @@ static const struct rtl9300_i2c_drv_data
+ [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,
++ .rd_reg = RTL9310_I2C_MST_DATA_CTRL,
++ .wd_reg = RTL9310_I2C_MST_DATA_CTRL,
+ .max_nchan = RTL9310_I2C_MUX_NCHAN,
+ };
+
--- /dev/null
+From 98773df61f8416594ac993e8464df596755ee1b8 Mon Sep 17 00:00:00 2001
+From: Rustam Adilov <adilov@disroot.org>
+Date: Wed, 1 Apr 2026 23:06:42 +0500
+Subject: i2c: rtl9300: introduce max length property to driver data
+
+In RTL9607C i2c controller, theoretical maximum the data length
+can be is 4 bytes as opposed to 16 bytes on rtl9300 and rtl9310.
+
+Introduce a new property to the driver data struct for that.
+Adjust if statement in prepare_xfer function to follow that new
+property instead of the hardcoded value.
+
+Signed-off-by: Rustam Adilov <adilov@disroot.org>
+Reviewed-by: Chris Packham <chris.packham@alliedtelesis.co.nz>
+Signed-off-by: Andi Shyti <andi.shyti@kernel.org>
+Link: https://lore.kernel.org/r/20260401180648.337834-3-adilov@disroot.org
+---
+ drivers/i2c/busses/i2c-rtl9300.c | 9 ++++++++-
+ 1 file changed, 8 insertions(+), 1 deletion(-)
+
+--- a/drivers/i2c/busses/i2c-rtl9300.c
++++ b/drivers/i2c/busses/i2c-rtl9300.c
+@@ -64,11 +64,14 @@ struct rtl9300_i2c_drv_data {
+ u32 rd_reg;
+ u32 wd_reg;
+ u8 max_nchan;
++ u8 max_data_len;
+ };
+
+ #define RTL9300_I2C_MUX_NCHAN 8
+ #define RTL9310_I2C_MUX_NCHAN 12
+
++#define RTL9300_I2C_MAX_DATA_LEN 16
++
+ struct rtl9300_i2c {
+ struct regmap *regmap;
+ struct device *dev;
+@@ -210,9 +213,11 @@ static int rtl9300_i2c_writel(struct rtl
+
+ static int rtl9300_i2c_prepare_xfer(struct rtl9300_i2c *i2c, struct rtl9300_i2c_xfer *xfer)
+ {
++ const struct rtl9300_i2c_drv_data *drv_data;
+ int ret;
+
+- if (xfer->data_len < 1 || xfer->data_len > 16)
++ drv_data = device_get_match_data(i2c->dev);
++ if (xfer->data_len < 1 || xfer->data_len > drv_data->max_data_len)
+ return -EINVAL;
+
+ ret = regmap_field_write(i2c->fields[F_DEV_ADDR], xfer->dev_addr);
+@@ -505,6 +510,7 @@ static const struct rtl9300_i2c_drv_data
+ .rd_reg = RTL9300_I2C_MST_DATA_WORD0,
+ .wd_reg = RTL9300_I2C_MST_DATA_WORD0,
+ .max_nchan = RTL9300_I2C_MUX_NCHAN,
++ .max_data_len = RTL9300_I2C_MAX_DATA_LEN,
+ };
+
+ static const struct rtl9300_i2c_drv_data rtl9310_i2c_drv_data = {
+@@ -526,6 +532,7 @@ static const struct rtl9300_i2c_drv_data
+ .rd_reg = RTL9310_I2C_MST_DATA_CTRL,
+ .wd_reg = RTL9310_I2C_MST_DATA_CTRL,
+ .max_nchan = RTL9310_I2C_MUX_NCHAN,
++ .max_data_len = RTL9300_I2C_MAX_DATA_LEN,
+ };
+
+ static const struct of_device_id i2c_rtl9300_dt_ids[] = {
--- /dev/null
+From 55284a806b63a412846b9ecd3846f2639eaeaff4 Mon Sep 17 00:00:00 2001
+From: Rustam Adilov <adilov@disroot.org>
+Date: Wed, 1 Apr 2026 23:06:43 +0500
+Subject: i2c: rtl9300: introduce F_BUSY to the reg_fields struct
+
+In RTL9607C i2c controller the busy check operation is done on the
+separate bit of the command register as opposed to self clearing
+command trigger bit on the rtl9300 and rtl9310 i2c controllers.
+
+Introduce a new F_BUSY field to the reg_fields struct for that
+and change the regmap read poll function to use F_BUSY
+instead of I2C_TRIG.
+
+Signed-off-by: Rustam Adilov <adilov@disroot.org>
+Reviewed-by: Chris Packham <chris.packham@alliedtelesis.co.nz>
+Signed-off-by: Andi Shyti <andi.shyti@kernel.org>
+Link: https://lore.kernel.org/r/20260401180648.337834-4-adilov@disroot.org
+---
+ drivers/i2c/busses/i2c-rtl9300.c | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+--- a/drivers/i2c/busses/i2c-rtl9300.c
++++ b/drivers/i2c/busses/i2c-rtl9300.c
+@@ -53,6 +53,7 @@ enum rtl9300_i2c_reg_fields {
+ F_SCL_SEL,
+ F_SDA_OUT_SEL,
+ F_SDA_SEL,
++ F_BUSY,
+
+ /* keep last */
+ F_NUM_FIELDS
+@@ -262,7 +263,7 @@ static int rtl9300_i2c_do_xfer(struct rt
+ if (ret)
+ return ret;
+
+- ret = regmap_field_read_poll_timeout(i2c->fields[F_I2C_TRIG], val, !val, 100, 100000);
++ ret = regmap_field_read_poll_timeout(i2c->fields[F_BUSY], val, !val, 100, 100000);
+ if (ret)
+ return ret;
+
+@@ -505,6 +506,7 @@ static const struct rtl9300_i2c_drv_data
+ [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),
++ [F_BUSY] = MST_REG_FIELD(RTL9300_I2C_MST_CTRL1, 0, 0),
+ },
+ .select_scl = rtl9300_i2c_select_scl,
+ .rd_reg = RTL9300_I2C_MST_DATA_WORD0,
+@@ -527,6 +529,7 @@ static const struct rtl9300_i2c_drv_data
+ [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),
++ [F_BUSY] = MST_REG_FIELD(RTL9310_I2C_MST_CTRL, 0, 0),
+ },
+ .select_scl = rtl9310_i2c_select_scl,
+ .rd_reg = RTL9310_I2C_MST_DATA_CTRL,
--- /dev/null
+From 6afde011baaf722aa66c11696b6383f9ce85b653 Mon Sep 17 00:00:00 2001
+From: Rustam Adilov <adilov@disroot.org>
+Date: Wed, 1 Apr 2026 23:06:44 +0500
+Subject: i2c: rtl9300: introduce a property for 8 bit width reg address
+
+In RTL9607C i2c controller, in order to indicate that the width of
+memory address is 8 bits, 0 is written to MEM_ADDR_WIDTH field as
+opposed to 1 for RTL9300 and RTL9310.
+
+Introduce a new property to a driver data to indicate what value
+need to written to MEM_ADDR_WIDTH field for this case.
+
+Signed-off-by: Rustam Adilov <adilov@disroot.org>
+Reviewed-by: Chris Packham <chris.packham@alliedtelesis.co.nz>
+Signed-off-by: Andi Shyti <andi.shyti@kernel.org>
+Link: https://lore.kernel.org/r/20260401180648.337834-5-adilov@disroot.org
+---
+ drivers/i2c/busses/i2c-rtl9300.c | 8 +++++++-
+ 1 file changed, 7 insertions(+), 1 deletion(-)
+
+--- a/drivers/i2c/busses/i2c-rtl9300.c
++++ b/drivers/i2c/busses/i2c-rtl9300.c
+@@ -66,6 +66,7 @@ struct rtl9300_i2c_drv_data {
+ u32 wd_reg;
+ u8 max_nchan;
+ u8 max_data_len;
++ u8 reg_addr_8bit_len;
+ };
+
+ #define RTL9300_I2C_MUX_NCHAN 8
+@@ -111,6 +112,7 @@ struct rtl9300_i2c_xfer {
+ #define RTL9300_I2C_MST_DATA_WORD2 0x10
+ #define RTL9300_I2C_MST_DATA_WORD3 0x14
+ #define RTL9300_I2C_MST_GLB_CTRL 0x384
++#define RTL9300_REG_ADDR_8BIT_LEN 1
+
+ #define RTL9310_I2C_MST_IF_CTRL 0x1004
+ #define RTL9310_I2C_MST_IF_SEL 0x1008
+@@ -305,6 +307,7 @@ static int rtl9300_i2c_smbus_xfer(struct
+ union i2c_smbus_data *data)
+ {
+ struct rtl9300_i2c_chan *chan = i2c_get_adapdata(adap);
++ const struct rtl9300_i2c_drv_data *drv_data;
+ struct rtl9300_i2c *i2c = chan->i2c;
+ struct rtl9300_i2c_xfer xfer = {0};
+ int ret;
+@@ -314,6 +317,7 @@ static int rtl9300_i2c_smbus_xfer(struct
+
+ guard(rtl9300_i2c)(i2c);
+
++ drv_data = device_get_match_data(i2c->dev);
+ ret = rtl9300_i2c_config_chan(i2c, chan);
+ if (ret)
+ return ret;
+@@ -321,7 +325,7 @@ static int rtl9300_i2c_smbus_xfer(struct
+ xfer.dev_addr = addr & 0x7f;
+ xfer.write = (read_write == I2C_SMBUS_WRITE);
+ xfer.reg_addr = command;
+- xfer.reg_addr_len = 1;
++ xfer.reg_addr_len = drv_data->reg_addr_8bit_len;
+
+ switch (size) {
+ case I2C_SMBUS_BYTE:
+@@ -513,6 +517,7 @@ static const struct rtl9300_i2c_drv_data
+ .wd_reg = RTL9300_I2C_MST_DATA_WORD0,
+ .max_nchan = RTL9300_I2C_MUX_NCHAN,
+ .max_data_len = RTL9300_I2C_MAX_DATA_LEN,
++ .reg_addr_8bit_len = RTL9300_REG_ADDR_8BIT_LEN,
+ };
+
+ static const struct rtl9300_i2c_drv_data rtl9310_i2c_drv_data = {
+@@ -536,6 +541,7 @@ static const struct rtl9300_i2c_drv_data
+ .wd_reg = RTL9310_I2C_MST_DATA_CTRL,
+ .max_nchan = RTL9310_I2C_MUX_NCHAN,
+ .max_data_len = RTL9300_I2C_MAX_DATA_LEN,
++ .reg_addr_8bit_len = RTL9300_REG_ADDR_8BIT_LEN,
+ };
+
+ static const struct of_device_id i2c_rtl9300_dt_ids[] = {
--- /dev/null
+From 1211ce1e11d23ec05d80a85b7187baa6abed3232 Mon Sep 17 00:00:00 2001
+From: Rustam Adilov <adilov@disroot.org>
+Date: Wed, 1 Apr 2026 23:06:45 +0500
+Subject: dt-bindings: i2c: realtek,rtl9301-i2c: extend for clocks and RTL9607C
+ support
+
+Add the "realtek,rtl9607-i2c" compatible for i2c controller on the
+RTL9607C SoC series.
+
+Add a clocks property to the properties to describe the i2c reference
+clock and make it available for all the compatibles. This i2c reference
+clock is assumed to be coming from switchcore region via Lexra bus as
+the other SoC peripherals.
+
+According to the info available about the existing devices, they also
+have the i2c master controller clocks.
+
+RTL9607C requires the "realtek,scl" and "clocks" to be specified
+and so handle it under separate if check for "realtek,rtl9607-i2c".
+
+Signed-off-by: Rustam Adilov <adilov@disroot.org>
+Acked-by: Conor Dooley <conor.dooley@microchip.com>
+Signed-off-by: Andi Shyti <andi.shyti@kernel.org>
+Link: https://lore.kernel.org/r/20260401180648.337834-6-adilov@disroot.org
+---
+ .../devicetree/bindings/i2c/realtek,rtl9301-i2c.yaml | 15 +++++++++++++++
+ 1 file changed, 15 insertions(+)
+
+--- a/Documentation/devicetree/bindings/i2c/realtek,rtl9301-i2c.yaml
++++ b/Documentation/devicetree/bindings/i2c/realtek,rtl9301-i2c.yaml
+@@ -15,6 +15,8 @@ description:
+ assigned to either I2C controller.
+ RTL9310 SoCs have equal capabilities but support 12 common SDA lines which
+ can be assigned to either I2C controller.
++ RTL9607C SoCs have equal capabilities but each controller only supports 1
++ SCL/SDA line.
+
+ properties:
+ compatible:
+@@ -34,6 +36,7 @@ properties:
+ - enum:
+ - realtek,rtl9301-i2c
+ - realtek,rtl9310-i2c
++ - realtek,rtl9607-i2c
+
+ reg:
+ items:
+@@ -51,6 +54,9 @@ properties:
+ The SCL line number of this I2C controller.
+ enum: [ 0, 1 ]
+
++ clocks:
++ maxItems: 1
++
+ patternProperties:
+ '^i2c@[0-9ab]$':
+ $ref: /schemas/i2c/i2c-controller.yaml
+@@ -82,6 +88,15 @@ allOf:
+ then:
+ patternProperties:
+ '^i2c@[89ab]$': false
++ - if:
++ properties:
++ compatible:
++ contains:
++ const: realtek,rtl9607-i2c
++ then:
++ required:
++ - realtek,scl
++ - clocks
+
+ required:
+ - compatible
--- /dev/null
+From f60d27926c9e2d547200fb0d26f61eec9b8291a6 Mon Sep 17 00:00:00 2001
+From: Rustam Adilov <adilov@disroot.org>
+Date: Wed, 1 Apr 2026 23:06:46 +0500
+Subject: i2c: rtl9300: introduce clk struct for upcoming rtl9607 support
+
+In RTL9607C i2c controller, there is 10 bit CLK_DIV field for
+setting the clock of i2c interface which depends on the rate
+of i2c clk (which seems be fixed to 62.5MHz according to Realtek SDK).
+
+Introduce the clk struct and the respective F_CLK_DIV and clk_div
+which are going to be used in the upcoming patch for rtl9607c i2c
+controller support addition.
+
+devm_clk_get_optional_enabled() function was used for cleaner code
+as it automatically returns NULL if the clk is not present, which is
+going to be the case for RTL9300 and RTL9310 i2c controllers.
+
+Signed-off-by: Rustam Adilov <adilov@disroot.org>
+Reviewed-by: Chris Packham <chris.packham@alliedtelesis.co.nz>
+Signed-off-by: Andi Shyti <andi.shyti@kernel.org>
+Link: https://lore.kernel.org/r/20260401180648.337834-7-adilov@disroot.org
+---
+ drivers/i2c/busses/i2c-rtl9300.c | 8 ++++++++
+ 1 file changed, 8 insertions(+)
+
+--- a/drivers/i2c/busses/i2c-rtl9300.c
++++ b/drivers/i2c/busses/i2c-rtl9300.c
+@@ -1,6 +1,7 @@
+ // SPDX-License-Identifier: GPL-2.0-only
+
+ #include <linux/bits.h>
++#include <linux/clk.h>
+ #include <linux/i2c.h>
+ #include <linux/i2c-mux.h>
+ #include <linux/mod_devicetable.h>
+@@ -28,6 +29,7 @@ struct rtl9300_i2c_chan {
+ struct rtl9300_i2c *i2c;
+ enum rtl9300_bus_freq bus_freq;
+ u8 sda_num;
++ u32 clk_div;
+ };
+
+ enum rtl9300_i2c_reg_scope {
+@@ -54,6 +56,7 @@ enum rtl9300_i2c_reg_fields {
+ F_SDA_OUT_SEL,
+ F_SDA_SEL,
+ F_BUSY,
++ F_CLK_DIV,
+
+ /* keep last */
+ F_NUM_FIELDS
+@@ -85,6 +88,7 @@ struct rtl9300_i2c {
+ u8 scl_num;
+ u8 sda_num;
+ struct mutex lock;
++ struct clk *clk;
+ };
+
+ DEFINE_GUARD(rtl9300_i2c, struct rtl9300_i2c *, mutex_lock(&_T->lock), mutex_unlock(&_T->lock))
+@@ -432,6 +436,10 @@ static int rtl9300_i2c_probe(struct plat
+ if (ret)
+ return ret;
+
++ i2c->clk = devm_clk_get_optional_enabled(dev, NULL);
++ if (IS_ERR(i2c->clk))
++ return dev_err_probe(dev, PTR_ERR(i2c->clk), "Failed to enable i2c clock\n");
++
+ i = 0;
+ for_each_child_of_node_scoped(dev->of_node, child) {
+ struct rtl9300_i2c_chan *chan = &i2c->chans[i];
--- /dev/null
+From 991cd899ecd03a1c3ef7d177a0b99e824c6be581 Mon Sep 17 00:00:00 2001
+From: Rustam Adilov <adilov@disroot.org>
+Date: Wed, 1 Apr 2026 23:06:47 +0500
+Subject: i2c: rtl9300: introduce new function properties to driver data
+
+Due to the very nature of differences between RTL9607C i2c controller
+and RTL9300 / RTL9310 that are incompatible with each other in some areas
+of this driver, for example in clock configuration, channel configuration
+and initialization at the end of the probe, introduce new function
+properties to the driver data struct to handle those differences.
+
+With these new properties, create configuration functions for RTL9300 and
+RTL9310 and assign them to their respective driver data structs.
+
+Signed-off-by: Rustam Adilov <adilov@disroot.org>
+Signed-off-by: Andi Shyti <andi.shyti@kernel.org>
+Link: https://lore.kernel.org/r/20260401180648.337834-8-adilov@disroot.org
+---
+ drivers/i2c/busses/i2c-rtl9300.c | 66 ++++++++++++++++++++++++++--------------
+ 1 file changed, 44 insertions(+), 22 deletions(-)
+
+--- a/drivers/i2c/busses/i2c-rtl9300.c
++++ b/drivers/i2c/busses/i2c-rtl9300.c
+@@ -65,6 +65,9 @@ enum rtl9300_i2c_reg_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);
++ int (*config_chan)(struct rtl9300_i2c *i2c, struct rtl9300_i2c_chan *chan);
++ void (*config_clock)(u32 clock_freq, struct rtl9300_i2c_chan *chan);
++ int (*misc_init)(struct rtl9300_i2c *i2c);
+ u32 rd_reg;
+ u32 wd_reg;
+ u8 max_nchan;
+@@ -175,6 +178,30 @@ static int rtl9300_i2c_config_chan(struc
+ return 0;
+ }
+
++static void rtl9300_i2c_config_clock(u32 clock_freq, struct rtl9300_i2c_chan *chan)
++{
++ struct rtl9300_i2c *i2c = chan->i2c;
++
++ switch (clock_freq) {
++ 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;
++ case RTL9300_I2C_MAX_SUPER_FAST_FREQ:
++ chan->bus_freq = RTL9300_I2C_SUPER_FAST_FREQ;
++ break;
++ case RTL9300_I2C_MAX_SLOW_FREQ:
++ chan->bus_freq = RTL9300_I2C_SLOW_FREQ;
++ break;
++ default:
++ dev_warn(i2c->dev, "SDA%d clock-frequency %d not supported using default\n",
++ chan->sda_num, clock_freq);
++ break;
++ }
++}
++
+ static int rtl9300_i2c_read(struct rtl9300_i2c *i2c, u8 *buf, u8 len)
+ {
+ u32 vals[4] = {};
+@@ -322,7 +349,7 @@ static int rtl9300_i2c_smbus_xfer(struct
+ guard(rtl9300_i2c)(i2c);
+
+ drv_data = device_get_match_data(i2c->dev);
+- ret = rtl9300_i2c_config_chan(i2c, chan);
++ ret = drv_data->config_chan(i2c, chan);
+ if (ret)
+ return ret;
+
+@@ -389,6 +416,12 @@ static struct i2c_adapter_quirks rtl9300
+ .max_write_len = 16,
+ };
+
++static int rtl9300_i2c_init(struct rtl9300_i2c *i2c)
++{
++ /* only use standard read format */
++ return regmap_field_write(i2c->fields[F_RD_MODE], 0);
++}
++
+ static int rtl9300_i2c_probe(struct platform_device *pdev)
+ {
+ struct device *dev = &pdev->dev;
+@@ -453,27 +486,11 @@ static int rtl9300_i2c_probe(struct plat
+ if (ret)
+ clock_freq = I2C_MAX_STANDARD_MODE_FREQ;
+
+- switch (clock_freq) {
+- 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;
+- case RTL9300_I2C_MAX_SUPER_FAST_FREQ:
+- chan->bus_freq = RTL9300_I2C_SUPER_FAST_FREQ;
+- break;
+- case RTL9300_I2C_MAX_SLOW_FREQ:
+- chan->bus_freq = RTL9300_I2C_SLOW_FREQ;
+- break;
+- default:
+- dev_warn(i2c->dev, "SDA%d clock-frequency %d not supported using default\n",
+- sda_num, clock_freq);
+- break;
+- }
+-
+ chan->sda_num = sda_num;
+ chan->i2c = i2c;
++
++ drv_data->config_clock(clock_freq, chan);
++
+ adap = &i2c->chans[i].adap;
+ adap->owner = THIS_MODULE;
+ adap->algo = &rtl9300_i2c_algo;
+@@ -491,8 +508,7 @@ 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);
++ ret = drv_data->misc_init(i2c);
+ if (ret)
+ return ret;
+
+@@ -521,6 +537,9 @@ static const struct rtl9300_i2c_drv_data
+ [F_BUSY] = MST_REG_FIELD(RTL9300_I2C_MST_CTRL1, 0, 0),
+ },
+ .select_scl = rtl9300_i2c_select_scl,
++ .config_chan = rtl9300_i2c_config_chan,
++ .config_clock = rtl9300_i2c_config_clock,
++ .misc_init = rtl9300_i2c_init,
+ .rd_reg = RTL9300_I2C_MST_DATA_WORD0,
+ .wd_reg = RTL9300_I2C_MST_DATA_WORD0,
+ .max_nchan = RTL9300_I2C_MUX_NCHAN,
+@@ -545,6 +564,9 @@ static const struct rtl9300_i2c_drv_data
+ [F_BUSY] = MST_REG_FIELD(RTL9310_I2C_MST_CTRL, 0, 0),
+ },
+ .select_scl = rtl9310_i2c_select_scl,
++ .config_chan = rtl9300_i2c_config_chan,
++ .config_clock = rtl9300_i2c_config_clock,
++ .misc_init = rtl9300_i2c_init,
+ .rd_reg = RTL9310_I2C_MST_DATA_CTRL,
+ .wd_reg = RTL9310_I2C_MST_DATA_CTRL,
+ .max_nchan = RTL9310_I2C_MUX_NCHAN,
--- /dev/null
+From 40890b5fe72b1a0d4913883844854f6641a2f4b3 Mon Sep 17 00:00:00 2001
+From: Rustam Adilov <adilov@disroot.org>
+Date: Wed, 1 Apr 2026 23:06:48 +0500
+Subject: i2c: rtl9300: add RTL9607C i2c controller support
+
+Add support for the internal I2C controllers of RTL9607C series based
+SoCs. Add register definitions, chip-specific functions and macros too.
+
+Make use of the clk introduced from the previous patch to get the clk_div
+value and use it during the rtl9607c channel configuration.
+
+Introduce a new EXT_SCK_5MS field to the reg fields struct which is going
+to be initialized by rtl9607c init function at the end of the probe.
+
+This patch depends on all the previous patches in this patch series.
+
+Signed-off-by: Rustam Adilov <adilov@disroot.org>
+Reviewed-by: Chris Packham <chris.packham@alliedtelesis.co.nz>
+Signed-off-by: Andi Shyti <andi.shyti@kernel.org>
+Link: https://lore.kernel.org/r/20260401180648.337834-9-adilov@disroot.org
+---
+ drivers/i2c/busses/i2c-rtl9300.c | 70 ++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 70 insertions(+)
+
+--- a/drivers/i2c/busses/i2c-rtl9300.c
++++ b/drivers/i2c/busses/i2c-rtl9300.c
+@@ -57,6 +57,7 @@ enum rtl9300_i2c_reg_fields {
+ F_SDA_SEL,
+ F_BUSY,
+ F_CLK_DIV,
++ F_EXT_SCK_5MS,
+
+ /* keep last */
+ F_NUM_FIELDS
+@@ -77,8 +78,10 @@ struct rtl9300_i2c_drv_data {
+
+ #define RTL9300_I2C_MUX_NCHAN 8
+ #define RTL9310_I2C_MUX_NCHAN 12
++#define RTL9607_I2C_MUX_NCHAN 1
+
+ #define RTL9300_I2C_MAX_DATA_LEN 16
++#define RTL9607_I2C_MAX_DATA_LEN 4
+
+ struct rtl9300_i2c {
+ struct regmap *regmap;
+@@ -127,6 +130,14 @@ struct rtl9300_i2c_xfer {
+ #define RTL9310_I2C_MST_MEMADDR_CTRL 0x4
+ #define RTL9310_I2C_MST_DATA_CTRL 0x8
+
++#define RTL9607_I2C_CONFIG 0x22f50
++#define RTL9607_IO_MODE_EN 0x23014
++#define RTL9607_I2C_IND_WD 0x0
++#define RTL9607_I2C_IND_ADR 0x8
++#define RTL9607_I2C_IND_CMD 0x10
++#define RTL9607_I2C_IND_RD 0x18
++#define RTL9607_REG_ADDR_8BIT_LEN 0
++
+ static int rtl9300_i2c_reg_addr_set(struct rtl9300_i2c *i2c, u32 reg, u16 len)
+ {
+ int ret;
+@@ -178,6 +189,27 @@ static int rtl9300_i2c_config_chan(struc
+ return 0;
+ }
+
++static int rtl9607_i2c_config_chan(struct rtl9300_i2c *i2c, struct rtl9300_i2c_chan *chan)
++{
++ const struct rtl9300_i2c_drv_data *drv_data;
++ int ret;
++
++ if (i2c->sda_num == chan->sda_num)
++ return 0;
++
++ ret = regmap_field_write(i2c->fields[F_CLK_DIV], chan->clk_div);
++ if (ret)
++ return ret;
++
++ drv_data = device_get_match_data(i2c->dev);
++ ret = drv_data->select_scl(i2c, i2c->scl_num);
++ if (ret)
++ return ret;
++
++ i2c->sda_num = chan->sda_num;
++ return 0;
++}
++
+ static void rtl9300_i2c_config_clock(u32 clock_freq, struct rtl9300_i2c_chan *chan)
+ {
+ struct rtl9300_i2c *i2c = chan->i2c;
+@@ -202,6 +234,13 @@ static void rtl9300_i2c_config_clock(u32
+ }
+ }
+
++static void rtl9607_i2c_config_clock(u32 clock_freq, struct rtl9300_i2c_chan *chan)
++{
++ struct rtl9300_i2c *i2c = chan->i2c;
++
++ chan->clk_div = clk_get_rate(i2c->clk) / clock_freq - 1;
++}
++
+ static int rtl9300_i2c_read(struct rtl9300_i2c *i2c, u8 *buf, u8 len)
+ {
+ u32 vals[4] = {};
+@@ -422,6 +461,11 @@ static int rtl9300_i2c_init(struct rtl93
+ return regmap_field_write(i2c->fields[F_RD_MODE], 0);
+ }
+
++static int rtl9607_i2c_init(struct rtl9300_i2c *i2c)
++{
++ return regmap_field_write(i2c->fields[F_EXT_SCK_5MS], 1);
++}
++
+ static int rtl9300_i2c_probe(struct platform_device *pdev)
+ {
+ struct device *dev = &pdev->dev;
+@@ -574,6 +618,31 @@ static const struct rtl9300_i2c_drv_data
+ .reg_addr_8bit_len = RTL9300_REG_ADDR_8BIT_LEN,
+ };
+
++static const struct rtl9300_i2c_drv_data rtl9607_i2c_drv_data = {
++ .field_desc = {
++ [F_SCL_SEL] = GLB_REG_FIELD(RTL9607_IO_MODE_EN, 13, 14),
++ [F_EXT_SCK_5MS] = MST_REG_FIELD(RTL9607_I2C_CONFIG, 26, 26),
++ [F_DEV_ADDR] = MST_REG_FIELD(RTL9607_I2C_CONFIG, 14, 20),
++ [F_MEM_ADDR_WIDTH] = MST_REG_FIELD(RTL9607_I2C_CONFIG, 12, 13),
++ [F_DATA_WIDTH] = MST_REG_FIELD(RTL9607_I2C_CONFIG, 10, 11),
++ [F_CLK_DIV] = MST_REG_FIELD(RTL9607_I2C_CONFIG, 0, 9),
++ [F_I2C_FAIL] = MST_REG_FIELD(RTL9607_I2C_IND_CMD, 3, 3),
++ [F_BUSY] = MST_REG_FIELD(RTL9607_I2C_IND_CMD, 2, 2),
++ [F_RWOP] = MST_REG_FIELD(RTL9607_I2C_IND_CMD, 1, 1),
++ [F_I2C_TRIG] = MST_REG_FIELD(RTL9607_I2C_IND_CMD, 0, 0),
++ [F_MEM_ADDR] = MST_REG_FIELD(RTL9607_I2C_IND_ADR, 0, 31),
++ },
++ .select_scl = rtl9310_i2c_select_scl,
++ .config_chan = rtl9607_i2c_config_chan,
++ .config_clock = rtl9607_i2c_config_clock,
++ .misc_init = rtl9607_i2c_init,
++ .rd_reg = RTL9607_I2C_IND_RD,
++ .wd_reg = RTL9607_I2C_IND_WD,
++ .max_nchan = RTL9607_I2C_MUX_NCHAN,
++ .max_data_len = RTL9607_I2C_MAX_DATA_LEN,
++ .reg_addr_8bit_len = RTL9607_REG_ADDR_8BIT_LEN,
++};
++
+ 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 },
+@@ -583,6 +652,7 @@ static const struct of_device_id i2c_rtl
+ { .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 },
++ { .compatible = "realtek,rtl9607-i2c", .data = (void *) &rtl9607_i2c_drv_data },
+ {}
+ };
+ MODULE_DEVICE_TABLE(of, i2c_rtl9300_dt_ids);
-From linux-i2c Fri Feb 27 11:11:34 2026
-From: Jan Kantert <jan-kernel () kantert ! net>
-Date: Fri, 27 Feb 2026 11:11:34 +0000
-To: linux-i2c
-Subject: [PATCH] i2c: rtl9300: add support for 50 kHz and 2.5 MHz bus speeds
-Message-Id: <20260227111134.2163701-1-jan-kernel () kantert ! net>
-X-MARC-Message: https://marc.info/?l=linux-i2c&m=177219358420283
+From 879766b58ea5cba79ff5fe46f062ed8e05e715aa Mon Sep 17 00:00:00 2001
+From: Jan Kantert <jan-kernel@kantert.net>
+Date: Fri, 27 Feb 2026 12:11:34 +0100
+Subject: i2c: rtl9300: add support for 50 kHz and 2.5 MHz bus speeds
Some SFP modules on certain switches (for example the ONTi ONT-S508CL-8S and
XikeStor SKS8300-8X) exhibit unreliable I2C communication at the currently
RTL9300 to improve compatibility with these devices.
Signed-off-by: Jan Kantert <jan-kernel@kantert.net>
+Reviewed-by: Chris Packham <chris.packham@alliedtelesis.co.nz>
+Signed-off-by: Andi Shyti <andi.shyti@kernel.org>
+Link: https://lore.kernel.org/r/20260227111134.2163701-1-jan-kernel@kantert.net
---
drivers/i2c/busses/i2c-rtl9300.c | 16 ++++++++++++++--
1 file changed, 14 insertions(+), 2 deletions(-)
--- /dev/null
+From 4c53b2eb4f18102c36d4bcaf8c604a1825701ffb Mon Sep 17 00:00:00 2001
+From: Rustam Adilov <adilov@disroot.org>
+Date: Wed, 1 Apr 2026 23:06:41 +0500
+Subject: i2c: rtl9300: split data_reg into read and write reg
+
+In RTL9607C i2c controller, there are 2 separate registers for reads
+and writes as opposed the combined 1 on rtl9300 and rtl9310.
+
+In preparation for RTL9607C support, split it up into rd_reg and wd_reg
+properties and change the i2c read and write functions accordingly.
+
+Signed-off-by: Rustam Adilov <adilov@disroot.org>
+Reviewed-by: Chris Packham <chris.packham@alliedtelesis.co.nz>
+Signed-off-by: Andi Shyti <andi.shyti@kernel.org>
+Link: https://lore.kernel.org/r/20260401180648.337834-2-adilov@disroot.org
+---
+ drivers/i2c/busses/i2c-rtl9300.c | 25 +++++++++++++++----------
+ 1 file changed, 15 insertions(+), 10 deletions(-)
+
+--- a/drivers/i2c/busses/i2c-rtl9300.c
++++ b/drivers/i2c/busses/i2c-rtl9300.c
+@@ -61,7 +61,8 @@ enum rtl9300_i2c_reg_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;
++ u32 rd_reg;
++ u32 wd_reg;
+ u8 max_nchan;
+ };
+
+@@ -74,7 +75,8 @@ struct rtl9300_i2c {
+ struct rtl9300_i2c_chan chans[RTL9310_I2C_MUX_NCHAN];
+ struct regmap_field *fields[F_NUM_FIELDS];
+ u32 reg_base;
+- u32 data_reg;
++ u32 rd_reg;
++ u32 wd_reg;
+ u8 scl_num;
+ u8 sda_num;
+ struct mutex lock;
+@@ -171,7 +173,7 @@ static int rtl9300_i2c_read(struct rtl93
+ if (len > 16)
+ return -EIO;
+
+- ret = regmap_bulk_read(i2c->regmap, i2c->data_reg, vals, ARRAY_SIZE(vals));
++ ret = regmap_bulk_read(i2c->regmap, i2c->rd_reg, vals, ARRAY_SIZE(vals));
+ if (ret)
+ return ret;
+
+@@ -198,12 +200,12 @@ static int rtl9300_i2c_write(struct rtl9
+ vals[reg] |= buf[i] << shift;
+ }
+
+- return regmap_bulk_write(i2c->regmap, i2c->data_reg, vals, ARRAY_SIZE(vals));
++ return regmap_bulk_write(i2c->regmap, i2c->wd_reg, vals, ARRAY_SIZE(vals));
+ }
+
+ static int rtl9300_i2c_writel(struct rtl9300_i2c *i2c, u32 data)
+ {
+- return regmap_write(i2c->regmap, i2c->data_reg, data);
++ return regmap_write(i2c->regmap, i2c->wd_reg, data);
+ }
+
+ static int rtl9300_i2c_prepare_xfer(struct rtl9300_i2c *i2c, struct rtl9300_i2c_xfer *xfer)
+@@ -268,14 +270,14 @@ static int rtl9300_i2c_do_xfer(struct rt
+ if (!xfer->write) {
+ switch (xfer->type) {
+ case RTL9300_I2C_XFER_BYTE:
+- ret = regmap_read(i2c->regmap, i2c->data_reg, &val);
++ ret = regmap_read(i2c->regmap, i2c->rd_reg, &val);
+ if (ret)
+ return ret;
+
+ *xfer->data = val & 0xff;
+ break;
+ case RTL9300_I2C_XFER_WORD:
+- ret = regmap_read(i2c->regmap, i2c->data_reg, &val);
++ ret = regmap_read(i2c->regmap, i2c->rd_reg, &val);
+ if (ret)
+ return ret;
+
+@@ -408,7 +410,8 @@ static int rtl9300_i2c_probe(struct plat
+ 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;
++ i2c->rd_reg = i2c->reg_base + drv_data->rd_reg;
++ i2c->wd_reg = i2c->reg_base + drv_data->wd_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)
+@@ -499,7 +502,8 @@ static const struct rtl9300_i2c_drv_data
+ [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,
++ .rd_reg = RTL9300_I2C_MST_DATA_WORD0,
++ .wd_reg = RTL9300_I2C_MST_DATA_WORD0,
+ .max_nchan = RTL9300_I2C_MUX_NCHAN,
+ };
+
+@@ -519,7 +523,8 @@ static const struct rtl9300_i2c_drv_data
+ [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,
++ .rd_reg = RTL9310_I2C_MST_DATA_CTRL,
++ .wd_reg = RTL9310_I2C_MST_DATA_CTRL,
+ .max_nchan = RTL9310_I2C_MUX_NCHAN,
+ };
+
--- /dev/null
+From 98773df61f8416594ac993e8464df596755ee1b8 Mon Sep 17 00:00:00 2001
+From: Rustam Adilov <adilov@disroot.org>
+Date: Wed, 1 Apr 2026 23:06:42 +0500
+Subject: i2c: rtl9300: introduce max length property to driver data
+
+In RTL9607C i2c controller, theoretical maximum the data length
+can be is 4 bytes as opposed to 16 bytes on rtl9300 and rtl9310.
+
+Introduce a new property to the driver data struct for that.
+Adjust if statement in prepare_xfer function to follow that new
+property instead of the hardcoded value.
+
+Signed-off-by: Rustam Adilov <adilov@disroot.org>
+Reviewed-by: Chris Packham <chris.packham@alliedtelesis.co.nz>
+Signed-off-by: Andi Shyti <andi.shyti@kernel.org>
+Link: https://lore.kernel.org/r/20260401180648.337834-3-adilov@disroot.org
+---
+ drivers/i2c/busses/i2c-rtl9300.c | 9 ++++++++-
+ 1 file changed, 8 insertions(+), 1 deletion(-)
+
+--- a/drivers/i2c/busses/i2c-rtl9300.c
++++ b/drivers/i2c/busses/i2c-rtl9300.c
+@@ -64,11 +64,14 @@ struct rtl9300_i2c_drv_data {
+ u32 rd_reg;
+ u32 wd_reg;
+ u8 max_nchan;
++ u8 max_data_len;
+ };
+
+ #define RTL9300_I2C_MUX_NCHAN 8
+ #define RTL9310_I2C_MUX_NCHAN 12
+
++#define RTL9300_I2C_MAX_DATA_LEN 16
++
+ struct rtl9300_i2c {
+ struct regmap *regmap;
+ struct device *dev;
+@@ -210,9 +213,11 @@ static int rtl9300_i2c_writel(struct rtl
+
+ static int rtl9300_i2c_prepare_xfer(struct rtl9300_i2c *i2c, struct rtl9300_i2c_xfer *xfer)
+ {
++ const struct rtl9300_i2c_drv_data *drv_data;
+ int ret;
+
+- if (xfer->data_len < 1 || xfer->data_len > 16)
++ drv_data = device_get_match_data(i2c->dev);
++ if (xfer->data_len < 1 || xfer->data_len > drv_data->max_data_len)
+ return -EINVAL;
+
+ ret = regmap_field_write(i2c->fields[F_DEV_ADDR], xfer->dev_addr);
+@@ -505,6 +510,7 @@ static const struct rtl9300_i2c_drv_data
+ .rd_reg = RTL9300_I2C_MST_DATA_WORD0,
+ .wd_reg = RTL9300_I2C_MST_DATA_WORD0,
+ .max_nchan = RTL9300_I2C_MUX_NCHAN,
++ .max_data_len = RTL9300_I2C_MAX_DATA_LEN,
+ };
+
+ static const struct rtl9300_i2c_drv_data rtl9310_i2c_drv_data = {
+@@ -526,6 +532,7 @@ static const struct rtl9300_i2c_drv_data
+ .rd_reg = RTL9310_I2C_MST_DATA_CTRL,
+ .wd_reg = RTL9310_I2C_MST_DATA_CTRL,
+ .max_nchan = RTL9310_I2C_MUX_NCHAN,
++ .max_data_len = RTL9300_I2C_MAX_DATA_LEN,
+ };
+
+ static const struct of_device_id i2c_rtl9300_dt_ids[] = {
--- /dev/null
+From 55284a806b63a412846b9ecd3846f2639eaeaff4 Mon Sep 17 00:00:00 2001
+From: Rustam Adilov <adilov@disroot.org>
+Date: Wed, 1 Apr 2026 23:06:43 +0500
+Subject: i2c: rtl9300: introduce F_BUSY to the reg_fields struct
+
+In RTL9607C i2c controller the busy check operation is done on the
+separate bit of the command register as opposed to self clearing
+command trigger bit on the rtl9300 and rtl9310 i2c controllers.
+
+Introduce a new F_BUSY field to the reg_fields struct for that
+and change the regmap read poll function to use F_BUSY
+instead of I2C_TRIG.
+
+Signed-off-by: Rustam Adilov <adilov@disroot.org>
+Reviewed-by: Chris Packham <chris.packham@alliedtelesis.co.nz>
+Signed-off-by: Andi Shyti <andi.shyti@kernel.org>
+Link: https://lore.kernel.org/r/20260401180648.337834-4-adilov@disroot.org
+---
+ drivers/i2c/busses/i2c-rtl9300.c | 5 ++++-
+ 1 file changed, 4 insertions(+), 1 deletion(-)
+
+--- a/drivers/i2c/busses/i2c-rtl9300.c
++++ b/drivers/i2c/busses/i2c-rtl9300.c
+@@ -53,6 +53,7 @@ enum rtl9300_i2c_reg_fields {
+ F_SCL_SEL,
+ F_SDA_OUT_SEL,
+ F_SDA_SEL,
++ F_BUSY,
+
+ /* keep last */
+ F_NUM_FIELDS
+@@ -262,7 +263,7 @@ static int rtl9300_i2c_do_xfer(struct rt
+ if (ret)
+ return ret;
+
+- ret = regmap_field_read_poll_timeout(i2c->fields[F_I2C_TRIG], val, !val, 100, 100000);
++ ret = regmap_field_read_poll_timeout(i2c->fields[F_BUSY], val, !val, 100, 100000);
+ if (ret)
+ return ret;
+
+@@ -505,6 +506,7 @@ static const struct rtl9300_i2c_drv_data
+ [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),
++ [F_BUSY] = MST_REG_FIELD(RTL9300_I2C_MST_CTRL1, 0, 0),
+ },
+ .select_scl = rtl9300_i2c_select_scl,
+ .rd_reg = RTL9300_I2C_MST_DATA_WORD0,
+@@ -527,6 +529,7 @@ static const struct rtl9300_i2c_drv_data
+ [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),
++ [F_BUSY] = MST_REG_FIELD(RTL9310_I2C_MST_CTRL, 0, 0),
+ },
+ .select_scl = rtl9310_i2c_select_scl,
+ .rd_reg = RTL9310_I2C_MST_DATA_CTRL,
--- /dev/null
+From 6afde011baaf722aa66c11696b6383f9ce85b653 Mon Sep 17 00:00:00 2001
+From: Rustam Adilov <adilov@disroot.org>
+Date: Wed, 1 Apr 2026 23:06:44 +0500
+Subject: i2c: rtl9300: introduce a property for 8 bit width reg address
+
+In RTL9607C i2c controller, in order to indicate that the width of
+memory address is 8 bits, 0 is written to MEM_ADDR_WIDTH field as
+opposed to 1 for RTL9300 and RTL9310.
+
+Introduce a new property to a driver data to indicate what value
+need to written to MEM_ADDR_WIDTH field for this case.
+
+Signed-off-by: Rustam Adilov <adilov@disroot.org>
+Reviewed-by: Chris Packham <chris.packham@alliedtelesis.co.nz>
+Signed-off-by: Andi Shyti <andi.shyti@kernel.org>
+Link: https://lore.kernel.org/r/20260401180648.337834-5-adilov@disroot.org
+---
+ drivers/i2c/busses/i2c-rtl9300.c | 8 +++++++-
+ 1 file changed, 7 insertions(+), 1 deletion(-)
+
+--- a/drivers/i2c/busses/i2c-rtl9300.c
++++ b/drivers/i2c/busses/i2c-rtl9300.c
+@@ -66,6 +66,7 @@ struct rtl9300_i2c_drv_data {
+ u32 wd_reg;
+ u8 max_nchan;
+ u8 max_data_len;
++ u8 reg_addr_8bit_len;
+ };
+
+ #define RTL9300_I2C_MUX_NCHAN 8
+@@ -111,6 +112,7 @@ struct rtl9300_i2c_xfer {
+ #define RTL9300_I2C_MST_DATA_WORD2 0x10
+ #define RTL9300_I2C_MST_DATA_WORD3 0x14
+ #define RTL9300_I2C_MST_GLB_CTRL 0x384
++#define RTL9300_REG_ADDR_8BIT_LEN 1
+
+ #define RTL9310_I2C_MST_IF_CTRL 0x1004
+ #define RTL9310_I2C_MST_IF_SEL 0x1008
+@@ -305,6 +307,7 @@ static int rtl9300_i2c_smbus_xfer(struct
+ union i2c_smbus_data *data)
+ {
+ struct rtl9300_i2c_chan *chan = i2c_get_adapdata(adap);
++ const struct rtl9300_i2c_drv_data *drv_data;
+ struct rtl9300_i2c *i2c = chan->i2c;
+ struct rtl9300_i2c_xfer xfer = {0};
+ int ret;
+@@ -314,6 +317,7 @@ static int rtl9300_i2c_smbus_xfer(struct
+
+ guard(rtl9300_i2c)(i2c);
+
++ drv_data = device_get_match_data(i2c->dev);
+ ret = rtl9300_i2c_config_chan(i2c, chan);
+ if (ret)
+ return ret;
+@@ -321,7 +325,7 @@ static int rtl9300_i2c_smbus_xfer(struct
+ xfer.dev_addr = addr & 0x7f;
+ xfer.write = (read_write == I2C_SMBUS_WRITE);
+ xfer.reg_addr = command;
+- xfer.reg_addr_len = 1;
++ xfer.reg_addr_len = drv_data->reg_addr_8bit_len;
+
+ switch (size) {
+ case I2C_SMBUS_BYTE:
+@@ -513,6 +517,7 @@ static const struct rtl9300_i2c_drv_data
+ .wd_reg = RTL9300_I2C_MST_DATA_WORD0,
+ .max_nchan = RTL9300_I2C_MUX_NCHAN,
+ .max_data_len = RTL9300_I2C_MAX_DATA_LEN,
++ .reg_addr_8bit_len = RTL9300_REG_ADDR_8BIT_LEN,
+ };
+
+ static const struct rtl9300_i2c_drv_data rtl9310_i2c_drv_data = {
+@@ -536,6 +541,7 @@ static const struct rtl9300_i2c_drv_data
+ .wd_reg = RTL9310_I2C_MST_DATA_CTRL,
+ .max_nchan = RTL9310_I2C_MUX_NCHAN,
+ .max_data_len = RTL9300_I2C_MAX_DATA_LEN,
++ .reg_addr_8bit_len = RTL9300_REG_ADDR_8BIT_LEN,
+ };
+
+ static const struct of_device_id i2c_rtl9300_dt_ids[] = {
--- /dev/null
+From 1211ce1e11d23ec05d80a85b7187baa6abed3232 Mon Sep 17 00:00:00 2001
+From: Rustam Adilov <adilov@disroot.org>
+Date: Wed, 1 Apr 2026 23:06:45 +0500
+Subject: dt-bindings: i2c: realtek,rtl9301-i2c: extend for clocks and RTL9607C
+ support
+
+Add the "realtek,rtl9607-i2c" compatible for i2c controller on the
+RTL9607C SoC series.
+
+Add a clocks property to the properties to describe the i2c reference
+clock and make it available for all the compatibles. This i2c reference
+clock is assumed to be coming from switchcore region via Lexra bus as
+the other SoC peripherals.
+
+According to the info available about the existing devices, they also
+have the i2c master controller clocks.
+
+RTL9607C requires the "realtek,scl" and "clocks" to be specified
+and so handle it under separate if check for "realtek,rtl9607-i2c".
+
+Signed-off-by: Rustam Adilov <adilov@disroot.org>
+Acked-by: Conor Dooley <conor.dooley@microchip.com>
+Signed-off-by: Andi Shyti <andi.shyti@kernel.org>
+Link: https://lore.kernel.org/r/20260401180648.337834-6-adilov@disroot.org
+---
+ .../devicetree/bindings/i2c/realtek,rtl9301-i2c.yaml | 15 +++++++++++++++
+ 1 file changed, 15 insertions(+)
+
+--- a/Documentation/devicetree/bindings/i2c/realtek,rtl9301-i2c.yaml
++++ b/Documentation/devicetree/bindings/i2c/realtek,rtl9301-i2c.yaml
+@@ -15,6 +15,8 @@ description:
+ assigned to either I2C controller.
+ RTL9310 SoCs have equal capabilities but support 12 common SDA lines which
+ can be assigned to either I2C controller.
++ RTL9607C SoCs have equal capabilities but each controller only supports 1
++ SCL/SDA line.
+
+ properties:
+ compatible:
+@@ -34,6 +36,7 @@ properties:
+ - enum:
+ - realtek,rtl9301-i2c
+ - realtek,rtl9310-i2c
++ - realtek,rtl9607-i2c
+
+ reg:
+ items:
+@@ -51,6 +54,9 @@ properties:
+ The SCL line number of this I2C controller.
+ enum: [ 0, 1 ]
+
++ clocks:
++ maxItems: 1
++
+ patternProperties:
+ '^i2c@[0-9ab]$':
+ $ref: /schemas/i2c/i2c-controller.yaml
+@@ -82,6 +88,15 @@ allOf:
+ then:
+ patternProperties:
+ '^i2c@[89ab]$': false
++ - if:
++ properties:
++ compatible:
++ contains:
++ const: realtek,rtl9607-i2c
++ then:
++ required:
++ - realtek,scl
++ - clocks
+
+ required:
+ - compatible
--- /dev/null
+From f60d27926c9e2d547200fb0d26f61eec9b8291a6 Mon Sep 17 00:00:00 2001
+From: Rustam Adilov <adilov@disroot.org>
+Date: Wed, 1 Apr 2026 23:06:46 +0500
+Subject: i2c: rtl9300: introduce clk struct for upcoming rtl9607 support
+
+In RTL9607C i2c controller, there is 10 bit CLK_DIV field for
+setting the clock of i2c interface which depends on the rate
+of i2c clk (which seems be fixed to 62.5MHz according to Realtek SDK).
+
+Introduce the clk struct and the respective F_CLK_DIV and clk_div
+which are going to be used in the upcoming patch for rtl9607c i2c
+controller support addition.
+
+devm_clk_get_optional_enabled() function was used for cleaner code
+as it automatically returns NULL if the clk is not present, which is
+going to be the case for RTL9300 and RTL9310 i2c controllers.
+
+Signed-off-by: Rustam Adilov <adilov@disroot.org>
+Reviewed-by: Chris Packham <chris.packham@alliedtelesis.co.nz>
+Signed-off-by: Andi Shyti <andi.shyti@kernel.org>
+Link: https://lore.kernel.org/r/20260401180648.337834-7-adilov@disroot.org
+---
+ drivers/i2c/busses/i2c-rtl9300.c | 8 ++++++++
+ 1 file changed, 8 insertions(+)
+
+--- a/drivers/i2c/busses/i2c-rtl9300.c
++++ b/drivers/i2c/busses/i2c-rtl9300.c
+@@ -1,6 +1,7 @@
+ // SPDX-License-Identifier: GPL-2.0-only
+
+ #include <linux/bits.h>
++#include <linux/clk.h>
+ #include <linux/i2c.h>
+ #include <linux/i2c-mux.h>
+ #include <linux/mod_devicetable.h>
+@@ -28,6 +29,7 @@ struct rtl9300_i2c_chan {
+ struct rtl9300_i2c *i2c;
+ enum rtl9300_bus_freq bus_freq;
+ u8 sda_num;
++ u32 clk_div;
+ };
+
+ enum rtl9300_i2c_reg_scope {
+@@ -54,6 +56,7 @@ enum rtl9300_i2c_reg_fields {
+ F_SDA_OUT_SEL,
+ F_SDA_SEL,
+ F_BUSY,
++ F_CLK_DIV,
+
+ /* keep last */
+ F_NUM_FIELDS
+@@ -85,6 +88,7 @@ struct rtl9300_i2c {
+ u8 scl_num;
+ u8 sda_num;
+ struct mutex lock;
++ struct clk *clk;
+ };
+
+ DEFINE_GUARD(rtl9300_i2c, struct rtl9300_i2c *, mutex_lock(&_T->lock), mutex_unlock(&_T->lock))
+@@ -432,6 +436,10 @@ static int rtl9300_i2c_probe(struct plat
+ if (ret)
+ return ret;
+
++ i2c->clk = devm_clk_get_optional_enabled(dev, NULL);
++ if (IS_ERR(i2c->clk))
++ return dev_err_probe(dev, PTR_ERR(i2c->clk), "Failed to enable i2c clock\n");
++
+ i = 0;
+ for_each_child_of_node_scoped(dev->of_node, child) {
+ struct rtl9300_i2c_chan *chan = &i2c->chans[i];
--- /dev/null
+From 991cd899ecd03a1c3ef7d177a0b99e824c6be581 Mon Sep 17 00:00:00 2001
+From: Rustam Adilov <adilov@disroot.org>
+Date: Wed, 1 Apr 2026 23:06:47 +0500
+Subject: i2c: rtl9300: introduce new function properties to driver data
+
+Due to the very nature of differences between RTL9607C i2c controller
+and RTL9300 / RTL9310 that are incompatible with each other in some areas
+of this driver, for example in clock configuration, channel configuration
+and initialization at the end of the probe, introduce new function
+properties to the driver data struct to handle those differences.
+
+With these new properties, create configuration functions for RTL9300 and
+RTL9310 and assign them to their respective driver data structs.
+
+Signed-off-by: Rustam Adilov <adilov@disroot.org>
+Signed-off-by: Andi Shyti <andi.shyti@kernel.org>
+Link: https://lore.kernel.org/r/20260401180648.337834-8-adilov@disroot.org
+---
+ drivers/i2c/busses/i2c-rtl9300.c | 66 ++++++++++++++++++++++++++--------------
+ 1 file changed, 44 insertions(+), 22 deletions(-)
+
+--- a/drivers/i2c/busses/i2c-rtl9300.c
++++ b/drivers/i2c/busses/i2c-rtl9300.c
+@@ -65,6 +65,9 @@ enum rtl9300_i2c_reg_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);
++ int (*config_chan)(struct rtl9300_i2c *i2c, struct rtl9300_i2c_chan *chan);
++ void (*config_clock)(u32 clock_freq, struct rtl9300_i2c_chan *chan);
++ int (*misc_init)(struct rtl9300_i2c *i2c);
+ u32 rd_reg;
+ u32 wd_reg;
+ u8 max_nchan;
+@@ -175,6 +178,30 @@ static int rtl9300_i2c_config_chan(struc
+ return 0;
+ }
+
++static void rtl9300_i2c_config_clock(u32 clock_freq, struct rtl9300_i2c_chan *chan)
++{
++ struct rtl9300_i2c *i2c = chan->i2c;
++
++ switch (clock_freq) {
++ 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;
++ case RTL9300_I2C_MAX_SUPER_FAST_FREQ:
++ chan->bus_freq = RTL9300_I2C_SUPER_FAST_FREQ;
++ break;
++ case RTL9300_I2C_MAX_SLOW_FREQ:
++ chan->bus_freq = RTL9300_I2C_SLOW_FREQ;
++ break;
++ default:
++ dev_warn(i2c->dev, "SDA%d clock-frequency %d not supported using default\n",
++ chan->sda_num, clock_freq);
++ break;
++ }
++}
++
+ static int rtl9300_i2c_read(struct rtl9300_i2c *i2c, u8 *buf, u8 len)
+ {
+ u32 vals[4] = {};
+@@ -322,7 +349,7 @@ static int rtl9300_i2c_smbus_xfer(struct
+ guard(rtl9300_i2c)(i2c);
+
+ drv_data = device_get_match_data(i2c->dev);
+- ret = rtl9300_i2c_config_chan(i2c, chan);
++ ret = drv_data->config_chan(i2c, chan);
+ if (ret)
+ return ret;
+
+@@ -389,6 +416,12 @@ static struct i2c_adapter_quirks rtl9300
+ .max_write_len = 16,
+ };
+
++static int rtl9300_i2c_init(struct rtl9300_i2c *i2c)
++{
++ /* only use standard read format */
++ return regmap_field_write(i2c->fields[F_RD_MODE], 0);
++}
++
+ static int rtl9300_i2c_probe(struct platform_device *pdev)
+ {
+ struct device *dev = &pdev->dev;
+@@ -453,27 +486,11 @@ static int rtl9300_i2c_probe(struct plat
+ if (ret)
+ clock_freq = I2C_MAX_STANDARD_MODE_FREQ;
+
+- switch (clock_freq) {
+- 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;
+- case RTL9300_I2C_MAX_SUPER_FAST_FREQ:
+- chan->bus_freq = RTL9300_I2C_SUPER_FAST_FREQ;
+- break;
+- case RTL9300_I2C_MAX_SLOW_FREQ:
+- chan->bus_freq = RTL9300_I2C_SLOW_FREQ;
+- break;
+- default:
+- dev_warn(i2c->dev, "SDA%d clock-frequency %d not supported using default\n",
+- sda_num, clock_freq);
+- break;
+- }
+-
+ chan->sda_num = sda_num;
+ chan->i2c = i2c;
++
++ drv_data->config_clock(clock_freq, chan);
++
+ adap = &i2c->chans[i].adap;
+ adap->owner = THIS_MODULE;
+ adap->algo = &rtl9300_i2c_algo;
+@@ -491,8 +508,7 @@ 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);
++ ret = drv_data->misc_init(i2c);
+ if (ret)
+ return ret;
+
+@@ -521,6 +537,9 @@ static const struct rtl9300_i2c_drv_data
+ [F_BUSY] = MST_REG_FIELD(RTL9300_I2C_MST_CTRL1, 0, 0),
+ },
+ .select_scl = rtl9300_i2c_select_scl,
++ .config_chan = rtl9300_i2c_config_chan,
++ .config_clock = rtl9300_i2c_config_clock,
++ .misc_init = rtl9300_i2c_init,
+ .rd_reg = RTL9300_I2C_MST_DATA_WORD0,
+ .wd_reg = RTL9300_I2C_MST_DATA_WORD0,
+ .max_nchan = RTL9300_I2C_MUX_NCHAN,
+@@ -545,6 +564,9 @@ static const struct rtl9300_i2c_drv_data
+ [F_BUSY] = MST_REG_FIELD(RTL9310_I2C_MST_CTRL, 0, 0),
+ },
+ .select_scl = rtl9310_i2c_select_scl,
++ .config_chan = rtl9300_i2c_config_chan,
++ .config_clock = rtl9300_i2c_config_clock,
++ .misc_init = rtl9300_i2c_init,
+ .rd_reg = RTL9310_I2C_MST_DATA_CTRL,
+ .wd_reg = RTL9310_I2C_MST_DATA_CTRL,
+ .max_nchan = RTL9310_I2C_MUX_NCHAN,
--- /dev/null
+From 40890b5fe72b1a0d4913883844854f6641a2f4b3 Mon Sep 17 00:00:00 2001
+From: Rustam Adilov <adilov@disroot.org>
+Date: Wed, 1 Apr 2026 23:06:48 +0500
+Subject: i2c: rtl9300: add RTL9607C i2c controller support
+
+Add support for the internal I2C controllers of RTL9607C series based
+SoCs. Add register definitions, chip-specific functions and macros too.
+
+Make use of the clk introduced from the previous patch to get the clk_div
+value and use it during the rtl9607c channel configuration.
+
+Introduce a new EXT_SCK_5MS field to the reg fields struct which is going
+to be initialized by rtl9607c init function at the end of the probe.
+
+This patch depends on all the previous patches in this patch series.
+
+Signed-off-by: Rustam Adilov <adilov@disroot.org>
+Reviewed-by: Chris Packham <chris.packham@alliedtelesis.co.nz>
+Signed-off-by: Andi Shyti <andi.shyti@kernel.org>
+Link: https://lore.kernel.org/r/20260401180648.337834-9-adilov@disroot.org
+---
+ drivers/i2c/busses/i2c-rtl9300.c | 70 ++++++++++++++++++++++++++++++++++++++++
+ 1 file changed, 70 insertions(+)
+
+--- a/drivers/i2c/busses/i2c-rtl9300.c
++++ b/drivers/i2c/busses/i2c-rtl9300.c
+@@ -57,6 +57,7 @@ enum rtl9300_i2c_reg_fields {
+ F_SDA_SEL,
+ F_BUSY,
+ F_CLK_DIV,
++ F_EXT_SCK_5MS,
+
+ /* keep last */
+ F_NUM_FIELDS
+@@ -77,8 +78,10 @@ struct rtl9300_i2c_drv_data {
+
+ #define RTL9300_I2C_MUX_NCHAN 8
+ #define RTL9310_I2C_MUX_NCHAN 12
++#define RTL9607_I2C_MUX_NCHAN 1
+
+ #define RTL9300_I2C_MAX_DATA_LEN 16
++#define RTL9607_I2C_MAX_DATA_LEN 4
+
+ struct rtl9300_i2c {
+ struct regmap *regmap;
+@@ -127,6 +130,14 @@ struct rtl9300_i2c_xfer {
+ #define RTL9310_I2C_MST_MEMADDR_CTRL 0x4
+ #define RTL9310_I2C_MST_DATA_CTRL 0x8
+
++#define RTL9607_I2C_CONFIG 0x22f50
++#define RTL9607_IO_MODE_EN 0x23014
++#define RTL9607_I2C_IND_WD 0x0
++#define RTL9607_I2C_IND_ADR 0x8
++#define RTL9607_I2C_IND_CMD 0x10
++#define RTL9607_I2C_IND_RD 0x18
++#define RTL9607_REG_ADDR_8BIT_LEN 0
++
+ static int rtl9300_i2c_reg_addr_set(struct rtl9300_i2c *i2c, u32 reg, u16 len)
+ {
+ int ret;
+@@ -178,6 +189,27 @@ static int rtl9300_i2c_config_chan(struc
+ return 0;
+ }
+
++static int rtl9607_i2c_config_chan(struct rtl9300_i2c *i2c, struct rtl9300_i2c_chan *chan)
++{
++ const struct rtl9300_i2c_drv_data *drv_data;
++ int ret;
++
++ if (i2c->sda_num == chan->sda_num)
++ return 0;
++
++ ret = regmap_field_write(i2c->fields[F_CLK_DIV], chan->clk_div);
++ if (ret)
++ return ret;
++
++ drv_data = device_get_match_data(i2c->dev);
++ ret = drv_data->select_scl(i2c, i2c->scl_num);
++ if (ret)
++ return ret;
++
++ i2c->sda_num = chan->sda_num;
++ return 0;
++}
++
+ static void rtl9300_i2c_config_clock(u32 clock_freq, struct rtl9300_i2c_chan *chan)
+ {
+ struct rtl9300_i2c *i2c = chan->i2c;
+@@ -202,6 +234,13 @@ static void rtl9300_i2c_config_clock(u32
+ }
+ }
+
++static void rtl9607_i2c_config_clock(u32 clock_freq, struct rtl9300_i2c_chan *chan)
++{
++ struct rtl9300_i2c *i2c = chan->i2c;
++
++ chan->clk_div = clk_get_rate(i2c->clk) / clock_freq - 1;
++}
++
+ static int rtl9300_i2c_read(struct rtl9300_i2c *i2c, u8 *buf, u8 len)
+ {
+ u32 vals[4] = {};
+@@ -422,6 +461,11 @@ static int rtl9300_i2c_init(struct rtl93
+ return regmap_field_write(i2c->fields[F_RD_MODE], 0);
+ }
+
++static int rtl9607_i2c_init(struct rtl9300_i2c *i2c)
++{
++ return regmap_field_write(i2c->fields[F_EXT_SCK_5MS], 1);
++}
++
+ static int rtl9300_i2c_probe(struct platform_device *pdev)
+ {
+ struct device *dev = &pdev->dev;
+@@ -574,6 +618,31 @@ static const struct rtl9300_i2c_drv_data
+ .reg_addr_8bit_len = RTL9300_REG_ADDR_8BIT_LEN,
+ };
+
++static const struct rtl9300_i2c_drv_data rtl9607_i2c_drv_data = {
++ .field_desc = {
++ [F_SCL_SEL] = GLB_REG_FIELD(RTL9607_IO_MODE_EN, 13, 14),
++ [F_EXT_SCK_5MS] = MST_REG_FIELD(RTL9607_I2C_CONFIG, 26, 26),
++ [F_DEV_ADDR] = MST_REG_FIELD(RTL9607_I2C_CONFIG, 14, 20),
++ [F_MEM_ADDR_WIDTH] = MST_REG_FIELD(RTL9607_I2C_CONFIG, 12, 13),
++ [F_DATA_WIDTH] = MST_REG_FIELD(RTL9607_I2C_CONFIG, 10, 11),
++ [F_CLK_DIV] = MST_REG_FIELD(RTL9607_I2C_CONFIG, 0, 9),
++ [F_I2C_FAIL] = MST_REG_FIELD(RTL9607_I2C_IND_CMD, 3, 3),
++ [F_BUSY] = MST_REG_FIELD(RTL9607_I2C_IND_CMD, 2, 2),
++ [F_RWOP] = MST_REG_FIELD(RTL9607_I2C_IND_CMD, 1, 1),
++ [F_I2C_TRIG] = MST_REG_FIELD(RTL9607_I2C_IND_CMD, 0, 0),
++ [F_MEM_ADDR] = MST_REG_FIELD(RTL9607_I2C_IND_ADR, 0, 31),
++ },
++ .select_scl = rtl9310_i2c_select_scl,
++ .config_chan = rtl9607_i2c_config_chan,
++ .config_clock = rtl9607_i2c_config_clock,
++ .misc_init = rtl9607_i2c_init,
++ .rd_reg = RTL9607_I2C_IND_RD,
++ .wd_reg = RTL9607_I2C_IND_WD,
++ .max_nchan = RTL9607_I2C_MUX_NCHAN,
++ .max_data_len = RTL9607_I2C_MAX_DATA_LEN,
++ .reg_addr_8bit_len = RTL9607_REG_ADDR_8BIT_LEN,
++};
++
+ 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 },
+@@ -583,6 +652,7 @@ static const struct of_device_id i2c_rtl
+ { .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 },
++ { .compatible = "realtek,rtl9607-i2c", .data = (void *) &rtl9607_i2c_drv_data },
+ {}
+ };
+ MODULE_DEVICE_TABLE(of, i2c_rtl9300_dt_ids);