realtek: add patch to extend upstream sfp smbus support
Add patches that extends the upstream smbus support in sfp driver. This only supports SMBus byte operations and in that case, disables hwmon and prints warnings because of that. Using the word/block capability of our I2C controller is totally fine for all that functionality. The patches are currently pending upstream. Signed-off-by: Jonas Jelonek <jelonek.jonas@gmail.com> Link: https://github.com/openwrt/openwrt/pull/21181 Signed-off-by: Robert Marko <robimarko@gmail.com>
This commit is contained in:
parent
19478cf334
commit
52d9c6c9cb
@ -0,0 +1,59 @@
|
||||
From 572304faf8c1d30f142060c76a3e731438fc5975 Mon Sep 17 00:00:00 2001
|
||||
From: Jonas Jelonek <jelonek.jonas@gmail.com>
|
||||
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 <jelonek.jonas@gmail.com>
|
||||
---
|
||||
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;
|
||||
}
|
||||
|
||||
@ -0,0 +1,204 @@
|
||||
From 475b0dd976de6db975bfef7d6fdb978d2d64c535 Mon Sep 17 00:00:00 2001
|
||||
From: Jonas Jelonek <jelonek.jonas@gmail.com>
|
||||
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 <jelonek.jonas@gmail.com>
|
||||
---
|
||||
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 <linux/platform_device.h>
|
||||
#include <linux/rtnetlink.h>
|
||||
#include <linux/slab.h>
|
||||
+#include <linux/unaligned.h>
|
||||
#include <linux/workqueue.h>
|
||||
|
||||
#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;
|
||||
Loading…
Reference in New Issue
Block a user