diff --git a/target/linux/realtek/patches-6.18/741-net-sfp-apply-I2C-adapter-quirks-to-limit-blocks.patch b/target/linux/realtek/patches-6.18/741-net-sfp-apply-I2C-adapter-quirks-to-limit-blocks.patch new file mode 100644 index 0000000000..5452551d8f --- /dev/null +++ b/target/linux/realtek/patches-6.18/741-net-sfp-apply-I2C-adapter-quirks-to-limit-blocks.patch @@ -0,0 +1,59 @@ +From 572304faf8c1d30f142060c76a3e731438fc5975 Mon Sep 17 00:00:00 2001 +From: Jonas Jelonek +Date: Thu, 22 Jan 2026 10:59:20 +0000 +Subject: [PATCH net-next v6 1/2] net: sfp: apply I2C adapter quirks to limit + block size + +The SFP driver assumes all I2C adapters support reading and writing the +pre-defined block size SFP_EEPROM_BLOCK_SIZE of 16 bytes. This constant +was probably chosen based on good guesses and known limitations of a +range of I2C adapters and SFP modules. + +However, I2C adapters may even support less and usually need to specify +this via I2C quirks. Theoretically, such an adapter may provide full +functionality but only support a read and write length of e.g. 8 bytes. +Currently, the SFP driver doesn't account for that. + +Add handling for I2C quirks in SFP I2C configuration taking the fields +max_read_len and max_write_len in struct i2c_adapter_quirks into account +to further limit the maximum block size if needed. + +Signed-off-by: Jonas Jelonek +--- + drivers/net/phy/sfp.c | 12 ++++++++++-- + 1 file changed, 10 insertions(+), 2 deletions(-) + +--- a/drivers/net/phy/sfp.c ++++ b/drivers/net/phy/sfp.c +@@ -785,21 +785,29 @@ static int sfp_smbus_byte_write(struct s + + static int sfp_i2c_configure(struct sfp *sfp, struct i2c_adapter *i2c) + { ++ size_t max_block_size; ++ + sfp->i2c = i2c; + + if (i2c_check_functionality(i2c, I2C_FUNC_I2C)) { + sfp->read = sfp_i2c_read; + sfp->write = sfp_i2c_write; +- sfp->i2c_max_block_size = SFP_EEPROM_BLOCK_SIZE; ++ max_block_size = SFP_EEPROM_BLOCK_SIZE; + } else if (i2c_check_functionality(i2c, I2C_FUNC_SMBUS_BYTE_DATA)) { + sfp->read = sfp_smbus_byte_read; + sfp->write = sfp_smbus_byte_write; +- sfp->i2c_max_block_size = 1; ++ max_block_size = 1; + } else { + sfp->i2c = NULL; + return -EINVAL; + } + ++ if (i2c->quirks && i2c->quirks->max_read_len) ++ max_block_size = min(max_block_size, i2c->quirks->max_read_len); ++ if (i2c->quirks && i2c->quirks->max_write_len) ++ max_block_size = min(max_block_size, i2c->quirks->max_write_len); ++ ++ sfp->i2c_max_block_size = max_block_size; + return 0; + } + diff --git a/target/linux/realtek/patches-6.18/742-net-sfp-extend-SMBus-support.patch b/target/linux/realtek/patches-6.18/742-net-sfp-extend-SMBus-support.patch new file mode 100644 index 0000000000..6391f56909 --- /dev/null +++ b/target/linux/realtek/patches-6.18/742-net-sfp-extend-SMBus-support.patch @@ -0,0 +1,204 @@ +From 475b0dd976de6db975bfef7d6fdb978d2d64c535 Mon Sep 17 00:00:00 2001 +From: Jonas Jelonek +Date: Fri, 9 Jan 2026 21:04:02 +0000 +Subject: [PATCH net-next v6 2/2] net: sfp: extend SMBus support + +Commit 7662abf4db94 ("net: phy: sfp: Add support for SMBus module access") +added support for SMBus-only controllers for module access. However, this +is restricted to single-byte accesses and has the implication that hwmon +is disabled (due to missing atomicity of 16-bit accesses) and warnings +are printed. + +There are probably a lot of SMBus-only I2C controllers out in the wild +which support more than just byte access. And it also seems that in +several devices, SFP slots are attached to these SMBus controllers +instead of full-featured I2C controllers. Right now, they don't work +with SFP modules. This applies - amongst others - to I2C/SMBus-only +controllers in Realtek longan and mango SoCs. They also support word +access and I2C block reads. + +Extend the current read/write SMBus operations to support SMBus I2C +block and SMBus word access. To avoid having dedicated operations for +each kind of transfer, provide generic read and write operations that +covers all kinds of access depending on whats supported. + +For block access, this requires I2C_FUNC_SMBUS_I2C_BLOCK to be +supported as it relies on reading a pre-defined amount of bytes. +This isn't intended by the official SMBus Block Read but supported by +several I2C controllers/drivers. + +Signed-off-by: Jonas Jelonek +--- + drivers/net/phy/sfp.c | 117 +++++++++++++++++++++++++++++++++--------- + 1 file changed, 92 insertions(+), 25 deletions(-) + +--- a/drivers/net/phy/sfp.c ++++ b/drivers/net/phy/sfp.c +@@ -14,6 +14,7 @@ + #include + #include + #include ++#include + #include + + #include "sfp.h" +@@ -734,50 +735,101 @@ static int sfp_i2c_write(struct sfp *sfp + return ret == ARRAY_SIZE(msgs) ? len : 0; + } + +-static int sfp_smbus_byte_read(struct sfp *sfp, bool a2, u8 dev_addr, +- void *buf, size_t len) ++static int sfp_smbus_read(struct sfp *sfp, bool a2, u8 dev_addr, void *buf, ++ size_t len) + { + union i2c_smbus_data smbus_data; + u8 bus_addr = a2 ? 0x51 : 0x50; ++ size_t this_len, transferred; ++ u32 functionality; + u8 *data = buf; + int ret; + ++ functionality = i2c_get_functionality(sfp->i2c); ++ + while (len) { +- ret = i2c_smbus_xfer(sfp->i2c, bus_addr, 0, +- I2C_SMBUS_READ, dev_addr, +- I2C_SMBUS_BYTE_DATA, &smbus_data); ++ this_len = min(len, sfp->i2c_max_block_size); ++ ++ if (functionality & I2C_FUNC_SMBUS_READ_I2C_BLOCK) { ++ smbus_data.block[0] = this_len; ++ ret = i2c_smbus_xfer(sfp->i2c, bus_addr, 0, ++ I2C_SMBUS_READ, dev_addr, ++ I2C_SMBUS_I2C_BLOCK_DATA, &smbus_data); ++ ++ memcpy(data, &smbus_data.block[1], this_len); ++ transferred = this_len; ++ } else if (this_len >= 2 && ++ functionality & I2C_FUNC_SMBUS_READ_WORD_DATA) { ++ ret = i2c_smbus_xfer(sfp->i2c, bus_addr, 0, ++ I2C_SMBUS_READ, dev_addr, ++ I2C_SMBUS_WORD_DATA, &smbus_data); ++ ++ put_unaligned_le16(smbus_data.word, data); ++ transferred = 2; ++ } else { ++ ret = i2c_smbus_xfer(sfp->i2c, bus_addr, 0, ++ I2C_SMBUS_READ, dev_addr, ++ I2C_SMBUS_BYTE_DATA, &smbus_data); ++ ++ *data = smbus_data.byte; ++ transferred = 1; ++ } ++ + if (ret < 0) + return ret; + +- *data = smbus_data.byte; +- +- len--; +- data++; +- dev_addr++; ++ data += transferred; ++ len -= transferred; ++ dev_addr += transferred; + } + + return data - (u8 *)buf; + } + +-static int sfp_smbus_byte_write(struct sfp *sfp, bool a2, u8 dev_addr, +- void *buf, size_t len) ++static int sfp_smbus_write(struct sfp *sfp, bool a2, u8 dev_addr, void *buf, ++ size_t len) + { + union i2c_smbus_data smbus_data; + u8 bus_addr = a2 ? 0x51 : 0x50; ++ size_t this_len, transferred; ++ u32 functionality; + u8 *data = buf; + int ret; + ++ functionality = i2c_get_functionality(sfp->i2c); ++ + while (len) { +- smbus_data.byte = *data; +- ret = i2c_smbus_xfer(sfp->i2c, bus_addr, 0, +- I2C_SMBUS_WRITE, dev_addr, +- I2C_SMBUS_BYTE_DATA, &smbus_data); +- if (ret) ++ this_len = min(len, sfp->i2c_max_block_size); ++ ++ if (functionality & I2C_FUNC_SMBUS_WRITE_I2C_BLOCK) { ++ smbus_data.block[0] = this_len; ++ memcpy(&smbus_data.block[1], data, this_len); ++ ++ ret = i2c_smbus_xfer(sfp->i2c, bus_addr, 0, ++ I2C_SMBUS_WRITE, dev_addr, ++ I2C_SMBUS_I2C_BLOCK_DATA, &smbus_data); ++ transferred = this_len; ++ } else if (this_len >= 2 && ++ functionality & I2C_FUNC_SMBUS_WRITE_WORD_DATA) { ++ smbus_data.word = get_unaligned_le16(data); ++ ret = i2c_smbus_xfer(sfp->i2c, bus_addr, 0, ++ I2C_SMBUS_WRITE, dev_addr, ++ I2C_SMBUS_WORD_DATA, &smbus_data); ++ transferred = 2; ++ } else { ++ smbus_data.byte = *data; ++ ret = i2c_smbus_xfer(sfp->i2c, bus_addr, 0, ++ I2C_SMBUS_WRITE, dev_addr, ++ I2C_SMBUS_BYTE_DATA, &smbus_data); ++ transferred = 1; ++ } ++ ++ if (ret < 0) + return ret; + +- len--; +- data++; +- dev_addr++; ++ data += transferred; ++ len -= transferred; ++ dev_addr += transferred; + } + + return data - (u8 *)buf; +@@ -786,17 +838,32 @@ static int sfp_smbus_byte_write(struct s + static int sfp_i2c_configure(struct sfp *sfp, struct i2c_adapter *i2c) + { + size_t max_block_size; ++ u32 functionality; + + sfp->i2c = i2c; ++ functionality = i2c_get_functionality(i2c); + +- if (i2c_check_functionality(i2c, I2C_FUNC_I2C)) { ++ if (functionality & I2C_FUNC_I2C) { + sfp->read = sfp_i2c_read; + sfp->write = sfp_i2c_write; + max_block_size = SFP_EEPROM_BLOCK_SIZE; +- } else if (i2c_check_functionality(i2c, I2C_FUNC_SMBUS_BYTE_DATA)) { +- sfp->read = sfp_smbus_byte_read; +- sfp->write = sfp_smbus_byte_write; +- max_block_size = 1; ++ } else if ((functionality & I2C_FUNC_SMBUS_BYTE_DATA) == I2C_FUNC_SMBUS_BYTE_DATA || ++ (functionality & I2C_FUNC_SMBUS_I2C_BLOCK) == I2C_FUNC_SMBUS_I2C_BLOCK) { ++ sfp->read = sfp_smbus_read; ++ sfp->write = sfp_smbus_write; ++ ++ if ((functionality & I2C_FUNC_SMBUS_I2C_BLOCK) == I2C_FUNC_SMBUS_I2C_BLOCK) ++ max_block_size = SFP_EEPROM_BLOCK_SIZE; ++ else if ((functionality & I2C_FUNC_SMBUS_WORD_DATA) == I2C_FUNC_SMBUS_WORD_DATA) ++ max_block_size = 2; ++ else ++ max_block_size = 1; ++ } else if ((functionality & I2C_FUNC_SMBUS_WORD_DATA) == I2C_FUNC_SMBUS_WORD_DATA) { ++ WARN(1, "SMBus adapter only supports word access, odd reads/writes will fail!\n"); ++ ++ sfp->read = sfp_smbus_read; ++ sfp->write = sfp_smbus_write; ++ max_block_size = 2; + } else { + sfp->i2c = NULL; + return -EINVAL;