* [PATCH v6 00/12] i2c: fix, rework and extend RTL9300 I2C driver
@ 2025-08-24 11:33 Jonas Jelonek
2025-08-24 11:33 ` [PATCH v6 01/12] i2c: rtl9300: fix channel number bound check Jonas Jelonek
` (13 more replies)
0 siblings, 14 replies; 17+ messages in thread
From: Jonas Jelonek @ 2025-08-24 11:33 UTC (permalink / raw)
To: Chris Packham, Andi Shyti, Rob Herring, Krzysztof Kozlowski
Cc: linux-i2c, Conor Dooley, devicetree, linux-kernel,
Markus Stockhausen, Sven Eckelmann, Harshal Gohel, Wolfram Sang,
Jonas Jelonek
This patch series for the RTL9300 I2C driver:
- fixes issues, one of them in some cases causing data corruption
- reworks significant parts of the current implementation
- add support for the (quite similar) RTL9310 series
Goal of this is to fix critical issues, improve overall code quality and
simplify maintainance and further extension of the driver. Moreover, it
should be brought on par feature-wise with OpenWrt's downstream driver
to be able to drop the downstream version.
The first three patches address bugs in the current implementation, on
of them being critical and causing data corruption under certain
circumstances. Although the hardware doesn't support SMBus Quick Write,
the driver claims to support it with a broken implementation. This
causes to execute a 16-byte Write instead of a Quick Write, e.g. causing
corruption on not-write-protected SFP EEPROMs and soft-bricking them.
These three patches are also sent to 'stable' because they fix critical
issues.
Subsequent patches introduce various smaller and bigger enhancements.
These include:
- use regmap_field + its API instead of macros + GENMASK + shifts
- refactor xfer handling
- variable renaming to avoid confusion
- move some register operations, calling them somewhere else and
less frequently
- use guarded mutex instead of explicit mutex_lock/_unlock to
simplify control flow
Finally, the last two patches add support for RTL9310 (mango) series to
the driver and adjust the dt-bindings accordingly.
Simple operations have been tested successfully on:
- Zyxel XGS1210-12 (RTL9302B)
- TP-Link TL-ST1008F v2.0 (RTL9303)
- Netgear MS510TXM (RTL9313)
with Byte-Read, Word-Read and I2C-Block-Read. Other operations need
testing from people with devices available.
Compile-tested with Linux, run-tested as backport in OpenWrt on the
aforementioned devices.
--
Changelog
v6: - patch 'i2c: rtl9300: check if xfer length is valid'
- renamed to 'ensure data length is within supported range'
- added I2C quirk for zero length as suggested by Wolfram Sang
- reordered patches to have backport-worthy fixes first and
enhancements/others after
- patches 'fix channel number bound check', 'check if xfer
length is valid' and 'remove SMBus Quick operation support'
were moved before all others
- added CC: stable to first three patches
- fixed commit message of 'dt-bindings: i2c: realtek,rtl9301-i2c:
extend for RTL9310 support'
- added a patch to use guard(mutex) instead of explicit lock/unlock
as suggested by Markus Elfring
- added Reviewed-by: Rob Herring ... to dt-bindings patches
- added Tested-by: Sven Eckelmann ... to all patches (except the
new patch in this version)
v5: - added more patches to fix further issues/do further cleanup
- remove SMBus Quick support (not supported by hardware)
- move setting SCL frequency to config_io
- only set read message format (RD_MODE) once on probing
- add check to avoid len = 0 being allowed as length
- adjusted cover letter
v4: - fixed an incorrect check for number of channels which was already
present in original code
v3: - narrowed vendor property per variant to be required only
for RTL9310
- narrowed usable child-node i2c addresses per variant
- no changes to driver patches
v2: - Patch 1:
- adjusted commit message
- retained Tested-By and Reviewed-By from Chris Packham
- Patch 2:
- simplified check as suggested by Markus Stockhausen
- fixed commit message
- Patch 3 (all requested by Krzysztof):
- use vendor property instead of generic
- add front compatibles to make binding complete
- fix commit message
- reordered patches, dt-bindings patch now comes before its 'user'
- properly add device-tree list and relevant maintainers to To/Cc
---
Jonas Jelonek (12):
i2c: rtl9300: fix channel number bound check
i2c: rtl9300: ensure data length is within supported range
i2c: rtl9300: remove broken SMBus Quick operation support
i2c: rtl9300: use regmap fields and API for registers
dt-bindings: i2c: realtek,rtl9301-i2c: fix wording and typos
i2c: rtl9300: rename internal sda_pin to sda_num
i2c: rtl9300: move setting SCL frequency to config_io
i2c: rtl9300: do not set read mode on every transfer
i2c: rtl9300: separate xfer configuration and execution
i2c: rtl9300: use scoped guard instead of explicit lock/unlock
dt-bindings: i2c: realtek,rtl9301-i2c: extend for RTL9310 support
i2c: rtl9300: add support for RTL9310 I2C controller
.../bindings/i2c/realtek,rtl9301-i2c.yaml | 45 +-
drivers/i2c/busses/i2c-rtl9300.c | 488 ++++++++++--------
2 files changed, 325 insertions(+), 208 deletions(-)
base-commit: 57774430864b721082b9bafd17fc839f31251c7b
--
2.48.1
^ permalink raw reply [flat|nested] 17+ messages in thread
* [PATCH v6 01/12] i2c: rtl9300: fix channel number bound check
2025-08-24 11:33 [PATCH v6 00/12] i2c: fix, rework and extend RTL9300 I2C driver Jonas Jelonek
@ 2025-08-24 11:33 ` Jonas Jelonek
2025-08-24 11:33 ` [PATCH v6 02/12] i2c: rtl9300: ensure data length is within supported range Jonas Jelonek
` (12 subsequent siblings)
13 siblings, 0 replies; 17+ messages in thread
From: Jonas Jelonek @ 2025-08-24 11:33 UTC (permalink / raw)
To: Chris Packham, Andi Shyti, Rob Herring, Krzysztof Kozlowski
Cc: linux-i2c, Conor Dooley, devicetree, linux-kernel,
Markus Stockhausen, Sven Eckelmann, Harshal Gohel, Wolfram Sang,
Jonas Jelonek, stable
Fix the current check for number of channels (child nodes in the device
tree). Before, this was:
if (device_get_child_node_count(dev) >= RTL9300_I2C_MUX_NCHAN)
RTL9300_I2C_MUX_NCHAN gives the maximum number of channels so checking
with '>=' isn't correct because it doesn't allow the last channel
number. Thus, fix it to:
if (device_get_child_node_count(dev) > RTL9300_I2C_MUX_NCHAN)
Issue occured on a TP-Link TL-ST1008F v2.0 device (8 SFP+ ports) and fix
is tested there.
Fixes: c366be720235 ("i2c: Add driver for the RTL9300 I2C controller")
Cc: <stable@vger.kernel.org> # v6.13+
Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com>
Tested-by: Sven Eckelmann <sven@narfation.org>
---
drivers/i2c/busses/i2c-rtl9300.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/drivers/i2c/busses/i2c-rtl9300.c b/drivers/i2c/busses/i2c-rtl9300.c
index 4b215f9a24e6..19c367703eaf 100644
--- a/drivers/i2c/busses/i2c-rtl9300.c
+++ b/drivers/i2c/busses/i2c-rtl9300.c
@@ -382,7 +382,7 @@ static int rtl9300_i2c_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, i2c);
- if (device_get_child_node_count(dev) >= RTL9300_I2C_MUX_NCHAN)
+ if (device_get_child_node_count(dev) > RTL9300_I2C_MUX_NCHAN)
return dev_err_probe(dev, -EINVAL, "Too many channels\n");
device_for_each_child_node(dev, child) {
--
2.48.1
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v6 02/12] i2c: rtl9300: ensure data length is within supported range
2025-08-24 11:33 [PATCH v6 00/12] i2c: fix, rework and extend RTL9300 I2C driver Jonas Jelonek
2025-08-24 11:33 ` [PATCH v6 01/12] i2c: rtl9300: fix channel number bound check Jonas Jelonek
@ 2025-08-24 11:33 ` Jonas Jelonek
2025-08-24 11:33 ` [PATCH v6 03/12] i2c: rtl9300: remove broken SMBus Quick operation support Jonas Jelonek
` (11 subsequent siblings)
13 siblings, 0 replies; 17+ messages in thread
From: Jonas Jelonek @ 2025-08-24 11:33 UTC (permalink / raw)
To: Chris Packham, Andi Shyti, Rob Herring, Krzysztof Kozlowski
Cc: linux-i2c, Conor Dooley, devicetree, linux-kernel,
Markus Stockhausen, Sven Eckelmann, Harshal Gohel, Wolfram Sang,
Jonas Jelonek, stable
Add an explicit check for the xfer length to 'rtl9300_i2c_config_xfer'
to ensure the data length isn't within the supported range. In
particular a data length of 0 is not supported by the hardware and
causes unintended or destructive behaviour.
This limitation becomes obvious when looking at the register
documentation [1]. 4 bits are reserved for DATA_WIDTH and the value
of these 4 bits is used as N + 1, allowing a data length range of
1 <= len <= 16.
Affected by this is the SMBus Quick Operation which works with a data
length of 0. Passing 0 as the length causes an underflow of the value
due to:
(len - 1) & 0xf
and effectively specifying a transfer length of 16 via the registers.
This causes a 16-byte write operation instead of a Quick Write. For
example, on SFP modules without write-protected EEPROM this soft-bricks
them by overwriting some initial bytes.
For completeness, also add a quirk for the zero length.
[1] https://svanheule.net/realtek/longan/register/i2c_mst1_ctrl2
Fixes: c366be720235 ("i2c: Add driver for the RTL9300 I2C controller")
Cc: <stable@vger.kernel.org> # v6.13+
Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com>
Tested-by: Sven Eckelmann <sven@narfation.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 19c367703eaf..ebd4a85e1bde 100644
--- a/drivers/i2c/busses/i2c-rtl9300.c
+++ b/drivers/i2c/busses/i2c-rtl9300.c
@@ -99,6 +99,9 @@ static int rtl9300_i2c_config_xfer(struct rtl9300_i2c *i2c, struct rtl9300_i2c_c
{
u32 val, mask;
+ if (len < 1 || len > 16)
+ return -EINVAL;
+
val = chan->bus_freq << RTL9300_I2C_MST_CTRL2_SCL_FREQ_OFS;
mask = RTL9300_I2C_MST_CTRL2_SCL_FREQ_MASK;
@@ -352,7 +355,7 @@ static const struct i2c_algorithm rtl9300_i2c_algo = {
};
static struct i2c_adapter_quirks rtl9300_i2c_quirks = {
- .flags = I2C_AQ_NO_CLK_STRETCH,
+ .flags = I2C_AQ_NO_CLK_STRETCH | I2C_AQ_NO_ZERO_LEN,
.max_read_len = 16,
.max_write_len = 16,
};
--
2.48.1
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v6 03/12] i2c: rtl9300: remove broken SMBus Quick operation support
2025-08-24 11:33 [PATCH v6 00/12] i2c: fix, rework and extend RTL9300 I2C driver Jonas Jelonek
2025-08-24 11:33 ` [PATCH v6 01/12] i2c: rtl9300: fix channel number bound check Jonas Jelonek
2025-08-24 11:33 ` [PATCH v6 02/12] i2c: rtl9300: ensure data length is within supported range Jonas Jelonek
@ 2025-08-24 11:33 ` Jonas Jelonek
2025-08-24 11:33 ` [PATCH v6 04/12] i2c: rtl9300: use regmap fields and API for registers Jonas Jelonek
` (10 subsequent siblings)
13 siblings, 0 replies; 17+ messages in thread
From: Jonas Jelonek @ 2025-08-24 11:33 UTC (permalink / raw)
To: Chris Packham, Andi Shyti, Rob Herring, Krzysztof Kozlowski
Cc: linux-i2c, Conor Dooley, devicetree, linux-kernel,
Markus Stockhausen, Sven Eckelmann, Harshal Gohel, Wolfram Sang,
Jonas Jelonek, stable
Remove the SMBus Quick operation from this driver because it is not
natively supported by the hardware and is wrongly implemented in the
driver.
The I2C controllers in Realtek RTL9300 and RTL9310 are SMBus-compliant
but there doesn't seem to be native support for the SMBus Quick
operation. It is not explicitly mentioned in the documentation but
looking at the registers which configure an SMBus transaction, one can
see that the data length cannot be set to 0. This suggests that the
hardware doesn't allow any SMBus message without data bytes (except for
those it does on it's own, see SMBus Block Read).
The current implementation of SMBus Quick operation passes a length of
0 (which is actually invalid). Before the fix of a bug in a previous
commit, this led to a read operation of 16 bytes from any register (the
one of a former transaction or any other value.
This caused issues like soft-bricked SFP modules after a simple probe
with i2cdetect which uses Quick by default. Running this with SFP
modules whose EEPROM isn't write-protected, some of the initial bytes
are overwritten because a 16-byte write operation is executed instead of
a Quick Write. (This temporarily soft-bricked one of my DAC cables.)
Because SMBus Quick operation is obviously not supported on these
controllers (because a length of 0 cannot be set, even when no register
address is set), remove that instead of claiming there is support. There
also shouldn't be any kind of emulated 'Quick' which just does another
kind of operation in the background. Otherwise, specific issues occur
in case of a 'Quick' Write which actually writes unknown data to an
unknown register.
Fixes: c366be720235 ("i2c: Add driver for the RTL9300 I2C controller")
Cc: <stable@vger.kernel.org> # v6.13+
Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com>
Tested-by: Sven Eckelmann <sven@narfation.org>
---
drivers/i2c/busses/i2c-rtl9300.c | 15 +++------------
1 file changed, 3 insertions(+), 12 deletions(-)
diff --git a/drivers/i2c/busses/i2c-rtl9300.c b/drivers/i2c/busses/i2c-rtl9300.c
index ebd4a85e1bde..9e6232075137 100644
--- a/drivers/i2c/busses/i2c-rtl9300.c
+++ b/drivers/i2c/busses/i2c-rtl9300.c
@@ -235,15 +235,6 @@ static int rtl9300_i2c_smbus_xfer(struct i2c_adapter *adap, u16 addr, unsigned s
}
switch (size) {
- case I2C_SMBUS_QUICK:
- ret = rtl9300_i2c_config_xfer(i2c, chan, addr, 0);
- if (ret)
- goto out_unlock;
- ret = rtl9300_i2c_reg_addr_set(i2c, 0, 0);
- if (ret)
- goto out_unlock;
- break;
-
case I2C_SMBUS_BYTE:
if (read_write == I2C_SMBUS_WRITE) {
ret = rtl9300_i2c_config_xfer(i2c, chan, addr, 0);
@@ -344,9 +335,9 @@ static int rtl9300_i2c_smbus_xfer(struct i2c_adapter *adap, u16 addr, unsigned s
static u32 rtl9300_i2c_func(struct i2c_adapter *a)
{
- return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE |
- I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA |
- I2C_FUNC_SMBUS_BLOCK_DATA | I2C_FUNC_SMBUS_I2C_BLOCK;
+ return I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA |
+ I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_BLOCK_DATA |
+ I2C_FUNC_SMBUS_I2C_BLOCK;
}
static const struct i2c_algorithm rtl9300_i2c_algo = {
--
2.48.1
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v6 04/12] i2c: rtl9300: use regmap fields and API for registers
2025-08-24 11:33 [PATCH v6 00/12] i2c: fix, rework and extend RTL9300 I2C driver Jonas Jelonek
` (2 preceding siblings ...)
2025-08-24 11:33 ` [PATCH v6 03/12] i2c: rtl9300: remove broken SMBus Quick operation support Jonas Jelonek
@ 2025-08-24 11:33 ` Jonas Jelonek
2025-08-24 11:33 ` [PATCH v6 05/12] dt-bindings: i2c: realtek,rtl9301-i2c: fix wording and typos Jonas Jelonek
` (9 subsequent siblings)
13 siblings, 0 replies; 17+ messages in thread
From: Jonas Jelonek @ 2025-08-24 11:33 UTC (permalink / raw)
To: Chris Packham, Andi Shyti, Rob Herring, Krzysztof Kozlowski
Cc: linux-i2c, Conor Dooley, devicetree, linux-kernel,
Markus Stockhausen, Sven Eckelmann, Harshal Gohel, Wolfram Sang,
Jonas Jelonek
Adapt the RTL9300 I2C controller driver to use more of the regmap
API, especially make use of reg_field and regmap_field instead of macros
to represent registers. Most register operations are performed through
regmap_field_* API then.
Handle SCL selection using separate chip-specific functions since this
is already known to differ between the Realtek SoC families in such a
way that this cannot be properly handled using just a different
reg_field.
This makes it easier to add support for newer generations or to handle
differences between specific revisions within a series. Just by
defining a separate driver data structure with the corresponding
register field definitions and linking it to a new compatible.
Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com>
Reviewed-by: Chris Packham <chris.packham@alliedtelesis.co.nz>
Tested-by: Chris Packham <chris.packham@alliedtelesis.co.nz> # On RTL9302c
Tested-by: Sven Eckelmann <sven@narfation.org>
---
drivers/i2c/busses/i2c-rtl9300.c | 192 ++++++++++++++++++++-----------
1 file changed, 124 insertions(+), 68 deletions(-)
diff --git a/drivers/i2c/busses/i2c-rtl9300.c b/drivers/i2c/busses/i2c-rtl9300.c
index 9e6232075137..8483bab72146 100644
--- a/drivers/i2c/busses/i2c-rtl9300.c
+++ b/drivers/i2c/busses/i2c-rtl9300.c
@@ -23,97 +23,117 @@ struct rtl9300_i2c_chan {
u8 sda_pin;
};
+enum rtl9300_i2c_reg_scope {
+ REG_SCOPE_GLOBAL,
+ REG_SCOPE_MASTER,
+};
+
+struct rtl9300_i2c_reg_field {
+ struct reg_field field;
+ enum rtl9300_i2c_reg_scope scope;
+};
+
+enum rtl9300_i2c_reg_fields {
+ F_DATA_WIDTH = 0,
+ F_DEV_ADDR,
+ F_I2C_FAIL,
+ F_I2C_TRIG,
+ F_MEM_ADDR,
+ F_MEM_ADDR_WIDTH,
+ F_RD_MODE,
+ F_RWOP,
+ F_SCL_FREQ,
+ F_SCL_SEL,
+ F_SDA_OUT_SEL,
+ F_SDA_SEL,
+
+ /* keep last */
+ F_NUM_FIELDS
+};
+
+struct rtl9300_i2c_drv_data {
+ struct rtl9300_i2c_reg_field field_desc[F_NUM_FIELDS];
+ int (*select_scl)(struct rtl9300_i2c *i2c, u8 scl);
+ u32 data_reg;
+ u8 max_nchan;
+};
+
#define RTL9300_I2C_MUX_NCHAN 8
struct rtl9300_i2c {
struct regmap *regmap;
struct device *dev;
struct rtl9300_i2c_chan chans[RTL9300_I2C_MUX_NCHAN];
+ struct regmap_field *fields[F_NUM_FIELDS];
u32 reg_base;
+ u32 data_reg;
u8 sda_pin;
struct mutex lock;
};
#define RTL9300_I2C_MST_CTRL1 0x0
-#define RTL9300_I2C_MST_CTRL1_MEM_ADDR_OFS 8
-#define RTL9300_I2C_MST_CTRL1_MEM_ADDR_MASK GENMASK(31, 8)
-#define RTL9300_I2C_MST_CTRL1_SDA_OUT_SEL_OFS 4
-#define RTL9300_I2C_MST_CTRL1_SDA_OUT_SEL_MASK GENMASK(6, 4)
-#define RTL9300_I2C_MST_CTRL1_GPIO_SCL_SEL BIT(3)
-#define RTL9300_I2C_MST_CTRL1_RWOP BIT(2)
-#define RTL9300_I2C_MST_CTRL1_I2C_FAIL BIT(1)
-#define RTL9300_I2C_MST_CTRL1_I2C_TRIG BIT(0)
#define RTL9300_I2C_MST_CTRL2 0x4
-#define RTL9300_I2C_MST_CTRL2_RD_MODE BIT(15)
-#define RTL9300_I2C_MST_CTRL2_DEV_ADDR_OFS 8
-#define RTL9300_I2C_MST_CTRL2_DEV_ADDR_MASK GENMASK(14, 8)
-#define RTL9300_I2C_MST_CTRL2_DATA_WIDTH_OFS 4
-#define RTL9300_I2C_MST_CTRL2_DATA_WIDTH_MASK GENMASK(7, 4)
-#define RTL9300_I2C_MST_CTRL2_MEM_ADDR_WIDTH_OFS 2
-#define RTL9300_I2C_MST_CTRL2_MEM_ADDR_WIDTH_MASK GENMASK(3, 2)
-#define RTL9300_I2C_MST_CTRL2_SCL_FREQ_OFS 0
-#define RTL9300_I2C_MST_CTRL2_SCL_FREQ_MASK GENMASK(1, 0)
#define RTL9300_I2C_MST_DATA_WORD0 0x8
#define RTL9300_I2C_MST_DATA_WORD1 0xc
#define RTL9300_I2C_MST_DATA_WORD2 0x10
#define RTL9300_I2C_MST_DATA_WORD3 0x14
-
#define RTL9300_I2C_MST_GLB_CTRL 0x384
static int rtl9300_i2c_reg_addr_set(struct rtl9300_i2c *i2c, u32 reg, u16 len)
{
- u32 val, mask;
int ret;
- val = len << RTL9300_I2C_MST_CTRL2_MEM_ADDR_WIDTH_OFS;
- mask = RTL9300_I2C_MST_CTRL2_MEM_ADDR_WIDTH_MASK;
-
- ret = regmap_update_bits(i2c->regmap, i2c->reg_base + RTL9300_I2C_MST_CTRL2, mask, val);
+ ret = regmap_field_write(i2c->fields[F_MEM_ADDR_WIDTH], len);
if (ret)
return ret;
- val = reg << RTL9300_I2C_MST_CTRL1_MEM_ADDR_OFS;
- mask = RTL9300_I2C_MST_CTRL1_MEM_ADDR_MASK;
+ return regmap_field_write(i2c->fields[F_MEM_ADDR], reg);
+}
- return regmap_update_bits(i2c->regmap, i2c->reg_base + RTL9300_I2C_MST_CTRL1, mask, val);
+static int rtl9300_i2c_select_scl(struct rtl9300_i2c *i2c, u8 scl)
+{
+ return regmap_field_write(i2c->fields[F_SCL_SEL], 1);
}
static int rtl9300_i2c_config_io(struct rtl9300_i2c *i2c, u8 sda_pin)
{
+ struct rtl9300_i2c_drv_data *drv_data;
int ret;
- u32 val, mask;
- ret = regmap_update_bits(i2c->regmap, RTL9300_I2C_MST_GLB_CTRL, BIT(sda_pin), BIT(sda_pin));
+ drv_data = (struct rtl9300_i2c_drv_data *)device_get_match_data(i2c->dev);
+
+ ret = regmap_field_update_bits(i2c->fields[F_SDA_SEL], BIT(sda_pin), BIT(sda_pin));
if (ret)
return ret;
- val = (sda_pin << RTL9300_I2C_MST_CTRL1_SDA_OUT_SEL_OFS) |
- RTL9300_I2C_MST_CTRL1_GPIO_SCL_SEL;
- mask = RTL9300_I2C_MST_CTRL1_SDA_OUT_SEL_MASK | RTL9300_I2C_MST_CTRL1_GPIO_SCL_SEL;
+ ret = regmap_field_write(i2c->fields[F_SDA_OUT_SEL], sda_pin);
+ if (ret)
+ return ret;
- return regmap_update_bits(i2c->regmap, i2c->reg_base + RTL9300_I2C_MST_CTRL1, mask, val);
+ return drv_data->select_scl(i2c, 0);
}
static int rtl9300_i2c_config_xfer(struct rtl9300_i2c *i2c, struct rtl9300_i2c_chan *chan,
u16 addr, u16 len)
{
- u32 val, mask;
+ int ret;
if (len < 1 || len > 16)
return -EINVAL;
- val = chan->bus_freq << RTL9300_I2C_MST_CTRL2_SCL_FREQ_OFS;
- mask = RTL9300_I2C_MST_CTRL2_SCL_FREQ_MASK;
-
- val |= addr << RTL9300_I2C_MST_CTRL2_DEV_ADDR_OFS;
- mask |= RTL9300_I2C_MST_CTRL2_DEV_ADDR_MASK;
+ ret = regmap_field_write(i2c->fields[F_SCL_FREQ], chan->bus_freq);
+ if (ret)
+ return ret;
- val |= ((len - 1) & 0xf) << RTL9300_I2C_MST_CTRL2_DATA_WIDTH_OFS;
- mask |= RTL9300_I2C_MST_CTRL2_DATA_WIDTH_MASK;
+ ret = regmap_field_write(i2c->fields[F_DEV_ADDR], addr);
+ if (ret)
+ return ret;
- mask |= RTL9300_I2C_MST_CTRL2_RD_MODE;
+ ret = regmap_field_write(i2c->fields[F_DATA_WIDTH], (len - 1) & 0xf);
+ if (ret)
+ return ret;
- return regmap_update_bits(i2c->regmap, i2c->reg_base + RTL9300_I2C_MST_CTRL2, mask, val);
+ return regmap_field_write(i2c->fields[F_RD_MODE], 0);
}
static int rtl9300_i2c_read(struct rtl9300_i2c *i2c, u8 *buf, int len)
@@ -124,8 +144,7 @@ static int rtl9300_i2c_read(struct rtl9300_i2c *i2c, u8 *buf, int len)
if (len > 16)
return -EIO;
- ret = regmap_bulk_read(i2c->regmap, i2c->reg_base + RTL9300_I2C_MST_DATA_WORD0,
- vals, ARRAY_SIZE(vals));
+ ret = regmap_bulk_read(i2c->regmap, i2c->data_reg, vals, ARRAY_SIZE(vals));
if (ret)
return ret;
@@ -152,52 +171,49 @@ static int rtl9300_i2c_write(struct rtl9300_i2c *i2c, u8 *buf, int len)
vals[reg] |= buf[i] << shift;
}
- return regmap_bulk_write(i2c->regmap, i2c->reg_base + RTL9300_I2C_MST_DATA_WORD0,
- vals, ARRAY_SIZE(vals));
+ return regmap_bulk_write(i2c->regmap, i2c->data_reg, vals, ARRAY_SIZE(vals));
}
static int rtl9300_i2c_writel(struct rtl9300_i2c *i2c, u32 data)
{
- return regmap_write(i2c->regmap, i2c->reg_base + RTL9300_I2C_MST_DATA_WORD0, data);
+ return regmap_write(i2c->regmap, i2c->data_reg, data);
}
static int rtl9300_i2c_execute_xfer(struct rtl9300_i2c *i2c, char read_write,
int size, union i2c_smbus_data *data, int len)
{
- u32 val, mask;
+ u32 val;
int ret;
- val = read_write == I2C_SMBUS_WRITE ? RTL9300_I2C_MST_CTRL1_RWOP : 0;
- mask = RTL9300_I2C_MST_CTRL1_RWOP;
-
- val |= RTL9300_I2C_MST_CTRL1_I2C_TRIG;
- mask |= RTL9300_I2C_MST_CTRL1_I2C_TRIG;
+ ret = regmap_field_write(i2c->fields[F_RWOP], read_write == I2C_SMBUS_WRITE);
+ if (ret)
+ return ret;
- ret = regmap_update_bits(i2c->regmap, i2c->reg_base + RTL9300_I2C_MST_CTRL1, mask, val);
+ ret = regmap_field_write(i2c->fields[F_I2C_TRIG], 1);
if (ret)
return ret;
- ret = regmap_read_poll_timeout(i2c->regmap, i2c->reg_base + RTL9300_I2C_MST_CTRL1,
- val, !(val & RTL9300_I2C_MST_CTRL1_I2C_TRIG), 100, 100000);
+ ret = regmap_field_read_poll_timeout(i2c->fields[F_I2C_TRIG], val, !val, 100, 100000);
if (ret)
return ret;
- if (val & RTL9300_I2C_MST_CTRL1_I2C_FAIL)
+ ret = regmap_field_read(i2c->fields[F_I2C_FAIL], &val);
+ if (ret)
+ return ret;
+ if (val)
return -EIO;
if (read_write == I2C_SMBUS_READ) {
switch (size) {
case I2C_SMBUS_BYTE:
case I2C_SMBUS_BYTE_DATA:
- ret = regmap_read(i2c->regmap,
- i2c->reg_base + RTL9300_I2C_MST_DATA_WORD0, &val);
+ ret = regmap_read(i2c->regmap, i2c->data_reg, &val);
if (ret)
return ret;
data->byte = val & 0xff;
break;
case I2C_SMBUS_WORD_DATA:
- ret = regmap_read(i2c->regmap,
- i2c->reg_base + RTL9300_I2C_MST_DATA_WORD0, &val);
+ ret = regmap_read(i2c->regmap, i2c->data_reg, &val);
if (ret)
return ret;
data->word = val & 0xffff;
@@ -355,9 +371,11 @@ static int rtl9300_i2c_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct rtl9300_i2c *i2c;
+ struct fwnode_handle *child;
+ struct rtl9300_i2c_drv_data *drv_data;
+ struct reg_field fields[F_NUM_FIELDS];
u32 clock_freq, sda_pin;
int ret, i = 0;
- struct fwnode_handle *child;
i2c = devm_kzalloc(dev, sizeof(*i2c), GFP_KERNEL);
if (!i2c)
@@ -376,9 +394,22 @@ static int rtl9300_i2c_probe(struct platform_device *pdev)
platform_set_drvdata(pdev, i2c);
- if (device_get_child_node_count(dev) > RTL9300_I2C_MUX_NCHAN)
+ drv_data = (struct rtl9300_i2c_drv_data *)device_get_match_data(i2c->dev);
+ if (device_get_child_node_count(dev) > drv_data->max_nchan)
return dev_err_probe(dev, -EINVAL, "Too many channels\n");
+ i2c->data_reg = i2c->reg_base + drv_data->data_reg;
+ for (i = 0; i < F_NUM_FIELDS; i++) {
+ fields[i] = drv_data->field_desc[i].field;
+ if (drv_data->field_desc[i].scope == REG_SCOPE_MASTER)
+ fields[i].reg += i2c->reg_base;
+ }
+ ret = devm_regmap_field_bulk_alloc(dev, i2c->regmap, i2c->fields,
+ fields, F_NUM_FIELDS);
+ if (ret)
+ return ret;
+
+ i = 0;
device_for_each_child_node(dev, child) {
struct rtl9300_i2c_chan *chan = &i2c->chans[i];
struct i2c_adapter *adap = &chan->adap;
@@ -395,7 +426,6 @@ static int rtl9300_i2c_probe(struct platform_device *pdev)
case I2C_MAX_STANDARD_MODE_FREQ:
chan->bus_freq = RTL9300_I2C_STD_FREQ;
break;
-
case I2C_MAX_FAST_MODE_FREQ:
chan->bus_freq = RTL9300_I2C_FAST_FREQ;
break;
@@ -427,11 +457,37 @@ static int rtl9300_i2c_probe(struct platform_device *pdev)
return 0;
}
+#define GLB_REG_FIELD(reg, msb, lsb) \
+ { .field = REG_FIELD(reg, msb, lsb), .scope = REG_SCOPE_GLOBAL }
+#define MST_REG_FIELD(reg, msb, lsb) \
+ { .field = REG_FIELD(reg, msb, lsb), .scope = REG_SCOPE_MASTER }
+
+static const struct rtl9300_i2c_drv_data rtl9300_i2c_drv_data = {
+ .field_desc = {
+ [F_MEM_ADDR] = MST_REG_FIELD(RTL9300_I2C_MST_CTRL1, 8, 31),
+ [F_SDA_OUT_SEL] = MST_REG_FIELD(RTL9300_I2C_MST_CTRL1, 4, 6),
+ [F_SCL_SEL] = MST_REG_FIELD(RTL9300_I2C_MST_CTRL1, 3, 3),
+ [F_RWOP] = MST_REG_FIELD(RTL9300_I2C_MST_CTRL1, 2, 2),
+ [F_I2C_FAIL] = MST_REG_FIELD(RTL9300_I2C_MST_CTRL1, 1, 1),
+ [F_I2C_TRIG] = MST_REG_FIELD(RTL9300_I2C_MST_CTRL1, 0, 0),
+ [F_RD_MODE] = MST_REG_FIELD(RTL9300_I2C_MST_CTRL2, 15, 15),
+ [F_DEV_ADDR] = MST_REG_FIELD(RTL9300_I2C_MST_CTRL2, 8, 14),
+ [F_DATA_WIDTH] = MST_REG_FIELD(RTL9300_I2C_MST_CTRL2, 4, 7),
+ [F_MEM_ADDR_WIDTH] = MST_REG_FIELD(RTL9300_I2C_MST_CTRL2, 2, 3),
+ [F_SCL_FREQ] = MST_REG_FIELD(RTL9300_I2C_MST_CTRL2, 0, 1),
+ [F_SDA_SEL] = GLB_REG_FIELD(RTL9300_I2C_MST_GLB_CTRL, 0, 7),
+ },
+ .select_scl = rtl9300_i2c_select_scl,
+ .data_reg = RTL9300_I2C_MST_DATA_WORD0,
+ .max_nchan = RTL9300_I2C_MUX_NCHAN,
+};
+
+
static const struct of_device_id i2c_rtl9300_dt_ids[] = {
- { .compatible = "realtek,rtl9301-i2c" },
- { .compatible = "realtek,rtl9302b-i2c" },
- { .compatible = "realtek,rtl9302c-i2c" },
- { .compatible = "realtek,rtl9303-i2c" },
+ { .compatible = "realtek,rtl9301-i2c", .data = (void *) &rtl9300_i2c_drv_data },
+ { .compatible = "realtek,rtl9302b-i2c", .data = (void *) &rtl9300_i2c_drv_data },
+ { .compatible = "realtek,rtl9302c-i2c", .data = (void *) &rtl9300_i2c_drv_data },
+ { .compatible = "realtek,rtl9303-i2c", .data = (void *) &rtl9300_i2c_drv_data },
{}
};
MODULE_DEVICE_TABLE(of, i2c_rtl9300_dt_ids);
--
2.48.1
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v6 05/12] dt-bindings: i2c: realtek,rtl9301-i2c: fix wording and typos
2025-08-24 11:33 [PATCH v6 00/12] i2c: fix, rework and extend RTL9300 I2C driver Jonas Jelonek
` (3 preceding siblings ...)
2025-08-24 11:33 ` [PATCH v6 04/12] i2c: rtl9300: use regmap fields and API for registers Jonas Jelonek
@ 2025-08-24 11:33 ` Jonas Jelonek
2025-08-24 11:33 ` [PATCH v6 06/12] i2c: rtl9300: rename internal sda_pin to sda_num Jonas Jelonek
` (8 subsequent siblings)
13 siblings, 0 replies; 17+ messages in thread
From: Jonas Jelonek @ 2025-08-24 11:33 UTC (permalink / raw)
To: Chris Packham, Andi Shyti, Rob Herring, Krzysztof Kozlowski
Cc: linux-i2c, Conor Dooley, devicetree, linux-kernel,
Markus Stockhausen, Sven Eckelmann, Harshal Gohel, Wolfram Sang,
Jonas Jelonek
Fix wording of binding description to use plural because there is not
only a single RTL9300 SoC. RTL9300 describes a whole family of Realtek
SoCs.
Add missing word 'of' in description of reg property.
Change 'SDA pin' to 'SDA line number' because the property must contain
the SDA (channel) number ranging from 0-7 instead of a real pin number.
Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com>
Reviewed-by: Rob Herring (Arm) <robh@kernel.org>
---
.../devicetree/bindings/i2c/realtek,rtl9301-i2c.yaml | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/Documentation/devicetree/bindings/i2c/realtek,rtl9301-i2c.yaml b/Documentation/devicetree/bindings/i2c/realtek,rtl9301-i2c.yaml
index 69ac5db8b914..274e2ab8b612 100644
--- a/Documentation/devicetree/bindings/i2c/realtek,rtl9301-i2c.yaml
+++ b/Documentation/devicetree/bindings/i2c/realtek,rtl9301-i2c.yaml
@@ -10,7 +10,7 @@ maintainers:
- Chris Packham <chris.packham@alliedtelesis.co.nz>
description:
- The RTL9300 SoC has two I2C controllers. Each of these has an SCL line (which
+ RTL9300 SoCs have two I2C controllers. Each of these has an SCL line (which
if not-used for SCL can be a GPIO). There are 8 common SDA lines that can be
assigned to either I2C controller.
@@ -27,7 +27,7 @@ properties:
reg:
items:
- - description: Register offset and size this I2C controller.
+ - description: Register offset and size of this I2C controller.
"#address-cells":
const: 1
@@ -42,7 +42,7 @@ patternProperties:
properties:
reg:
- description: The SDA pin associated with the I2C bus.
+ description: The SDA line number associated with the I2C bus.
maxItems: 1
required:
--
2.48.1
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v6 06/12] i2c: rtl9300: rename internal sda_pin to sda_num
2025-08-24 11:33 [PATCH v6 00/12] i2c: fix, rework and extend RTL9300 I2C driver Jonas Jelonek
` (4 preceding siblings ...)
2025-08-24 11:33 ` [PATCH v6 05/12] dt-bindings: i2c: realtek,rtl9301-i2c: fix wording and typos Jonas Jelonek
@ 2025-08-24 11:33 ` Jonas Jelonek
2025-08-24 11:33 ` [PATCH v6 07/12] i2c: rtl9300: move setting SCL frequency to config_io Jonas Jelonek
` (7 subsequent siblings)
13 siblings, 0 replies; 17+ messages in thread
From: Jonas Jelonek @ 2025-08-24 11:33 UTC (permalink / raw)
To: Chris Packham, Andi Shyti, Rob Herring, Krzysztof Kozlowski
Cc: linux-i2c, Conor Dooley, devicetree, linux-kernel,
Markus Stockhausen, Sven Eckelmann, Harshal Gohel, Wolfram Sang,
Jonas Jelonek
Rename the internally used 'sda_pin' to 'sda_num' to make it clear that
this is NOT the actual pin number of the GPIO pin but rather the logical
SDA channel number. Although the alternate function SDA_Y is sometimes
given with the GPIO number, this is not always the case. Thus, avoid any
confusion or misconfiguration by giving the variable the correct name.
This follows the description change in the devicetree bindings.
Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com>
Tested-by: Sven Eckelmann <sven@narfation.org>
---
drivers/i2c/busses/i2c-rtl9300.c | 24 ++++++++++++------------
1 file changed, 12 insertions(+), 12 deletions(-)
diff --git a/drivers/i2c/busses/i2c-rtl9300.c b/drivers/i2c/busses/i2c-rtl9300.c
index 8483bab72146..f9b5ac7670c2 100644
--- a/drivers/i2c/busses/i2c-rtl9300.c
+++ b/drivers/i2c/busses/i2c-rtl9300.c
@@ -20,7 +20,7 @@ struct rtl9300_i2c_chan {
struct i2c_adapter adap;
struct rtl9300_i2c *i2c;
enum rtl9300_bus_freq bus_freq;
- u8 sda_pin;
+ u8 sda_num;
};
enum rtl9300_i2c_reg_scope {
@@ -67,7 +67,7 @@ struct rtl9300_i2c {
struct regmap_field *fields[F_NUM_FIELDS];
u32 reg_base;
u32 data_reg;
- u8 sda_pin;
+ u8 sda_num;
struct mutex lock;
};
@@ -102,11 +102,11 @@ static int rtl9300_i2c_config_io(struct rtl9300_i2c *i2c, u8 sda_pin)
drv_data = (struct rtl9300_i2c_drv_data *)device_get_match_data(i2c->dev);
- ret = regmap_field_update_bits(i2c->fields[F_SDA_SEL], BIT(sda_pin), BIT(sda_pin));
+ ret = regmap_field_update_bits(i2c->fields[F_SDA_SEL], BIT(sda_num), BIT(sda_num));
if (ret)
return ret;
- ret = regmap_field_write(i2c->fields[F_SDA_OUT_SEL], sda_pin);
+ ret = regmap_field_write(i2c->fields[F_SDA_OUT_SEL], sda_num);
if (ret)
return ret;
@@ -243,11 +243,11 @@ static int rtl9300_i2c_smbus_xfer(struct i2c_adapter *adap, u16 addr, unsigned s
int len = 0, ret;
mutex_lock(&i2c->lock);
- if (chan->sda_pin != i2c->sda_pin) {
+ if (chan->sda_num != i2c->sda_num) {
ret = rtl9300_i2c_config_io(i2c, chan->sda_pin);
if (ret)
goto out_unlock;
- i2c->sda_pin = chan->sda_pin;
+ i2c->sda_num = chan->sda_num;
}
switch (size) {
@@ -374,7 +374,7 @@ static int rtl9300_i2c_probe(struct platform_device *pdev)
struct fwnode_handle *child;
struct rtl9300_i2c_drv_data *drv_data;
struct reg_field fields[F_NUM_FIELDS];
- u32 clock_freq, sda_pin;
+ u32 clock_freq, sda_num;
int ret, i = 0;
i2c = devm_kzalloc(dev, sizeof(*i2c), GFP_KERNEL);
@@ -414,7 +414,7 @@ static int rtl9300_i2c_probe(struct platform_device *pdev)
struct rtl9300_i2c_chan *chan = &i2c->chans[i];
struct i2c_adapter *adap = &chan->adap;
- ret = fwnode_property_read_u32(child, "reg", &sda_pin);
+ ret = fwnode_property_read_u32(child, "reg", &sda_num);
if (ret)
return ret;
@@ -431,11 +431,11 @@ static int rtl9300_i2c_probe(struct platform_device *pdev)
break;
default:
dev_warn(i2c->dev, "SDA%d clock-frequency %d not supported using default\n",
- sda_pin, clock_freq);
+ sda_num, clock_freq);
break;
}
- chan->sda_pin = sda_pin;
+ chan->sda_num = sda_num;
chan->i2c = i2c;
adap = &i2c->chans[i].adap;
adap->owner = THIS_MODULE;
@@ -445,14 +445,14 @@ static int rtl9300_i2c_probe(struct platform_device *pdev)
adap->dev.parent = dev;
i2c_set_adapdata(adap, chan);
adap->dev.of_node = to_of_node(child);
- snprintf(adap->name, sizeof(adap->name), "%s SDA%d\n", dev_name(dev), sda_pin);
+ snprintf(adap->name, sizeof(adap->name), "%s SDA%d\n", dev_name(dev), sda_num);
i++;
ret = devm_i2c_add_adapter(dev, adap);
if (ret)
return ret;
}
- i2c->sda_pin = 0xff;
+ i2c->sda_num = 0xff;
return 0;
}
--
2.48.1
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v6 07/12] i2c: rtl9300: move setting SCL frequency to config_io
2025-08-24 11:33 [PATCH v6 00/12] i2c: fix, rework and extend RTL9300 I2C driver Jonas Jelonek
` (5 preceding siblings ...)
2025-08-24 11:33 ` [PATCH v6 06/12] i2c: rtl9300: rename internal sda_pin to sda_num Jonas Jelonek
@ 2025-08-24 11:33 ` Jonas Jelonek
2025-08-24 11:33 ` [PATCH v6 08/12] i2c: rtl9300: do not set read mode on every transfer Jonas Jelonek
` (6 subsequent siblings)
13 siblings, 0 replies; 17+ messages in thread
From: Jonas Jelonek @ 2025-08-24 11:33 UTC (permalink / raw)
To: Chris Packham, Andi Shyti, Rob Herring, Krzysztof Kozlowski
Cc: linux-i2c, Conor Dooley, devicetree, linux-kernel,
Markus Stockhausen, Sven Eckelmann, Harshal Gohel, Wolfram Sang,
Jonas Jelonek
Move the register operation to set the SCL frequency to the
rtl9300_i2c_config_io function instead of the rtl9300_i2c_config_xfer
function. This rather belongs there next to selecting the current SDA
output line.
Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com>
Tested-by: Sven Eckelmann <sven@narfation.org>
---
drivers/i2c/busses/i2c-rtl9300.c | 17 +++++++++--------
1 file changed, 9 insertions(+), 8 deletions(-)
diff --git a/drivers/i2c/busses/i2c-rtl9300.c b/drivers/i2c/busses/i2c-rtl9300.c
index f9b5ac7670c2..4177cfb77094 100644
--- a/drivers/i2c/busses/i2c-rtl9300.c
+++ b/drivers/i2c/busses/i2c-rtl9300.c
@@ -95,18 +95,23 @@ static int rtl9300_i2c_select_scl(struct rtl9300_i2c *i2c, u8 scl)
return regmap_field_write(i2c->fields[F_SCL_SEL], 1);
}
-static int rtl9300_i2c_config_io(struct rtl9300_i2c *i2c, u8 sda_pin)
+static int rtl9300_i2c_config_io(struct rtl9300_i2c *i2c, struct rtl9300_i2c_chan *chan)
{
struct rtl9300_i2c_drv_data *drv_data;
int ret;
drv_data = (struct rtl9300_i2c_drv_data *)device_get_match_data(i2c->dev);
- ret = regmap_field_update_bits(i2c->fields[F_SDA_SEL], BIT(sda_num), BIT(sda_num));
+ ret = regmap_field_update_bits(i2c->fields[F_SDA_SEL], BIT(chan->sda_num),
+ BIT(chan->sda_num));
if (ret)
return ret;
- ret = regmap_field_write(i2c->fields[F_SDA_OUT_SEL], sda_num);
+ ret = regmap_field_write(i2c->fields[F_SDA_OUT_SEL], chan->sda_num);
+ if (ret)
+ return ret;
+
+ ret = regmap_field_write(i2c->fields[F_SCL_FREQ], chan->bus_freq);
if (ret)
return ret;
@@ -121,10 +126,6 @@ static int rtl9300_i2c_config_xfer(struct rtl9300_i2c *i2c, struct rtl9300_i2c_c
if (len < 1 || len > 16)
return -EINVAL;
- ret = regmap_field_write(i2c->fields[F_SCL_FREQ], chan->bus_freq);
- if (ret)
- return ret;
-
ret = regmap_field_write(i2c->fields[F_DEV_ADDR], addr);
if (ret)
return ret;
@@ -244,7 +245,7 @@ static int rtl9300_i2c_smbus_xfer(struct i2c_adapter *adap, u16 addr, unsigned s
mutex_lock(&i2c->lock);
if (chan->sda_num != i2c->sda_num) {
- ret = rtl9300_i2c_config_io(i2c, chan->sda_pin);
+ ret = rtl9300_i2c_config_io(i2c, chan);
if (ret)
goto out_unlock;
i2c->sda_num = chan->sda_num;
--
2.48.1
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v6 08/12] i2c: rtl9300: do not set read mode on every transfer
2025-08-24 11:33 [PATCH v6 00/12] i2c: fix, rework and extend RTL9300 I2C driver Jonas Jelonek
` (6 preceding siblings ...)
2025-08-24 11:33 ` [PATCH v6 07/12] i2c: rtl9300: move setting SCL frequency to config_io Jonas Jelonek
@ 2025-08-24 11:33 ` Jonas Jelonek
2025-08-24 11:33 ` [PATCH v6 09/12] i2c: rtl9300: separate xfer configuration and execution Jonas Jelonek
` (5 subsequent siblings)
13 siblings, 0 replies; 17+ messages in thread
From: Jonas Jelonek @ 2025-08-24 11:33 UTC (permalink / raw)
To: Chris Packham, Andi Shyti, Rob Herring, Krzysztof Kozlowski
Cc: linux-i2c, Conor Dooley, devicetree, linux-kernel,
Markus Stockhausen, Sven Eckelmann, Harshal Gohel, Wolfram Sang,
Jonas Jelonek
Move the operation to set the read mode from config_xfer to probe.
The I2C controller of RTL9300 and RTL9310 support a legacy message mode
for READs with 'Read Address Data' instead of the standard format 'Write
Address ; Read Data'. There is no way to pass that via smbus_xfer, thus
there is no point in supported this in the driver and moreover no point
in setting this on every transaction. Setting this once in the probe
call is sufficient.
Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com>
Tested-by: Sven Eckelmann <sven@narfation.org>
---
drivers/i2c/busses/i2c-rtl9300.c | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)
diff --git a/drivers/i2c/busses/i2c-rtl9300.c b/drivers/i2c/busses/i2c-rtl9300.c
index 4177cfb77094..9e3517b09b3d 100644
--- a/drivers/i2c/busses/i2c-rtl9300.c
+++ b/drivers/i2c/busses/i2c-rtl9300.c
@@ -130,11 +130,7 @@ static int rtl9300_i2c_config_xfer(struct rtl9300_i2c *i2c, struct rtl9300_i2c_c
if (ret)
return ret;
- ret = regmap_field_write(i2c->fields[F_DATA_WIDTH], (len - 1) & 0xf);
- if (ret)
- return ret;
-
- return regmap_field_write(i2c->fields[F_RD_MODE], 0);
+ return regmap_field_write(i2c->fields[F_DATA_WIDTH], (len - 1) & 0xf);
}
static int rtl9300_i2c_read(struct rtl9300_i2c *i2c, u8 *buf, int len)
@@ -455,6 +451,11 @@ static int rtl9300_i2c_probe(struct platform_device *pdev)
}
i2c->sda_num = 0xff;
+ /* only use standard read format */
+ ret = regmap_field_write(i2c->fields[F_RD_MODE], 0);
+ if (ret)
+ return ret;
+
return 0;
}
--
2.48.1
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v6 09/12] i2c: rtl9300: separate xfer configuration and execution
2025-08-24 11:33 [PATCH v6 00/12] i2c: fix, rework and extend RTL9300 I2C driver Jonas Jelonek
` (7 preceding siblings ...)
2025-08-24 11:33 ` [PATCH v6 08/12] i2c: rtl9300: do not set read mode on every transfer Jonas Jelonek
@ 2025-08-24 11:33 ` Jonas Jelonek
2025-08-24 11:33 ` [PATCH v6 10/12] i2c: rtl9300: use scoped guard instead of explicit lock/unlock Jonas Jelonek
` (4 subsequent siblings)
13 siblings, 0 replies; 17+ messages in thread
From: Jonas Jelonek @ 2025-08-24 11:33 UTC (permalink / raw)
To: Chris Packham, Andi Shyti, Rob Herring, Krzysztof Kozlowski
Cc: linux-i2c, Conor Dooley, devicetree, linux-kernel,
Markus Stockhausen, Sven Eckelmann, Harshal Gohel, Wolfram Sang,
Jonas Jelonek
So far, the rtl9300_i2c_smbus_xfer code is quite a mess with function
calls distributed over the whole function setting different values in
different cases. Calls to rtl9300_i2c_config_xfer and
rtl9300_i2c_reg_addr_set are used in every case-block with varying
values whose meaning is not instantly obvious. In some cases, there are
additional calls within these case-blocks doing more things.
This is in general a bad design and especially really bad for
readability and maintainability because it distributes changes or
issues to multiple locations due to the same function being called with
different hardcoded values in different places.
To have a good structure, setting different parameters based on the
desired operation should not be interleaved with applying these
parameters to the hardware registers. Or in different words, the
parameter site should be mixed with the call site.
Thus, separate configuration and execution of an SMBus xfer within
rtl9300_i2c_smbus_xfer to improve readability and maintainability. Add a
new 'struct rtl9300_i2c_xfer' to carry the required parameters for an
xfer which are configured based on the input parameters within a single
switch-case block, without having any function calls within this block.
The function calls to actually apply these values to the hardware
registers then appear below in a single place and just operate on the
passed instance of 'struct rtl9300_i2c_xfer'. These are
'rtl9300_i2c_prepare_xfer' which combines applying all parameters of the
xfer to the corresponding register, and 'rtl9300_i2c_do_xfer' which
actually executes the xfer and does post-processing if needed.
Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com>
Tested-by: Sven Eckelmann <sven@narfation.org>
---
drivers/i2c/busses/i2c-rtl9300.c | 234 +++++++++++++++----------------
1 file changed, 114 insertions(+), 120 deletions(-)
diff --git a/drivers/i2c/busses/i2c-rtl9300.c b/drivers/i2c/busses/i2c-rtl9300.c
index 9e3517b09b3d..fb3ebbd46a18 100644
--- a/drivers/i2c/busses/i2c-rtl9300.c
+++ b/drivers/i2c/busses/i2c-rtl9300.c
@@ -8,6 +8,7 @@
#include <linux/mutex.h>
#include <linux/platform_device.h>
#include <linux/regmap.h>
+#include <linux/unaligned.h>
enum rtl9300_bus_freq {
RTL9300_I2C_STD_FREQ,
@@ -71,6 +72,22 @@ struct rtl9300_i2c {
struct mutex lock;
};
+enum rtl9300_i2c_xfer_type {
+ RTL9300_I2C_XFER_BYTE,
+ RTL9300_I2C_XFER_WORD,
+ RTL9300_I2C_XFER_BLOCK,
+};
+
+struct rtl9300_i2c_xfer {
+ enum rtl9300_i2c_xfer_type type;
+ u16 dev_addr;
+ u8 reg_addr;
+ u8 reg_addr_len;
+ u8 *data;
+ u8 data_len;
+ bool write;
+};
+
#define RTL9300_I2C_MST_CTRL1 0x0
#define RTL9300_I2C_MST_CTRL2 0x4
#define RTL9300_I2C_MST_DATA_WORD0 0x8
@@ -95,45 +112,37 @@ static int rtl9300_i2c_select_scl(struct rtl9300_i2c *i2c, u8 scl)
return regmap_field_write(i2c->fields[F_SCL_SEL], 1);
}
-static int rtl9300_i2c_config_io(struct rtl9300_i2c *i2c, struct rtl9300_i2c_chan *chan)
+static int rtl9300_i2c_config_chan(struct rtl9300_i2c *i2c, struct rtl9300_i2c_chan *chan)
{
struct rtl9300_i2c_drv_data *drv_data;
int ret;
- drv_data = (struct rtl9300_i2c_drv_data *)device_get_match_data(i2c->dev);
+ if (i2c->sda_num == chan->sda_num)
+ return 0;
- ret = regmap_field_update_bits(i2c->fields[F_SDA_SEL], BIT(chan->sda_num),
- BIT(chan->sda_num));
+ ret = regmap_field_write(i2c->fields[F_SCL_FREQ], chan->bus_freq);
if (ret)
return ret;
- ret = regmap_field_write(i2c->fields[F_SDA_OUT_SEL], chan->sda_num);
+ drv_data = (struct rtl9300_i2c_drv_data *)device_get_match_data(i2c->dev);
+ ret = drv_data->select_scl(i2c, 0);
if (ret)
return ret;
- ret = regmap_field_write(i2c->fields[F_SCL_FREQ], chan->bus_freq);
+ ret = regmap_field_update_bits(i2c->fields[F_SDA_SEL], BIT(chan->sda_num),
+ BIT(chan->sda_num));
if (ret)
return ret;
- return drv_data->select_scl(i2c, 0);
-}
-
-static int rtl9300_i2c_config_xfer(struct rtl9300_i2c *i2c, struct rtl9300_i2c_chan *chan,
- u16 addr, u16 len)
-{
- int ret;
-
- if (len < 1 || len > 16)
- return -EINVAL;
-
- ret = regmap_field_write(i2c->fields[F_DEV_ADDR], addr);
+ ret = regmap_field_write(i2c->fields[F_SDA_OUT_SEL], chan->sda_num);
if (ret)
return ret;
- return regmap_field_write(i2c->fields[F_DATA_WIDTH], (len - 1) & 0xf);
+ i2c->sda_num = chan->sda_num;
+ return 0;
}
-static int rtl9300_i2c_read(struct rtl9300_i2c *i2c, u8 *buf, int len)
+static int rtl9300_i2c_read(struct rtl9300_i2c *i2c, u8 *buf, u8 len)
{
u32 vals[4] = {};
int i, ret;
@@ -153,7 +162,7 @@ static int rtl9300_i2c_read(struct rtl9300_i2c *i2c, u8 *buf, int len)
return 0;
}
-static int rtl9300_i2c_write(struct rtl9300_i2c *i2c, u8 *buf, int len)
+static int rtl9300_i2c_write(struct rtl9300_i2c *i2c, u8 *buf, u8 len)
{
u32 vals[4] = {};
int i;
@@ -176,16 +185,51 @@ static int rtl9300_i2c_writel(struct rtl9300_i2c *i2c, u32 data)
return regmap_write(i2c->regmap, i2c->data_reg, data);
}
-static int rtl9300_i2c_execute_xfer(struct rtl9300_i2c *i2c, char read_write,
- int size, union i2c_smbus_data *data, int len)
+static int rtl9300_i2c_prepare_xfer(struct rtl9300_i2c *i2c, struct rtl9300_i2c_xfer *xfer)
{
- u32 val;
int ret;
- ret = regmap_field_write(i2c->fields[F_RWOP], read_write == I2C_SMBUS_WRITE);
+ if (xfer->data_len < 1 || xfer->data_len > 16)
+ return -EINVAL;
+
+ ret = regmap_field_write(i2c->fields[F_DEV_ADDR], xfer->dev_addr);
+ if (ret)
+ return ret;
+
+ ret = rtl9300_i2c_reg_addr_set(i2c, xfer->reg_addr, xfer->reg_addr_len);
+ if (ret)
+ return ret;
+
+ ret = regmap_field_write(i2c->fields[F_RWOP], xfer->write);
+ if (ret)
+ return ret;
+
+ ret = regmap_field_write(i2c->fields[F_DATA_WIDTH], (xfer->data_len - 1) & 0xf);
if (ret)
return ret;
+ if (xfer->write) {
+ switch (xfer->type) {
+ case RTL9300_I2C_XFER_BYTE:
+ ret = rtl9300_i2c_writel(i2c, *xfer->data);
+ break;
+ case RTL9300_I2C_XFER_WORD:
+ ret = rtl9300_i2c_writel(i2c, get_unaligned((const u16 *)xfer->data));
+ break;
+ default:
+ ret = rtl9300_i2c_write(i2c, xfer->data, xfer->data_len);
+ break;
+ }
+ }
+
+ return ret;
+}
+
+static int rtl9300_i2c_do_xfer(struct rtl9300_i2c *i2c, struct rtl9300_i2c_xfer *xfer)
+{
+ u32 val;
+ int ret;
+
ret = regmap_field_write(i2c->fields[F_I2C_TRIG], 1);
if (ret)
return ret;
@@ -200,28 +244,24 @@ static int rtl9300_i2c_execute_xfer(struct rtl9300_i2c *i2c, char read_write,
if (val)
return -EIO;
- if (read_write == I2C_SMBUS_READ) {
- switch (size) {
- case I2C_SMBUS_BYTE:
- case I2C_SMBUS_BYTE_DATA:
+ if (!xfer->write) {
+ switch (xfer->type) {
+ case RTL9300_I2C_XFER_BYTE:
ret = regmap_read(i2c->regmap, i2c->data_reg, &val);
if (ret)
return ret;
- data->byte = val & 0xff;
+
+ *xfer->data = val & 0xff;
break;
- case I2C_SMBUS_WORD_DATA:
+ case RTL9300_I2C_XFER_WORD:
ret = regmap_read(i2c->regmap, i2c->data_reg, &val);
if (ret)
return ret;
- data->word = val & 0xffff;
- break;
- case I2C_SMBUS_I2C_BLOCK_DATA:
- ret = rtl9300_i2c_read(i2c, &data->block[1], len);
- if (ret)
- return ret;
+
+ put_unaligned(val & 0xffff, (u16*)xfer->data);
break;
default:
- ret = rtl9300_i2c_read(i2c, &data->block[0], len);
+ ret = rtl9300_i2c_read(i2c, xfer->data, xfer->data_len);
if (ret)
return ret;
break;
@@ -237,108 +277,62 @@ static int rtl9300_i2c_smbus_xfer(struct i2c_adapter *adap, u16 addr, unsigned s
{
struct rtl9300_i2c_chan *chan = i2c_get_adapdata(adap);
struct rtl9300_i2c *i2c = chan->i2c;
- int len = 0, ret;
+ struct rtl9300_i2c_xfer xfer = {0};
+ int ret;
+
+ if (addr > 0x7f)
+ return -EINVAL;
mutex_lock(&i2c->lock);
- if (chan->sda_num != i2c->sda_num) {
- ret = rtl9300_i2c_config_io(i2c, chan);
- if (ret)
- goto out_unlock;
- i2c->sda_num = chan->sda_num;
- }
+
+ ret = rtl9300_i2c_config_chan(i2c, chan);
+ if (ret)
+ goto out_unlock;
+
+ xfer.dev_addr = addr & 0x7f;
+ xfer.write = (read_write == I2C_SMBUS_WRITE);
+ xfer.reg_addr = command;
+ xfer.reg_addr_len = 1;
switch (size) {
case I2C_SMBUS_BYTE:
- if (read_write == I2C_SMBUS_WRITE) {
- ret = rtl9300_i2c_config_xfer(i2c, chan, addr, 0);
- if (ret)
- goto out_unlock;
- ret = rtl9300_i2c_reg_addr_set(i2c, command, 1);
- if (ret)
- goto out_unlock;
- } else {
- ret = rtl9300_i2c_config_xfer(i2c, chan, addr, 1);
- if (ret)
- goto out_unlock;
- ret = rtl9300_i2c_reg_addr_set(i2c, 0, 0);
- if (ret)
- goto out_unlock;
- }
+ xfer.data = (read_write == I2C_SMBUS_READ) ? &data->byte : &command;
+ xfer.data_len = 1;
+ xfer.reg_addr = 0;
+ xfer.reg_addr_len = 0;
+ xfer.type = RTL9300_I2C_XFER_BYTE;
break;
-
case I2C_SMBUS_BYTE_DATA:
- ret = rtl9300_i2c_reg_addr_set(i2c, command, 1);
- if (ret)
- goto out_unlock;
- ret = rtl9300_i2c_config_xfer(i2c, chan, addr, 1);
- if (ret)
- goto out_unlock;
- if (read_write == I2C_SMBUS_WRITE) {
- ret = rtl9300_i2c_writel(i2c, data->byte);
- if (ret)
- goto out_unlock;
- }
+ xfer.data = &data->byte;
+ xfer.data_len = 1;
+ xfer.type = RTL9300_I2C_XFER_BYTE;
break;
-
case I2C_SMBUS_WORD_DATA:
- ret = rtl9300_i2c_reg_addr_set(i2c, command, 1);
- if (ret)
- goto out_unlock;
- ret = rtl9300_i2c_config_xfer(i2c, chan, addr, 2);
- if (ret)
- goto out_unlock;
- if (read_write == I2C_SMBUS_WRITE) {
- ret = rtl9300_i2c_writel(i2c, data->word);
- if (ret)
- goto out_unlock;
- }
+ xfer.data = (u8 *)&data->word;
+ xfer.data_len = 2;
+ xfer.type = RTL9300_I2C_XFER_WORD;
break;
-
case I2C_SMBUS_BLOCK_DATA:
- ret = rtl9300_i2c_reg_addr_set(i2c, command, 1);
- if (ret)
- goto out_unlock;
- if (data->block[0] < 1 || data->block[0] > I2C_SMBUS_BLOCK_MAX) {
- ret = -EINVAL;
- goto out_unlock;
- }
- ret = rtl9300_i2c_config_xfer(i2c, chan, addr, data->block[0] + 1);
- if (ret)
- goto out_unlock;
- if (read_write == I2C_SMBUS_WRITE) {
- ret = rtl9300_i2c_write(i2c, &data->block[0], data->block[0] + 1);
- if (ret)
- goto out_unlock;
- }
- len = data->block[0] + 1;
+ xfer.data = &data->block[0];
+ xfer.data_len = data->block[0] + 1;
+ xfer.type = RTL9300_I2C_XFER_BLOCK;
break;
-
case I2C_SMBUS_I2C_BLOCK_DATA:
- ret = rtl9300_i2c_reg_addr_set(i2c, command, 1);
- if (ret)
- goto out_unlock;
- if (data->block[0] < 1 || data->block[0] > I2C_SMBUS_BLOCK_MAX) {
- ret = -EINVAL;
- goto out_unlock;
- }
- ret = rtl9300_i2c_config_xfer(i2c, chan, addr, data->block[0]);
- if (ret)
- goto out_unlock;
- if (read_write == I2C_SMBUS_WRITE) {
- ret = rtl9300_i2c_write(i2c, &data->block[1], data->block[0]);
- if (ret)
- goto out_unlock;
- }
- len = data->block[0];
+ xfer.data = &data->block[1];
+ xfer.data_len = data->block[0];
+ xfer.type = RTL9300_I2C_XFER_BLOCK;
break;
-
default:
dev_err(&adap->dev, "Unsupported transaction %d\n", size);
ret = -EOPNOTSUPP;
goto out_unlock;
}
- ret = rtl9300_i2c_execute_xfer(i2c, read_write, size, data, len);
+ ret = rtl9300_i2c_prepare_xfer(i2c, &xfer);
+ if (ret)
+ goto out_unlock;
+
+ ret = rtl9300_i2c_do_xfer(i2c, &xfer);
out_unlock:
mutex_unlock(&i2c->lock);
--
2.48.1
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v6 10/12] i2c: rtl9300: use scoped guard instead of explicit lock/unlock
2025-08-24 11:33 [PATCH v6 00/12] i2c: fix, rework and extend RTL9300 I2C driver Jonas Jelonek
` (8 preceding siblings ...)
2025-08-24 11:33 ` [PATCH v6 09/12] i2c: rtl9300: separate xfer configuration and execution Jonas Jelonek
@ 2025-08-24 11:33 ` Jonas Jelonek
2025-08-24 14:12 ` Markus Elfring
2025-08-24 11:33 ` [PATCH v6 11/12] dt-bindings: i2c: realtek,rtl9301-i2c: extend for RTL9310 support Jonas Jelonek
` (3 subsequent siblings)
13 siblings, 1 reply; 17+ messages in thread
From: Jonas Jelonek @ 2025-08-24 11:33 UTC (permalink / raw)
To: Chris Packham, Andi Shyti, Rob Herring, Krzysztof Kozlowski
Cc: linux-i2c, Conor Dooley, devicetree, linux-kernel,
Markus Stockhausen, Sven Eckelmann, Harshal Gohel, Wolfram Sang,
Jonas Jelonek
Use the scoped guard infrastructure which unlocks a mutex automatically
when the guard goes out of scope, instead of explicity lock/unlock. This
simplifies the code and control flow in rtl9300_i2c_smbus_xfer and
removes the need of using goto in error cases to unlock before
returning.
Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com>
---
drivers/i2c/busses/i2c-rtl9300.c | 18 +++++++-----------
1 file changed, 7 insertions(+), 11 deletions(-)
diff --git a/drivers/i2c/busses/i2c-rtl9300.c b/drivers/i2c/busses/i2c-rtl9300.c
index fb3ebbd46a18..c67463228604 100644
--- a/drivers/i2c/busses/i2c-rtl9300.c
+++ b/drivers/i2c/busses/i2c-rtl9300.c
@@ -72,6 +72,8 @@ struct rtl9300_i2c {
struct mutex lock;
};
+DEFINE_GUARD(rtl9300_i2c, struct rtl9300_i2c *, mutex_lock(&_T->lock), mutex_unlock(&_T->lock))
+
enum rtl9300_i2c_xfer_type {
RTL9300_I2C_XFER_BYTE,
RTL9300_I2C_XFER_WORD,
@@ -283,11 +285,11 @@ static int rtl9300_i2c_smbus_xfer(struct i2c_adapter *adap, u16 addr, unsigned s
if (addr > 0x7f)
return -EINVAL;
- mutex_lock(&i2c->lock);
+ guard(rtl9300_i2c)(i2c);
ret = rtl9300_i2c_config_chan(i2c, chan);
if (ret)
- goto out_unlock;
+ return ret;
xfer.dev_addr = addr & 0x7f;
xfer.write = (read_write == I2C_SMBUS_WRITE);
@@ -324,20 +326,14 @@ static int rtl9300_i2c_smbus_xfer(struct i2c_adapter *adap, u16 addr, unsigned s
break;
default:
dev_err(&adap->dev, "Unsupported transaction %d\n", size);
- ret = -EOPNOTSUPP;
- goto out_unlock;
+ return -EOPNOTSUPP;
}
ret = rtl9300_i2c_prepare_xfer(i2c, &xfer);
if (ret)
- goto out_unlock;
-
- ret = rtl9300_i2c_do_xfer(i2c, &xfer);
-
-out_unlock:
- mutex_unlock(&i2c->lock);
+ return ret;
- return ret;
+ return rtl9300_i2c_do_xfer(i2c, &xfer);
}
static u32 rtl9300_i2c_func(struct i2c_adapter *a)
--
2.48.1
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v6 11/12] dt-bindings: i2c: realtek,rtl9301-i2c: extend for RTL9310 support
2025-08-24 11:33 [PATCH v6 00/12] i2c: fix, rework and extend RTL9300 I2C driver Jonas Jelonek
` (9 preceding siblings ...)
2025-08-24 11:33 ` [PATCH v6 10/12] i2c: rtl9300: use scoped guard instead of explicit lock/unlock Jonas Jelonek
@ 2025-08-24 11:33 ` Jonas Jelonek
2025-08-24 11:33 ` [PATCH v6 12/12] i2c: rtl9300: add support for RTL9310 I2C controller Jonas Jelonek
` (2 subsequent siblings)
13 siblings, 0 replies; 17+ messages in thread
From: Jonas Jelonek @ 2025-08-24 11:33 UTC (permalink / raw)
To: Chris Packham, Andi Shyti, Rob Herring, Krzysztof Kozlowski
Cc: linux-i2c, Conor Dooley, devicetree, linux-kernel,
Markus Stockhausen, Sven Eckelmann, Harshal Gohel, Wolfram Sang,
Jonas Jelonek
Adjust the regex for child-node address to account for the fact that
RTL9310 supports 12 instead of only 8 SDA lines. Also, narrow this per
variant.
Add a vendor-specific property to explicitly specify the SCL line number
of the defined I2C controller/master. This is required, in particular
for RTL9310, to operate on the correct SCL for each controller. Require
this property to be specified for RTL9310.
Add compatibles for known SoC variants RTL9311, RTL9312 and RTL9313.
Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com>
Reviewed-by: Rob Herring (Arm) <robh@kernel.org>
---
.../bindings/i2c/realtek,rtl9301-i2c.yaml | 39 ++++++++++++++++++-
1 file changed, 37 insertions(+), 2 deletions(-)
diff --git a/Documentation/devicetree/bindings/i2c/realtek,rtl9301-i2c.yaml b/Documentation/devicetree/bindings/i2c/realtek,rtl9301-i2c.yaml
index 274e2ab8b612..17ce39c19ab1 100644
--- a/Documentation/devicetree/bindings/i2c/realtek,rtl9301-i2c.yaml
+++ b/Documentation/devicetree/bindings/i2c/realtek,rtl9301-i2c.yaml
@@ -13,6 +13,8 @@ description:
RTL9300 SoCs have two I2C controllers. Each of these has an SCL line (which
if not-used for SCL can be a GPIO). There are 8 common SDA lines that can be
assigned to either I2C controller.
+ RTL9310 SoCs have equal capabilities but support 12 common SDA lines which
+ can be assigned to either I2C controller.
properties:
compatible:
@@ -23,7 +25,15 @@ properties:
- realtek,rtl9302c-i2c
- realtek,rtl9303-i2c
- const: realtek,rtl9301-i2c
- - const: realtek,rtl9301-i2c
+ - items:
+ - enum:
+ - realtek,rtl9311-i2c
+ - realtek,rtl9312-i2c
+ - realtek,rtl9313-i2c
+ - const: realtek,rtl9310-i2c
+ - enum:
+ - realtek,rtl9301-i2c
+ - realtek,rtl9310-i2c
reg:
items:
@@ -35,8 +45,14 @@ properties:
"#size-cells":
const: 0
+ realtek,scl:
+ $ref: /schemas/types.yaml#/definitions/uint32
+ description:
+ The SCL line number of this I2C controller.
+ enum: [ 0, 1 ]
+
patternProperties:
- '^i2c@[0-7]$':
+ '^i2c@[0-9ab]$':
$ref: /schemas/i2c/i2c-controller.yaml
unevaluatedProperties: false
@@ -48,6 +64,25 @@ patternProperties:
required:
- reg
+
+allOf:
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: realtek,rtl9310-i2c
+ then:
+ required:
+ - realtek,scl
+ - if:
+ properties:
+ compatible:
+ contains:
+ const: realtek,rtl9301-i2c
+ then:
+ patternProperties:
+ '^i2c@[89ab]$': false
+
required:
- compatible
- reg
--
2.48.1
^ permalink raw reply related [flat|nested] 17+ messages in thread
* [PATCH v6 12/12] i2c: rtl9300: add support for RTL9310 I2C controller
2025-08-24 11:33 [PATCH v6 00/12] i2c: fix, rework and extend RTL9300 I2C driver Jonas Jelonek
` (10 preceding siblings ...)
2025-08-24 11:33 ` [PATCH v6 11/12] dt-bindings: i2c: realtek,rtl9301-i2c: extend for RTL9310 support Jonas Jelonek
@ 2025-08-24 11:33 ` Jonas Jelonek
2025-08-24 21:31 ` [PATCH v6 00/12] i2c: fix, rework and extend RTL9300 I2C driver Chris Packham
2025-08-25 19:33 ` AW: " markus.stockhausen
13 siblings, 0 replies; 17+ messages in thread
From: Jonas Jelonek @ 2025-08-24 11:33 UTC (permalink / raw)
To: Chris Packham, Andi Shyti, Rob Herring, Krzysztof Kozlowski
Cc: linux-i2c, Conor Dooley, devicetree, linux-kernel,
Markus Stockhausen, Sven Eckelmann, Harshal Gohel, Wolfram Sang,
Jonas Jelonek
Add support for the internal I2C controllers of RTL9310 series based
SoCs to the driver for RTL9300. Add register definitions, chip-specific
functions and compatible strings for known RTL9310-based SoCs RTL9311,
RTL9312 and RTL9313.
Make use of a new device tree property 'realtek,scl' which needs to be
specified in case both or only the second master is used. This is
required due how the register layout changed in contrast to RTL9300,
which has SCL selection in a global register instead of a
master-specific one.
Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com>
Tested-by: Sven Eckelmann <sven@narfation.org>
---
drivers/i2c/busses/i2c-rtl9300.c | 44 ++++++++++++++++++++++++++++++--
1 file changed, 42 insertions(+), 2 deletions(-)
diff --git a/drivers/i2c/busses/i2c-rtl9300.c b/drivers/i2c/busses/i2c-rtl9300.c
index c67463228604..bfceca24ad7d 100644
--- a/drivers/i2c/busses/i2c-rtl9300.c
+++ b/drivers/i2c/busses/i2c-rtl9300.c
@@ -60,14 +60,16 @@ struct rtl9300_i2c_drv_data {
};
#define RTL9300_I2C_MUX_NCHAN 8
+#define RTL9310_I2C_MUX_NCHAN 12
struct rtl9300_i2c {
struct regmap *regmap;
struct device *dev;
- struct rtl9300_i2c_chan chans[RTL9300_I2C_MUX_NCHAN];
+ struct rtl9300_i2c_chan chans[RTL9310_I2C_MUX_NCHAN];
struct regmap_field *fields[F_NUM_FIELDS];
u32 reg_base;
u32 data_reg;
+ u8 scl_num;
u8 sda_num;
struct mutex lock;
};
@@ -98,6 +100,12 @@ struct rtl9300_i2c_xfer {
#define RTL9300_I2C_MST_DATA_WORD3 0x14
#define RTL9300_I2C_MST_GLB_CTRL 0x384
+#define RTL9310_I2C_MST_IF_CTRL 0x1004
+#define RTL9310_I2C_MST_IF_SEL 0x1008
+#define RTL9310_I2C_MST_CTRL 0x0
+#define RTL9310_I2C_MST_MEMADDR_CTRL 0x4
+#define RTL9310_I2C_MST_DATA_CTRL 0x8
+
static int rtl9300_i2c_reg_addr_set(struct rtl9300_i2c *i2c, u32 reg, u16 len)
{
int ret;
@@ -114,6 +122,11 @@ static int rtl9300_i2c_select_scl(struct rtl9300_i2c *i2c, u8 scl)
return regmap_field_write(i2c->fields[F_SCL_SEL], 1);
}
+static int rtl9310_i2c_select_scl(struct rtl9300_i2c *i2c, u8 scl)
+{
+ return regmap_field_update_bits(i2c->fields[F_SCL_SEL], BIT(scl), BIT(scl));
+}
+
static int rtl9300_i2c_config_chan(struct rtl9300_i2c *i2c, struct rtl9300_i2c_chan *chan)
{
struct rtl9300_i2c_drv_data *drv_data;
@@ -127,7 +140,7 @@ static int rtl9300_i2c_config_chan(struct rtl9300_i2c *i2c, struct rtl9300_i2c_c
return ret;
drv_data = (struct rtl9300_i2c_drv_data *)device_get_match_data(i2c->dev);
- ret = drv_data->select_scl(i2c, 0);
+ ret = drv_data->select_scl(i2c, i2c->scl_num);
if (ret)
return ret;
@@ -379,6 +392,10 @@ static int rtl9300_i2c_probe(struct platform_device *pdev)
if (ret)
return ret;
+ ret = device_property_read_u8(dev, "realtek,scl", &i2c->scl_num);
+ if (ret || i2c->scl_num != 1)
+ i2c->scl_num = 0;
+
platform_set_drvdata(pdev, i2c);
drv_data = (struct rtl9300_i2c_drv_data *)device_get_match_data(i2c->dev);
@@ -474,12 +491,35 @@ static const struct rtl9300_i2c_drv_data rtl9300_i2c_drv_data = {
.max_nchan = RTL9300_I2C_MUX_NCHAN,
};
+static const struct rtl9300_i2c_drv_data rtl9310_i2c_drv_data = {
+ .field_desc = {
+ [F_SCL_SEL] = GLB_REG_FIELD(RTL9310_I2C_MST_IF_SEL, 12, 13),
+ [F_SDA_SEL] = GLB_REG_FIELD(RTL9310_I2C_MST_IF_SEL, 0, 11),
+ [F_SCL_FREQ] = MST_REG_FIELD(RTL9310_I2C_MST_CTRL, 30, 31),
+ [F_DEV_ADDR] = MST_REG_FIELD(RTL9310_I2C_MST_CTRL, 11, 17),
+ [F_SDA_OUT_SEL] = MST_REG_FIELD(RTL9310_I2C_MST_CTRL, 18, 21),
+ [F_MEM_ADDR_WIDTH] = MST_REG_FIELD(RTL9310_I2C_MST_CTRL, 9, 10),
+ [F_DATA_WIDTH] = MST_REG_FIELD(RTL9310_I2C_MST_CTRL, 5, 8),
+ [F_RD_MODE] = MST_REG_FIELD(RTL9310_I2C_MST_CTRL, 4, 4),
+ [F_RWOP] = MST_REG_FIELD(RTL9310_I2C_MST_CTRL, 2, 2),
+ [F_I2C_FAIL] = MST_REG_FIELD(RTL9310_I2C_MST_CTRL, 1, 1),
+ [F_I2C_TRIG] = MST_REG_FIELD(RTL9310_I2C_MST_CTRL, 0, 0),
+ [F_MEM_ADDR] = MST_REG_FIELD(RTL9310_I2C_MST_MEMADDR_CTRL, 0, 23),
+ },
+ .select_scl = rtl9310_i2c_select_scl,
+ .data_reg = RTL9310_I2C_MST_DATA_CTRL,
+ .max_nchan = RTL9310_I2C_MUX_NCHAN,
+};
static const struct of_device_id i2c_rtl9300_dt_ids[] = {
{ .compatible = "realtek,rtl9301-i2c", .data = (void *) &rtl9300_i2c_drv_data },
{ .compatible = "realtek,rtl9302b-i2c", .data = (void *) &rtl9300_i2c_drv_data },
{ .compatible = "realtek,rtl9302c-i2c", .data = (void *) &rtl9300_i2c_drv_data },
{ .compatible = "realtek,rtl9303-i2c", .data = (void *) &rtl9300_i2c_drv_data },
+ { .compatible = "realtek,rtl9310-i2c", .data = (void *) &rtl9310_i2c_drv_data },
+ { .compatible = "realtek,rtl9311-i2c", .data = (void *) &rtl9310_i2c_drv_data },
+ { .compatible = "realtek,rtl9312-i2c", .data = (void *) &rtl9310_i2c_drv_data },
+ { .compatible = "realtek,rtl9313-i2c", .data = (void *) &rtl9310_i2c_drv_data },
{}
};
MODULE_DEVICE_TABLE(of, i2c_rtl9300_dt_ids);
--
2.48.1
^ permalink raw reply related [flat|nested] 17+ messages in thread
* Re: [PATCH v6 10/12] i2c: rtl9300: use scoped guard instead of explicit lock/unlock
2025-08-24 11:33 ` [PATCH v6 10/12] i2c: rtl9300: use scoped guard instead of explicit lock/unlock Jonas Jelonek
@ 2025-08-24 14:12 ` Markus Elfring
2025-08-24 17:44 ` Jonas Jelonek
0 siblings, 1 reply; 17+ messages in thread
From: Markus Elfring @ 2025-08-24 14:12 UTC (permalink / raw)
To: Jonas Jelonek, linux-i2c, devicetree
Cc: LKML, Andi Shyti, Chris Packham, Conor Dooley, Harshal Gohel,
Krzysztof Kozlowski, Markus Stockhausen, Rob Herring,
Sven Eckelmann, Wolfram Sang
…
> when the guard goes out of scope, instead of explicity lock/unlock. This
…
explicit? Would such a word be nicer in the next line?
Regards,
Markus
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v6 10/12] i2c: rtl9300: use scoped guard instead of explicit lock/unlock
2025-08-24 14:12 ` Markus Elfring
@ 2025-08-24 17:44 ` Jonas Jelonek
0 siblings, 0 replies; 17+ messages in thread
From: Jonas Jelonek @ 2025-08-24 17:44 UTC (permalink / raw)
To: Markus Elfring, linux-i2c, devicetree
Cc: LKML, Andi Shyti, Chris Packham, Conor Dooley, Harshal Gohel,
Krzysztof Kozlowski, Markus Stockhausen, Rob Herring,
Sven Eckelmann, Wolfram Sang
On 24.08.25 16:12, Markus Elfring wrote:
> …
>> when the guard goes out of scope, instead of explicity lock/unlock. This
> …
>
> explicit? Would such a word be nicer in the next line?
I think I wanted to write 'explicitly' but not sure which variant is better.
Having it in the next line would probably be better, I'll fix this.
> Regards,
> Markus
Regards,
Jonas
^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH v6 00/12] i2c: fix, rework and extend RTL9300 I2C driver
2025-08-24 11:33 [PATCH v6 00/12] i2c: fix, rework and extend RTL9300 I2C driver Jonas Jelonek
` (11 preceding siblings ...)
2025-08-24 11:33 ` [PATCH v6 12/12] i2c: rtl9300: add support for RTL9310 I2C controller Jonas Jelonek
@ 2025-08-24 21:31 ` Chris Packham
2025-08-25 19:33 ` AW: " markus.stockhausen
13 siblings, 0 replies; 17+ messages in thread
From: Chris Packham @ 2025-08-24 21:31 UTC (permalink / raw)
To: Jonas Jelonek, Andi Shyti, Rob Herring, Krzysztof Kozlowski
Cc: linux-i2c@vger.kernel.org, Conor Dooley,
devicetree@vger.kernel.org, linux-kernel@vger.kernel.org,
Markus Stockhausen, Sven Eckelmann, Harshal Gohel, Wolfram Sang
Hi Jonas,
On 24/08/2025 23:33, Jonas Jelonek wrote:
> This patch series for the RTL9300 I2C driver:
> - fixes issues, one of them in some cases causing data corruption
> - reworks significant parts of the current implementation
> - add support for the (quite similar) RTL9310 series
>
> Goal of this is to fix critical issues, improve overall code quality and
> simplify maintainance and further extension of the driver. Moreover, it
> should be brought on par feature-wise with OpenWrt's downstream driver
> to be able to drop the downstream version.
>
> The first three patches address bugs in the current implementation, on
> of them being critical and causing data corruption under certain
> circumstances. Although the hardware doesn't support SMBus Quick Write,
> the driver claims to support it with a broken implementation. This
> causes to execute a 16-byte Write instead of a Quick Write, e.g. causing
> corruption on not-write-protected SFP EEPROMs and soft-bricking them.
> These three patches are also sent to 'stable' because they fix critical
> issues.
>
> Subsequent patches introduce various smaller and bigger enhancements.
> These include:
> - use regmap_field + its API instead of macros + GENMASK + shifts
> - refactor xfer handling
> - variable renaming to avoid confusion
> - move some register operations, calling them somewhere else and
> less frequently
> - use guarded mutex instead of explicit mutex_lock/_unlock to
> simplify control flow
>
> Finally, the last two patches add support for RTL9310 (mango) series to
> the driver and adjust the dt-bindings accordingly.
>
> Simple operations have been tested successfully on:
> - Zyxel XGS1210-12 (RTL9302B)
> - TP-Link TL-ST1008F v2.0 (RTL9303)
> - Netgear MS510TXM (RTL9313)
>
> with Byte-Read, Word-Read and I2C-Block-Read. Other operations need
> testing from people with devices available.
>
> Compile-tested with Linux, run-tested as backport in OpenWrt on the
> aforementioned devices.
For the series
Reviewed-by: Chris Packham <chris.packham@alliedtelesis.co.nz>
Tested-by: Chris Packham <chris.packham@alliedtelesis.co.nz> # On
RTL9302C based board
> --
> Changelog
>
> v6: - patch 'i2c: rtl9300: check if xfer length is valid'
> - renamed to 'ensure data length is within supported range'
> - added I2C quirk for zero length as suggested by Wolfram Sang
> - reordered patches to have backport-worthy fixes first and
> enhancements/others after
> - patches 'fix channel number bound check', 'check if xfer
> length is valid' and 'remove SMBus Quick operation support'
> were moved before all others
> - added CC: stable to first three patches
> - fixed commit message of 'dt-bindings: i2c: realtek,rtl9301-i2c:
> extend for RTL9310 support'
> - added a patch to use guard(mutex) instead of explicit lock/unlock
> as suggested by Markus Elfring
> - added Reviewed-by: Rob Herring ... to dt-bindings patches
> - added Tested-by: Sven Eckelmann ... to all patches (except the
> new patch in this version)
>
> v5: - added more patches to fix further issues/do further cleanup
> - remove SMBus Quick support (not supported by hardware)
> - move setting SCL frequency to config_io
> - only set read message format (RD_MODE) once on probing
> - add check to avoid len = 0 being allowed as length
> - adjusted cover letter
>
> v4: - fixed an incorrect check for number of channels which was already
> present in original code
>
> v3: - narrowed vendor property per variant to be required only
> for RTL9310
> - narrowed usable child-node i2c addresses per variant
> - no changes to driver patches
>
> v2: - Patch 1:
> - adjusted commit message
> - retained Tested-By and Reviewed-By from Chris Packham
> - Patch 2:
> - simplified check as suggested by Markus Stockhausen
> - fixed commit message
> - Patch 3 (all requested by Krzysztof):
> - use vendor property instead of generic
> - add front compatibles to make binding complete
> - fix commit message
> - reordered patches, dt-bindings patch now comes before its 'user'
> - properly add device-tree list and relevant maintainers to To/Cc
>
> ---
> Jonas Jelonek (12):
> i2c: rtl9300: fix channel number bound check
> i2c: rtl9300: ensure data length is within supported range
> i2c: rtl9300: remove broken SMBus Quick operation support
> i2c: rtl9300: use regmap fields and API for registers
> dt-bindings: i2c: realtek,rtl9301-i2c: fix wording and typos
> i2c: rtl9300: rename internal sda_pin to sda_num
> i2c: rtl9300: move setting SCL frequency to config_io
> i2c: rtl9300: do not set read mode on every transfer
> i2c: rtl9300: separate xfer configuration and execution
> i2c: rtl9300: use scoped guard instead of explicit lock/unlock
> dt-bindings: i2c: realtek,rtl9301-i2c: extend for RTL9310 support
> i2c: rtl9300: add support for RTL9310 I2C controller
>
> .../bindings/i2c/realtek,rtl9301-i2c.yaml | 45 +-
> drivers/i2c/busses/i2c-rtl9300.c | 488 ++++++++++--------
> 2 files changed, 325 insertions(+), 208 deletions(-)
>
>
> base-commit: 57774430864b721082b9bafd17fc839f31251c7b
^ permalink raw reply [flat|nested] 17+ messages in thread
* AW: [PATCH v6 00/12] i2c: fix, rework and extend RTL9300 I2C driver
2025-08-24 11:33 [PATCH v6 00/12] i2c: fix, rework and extend RTL9300 I2C driver Jonas Jelonek
` (12 preceding siblings ...)
2025-08-24 21:31 ` [PATCH v6 00/12] i2c: fix, rework and extend RTL9300 I2C driver Chris Packham
@ 2025-08-25 19:33 ` markus.stockhausen
13 siblings, 0 replies; 17+ messages in thread
From: markus.stockhausen @ 2025-08-25 19:33 UTC (permalink / raw)
To: 'Jonas Jelonek', 'Chris Packham',
'Andi Shyti'
Cc: linux-i2c, linux-kernel, 'Sven Eckelmann',
'Harshal Gohel', 'Wolfram Sang'
> Von: Jonas Jelonek <jelonek.jonas@gmail.com>
> Gesendet: Sonntag, 24. August 2025 13:34
> Betreff: [PATCH v6 00/12] i2c: fix, rework and extend RTL9300 I2C driver
>
> This patch series for the RTL9300 I2C driver:
> - fixes issues, one of them in some cases causing data corruption
> - reworks significant parts of the current implementation
> - add support for the (quite similar) RTL9310 series
>
> Goal of this is to fix critical issues, improve overall code quality and
> simplify maintainance and further extension of the driver. Moreover, it
> should be brought on par feature-wise with OpenWrt's downstream driver
> to be able to drop the downstream version.
>
> The first three patches address bugs in the current implementation, on
> of them being critical and causing data corruption under certain
> circumstances. Although the hardware doesn't support SMBus Quick Write,
> the driver claims to support it with a broken implementation. This
> causes to execute a 16-byte Write instead of a Quick Write, e.g. causing
> corruption on not-write-protected SFP EEPROMs and soft-bricking them.
> These three patches are also sent to 'stable' because they fix critical
> issues.
>
> Subsequent patches introduce various smaller and bigger enhancements.
> These include:
> - use regmap_field + its API instead of macros + GENMASK + shifts
> - refactor xfer handling
> - variable renaming to avoid confusion
> - move some register operations, calling them somewhere else and
> less frequently
> - use guarded mutex instead of explicit mutex_lock/_unlock to
> simplify control flow
>
> Finally, the last two patches add support for RTL9310 (mango) series to
> the driver and adjust the dt-bindings accordingly.
>
> Simple operations have been tested successfully on:
> - Zyxel XGS1210-12 (RTL9302B)
> - TP-Link TL-ST1008F v2.0 (RTL9303)
> - Netgear MS510TXM (RTL9313)
>
> with Byte-Read, Word-Read and I2C-Block-Read. Other operations need
> testing from people with devices available.
>
> Compile-tested with Linux, run-tested as backport in OpenWrt on the
> aforementioned devices.
Repeated the downstream tests with this series backported to 6.12.
Successfully tested on Linksys LGS328C (RTL9301) and LGS352C (RTL9311)
Tested-by: Markus Stockhausen <markus.stockhausen@gmx.de
^ permalink raw reply [flat|nested] 17+ messages in thread
end of thread, other threads:[~2025-08-25 19:33 UTC | newest]
Thread overview: 17+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-08-24 11:33 [PATCH v6 00/12] i2c: fix, rework and extend RTL9300 I2C driver Jonas Jelonek
2025-08-24 11:33 ` [PATCH v6 01/12] i2c: rtl9300: fix channel number bound check Jonas Jelonek
2025-08-24 11:33 ` [PATCH v6 02/12] i2c: rtl9300: ensure data length is within supported range Jonas Jelonek
2025-08-24 11:33 ` [PATCH v6 03/12] i2c: rtl9300: remove broken SMBus Quick operation support Jonas Jelonek
2025-08-24 11:33 ` [PATCH v6 04/12] i2c: rtl9300: use regmap fields and API for registers Jonas Jelonek
2025-08-24 11:33 ` [PATCH v6 05/12] dt-bindings: i2c: realtek,rtl9301-i2c: fix wording and typos Jonas Jelonek
2025-08-24 11:33 ` [PATCH v6 06/12] i2c: rtl9300: rename internal sda_pin to sda_num Jonas Jelonek
2025-08-24 11:33 ` [PATCH v6 07/12] i2c: rtl9300: move setting SCL frequency to config_io Jonas Jelonek
2025-08-24 11:33 ` [PATCH v6 08/12] i2c: rtl9300: do not set read mode on every transfer Jonas Jelonek
2025-08-24 11:33 ` [PATCH v6 09/12] i2c: rtl9300: separate xfer configuration and execution Jonas Jelonek
2025-08-24 11:33 ` [PATCH v6 10/12] i2c: rtl9300: use scoped guard instead of explicit lock/unlock Jonas Jelonek
2025-08-24 14:12 ` Markus Elfring
2025-08-24 17:44 ` Jonas Jelonek
2025-08-24 11:33 ` [PATCH v6 11/12] dt-bindings: i2c: realtek,rtl9301-i2c: extend for RTL9310 support Jonas Jelonek
2025-08-24 11:33 ` [PATCH v6 12/12] i2c: rtl9300: add support for RTL9310 I2C controller Jonas Jelonek
2025-08-24 21:31 ` [PATCH v6 00/12] i2c: fix, rework and extend RTL9300 I2C driver Chris Packham
2025-08-25 19:33 ` AW: " markus.stockhausen
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).