* [PATCH] net: sfp: add SMBus I2C block support
@ 2025-12-28 21:33 Jonas Jelonek
2026-01-04 16:05 ` Jakub Kicinski
0 siblings, 1 reply; 3+ messages in thread
From: Jonas Jelonek @ 2025-12-28 21:33 UTC (permalink / raw)
To: Russell King, Andrew Lunn, Heiner Kallweit, David S . Miller,
Eric Dumazet, Jakub Kicinski, Paolo Abeni
Cc: netdev, linux-kernel, Bjørn Mork, Jonas Jelonek
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 block reads. Right now, they don't work with SFP modules.
This applies - amongst others - to I2C/SMBus-only controllers in Realtek
longan and mango SoCs.
Downstream in OpenWrt, a patch similar to the abovementioned patch is
used for current LTS kernel 6.12. However, this uses byte-access for all
kinds of access and thus disregards the atomicity for wider access.
Introduce read/write SMBus I2C block operations to support SMBus-only
controllers with appropriate support for block read/write. Those
operations are used for all accesses if supported, otherwise the
single-byte operations will be used. With block reads, atomicity for
16-bit reads as required by hwmon is preserved and thus, hwmon can be
used.
The implementation requires the 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.
Support for word access is not implemented due to issues regarding
endianness.
Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com>
---
drivers/net/phy/sfp.c | 77 +++++++++++++++++++++++++++++++++++++++++--
1 file changed, 75 insertions(+), 2 deletions(-)
diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c
index 6166e9196364..c5019fb682a3 100644
--- a/drivers/net/phy/sfp.c
+++ b/drivers/net/phy/sfp.c
@@ -744,6 +744,35 @@ static int sfp_smbus_byte_read(struct sfp *sfp, bool a2, u8 dev_addr,
return data - (u8 *)buf;
}
+static int sfp_smbus_block_read(struct sfp *sfp, bool a2, u8 dev_addr,
+ void *buf, size_t len)
+{
+ size_t block_size = sfp->i2c_block_size;
+ union i2c_smbus_data smbus_data;
+ u8 bus_addr = a2 ? 0x51 : 0x50;
+ u8 *data = buf;
+ u8 this_len;
+ int ret;
+
+ while (len) {
+ this_len = min(len, block_size);
+
+ 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);
+ if (ret < 0)
+ return ret;
+
+ memcpy(data, &smbus_data.block[1], this_len);
+ len -= this_len;
+ data += this_len;
+ dev_addr += this_len;
+ }
+
+ return data - (u8 *)buf;
+}
+
static int sfp_smbus_byte_write(struct sfp *sfp, bool a2, u8 dev_addr,
void *buf, size_t len)
{
@@ -768,23 +797,67 @@ static int sfp_smbus_byte_write(struct sfp *sfp, bool a2, u8 dev_addr,
return 0;
}
+static int sfp_smbus_block_write(struct sfp *sfp, bool a2, u8 dev_addr,
+ void *buf, size_t len)
+{
+ size_t block_size = sfp->i2c_block_size;
+ union i2c_smbus_data smbus_data;
+ u8 bus_addr = a2 ? 0x51 : 0x50;
+ u8 *data = buf;
+ u8 this_len;
+ int ret;
+
+ while (len) {
+ this_len = min(len, block_size);
+
+ 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);
+ if (ret)
+ return ret;
+
+ len -= this_len;
+ data += this_len;
+ dev_addr += this_len;
+ }
+
+ return 0;
+}
+
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_I2C_BLOCK)) {
+ sfp->read = sfp_smbus_block_read;
+ sfp->write = sfp_smbus_block_write;
+
+ max_block_size = SFP_EEPROM_BLOCK_SIZE;
+ 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);
+
} 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;
}
+ sfp->i2c_max_block_size = max_block_size;
return 0;
}
base-commit: 3387a7ad478b46970ae8254049167d166e398aeb
--
2.48.1
^ permalink raw reply related [flat|nested] 3+ messages in thread* Re: [PATCH] net: sfp: add SMBus I2C block support
2025-12-28 21:33 [PATCH] net: sfp: add SMBus I2C block support Jonas Jelonek
@ 2026-01-04 16:05 ` Jakub Kicinski
2026-01-05 8:29 ` Maxime Chevallier
0 siblings, 1 reply; 3+ messages in thread
From: Jakub Kicinski @ 2026-01-04 16:05 UTC (permalink / raw)
To: Jonas Jelonek
Cc: Russell King, Andrew Lunn, Heiner Kallweit, David S . Miller,
Eric Dumazet, Paolo Abeni, netdev, linux-kernel, Bjørn Mork
On Sun, 28 Dec 2025 21:33:31 +0000 Jonas Jelonek wrote:
> +static int sfp_smbus_block_write(struct sfp *sfp, bool a2, u8 dev_addr,
> + void *buf, size_t len)
> +{
> + size_t block_size = sfp->i2c_block_size;
> + union i2c_smbus_data smbus_data;
> + u8 bus_addr = a2 ? 0x51 : 0x50;
> + u8 *data = buf;
> + u8 this_len;
> + int ret;
> +
> + while (len) {
> + this_len = min(len, block_size);
> +
> + 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);
> + if (ret)
> + return ret;
> +
> + len -= this_len;
> + data += this_len;
> + dev_addr += this_len;
> + }
> +
> + return 0;
> +}
AI code review says:
Should this return the number of bytes written instead of 0?
The existing sfp_i2c_write() returns the byte count on success, and several
callers depend on this return value:
sfp_cotsworks_fixup_check() checks:
err = sfp_write(sfp, false, SFP_PHYS_ID, &id->base, 3);
if (err != 3) { ... error path ... }
sfp_sm_mod_hpower() via sfp_modify_u8() checks:
if (err != sizeof(u8)) { ... error path ... }
With this function returning 0 on success, these checks will always fail,
causing high-power SFP modules to fail initialization with "failed to enable
high power" errors, and Cotsworks module EEPROM fixups to fail with "Failed
to rewrite module EEPROM" errors.
Either way, you'll need to repost, net-next was closed when you posted.
--
pw-bot: cr
^ permalink raw reply [flat|nested] 3+ messages in thread* Re: [PATCH] net: sfp: add SMBus I2C block support
2026-01-04 16:05 ` Jakub Kicinski
@ 2026-01-05 8:29 ` Maxime Chevallier
0 siblings, 0 replies; 3+ messages in thread
From: Maxime Chevallier @ 2026-01-05 8:29 UTC (permalink / raw)
To: Jakub Kicinski, Jonas Jelonek
Cc: Russell King, Andrew Lunn, Heiner Kallweit, David S . Miller,
Eric Dumazet, Paolo Abeni, netdev, linux-kernel, Bjørn Mork
Hi folks,
On 04/01/2026 17:05, Jakub Kicinski wrote:
> On Sun, 28 Dec 2025 21:33:31 +0000 Jonas Jelonek wrote:
>> +static int sfp_smbus_block_write(struct sfp *sfp, bool a2, u8 dev_addr,
>> + void *buf, size_t len)
>> +{
>> + size_t block_size = sfp->i2c_block_size;
>> + union i2c_smbus_data smbus_data;
>> + u8 bus_addr = a2 ? 0x51 : 0x50;
>> + u8 *data = buf;
>> + u8 this_len;
>> + int ret;
>> +
>> + while (len) {
>> + this_len = min(len, block_size);
>> +
>> + 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);
>> + if (ret)
>> + return ret;
>> +
>> + len -= this_len;
>> + data += this_len;
>> + dev_addr += this_len;
>> + }
>> +
>> + return 0;
>> +}
>
> AI code review says:
>
> Should this return the number of bytes written instead of 0?
>
> The existing sfp_i2c_write() returns the byte count on success, and several
> callers depend on this return value:
>
> sfp_cotsworks_fixup_check() checks:
> err = sfp_write(sfp, false, SFP_PHYS_ID, &id->base, 3);
> if (err != 3) { ... error path ... }
>
> sfp_sm_mod_hpower() via sfp_modify_u8() checks:
> if (err != sizeof(u8)) { ... error path ... }
>
> With this function returning 0 on success, these checks will always fail,
> causing high-power SFP modules to fail initialization with "failed to enable
> high power" errors, and Cotsworks module EEPROM fixups to fail with "Failed
> to rewrite module EEPROM" errors.
>
> Either way, you'll need to repost, net-next was closed when you posted.
Looks like I made the same mistake in sfp_smbus_byte_write(). I'll send
a fix for that/
Maxime
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-01-05 8:29 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-12-28 21:33 [PATCH] net: sfp: add SMBus I2C block support Jonas Jelonek
2026-01-04 16:05 ` Jakub Kicinski
2026-01-05 8:29 ` Maxime Chevallier
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox