* [PATCH v3 1/8] i2c: rtl9300: split data_reg into read and write reg
2026-03-22 12:34 [PATCH v3 0/8] i2c: rtl9300: support for RTL9607C I2C controller Rustam Adilov
@ 2026-03-22 12:34 ` Rustam Adilov
2026-03-22 12:34 ` [PATCH v3 2/8] i2c: rtl9300: introduce max length property to driver data Rustam Adilov
` (6 subsequent siblings)
7 siblings, 0 replies; 13+ messages in thread
From: Rustam Adilov @ 2026-03-22 12:34 UTC (permalink / raw)
To: Chris Packham, Andi Shyti, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, linux-i2c, devicetree, linux-kernel
Cc: Rustam Adilov
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.
Reviewed-by: Chris Packham <chris.packham@alliedtelesis.co.nz>
Signed-off-by: Rustam Adilov <adilov@disroot.org>
---
drivers/i2c/busses/i2c-rtl9300.c | 25 +++++++++++++++----------
1 file changed, 15 insertions(+), 10 deletions(-)
diff --git a/drivers/i2c/busses/i2c-rtl9300.c b/drivers/i2c/busses/i2c-rtl9300.c
index 67a5c4228fc9..9bf4c6b08e05 100644
--- 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 rtl9300_i2c *i2c, u8 *buf, u8 len)
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 rtl9300_i2c *i2c, u8 *buf, u8 len)
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 rtl9300_i2c *i2c, struct rtl9300_i2c_xfer
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 platform_device *pdev)
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 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 rtl9310_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,
};
--
2.53.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH v3 2/8] i2c: rtl9300: introduce max length property to driver data
2026-03-22 12:34 [PATCH v3 0/8] i2c: rtl9300: support for RTL9607C I2C controller Rustam Adilov
2026-03-22 12:34 ` [PATCH v3 1/8] i2c: rtl9300: split data_reg into read and write reg Rustam Adilov
@ 2026-03-22 12:34 ` Rustam Adilov
2026-03-22 12:34 ` [PATCH v3 3/8] i2c: rtl9300: introduce F_BUSY to the reg_fields struct Rustam Adilov
` (5 subsequent siblings)
7 siblings, 0 replies; 13+ messages in thread
From: Rustam Adilov @ 2026-03-22 12:34 UTC (permalink / raw)
To: Chris Packham, Andi Shyti, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, linux-i2c, devicetree, linux-kernel
Cc: Rustam Adilov
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.
Reviewed-by: Chris Packham <chris.packham@alliedtelesis.co.nz>
Signed-off-by: Rustam Adilov <adilov@disroot.org>
---
drivers/i2c/busses/i2c-rtl9300.c | 9 ++++++++-
1 file changed, 8 insertions(+), 1 deletion(-)
diff --git a/drivers/i2c/busses/i2c-rtl9300.c b/drivers/i2c/busses/i2c-rtl9300.c
index 9bf4c6b08e05..2cada6038b44 100644
--- 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 rtl9300_i2c *i2c, u32 data)
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 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 rtl9310_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[] = {
--
2.53.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH v3 3/8] i2c: rtl9300: introduce F_BUSY to the reg_fields struct
2026-03-22 12:34 [PATCH v3 0/8] i2c: rtl9300: support for RTL9607C I2C controller Rustam Adilov
2026-03-22 12:34 ` [PATCH v3 1/8] i2c: rtl9300: split data_reg into read and write reg Rustam Adilov
2026-03-22 12:34 ` [PATCH v3 2/8] i2c: rtl9300: introduce max length property to driver data Rustam Adilov
@ 2026-03-22 12:34 ` Rustam Adilov
2026-03-22 12:34 ` [PATCH v3 4/8] i2c: rtl9300: introduce a property for 8 bit width reg address Rustam Adilov
` (4 subsequent siblings)
7 siblings, 0 replies; 13+ messages in thread
From: Rustam Adilov @ 2026-03-22 12:34 UTC (permalink / raw)
To: Chris Packham, Andi Shyti, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, linux-i2c, devicetree, linux-kernel
Cc: Rustam Adilov
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.
Reviewed-by: Chris Packham <chris.packham@alliedtelesis.co.nz>
Signed-off-by: Rustam Adilov <adilov@disroot.org>
---
drivers/i2c/busses/i2c-rtl9300.c | 5 ++++-
1 file changed, 4 insertions(+), 1 deletion(-)
diff --git a/drivers/i2c/busses/i2c-rtl9300.c b/drivers/i2c/busses/i2c-rtl9300.c
index 2cada6038b44..e40b4692a3fa 100644
--- 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 rtl9300_i2c *i2c, struct rtl9300_i2c_xfer
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 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 rtl9310_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,
--
2.53.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH v3 4/8] i2c: rtl9300: introduce a property for 8 bit width reg address
2026-03-22 12:34 [PATCH v3 0/8] i2c: rtl9300: support for RTL9607C I2C controller Rustam Adilov
` (2 preceding siblings ...)
2026-03-22 12:34 ` [PATCH v3 3/8] i2c: rtl9300: introduce F_BUSY to the reg_fields struct Rustam Adilov
@ 2026-03-22 12:34 ` Rustam Adilov
2026-03-22 12:34 ` [PATCH v3 5/8] i2c: rtl9300: introduce clk struct for upcoming rtl9607 support Rustam Adilov
` (3 subsequent siblings)
7 siblings, 0 replies; 13+ messages in thread
From: Rustam Adilov @ 2026-03-22 12:34 UTC (permalink / raw)
To: Chris Packham, Andi Shyti, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, linux-i2c, devicetree, linux-kernel
Cc: Rustam Adilov
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.
Reviewed-by: Chris Packham <chris.packham@alliedtelesis.co.nz>
Signed-off-by: Rustam Adilov <adilov@disroot.org>
---
drivers/i2c/busses/i2c-rtl9300.c | 8 +++++++-
1 file changed, 7 insertions(+), 1 deletion(-)
diff --git a/drivers/i2c/busses/i2c-rtl9300.c b/drivers/i2c/busses/i2c-rtl9300.c
index e40b4692a3fa..ffbc6c52861b 100644
--- 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 i2c_adapter *adap, u16 addr, unsigned s
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 i2c_adapter *adap, u16 addr, unsigned s
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 i2c_adapter *adap, u16 addr, unsigned s
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 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 rtl9310_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[] = {
--
2.53.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH v3 5/8] i2c: rtl9300: introduce clk struct for upcoming rtl9607 support
2026-03-22 12:34 [PATCH v3 0/8] i2c: rtl9300: support for RTL9607C I2C controller Rustam Adilov
` (3 preceding siblings ...)
2026-03-22 12:34 ` [PATCH v3 4/8] i2c: rtl9300: introduce a property for 8 bit width reg address Rustam Adilov
@ 2026-03-22 12:34 ` Rustam Adilov
2026-03-22 21:01 ` Krzysztof Kozlowski
2026-03-22 12:34 ` [PATCH v3 6/8] i2c: rtl9300: intoduce new function properties to driver data Rustam Adilov
` (2 subsequent siblings)
7 siblings, 1 reply; 13+ messages in thread
From: Rustam Adilov @ 2026-03-22 12:34 UTC (permalink / raw)
To: Chris Packham, Andi Shyti, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, linux-i2c, devicetree, linux-kernel
Cc: Rustam Adilov
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.
Reviewed-by: Chris Packham <chris.packham@alliedtelesis.co.nz>
Signed-off-by: Rustam Adilov <adilov@disroot.org>
---
drivers/i2c/busses/i2c-rtl9300.c | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/drivers/i2c/busses/i2c-rtl9300.c b/drivers/i2c/busses/i2c-rtl9300.c
index ffbc6c52861b..16af49ccd1dd 100644
--- 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 platform_device *pdev)
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];
--
2.53.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* Re: [PATCH v3 5/8] i2c: rtl9300: introduce clk struct for upcoming rtl9607 support
2026-03-22 12:34 ` [PATCH v3 5/8] i2c: rtl9300: introduce clk struct for upcoming rtl9607 support Rustam Adilov
@ 2026-03-22 21:01 ` Krzysztof Kozlowski
2026-03-23 6:24 ` Rustam Adilov
0 siblings, 1 reply; 13+ messages in thread
From: Krzysztof Kozlowski @ 2026-03-22 21:01 UTC (permalink / raw)
To: Rustam Adilov, Chris Packham, Andi Shyti, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, linux-i2c, devicetree,
linux-kernel
On 22/03/2026 13:34, Rustam Adilov wrote:
> 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 platform_device *pdev)
> if (ret)
> return ret;
>
> + i2c->clk = devm_clk_get_optional_enabled(dev, NULL);
Undocumented ABI at this point.
Order your patches in correct order. First go bindings.
> + if (IS_ERR(i2c->clk))
> + return dev_err_probe(dev, PTR_ERR(i2c->clk), "Failed to enable i2c clock\n");
> +
> i = 0;
Best regards,
Krzysztof
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH v3 5/8] i2c: rtl9300: introduce clk struct for upcoming rtl9607 support
2026-03-22 21:01 ` Krzysztof Kozlowski
@ 2026-03-23 6:24 ` Rustam Adilov
0 siblings, 0 replies; 13+ messages in thread
From: Rustam Adilov @ 2026-03-23 6:24 UTC (permalink / raw)
To: Krzysztof Kozlowski
Cc: Chris Packham, Andi Shyti, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, linux-i2c, devicetree, linux-kernel
On 2026-03-22 21:01, Krzysztof Kozlowski wrote:
> On 22/03/2026 13:34, Rustam Adilov wrote:
>> 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 platform_device *pdev)
>> if (ret)
>> return ret;
>>
>> + i2c->clk = devm_clk_get_optional_enabled(dev, NULL);
>
> Undocumented ABI at this point.
>
> Order your patches in correct order. First go bindings.
Will move the patch 7 to be before patch 5 in v4 of this patch series.
>> + if (IS_ERR(i2c->clk))
>> + return dev_err_probe(dev, PTR_ERR(i2c->clk), "Failed to enable i2c clock\n");
>> +
>> i = 0;
> Best regards,
> Krzysztof
Thanks,
Rustam
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH v3 6/8] i2c: rtl9300: intoduce new function properties to driver data
2026-03-22 12:34 [PATCH v3 0/8] i2c: rtl9300: support for RTL9607C I2C controller Rustam Adilov
` (4 preceding siblings ...)
2026-03-22 12:34 ` [PATCH v3 5/8] i2c: rtl9300: introduce clk struct for upcoming rtl9607 support Rustam Adilov
@ 2026-03-22 12:34 ` Rustam Adilov
2026-03-22 12:34 ` [PATCH v3 7/8] dt-bindings: i2c: realtek,rtl9301-i2c: extend for RTL9607C support Rustam Adilov
2026-03-22 12:34 ` [PATCH v3 8/8] i2c: rtl9300: add RTL9607C i2c controller support Rustam Adilov
7 siblings, 0 replies; 13+ messages in thread
From: Rustam Adilov @ 2026-03-22 12:34 UTC (permalink / raw)
To: Chris Packham, Andi Shyti, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, linux-i2c, devicetree, linux-kernel
Cc: Rustam Adilov
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>
---
drivers/i2c/busses/i2c-rtl9300.c | 66 +++++++++++++++++++++-----------
1 file changed, 44 insertions(+), 22 deletions(-)
diff --git a/drivers/i2c/busses/i2c-rtl9300.c b/drivers/i2c/busses/i2c-rtl9300.c
index 16af49ccd1dd..b718b74afe0d 100644
--- 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(struct rtl9300_i2c *i2c, struct rtl9300_i2c_c
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 i2c_adapter *adap, u16 addr, unsigned s
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_i2c_quirks = {
.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 platform_device *pdev)
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 platform_device *pdev)
}
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 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 rtl9310_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,
--
2.53.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* [PATCH v3 7/8] dt-bindings: i2c: realtek,rtl9301-i2c: extend for RTL9607C support
2026-03-22 12:34 [PATCH v3 0/8] i2c: rtl9300: support for RTL9607C I2C controller Rustam Adilov
` (5 preceding siblings ...)
2026-03-22 12:34 ` [PATCH v3 6/8] i2c: rtl9300: intoduce new function properties to driver data Rustam Adilov
@ 2026-03-22 12:34 ` Rustam Adilov
2026-03-22 21:02 ` Krzysztof Kozlowski
2026-03-22 12:34 ` [PATCH v3 8/8] i2c: rtl9300: add RTL9607C i2c controller support Rustam Adilov
7 siblings, 1 reply; 13+ messages in thread
From: Rustam Adilov @ 2026-03-22 12:34 UTC (permalink / raw)
To: Chris Packham, Andi Shyti, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, linux-i2c, devicetree, linux-kernel
Cc: Rustam Adilov
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.
RTL9607C requires the "realtek,scl" to be specified the same way as
RTL9310 so add it to the same if check.
Signed-off-by: Rustam Adilov <adilov@disroot.org>
---
.../devicetree/bindings/i2c/realtek,rtl9301-i2c.yaml | 10 +++++++++-
1 file changed, 9 insertions(+), 1 deletion(-)
diff --git a/Documentation/devicetree/bindings/i2c/realtek,rtl9301-i2c.yaml b/Documentation/devicetree/bindings/i2c/realtek,rtl9301-i2c.yaml
index f9a449fee2b0..4a83923fee15 100644
--- 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
@@ -69,7 +75,9 @@ allOf:
properties:
compatible:
contains:
- const: realtek,rtl9310-i2c
+ enum:
+ - realtek,rtl9310-i2c
+ - realtek,rtl9607-i2c
then:
required:
- realtek,scl
--
2.53.0
^ permalink raw reply related [flat|nested] 13+ messages in thread* Re: [PATCH v3 7/8] dt-bindings: i2c: realtek,rtl9301-i2c: extend for RTL9607C support
2026-03-22 12:34 ` [PATCH v3 7/8] dt-bindings: i2c: realtek,rtl9301-i2c: extend for RTL9607C support Rustam Adilov
@ 2026-03-22 21:02 ` Krzysztof Kozlowski
2026-03-23 6:21 ` Rustam Adilov
0 siblings, 1 reply; 13+ messages in thread
From: Krzysztof Kozlowski @ 2026-03-22 21:02 UTC (permalink / raw)
To: Rustam Adilov, Chris Packham, Andi Shyti, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, linux-i2c, devicetree,
linux-kernel
On 22/03/2026 13:34, Rustam Adilov wrote:
> 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.
>
Why the clock is not required for rtl9607? I did not find explanation of
such change in the changelog/cover letter.
Best regards,
Krzysztof
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH v3 7/8] dt-bindings: i2c: realtek,rtl9301-i2c: extend for RTL9607C support
2026-03-22 21:02 ` Krzysztof Kozlowski
@ 2026-03-23 6:21 ` Rustam Adilov
0 siblings, 0 replies; 13+ messages in thread
From: Rustam Adilov @ 2026-03-23 6:21 UTC (permalink / raw)
To: Krzysztof Kozlowski
Cc: Chris Packham, Andi Shyti, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, linux-i2c, devicetree, linux-kernel
On 2026-03-22 21:02, Krzysztof Kozlowski wrote:
> On 22/03/2026 13:34, Rustam Adilov wrote:
>> 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.
>>
>
> Why the clock is not required for rtl9607? I did not find explanation
> of
> such change in the changelog/cover letter.
I have apparently forgotten to include it back after moving
"realtek,rtl9607-i2c" to another spot.
Will add it back in v4.
> Best regards,
> Krzysztof
Thanks,
Rustam
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH v3 8/8] i2c: rtl9300: add RTL9607C i2c controller support
2026-03-22 12:34 [PATCH v3 0/8] i2c: rtl9300: support for RTL9607C I2C controller Rustam Adilov
` (6 preceding siblings ...)
2026-03-22 12:34 ` [PATCH v3 7/8] dt-bindings: i2c: realtek,rtl9301-i2c: extend for RTL9607C support Rustam Adilov
@ 2026-03-22 12:34 ` Rustam Adilov
7 siblings, 0 replies; 13+ messages in thread
From: Rustam Adilov @ 2026-03-22 12:34 UTC (permalink / raw)
To: Chris Packham, Andi Shyti, Rob Herring, Krzysztof Kozlowski,
Conor Dooley, linux-i2c, devicetree, linux-kernel
Cc: Rustam Adilov
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.
Reviewed-by: Chris Packham <chris.packham@alliedtelesis.co.nz>
Signed-off-by: Rustam Adilov <adilov@disroot.org>
---
drivers/i2c/busses/i2c-rtl9300.c | 70 ++++++++++++++++++++++++++++++++
1 file changed, 70 insertions(+)
diff --git a/drivers/i2c/busses/i2c-rtl9300.c b/drivers/i2c/busses/i2c-rtl9300.c
index b718b74afe0d..8cedffbb2964 100644
--- 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(struct rtl9300_i2c *i2c, struct rtl9300_i2c_c
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 clock_freq, struct rtl9300_i2c_chan *ch
}
}
+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 rtl9300_i2c *i2c)
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 rtl9310_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_rtl9300_dt_ids[] = {
{ .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);
--
2.53.0
^ permalink raw reply related [flat|nested] 13+ messages in thread