From d3c2191d493a9a017ea816f3b38dfbcadb6cccb5 Mon Sep 17 00:00:00 2001 From: Harshal Gohel Date: Sat, 27 Sep 2025 11:52:17 +0200 Subject: i2c: rtl9300: Implement I2C block read and write It was noticed that the original implementation of SMBus Block Write in the driver was actually an I2C Block Write. Both differ only in the Count byte before the actual data: S Addr Wr [A] Comm [A] Count [A] Data [A] Data [A] ... [A] Data [A] P The I2C Block Write is just skipping this Count byte and starts directly with the data: S Addr Wr [A] Comm [A] Data [A] Data [A] ... [A] Data [A] P The I2C controller of RTL93xx doesn't handle this Count byte special and it is simply another one of (16 possible) data bytes. Adding support for the I2C Block Write therefore only requires skipping the count byte (0) in data->block. It is similar for reads. The SMBUS Block read is having a Count byte before the data: S Addr Wr [A] Comm [A] Sr Addr Rd [A] [Count] A [Data] A [Data] A ... A [Data] NA P And the I2C Block Read is directly starting with the actual data: S Addr Wr [A] Comm [A] Sr Addr Rd [A] [Data] A [Data] A ... A [Data] NA P The I2C controller is also not handling this byte in a special way. It simply provides every byte after the Rd marker + Ack as part of the 16 byte receive buffer (registers). The content of this buffer just has to be copied to the right position in the receive data->block. Signed-off-by: Harshal Gohel Co-developed-by: Sven Eckelmann Signed-off-by: Sven Eckelmann Reviewed-by: Chris Packham Tested-by: Chris Packham Reviewed-by: Jonas Jelonek Tested-by: Jonas Jelonek Signed-off-by: Andi Shyti Link: https://lore.kernel.org/all/20250927-i2c-rtl9300-multi-byte-v7-2-c0fd0e78b818@narfation.org --- drivers/i2c/busses/i2c-rtl9300.c | 38 ++++++++++++++++++++++++++++++++++---- 1 file changed, 34 insertions(+), 4 deletions(-) diff --git a/drivers/i2c/busses/i2c-rtl9300.c b/drivers/i2c/busses/i2c-rtl9300.c index af991b28e4f8..9e6232075137 100644 --- a/drivers/i2c/busses/i2c-rtl9300.c +++ b/drivers/i2c/busses/i2c-rtl9300.c @@ -186,22 +186,32 @@ static int rtl9300_i2c_execute_xfer(struct rtl9300_i2c *i2c, char read_write, return -EIO; if (read_write == I2C_SMBUS_READ) { - if (size == I2C_SMBUS_BYTE || size == I2C_SMBUS_BYTE_DATA) { + switch (size) { + case I2C_SMBUS_BYTE: + case I2C_SMBUS_BYTE_DATA: ret = regmap_read(i2c->regmap, i2c->reg_base + RTL9300_I2C_MST_DATA_WORD0, &val); if (ret) return ret; data->byte = val & 0xff; - } else if (size == I2C_SMBUS_WORD_DATA) { + break; + case I2C_SMBUS_WORD_DATA: ret = regmap_read(i2c->regmap, i2c->reg_base + RTL9300_I2C_MST_DATA_WORD0, &val); if (ret) return ret; data->word = val & 0xffff; - } else { + break; + case I2C_SMBUS_I2C_BLOCK_DATA: + ret = rtl9300_i2c_read(i2c, &data->block[1], len); + if (ret) + return ret; + break; + default: ret = rtl9300_i2c_read(i2c, &data->block[0], len); if (ret) return ret; + break; } } @@ -290,6 +300,25 @@ static int rtl9300_i2c_smbus_xfer(struct i2c_adapter *adap, u16 addr, unsigned s len = data->block[0] + 1; break; + case I2C_SMBUS_I2C_BLOCK_DATA: + ret = rtl9300_i2c_reg_addr_set(i2c, command, 1); + if (ret) + goto out_unlock; + if (data->block[0] < 1 || data->block[0] > I2C_SMBUS_BLOCK_MAX) { + ret = -EINVAL; + goto out_unlock; + } + ret = rtl9300_i2c_config_xfer(i2c, chan, addr, data->block[0]); + if (ret) + goto out_unlock; + if (read_write == I2C_SMBUS_WRITE) { + ret = rtl9300_i2c_write(i2c, &data->block[1], data->block[0]); + if (ret) + goto out_unlock; + } + len = data->block[0]; + break; + default: dev_err(&adap->dev, "Unsupported transaction %d\n", size); ret = -EOPNOTSUPP; @@ -307,7 +336,8 @@ out_unlock: static u32 rtl9300_i2c_func(struct i2c_adapter *a) { return I2C_FUNC_SMBUS_BYTE | I2C_FUNC_SMBUS_BYTE_DATA | - I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_BLOCK_DATA; + I2C_FUNC_SMBUS_WORD_DATA | I2C_FUNC_SMBUS_BLOCK_DATA | + I2C_FUNC_SMBUS_I2C_BLOCK; } static const struct i2c_algorithm rtl9300_i2c_algo = { -- cgit v1.2.3 From 85f1c01ce2f98a4925d2610d9d3f4c2749eb9331 Mon Sep 17 00:00:00 2001 From: Jonas Jelonek Date: Sat, 27 Sep 2025 10:19:23 +0000 Subject: i2c: rtl9300: use regmap fields and API for registers Adapt the RTL9300 I2C controller driver to use more of the regmap API, especially make use of reg_field and regmap_field instead of macros to represent registers. Most register operations are performed through regmap_field_* API then. Handle SCL selection using separate chip-specific functions since this is already known to differ between the Realtek SoC families in such a way that this cannot be properly handled using just a different reg_field. This makes it easier to add support for newer generations or to handle differences between specific revisions within a series. Just by defining a separate driver data structure with the corresponding register field definitions and linking it to a new compatible. Signed-off-by: Jonas Jelonek Tested-by: Sven Eckelmann Reviewed-by: Chris Packham Tested-by: Chris Packham # On RTL9302C based board Tested-by: Markus Stockhausen Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20250927101931.71575-2-jelonek.jonas@gmail.com --- 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); -- cgit v1.2.3 From 0395a5e8fd07645df6743fd2a6883708196d9d75 Mon Sep 17 00:00:00 2001 From: Jonas Jelonek Date: Sat, 27 Sep 2025 10:19:24 +0000 Subject: dt-bindings: i2c: realtek,rtl9301-i2c: fix wording and typos Fix wording of binding description to use plural because there is not only a single RTL9300 SoC. RTL9300 describes a whole family of Realtek SoCs. Add missing word 'of' in description of reg property. Change 'SDA pin' to 'SDA line number' because the property must contain the SDA (channel) number ranging from 0-7 instead of a real pin number. Signed-off-by: Jonas Jelonek Reviewed-by: Rob Herring (Arm) Reviewed-by: Chris Packham Tested-by: Chris Packham # On RTL9302C based board Tested-by: Markus Stockhausen Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20250927101931.71575-3-jelonek.jonas@gmail.com --- Documentation/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 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: -- cgit v1.2.3 From c840492ad7487dcb58b0f5f249793e7350114076 Mon Sep 17 00:00:00 2001 From: Jonas Jelonek Date: Sat, 27 Sep 2025 10:19:25 +0000 Subject: i2c: rtl9300: rename internal sda_pin to sda_num Rename the internally used 'sda_pin' to 'sda_num' to make it clear that this is NOT the actual pin number of the GPIO pin but rather the logical SDA channel number. Although the alternate function SDA_Y is sometimes given with the GPIO number, this is not always the case. Thus, avoid any confusion or misconfiguration by giving the variable the correct name. This follows the description change in the devicetree bindings. Signed-off-by: Jonas Jelonek Tested-by: Sven Eckelmann Reviewed-by: Chris Packham Tested-by: Chris Packham # On RTL9302C based board Tested-by: Markus Stockhausen Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20250927101931.71575-4-jelonek.jonas@gmail.com --- 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; } -- cgit v1.2.3 From d1cef7afc3c79260d4d1a86f7a814de2a1bf3fe4 Mon Sep 17 00:00:00 2001 From: Jonas Jelonek Date: Sat, 27 Sep 2025 10:19:26 +0000 Subject: i2c: rtl9300: move setting SCL frequency to config_io Move the register operation to set the SCL frequency to the rtl9300_i2c_config_io function instead of the rtl9300_i2c_config_xfer function. This rather belongs there next to selecting the current SDA output line. Signed-off-by: Jonas Jelonek Tested-by: Sven Eckelmann Reviewed-by: Chris Packham Tested-by: Chris Packham # On RTL9302C based board Tested-by: Markus Stockhausen Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20250927101931.71575-5-jelonek.jonas@gmail.com --- 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; -- cgit v1.2.3 From d5b4fd6ed8ea3eb5dc072285a5e4c0ee32e829b2 Mon Sep 17 00:00:00 2001 From: Jonas Jelonek Date: Sat, 27 Sep 2025 10:19:27 +0000 Subject: i2c: rtl9300: do not set read mode on every transfer Move the operation to set the read mode from config_xfer to probe. The I2C controller of RTL9300 and RTL9310 support a legacy message mode for READs with 'Read Address Data' instead of the standard format 'Write Address ; Read Data'. There is no way to pass that via smbus_xfer, thus there is no point in supported this in the driver and moreover no point in setting this on every transaction. Setting this once in the probe call is sufficient. Signed-off-by: Jonas Jelonek Tested-by: Sven Eckelmann Reviewed-by: Chris Packham Tested-by: Chris Packham # On RTL9302C based board Tested-by: Markus Stockhausen Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20250927101931.71575-6-jelonek.jonas@gmail.com --- 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; } -- cgit v1.2.3 From 059374aa0ab11a758a9e2219e90361911397b03d Mon Sep 17 00:00:00 2001 From: Jonas Jelonek Date: Sat, 27 Sep 2025 10:19:28 +0000 Subject: i2c: rtl9300: separate xfer configuration and execution So far, the rtl9300_i2c_smbus_xfer code is quite a mess with function calls distributed over the whole function setting different values in different cases. Calls to rtl9300_i2c_config_xfer and rtl9300_i2c_reg_addr_set are used in every case-block with varying values whose meaning is not instantly obvious. In some cases, there are additional calls within these case-blocks doing more things. This is in general a bad design and especially really bad for readability and maintainability because it distributes changes or issues to multiple locations due to the same function being called with different hardcoded values in different places. To have a good structure, setting different parameters based on the desired operation should not be interleaved with applying these parameters to the hardware registers. Or in different words, the parameter site should be mixed with the call site. Thus, separate configuration and execution of an SMBus xfer within rtl9300_i2c_smbus_xfer to improve readability and maintainability. Add a new 'struct rtl9300_i2c_xfer' to carry the required parameters for an xfer which are configured based on the input parameters within a single switch-case block, without having any function calls within this block. The function calls to actually apply these values to the hardware registers then appear below in a single place and just operate on the passed instance of 'struct rtl9300_i2c_xfer'. These are 'rtl9300_i2c_prepare_xfer' which combines applying all parameters of the xfer to the corresponding register, and 'rtl9300_i2c_do_xfer' which actually executes the xfer and does post-processing if needed. Signed-off-by: Jonas Jelonek Tested-by: Sven Eckelmann Reviewed-by: Chris Packham Tested-by: Chris Packham # On RTL9302C based board Tested-by: Markus Stockhausen Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20250927101931.71575-7-jelonek.jonas@gmail.com --- 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 #include #include +#include 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); -- cgit v1.2.3 From 46fe8265685cb6003a0989b53e11e8afa4826572 Mon Sep 17 00:00:00 2001 From: Jonas Jelonek Date: Sat, 27 Sep 2025 10:19:29 +0000 Subject: i2c: rtl9300: use scoped guard instead of explicit lock/unlock Use the scoped guard infrastructure which unlocks a mutex automatically when the guard goes out of scope, instead of explicit lock and unlock. This simplifies the code and control flow in rtl9300_i2c_smbus_xfer and removes the need of using goto in error cases to unlock before returning. Signed-off-by: Jonas Jelonek Reviewed-by: Chris Packham Tested-by: Chris Packham # On RTL9302C based board Tested-by: Markus Stockhausen Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20250927101931.71575-8-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) -- cgit v1.2.3 From 99fd09e01db2c6fc4c6ffe851b337f64ff93e1b5 Mon Sep 17 00:00:00 2001 From: Jonas Jelonek Date: Sat, 27 Sep 2025 10:19:30 +0000 Subject: dt-bindings: i2c: realtek,rtl9301-i2c: extend for RTL9310 support Adjust the regex for child-node address to account for the fact that RTL9310 supports 12 instead of only 8 SDA lines. Also, narrow this per variant. Add a vendor-specific property to explicitly specify the SCL line number of the defined I2C controller/master. This is required, in particular for RTL9310, to operate on the correct SCL for each controller. Require this property to be specified for RTL9310. Add compatibles for known SoC variants RTL9311, RTL9312 and RTL9313. Signed-off-by: Jonas Jelonek Reviewed-by: Rob Herring (Arm) Reviewed-by: Chris Packham Tested-by: Markus Stockhausen Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20250927101931.71575-9-jelonek.jonas@gmail.com --- .../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 -- cgit v1.2.3 From 1e33137d47105a807262aa17e028374463876f85 Mon Sep 17 00:00:00 2001 From: Jonas Jelonek Date: Sat, 27 Sep 2025 10:19:31 +0000 Subject: i2c: rtl9300: add support for RTL9310 I2C controller Add support for the internal I2C controllers of RTL9310 series based SoCs to the driver for RTL9300. Add register definitions, chip-specific functions and compatible strings for known RTL9310-based SoCs RTL9311, RTL9312 and RTL9313. Make use of a new device tree property 'realtek,scl' which needs to be specified in case both or only the second master is used. This is required due how the register layout changed in contrast to RTL9300, which has SCL selection in a global register instead of a master-specific one. Signed-off-by: Jonas Jelonek Tested-by: Sven Eckelmann Reviewed-by: Chris Packham Tested-by: Markus Stockhausen Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20250927101931.71575-10-jelonek.jonas@gmail.com --- drivers/i2c/busses/i2c-rtl9300.c | 47 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 44 insertions(+), 3 deletions(-) diff --git a/drivers/i2c/busses/i2c-rtl9300.c b/drivers/i2c/busses/i2c-rtl9300.c index c67463228604..4723e48cfe18 100644 --- a/drivers/i2c/busses/i2c-rtl9300.c +++ b/drivers/i2c/busses/i2c-rtl9300.c @@ -60,14 +60,16 @@ struct rtl9300_i2c_drv_data { }; #define RTL9300_I2C_MUX_NCHAN 8 +#define RTL9310_I2C_MUX_NCHAN 12 struct rtl9300_i2c { struct regmap *regmap; struct device *dev; - struct rtl9300_i2c_chan chans[RTL9300_I2C_MUX_NCHAN]; + struct rtl9300_i2c_chan chans[RTL9310_I2C_MUX_NCHAN]; struct regmap_field *fields[F_NUM_FIELDS]; u32 reg_base; u32 data_reg; + u8 scl_num; u8 sda_num; struct mutex lock; }; @@ -98,6 +100,12 @@ struct rtl9300_i2c_xfer { #define RTL9300_I2C_MST_DATA_WORD3 0x14 #define RTL9300_I2C_MST_GLB_CTRL 0x384 +#define RTL9310_I2C_MST_IF_CTRL 0x1004 +#define RTL9310_I2C_MST_IF_SEL 0x1008 +#define RTL9310_I2C_MST_CTRL 0x0 +#define RTL9310_I2C_MST_MEMADDR_CTRL 0x4 +#define RTL9310_I2C_MST_DATA_CTRL 0x8 + static int rtl9300_i2c_reg_addr_set(struct rtl9300_i2c *i2c, u32 reg, u16 len) { int ret; @@ -114,6 +122,11 @@ static int rtl9300_i2c_select_scl(struct rtl9300_i2c *i2c, u8 scl) return regmap_field_write(i2c->fields[F_SCL_SEL], 1); } +static int rtl9310_i2c_select_scl(struct rtl9300_i2c *i2c, u8 scl) +{ + return regmap_field_update_bits(i2c->fields[F_SCL_SEL], BIT(scl), BIT(scl)); +} + static int rtl9300_i2c_config_chan(struct rtl9300_i2c *i2c, struct rtl9300_i2c_chan *chan) { struct rtl9300_i2c_drv_data *drv_data; @@ -127,7 +140,7 @@ static int rtl9300_i2c_config_chan(struct rtl9300_i2c *i2c, struct rtl9300_i2c_c return ret; drv_data = (struct rtl9300_i2c_drv_data *)device_get_match_data(i2c->dev); - ret = drv_data->select_scl(i2c, 0); + ret = drv_data->select_scl(i2c, i2c->scl_num); if (ret) return ret; @@ -361,7 +374,7 @@ static int rtl9300_i2c_probe(struct platform_device *pdev) struct fwnode_handle *child; struct rtl9300_i2c_drv_data *drv_data; struct reg_field fields[F_NUM_FIELDS]; - u32 clock_freq, sda_num; + u32 clock_freq, scl_num, sda_num; int ret, i = 0; i2c = devm_kzalloc(dev, sizeof(*i2c), GFP_KERNEL); @@ -379,6 +392,11 @@ static int rtl9300_i2c_probe(struct platform_device *pdev) if (ret) return ret; + ret = device_property_read_u32(dev, "realtek,scl", &scl_num); + if (ret || scl_num != 1) + scl_num = 0; + i2c->scl_num = (u8)scl_num; + platform_set_drvdata(pdev, i2c); drv_data = (struct rtl9300_i2c_drv_data *)device_get_match_data(i2c->dev); @@ -474,12 +492,35 @@ static const struct rtl9300_i2c_drv_data rtl9300_i2c_drv_data = { .max_nchan = RTL9300_I2C_MUX_NCHAN, }; +static const struct rtl9300_i2c_drv_data rtl9310_i2c_drv_data = { + .field_desc = { + [F_SCL_SEL] = GLB_REG_FIELD(RTL9310_I2C_MST_IF_SEL, 12, 13), + [F_SDA_SEL] = GLB_REG_FIELD(RTL9310_I2C_MST_IF_SEL, 0, 11), + [F_SCL_FREQ] = MST_REG_FIELD(RTL9310_I2C_MST_CTRL, 30, 31), + [F_DEV_ADDR] = MST_REG_FIELD(RTL9310_I2C_MST_CTRL, 11, 17), + [F_SDA_OUT_SEL] = MST_REG_FIELD(RTL9310_I2C_MST_CTRL, 18, 21), + [F_MEM_ADDR_WIDTH] = MST_REG_FIELD(RTL9310_I2C_MST_CTRL, 9, 10), + [F_DATA_WIDTH] = MST_REG_FIELD(RTL9310_I2C_MST_CTRL, 5, 8), + [F_RD_MODE] = MST_REG_FIELD(RTL9310_I2C_MST_CTRL, 4, 4), + [F_RWOP] = MST_REG_FIELD(RTL9310_I2C_MST_CTRL, 2, 2), + [F_I2C_FAIL] = MST_REG_FIELD(RTL9310_I2C_MST_CTRL, 1, 1), + [F_I2C_TRIG] = MST_REG_FIELD(RTL9310_I2C_MST_CTRL, 0, 0), + [F_MEM_ADDR] = MST_REG_FIELD(RTL9310_I2C_MST_MEMADDR_CTRL, 0, 23), + }, + .select_scl = rtl9310_i2c_select_scl, + .data_reg = RTL9310_I2C_MST_DATA_CTRL, + .max_nchan = RTL9310_I2C_MUX_NCHAN, +}; static const struct of_device_id i2c_rtl9300_dt_ids[] = { { .compatible = "realtek,rtl9301-i2c", .data = (void *) &rtl9300_i2c_drv_data }, { .compatible = "realtek,rtl9302b-i2c", .data = (void *) &rtl9300_i2c_drv_data }, { .compatible = "realtek,rtl9302c-i2c", .data = (void *) &rtl9300_i2c_drv_data }, { .compatible = "realtek,rtl9303-i2c", .data = (void *) &rtl9300_i2c_drv_data }, + { .compatible = "realtek,rtl9310-i2c", .data = (void *) &rtl9310_i2c_drv_data }, + { .compatible = "realtek,rtl9311-i2c", .data = (void *) &rtl9310_i2c_drv_data }, + { .compatible = "realtek,rtl9312-i2c", .data = (void *) &rtl9310_i2c_drv_data }, + { .compatible = "realtek,rtl9313-i2c", .data = (void *) &rtl9310_i2c_drv_data }, {} }; MODULE_DEVICE_TABLE(of, i2c_rtl9300_dt_ids); -- cgit v1.2.3 From b71a6e2a1b710ea31c363a72c75f510ef35d8e69 Mon Sep 17 00:00:00 2001 From: Byungchul Park Date: Thu, 2 Oct 2025 17:12:35 +0900 Subject: i2c: rename wait_for_completion callback to wait_for_completion_cb Functionally no change. Remove the ambiguity of 'wait_for_completion'. It helps development of the DEPT dependency tracker, but seems favorable in any case. Signed-off-by: Byungchul Park [wsa: reworded commit message] Signed-off-by: Wolfram Sang --- drivers/i2c/algos/i2c-algo-pca.c | 2 +- drivers/i2c/busses/i2c-pca-isa.c | 2 +- drivers/i2c/busses/i2c-pca-platform.c | 2 +- include/linux/i2c-algo-pca.h | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/i2c/algos/i2c-algo-pca.c b/drivers/i2c/algos/i2c-algo-pca.c index 74b66aec33d4..ee86df4cff4b 100644 --- a/drivers/i2c/algos/i2c-algo-pca.c +++ b/drivers/i2c/algos/i2c-algo-pca.c @@ -30,7 +30,7 @@ static int i2c_debug; #define pca_clock(adap) adap->i2c_clock #define pca_set_con(adap, val) pca_outw(adap, I2C_PCA_CON, val) #define pca_get_con(adap) pca_inw(adap, I2C_PCA_CON) -#define pca_wait(adap) adap->wait_for_completion(adap->data) +#define pca_wait(adap) adap->wait_for_completion_cb(adap->data) static void pca_reset(struct i2c_algo_pca_data *adap) { diff --git a/drivers/i2c/busses/i2c-pca-isa.c b/drivers/i2c/busses/i2c-pca-isa.c index 85e8cf58e8bf..0cbf2f509527 100644 --- a/drivers/i2c/busses/i2c-pca-isa.c +++ b/drivers/i2c/busses/i2c-pca-isa.c @@ -95,7 +95,7 @@ static struct i2c_algo_pca_data pca_isa_data = { /* .data intentionally left NULL, not needed with ISA */ .write_byte = pca_isa_writebyte, .read_byte = pca_isa_readbyte, - .wait_for_completion = pca_isa_waitforcompletion, + .wait_for_completion_cb = pca_isa_waitforcompletion, .reset_chip = pca_isa_resetchip, }; diff --git a/drivers/i2c/busses/i2c-pca-platform.c b/drivers/i2c/busses/i2c-pca-platform.c index 87da8241b927..c0f35ebbe37d 100644 --- a/drivers/i2c/busses/i2c-pca-platform.c +++ b/drivers/i2c/busses/i2c-pca-platform.c @@ -180,7 +180,7 @@ static int i2c_pca_pf_probe(struct platform_device *pdev) } i2c->algo_data.data = i2c; - i2c->algo_data.wait_for_completion = i2c_pca_pf_waitforcompletion; + i2c->algo_data.wait_for_completion_cb = i2c_pca_pf_waitforcompletion; if (i2c->gpio) i2c->algo_data.reset_chip = i2c_pca_pf_resetchip; else diff --git a/include/linux/i2c-algo-pca.h b/include/linux/i2c-algo-pca.h index 7c522fdd9ea7..e305bf32e40a 100644 --- a/include/linux/i2c-algo-pca.h +++ b/include/linux/i2c-algo-pca.h @@ -71,7 +71,7 @@ struct i2c_algo_pca_data { void *data; /* private low level data */ void (*write_byte) (void *data, int reg, int val); int (*read_byte) (void *data, int reg); - int (*wait_for_completion) (void *data); + int (*wait_for_completion_cb) (void *data); void (*reset_chip) (void *data); /* For PCA9564, use one of the predefined frequencies: * 330000, 288000, 217000, 146000, 88000, 59000, 44000, 36000 -- cgit v1.2.3 From d51e7cfca3fe5540466322c33d665674530148dd Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Thu, 25 Sep 2025 23:05:18 +0200 Subject: i2c: mt65xx: convert set_speed function to void cppcheck rightfully reports: drivers/i2c/busses/i2c-mt65xx.c:1464:6: warning: Condition 'ret' is always false [knownConditionTrueFalse] Make the function void and simplify the code. Signed-off-by: Wolfram Sang Reviewed-by: AngeloGioacchino Del Regno --- drivers/i2c/busses/i2c-mt65xx.c | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/drivers/i2c/busses/i2c-mt65xx.c b/drivers/i2c/busses/i2c-mt65xx.c index dee40704825c..aefdbee1f03c 100644 --- a/drivers/i2c/busses/i2c-mt65xx.c +++ b/drivers/i2c/busses/i2c-mt65xx.c @@ -868,7 +868,7 @@ static int mtk_i2c_calculate_speed(struct mtk_i2c *i2c, unsigned int clk_src, return 0; } -static int mtk_i2c_set_speed(struct mtk_i2c *i2c, unsigned int parent_clk) +static void mtk_i2c_set_speed(struct mtk_i2c *i2c, unsigned int parent_clk) { unsigned int clk_src; unsigned int step_cnt; @@ -938,9 +938,6 @@ static int mtk_i2c_set_speed(struct mtk_i2c *i2c, unsigned int parent_clk) break; } - - - return 0; } static void i2c_dump_register(struct mtk_i2c *i2c) @@ -1460,11 +1457,7 @@ static int mtk_i2c_probe(struct platform_device *pdev) strscpy(i2c->adap.name, I2C_DRV_NAME, sizeof(i2c->adap.name)); - ret = mtk_i2c_set_speed(i2c, clk_get_rate(i2c->clocks[speed_clk].clk)); - if (ret) { - dev_err(&pdev->dev, "Failed to set the speed.\n"); - return -EINVAL; - } + mtk_i2c_set_speed(i2c, clk_get_rate(i2c->clocks[speed_clk].clk)); if (i2c->dev_comp->max_dma_support > 32) { ret = dma_set_mask(&pdev->dev, -- cgit v1.2.3 From eb4faf6343889fcd7edba3deeae49fc5a06531fd Mon Sep 17 00:00:00 2001 From: Kael D'Alcamo Date: Wed, 8 Oct 2025 22:04:27 +0200 Subject: dt-bindings: i2c: hisilicon,hix5hd2: convert to DT schema Convert the Devicetree binding documentation for hisilicon,hix5hd2-i2c from plain text to DT binding schema. Signed-off-by: Kael D'Alcamo Reviewed-by: Krzysztof Kozlowski Signed-off-by: Wolfram Sang --- .../bindings/i2c/hisilicon,hix5hd2-i2c.yaml | 51 ++++++++++++++++++++++ .../devicetree/bindings/i2c/i2c-hix5hd2.txt | 24 ---------- 2 files changed, 51 insertions(+), 24 deletions(-) create mode 100644 Documentation/devicetree/bindings/i2c/hisilicon,hix5hd2-i2c.yaml delete mode 100644 Documentation/devicetree/bindings/i2c/i2c-hix5hd2.txt diff --git a/Documentation/devicetree/bindings/i2c/hisilicon,hix5hd2-i2c.yaml b/Documentation/devicetree/bindings/i2c/hisilicon,hix5hd2-i2c.yaml new file mode 100644 index 000000000000..3faa7954e411 --- /dev/null +++ b/Documentation/devicetree/bindings/i2c/hisilicon,hix5hd2-i2c.yaml @@ -0,0 +1,51 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/i2c/hisilicon,hix5hd2-i2c.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# +title: I2C for HiSilicon hix5hd2 chipset platform + +maintainers: + - Wei Yan + +allOf: + - $ref: /schemas/i2c/i2c-controller.yaml# + +properties: + compatible: + enum: + - hisilicon,hix5hd2-i2c + + reg: + maxItems: 1 + + interrupts: + maxItems: 1 + + clocks: + maxItems: 1 + + clock-frequency: + description: Desired I2C bus frequency in Hz + default: 100000 + +required: + - compatible + - reg + - interrupts + - clocks + +unevaluatedProperties: false + +examples: + - | + #include + + i2c@f8b10000 { + compatible = "hisilicon,hix5hd2-i2c"; + reg = <0xf8b10000 0x1000>; + interrupts = <0 38 4>; + clocks = <&clock HIX5HD2_I2C0_RST>; + #address-cells = <1>; + #size-cells = <0>; + }; diff --git a/Documentation/devicetree/bindings/i2c/i2c-hix5hd2.txt b/Documentation/devicetree/bindings/i2c/i2c-hix5hd2.txt deleted file mode 100644 index f98b37401e6e..000000000000 --- a/Documentation/devicetree/bindings/i2c/i2c-hix5hd2.txt +++ /dev/null @@ -1,24 +0,0 @@ -I2C for Hisilicon hix5hd2 chipset platform - -Required properties: -- compatible: Must be "hisilicon,hix5hd2-i2c" -- reg: physical base address of the controller and length of memory mapped - region. -- interrupts: interrupt number to the cpu. -- #address-cells = <1>; -- #size-cells = <0>; -- clocks: phandles to input clocks. - -Optional properties: -- clock-frequency: Desired I2C bus frequency in Hz, otherwise defaults to 100000 -- Child nodes conforming to i2c bus binding - -Examples: -I2C0@f8b10000 { - compatible = "hisilicon,hix5hd2-i2c"; - reg = <0xf8b10000 0x1000>; - interrupts = <0 38 4>; - clocks = <&clock HIX5HD2_I2C0_RST>; - #address-cells = <1>; - #size-cells = <0>; -} -- cgit v1.2.3