mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-05-02 07:29:12 -04:00
Merge branch 'net-phy-sfp-add-single-byte-smbus-sfp-access'
Maxime Chevallier says: ==================== net: phy: sfp: Add single-byte SMBus SFP access This is V4 for the single-byte SMBus support for SFP cages as well as embedded PHYs accessed over mdio-i2c. v3: https://lore.kernel.org/20250314162319.516163-1-maxime.chevallier@bootlin.com v2: https://lore.kernel.org/20250225112043.419189-1-maxime.chevallier@bootlin.com v1: https://lore.kernel.org/20250223172848.1098621-1-maxime.chevallier@bootlin.com ==================== Link: https://patch.msgid.link/20250322075745.120831-1-maxime.chevallier@bootlin.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
@@ -106,6 +106,62 @@ static int i2c_mii_write_default_c22(struct mii_bus *bus, int phy_id, int reg,
|
||||
return i2c_mii_write_default_c45(bus, phy_id, -1, reg, val);
|
||||
}
|
||||
|
||||
static int smbus_byte_mii_read_default_c22(struct mii_bus *bus, int phy_id,
|
||||
int reg)
|
||||
{
|
||||
struct i2c_adapter *i2c = bus->priv;
|
||||
union i2c_smbus_data smbus_data;
|
||||
int val = 0, ret;
|
||||
|
||||
if (!i2c_mii_valid_phy_id(phy_id))
|
||||
return 0;
|
||||
|
||||
ret = i2c_smbus_xfer(i2c, i2c_mii_phy_addr(phy_id), 0,
|
||||
I2C_SMBUS_READ, reg,
|
||||
I2C_SMBUS_BYTE_DATA, &smbus_data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
val = (smbus_data.byte & 0xff) << 8;
|
||||
|
||||
ret = i2c_smbus_xfer(i2c, i2c_mii_phy_addr(phy_id), 0,
|
||||
I2C_SMBUS_READ, reg,
|
||||
I2C_SMBUS_BYTE_DATA, &smbus_data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
val |= smbus_data.byte & 0xff;
|
||||
|
||||
return val;
|
||||
}
|
||||
|
||||
static int smbus_byte_mii_write_default_c22(struct mii_bus *bus, int phy_id,
|
||||
int reg, u16 val)
|
||||
{
|
||||
struct i2c_adapter *i2c = bus->priv;
|
||||
union i2c_smbus_data smbus_data;
|
||||
int ret;
|
||||
|
||||
if (!i2c_mii_valid_phy_id(phy_id))
|
||||
return 0;
|
||||
|
||||
smbus_data.byte = (val & 0xff00) >> 8;
|
||||
|
||||
ret = i2c_smbus_xfer(i2c, i2c_mii_phy_addr(phy_id), 0,
|
||||
I2C_SMBUS_WRITE, reg,
|
||||
I2C_SMBUS_BYTE_DATA, &smbus_data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
smbus_data.byte = val & 0xff;
|
||||
|
||||
ret = i2c_smbus_xfer(i2c, i2c_mii_phy_addr(phy_id), 0,
|
||||
I2C_SMBUS_WRITE, reg,
|
||||
I2C_SMBUS_BYTE_DATA, &smbus_data);
|
||||
|
||||
return ret < 0 ? ret : 0;
|
||||
}
|
||||
|
||||
/* RollBall SFPs do not access internal PHY via I2C address 0x56, but
|
||||
* instead via address 0x51, when SFP page is set to 0x03 and password to
|
||||
* 0xffffffff.
|
||||
@@ -378,13 +434,26 @@ static int i2c_mii_init_rollball(struct i2c_adapter *i2c)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool mdio_i2c_check_functionality(struct i2c_adapter *i2c,
|
||||
enum mdio_i2c_proto protocol)
|
||||
{
|
||||
if (i2c_check_functionality(i2c, I2C_FUNC_I2C))
|
||||
return true;
|
||||
|
||||
if (i2c_check_functionality(i2c, I2C_FUNC_SMBUS_BYTE_DATA) &&
|
||||
protocol == MDIO_I2C_MARVELL_C22)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
struct mii_bus *mdio_i2c_alloc(struct device *parent, struct i2c_adapter *i2c,
|
||||
enum mdio_i2c_proto protocol)
|
||||
{
|
||||
struct mii_bus *mii;
|
||||
int ret;
|
||||
|
||||
if (!i2c_check_functionality(i2c, I2C_FUNC_I2C))
|
||||
if (!mdio_i2c_check_functionality(i2c, protocol))
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
mii = mdiobus_alloc();
|
||||
@@ -395,6 +464,14 @@ struct mii_bus *mdio_i2c_alloc(struct device *parent, struct i2c_adapter *i2c,
|
||||
mii->parent = parent;
|
||||
mii->priv = i2c;
|
||||
|
||||
/* Only use SMBus if we have no other choice */
|
||||
if (i2c_check_functionality(i2c, I2C_FUNC_SMBUS_BYTE_DATA) &&
|
||||
!i2c_check_functionality(i2c, I2C_FUNC_I2C)) {
|
||||
mii->read = smbus_byte_mii_read_default_c22;
|
||||
mii->write = smbus_byte_mii_write_default_c22;
|
||||
return mii;
|
||||
}
|
||||
|
||||
switch (protocol) {
|
||||
case MDIO_I2C_ROLLBALL:
|
||||
ret = i2c_mii_init_rollball(i2c);
|
||||
|
||||
@@ -234,6 +234,7 @@ struct sfp {
|
||||
enum mdio_i2c_proto mdio_protocol;
|
||||
struct phy_device *mod_phy;
|
||||
const struct sff_data *type;
|
||||
size_t i2c_max_block_size;
|
||||
size_t i2c_block_size;
|
||||
u32 max_power_mW;
|
||||
|
||||
@@ -691,14 +692,71 @@ 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)
|
||||
{
|
||||
union i2c_smbus_data smbus_data;
|
||||
u8 bus_addr = a2 ? 0x51 : 0x50;
|
||||
u8 *data = buf;
|
||||
int ret;
|
||||
|
||||
while (len) {
|
||||
ret = i2c_smbus_xfer(sfp->i2c, bus_addr, 0,
|
||||
I2C_SMBUS_READ, dev_addr,
|
||||
I2C_SMBUS_BYTE_DATA, &smbus_data);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
*data = smbus_data.byte;
|
||||
|
||||
len--;
|
||||
data++;
|
||||
dev_addr++;
|
||||
}
|
||||
|
||||
return data - (u8 *)buf;
|
||||
}
|
||||
|
||||
static int sfp_smbus_byte_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;
|
||||
u8 *data = buf;
|
||||
int ret;
|
||||
|
||||
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)
|
||||
return ret;
|
||||
|
||||
len--;
|
||||
data++;
|
||||
dev_addr++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sfp_i2c_configure(struct sfp *sfp, struct i2c_adapter *i2c)
|
||||
{
|
||||
if (!i2c_check_functionality(i2c, I2C_FUNC_I2C))
|
||||
return -EINVAL;
|
||||
|
||||
sfp->i2c = i2c;
|
||||
sfp->read = sfp_i2c_read;
|
||||
sfp->write = sfp_i2c_write;
|
||||
|
||||
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;
|
||||
} 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;
|
||||
} else {
|
||||
sfp->i2c = NULL;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -1594,7 +1652,7 @@ static void sfp_hwmon_probe(struct work_struct *work)
|
||||
*/
|
||||
if (sfp->i2c_block_size < 2) {
|
||||
dev_info(sfp->dev,
|
||||
"skipping hwmon device registration due to broken EEPROM\n");
|
||||
"skipping hwmon device registration\n");
|
||||
dev_info(sfp->dev,
|
||||
"diagnostic EEPROM area cannot be read atomically to guarantee data coherency\n");
|
||||
return;
|
||||
@@ -2201,7 +2259,7 @@ static int sfp_sm_mod_probe(struct sfp *sfp, bool report)
|
||||
u8 check;
|
||||
int ret;
|
||||
|
||||
sfp->i2c_block_size = SFP_EEPROM_BLOCK_SIZE;
|
||||
sfp->i2c_block_size = sfp->i2c_max_block_size;
|
||||
|
||||
ret = sfp_read(sfp, false, 0, &id.base, sizeof(id.base));
|
||||
if (ret < 0) {
|
||||
@@ -2941,7 +2999,6 @@ static struct sfp *sfp_alloc(struct device *dev)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
sfp->dev = dev;
|
||||
sfp->i2c_block_size = SFP_EEPROM_BLOCK_SIZE;
|
||||
|
||||
mutex_init(&sfp->sm_mutex);
|
||||
mutex_init(&sfp->st_mutex);
|
||||
@@ -3115,6 +3172,15 @@ static int sfp_probe(struct platform_device *pdev)
|
||||
if (!sfp->sfp_bus)
|
||||
return -ENOMEM;
|
||||
|
||||
if (sfp->i2c_max_block_size < 2)
|
||||
dev_warn(sfp->dev,
|
||||
"Please note:\n"
|
||||
"This SFP cage is accessed via an SMBus only capable of single byte\n"
|
||||
"transactions. Some features are disabled, other may be unreliable or\n"
|
||||
"sporadically fail. Use with caution. There is nothing that the kernel\n"
|
||||
"or community can do to fix it, the kernel will try best efforts. Please\n"
|
||||
"verify any problems on hardware that supports multi-byte I2C transactions.\n");
|
||||
|
||||
sfp_debugfs_init(sfp);
|
||||
|
||||
return 0;
|
||||
|
||||
Reference in New Issue
Block a user