mirror of
https://github.com/openwrt/openwrt.git
synced 2026-06-17 17:01:44 +04:00
realtek: i2c: rtl9300: backport rtl9607c i2c support and speed patches
The [1] patch and [2] patch series has been accepted by upstream linux so backport them to OpenWrt. Patch from [1] adds 50 kHz and 2.5 MHz bus speeds. Patch series from [2] adds support for RTL9607C i2c controller. [1] - https://lore.kernel.org/linux-i2c/20260227111134.2163701-1-jan-kernel@kantert.net/ [2] - https://lore.kernel.org/linux-i2c/20260401180648.337834-1-adilov@disroot.org/ Signed-off-by: Rustam Adilov <adilov@tutamail.com> Link: https://github.com/openwrt/openwrt/pull/22663 Signed-off-by: Hauke Mehrtens <hauke@hauke-m.de>
This commit is contained in:
committed by
Hauke Mehrtens
parent
e1c2f02325
commit
39b31b31f5
+7
-7
@@ -1,10 +1,7 @@
|
||||
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
|
||||
@@ -12,6 +9,9 @@ supported speeds. Add support for 50 kHz and 2.5 MHz I2C bus modes on the
|
||||
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(-)
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
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,
|
||||
};
|
||||
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
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[] = {
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
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,
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
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[] = {
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
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
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
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];
|
||||
+147
@@ -0,0 +1,147 @@
|
||||
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,
|
||||
+154
@@ -0,0 +1,154 @@
|
||||
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);
|
||||
+7
-7
@@ -1,10 +1,7 @@
|
||||
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
|
||||
@@ -12,6 +9,9 @@ supported speeds. Add support for 50 kHz and 2.5 MHz I2C bus modes on the
|
||||
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(-)
|
||||
+112
@@ -0,0 +1,112 @@
|
||||
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,
|
||||
};
|
||||
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
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[] = {
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
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,
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
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[] = {
|
||||
+73
@@ -0,0 +1,73 @@
|
||||
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
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
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];
|
||||
+147
@@ -0,0 +1,147 @@
|
||||
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,
|
||||
+154
@@ -0,0 +1,154 @@
|
||||
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);
|
||||
Reference in New Issue
Block a user