[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Message-ID: <20260116113105.244592-1-jelonek.jonas@gmail.com>
Date: Fri, 16 Jan 2026 11:31:05 +0000
From: Jonas Jelonek <jelonek.jonas@...il.com>
To: Russell King <linux@...linux.org.uk>,
Andrew Lunn <andrew@...n.ch>,
Heiner Kallweit <hkallweit1@...il.com>,
"David S . Miller" <davem@...emloft.net>,
Eric Dumazet <edumazet@...gle.com>,
Jakub Kicinski <kuba@...nel.org>,
Paolo Abeni <pabeni@...hat.com>,
Maxime Chevallier <maxime.chevallier@...tlin.com>
Cc: netdev@...r.kernel.org,
linux-kernel@...r.kernel.org,
Bjørn Mork <bjorn@...k.no>,
Jonas Jelonek <jelonek.jonas@...il.com>
Subject: [PATCH net-next v5] 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 <jelonek.jonas@...il.com>
---
changes since v4:
- made a more general approach, also covering word access
v4: https://lore.kernel.org/netdev/20260109101321.2804-1-jelonek.jonas@gmail.com/
changes since v3:
- fix formal issues
v3: https://lore.kernel.org/netdev/20260105161242.578487-1-jelonek.jonas@gmail.com/
changes since v2:
- fix previous attempt of v2 to fix return value
v2: https://lore.kernel.org/netdev/20260105154653.575397-1-jelonek.jonas@gmail.com/
changes since v1:
- return number of written bytes instead of zero
v1: https://lore.kernel.org/netdev/20251228213331.472887-1-jelonek.jonas@gmail.com/
---
drivers/net/phy/sfp.c | 124 +++++++++++++++++++++++++++++++++---------
1 file changed, 98 insertions(+), 26 deletions(-)
diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c
index 84bef5099dda..8f0b34a93ae8 100644
--- a/drivers/net/phy/sfp.c
+++ b/drivers/net/phy/sfp.c
@@ -14,6 +14,7 @@
#include <linux/platform_device.h>
#include <linux/rtnetlink.h>
#include <linux/slab.h>
+#include <linux/unaligned.h>
#include <linux/workqueue.h>
#include "sfp.h"
@@ -719,50 +720,103 @@ static int sfp_i2c_write(struct sfp *sfp, bool a2, u8 dev_addr, void *buf,
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 (this_len > 2 &&
+ 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 (this_len > 2 &&
+ 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_WORD_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;
@@ -770,21 +824,39 @@ static int sfp_smbus_byte_write(struct sfp *sfp, bool a2, u8 dev_addr,
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;
- sfp->i2c_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 = SFP_EEPROM_BLOCK_SIZE;
+ } else if (functionality & I2C_FUNC_SMBUS_BYTE_DATA) {
+ sfp->read = sfp_smbus_read;
+ sfp->write = sfp_smbus_write;
+
+ if (functionality & I2C_FUNC_SMBUS_I2C_BLOCK)
+ max_block_size = SFP_EEPROM_BLOCK_SIZE;
+ else if (functionality & I2C_FUNC_SMBUS_WORD_DATA)
+ max_block_size = 2;
+ else
+ max_block_size = 1;
+
+ 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 {
sfp->i2c = NULL;
return -EINVAL;
}
+ sfp->i2c_max_block_size = max_block_size;
return 0;
}
base-commit: 74ecff77dace0f9aead6aac852b57af5d4ad3b85
--
2.48.1
Powered by blists - more mailing lists