From nobody Sun Feb 8 08:27:53 2026 Received: from mail-ej1-f52.google.com (mail-ej1-f52.google.com [209.85.218.52]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 1E5EE378D82 for ; Fri, 16 Jan 2026 11:31:12 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.218.52 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768563075; cv=none; b=S39dwZhyrhmjWpILE6KgIFNOQmN15JtBT9vrWinMVncnsk4W6uOjcA9YLl0rTYmSy2QAiPPdYOcU0gsiaQPH0evV20z1wjf0MRyYwojhH742I2TImFwxYNemlKxI9wp0ZpZ+UArKHxfMfDTEh0ujbsrGgbLdCo10BGrH+tvBcnE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1768563075; c=relaxed/simple; bh=S0PkLCZjarOG7ySOVOl2alRaMDNY0B9BDVZlqcYzNIU=; h=From:To:Cc:Subject:Date:Message-ID:MIME-Version; b=BilQpnzDah8ahg3FGT+PPDiIs61yg+eAc++2J6QGSdcNZo2qnV54WlVoV2OeZapn6cV2qQeMs9iHqGza/OEo/v9E6SSPWCIQyIqVY+VTDGVj34GcSpLfYUTJWcI1dB/ZEPdpsXPrY47CP+GZhpGNS60Sw2QzUXoNVeZoTDeQe2o= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=StWvrU1m; arc=none smtp.client-ip=209.85.218.52 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="StWvrU1m" Received: by mail-ej1-f52.google.com with SMTP id a640c23a62f3a-b79f8f7ea43so457457066b.2 for ; Fri, 16 Jan 2026 03:31:12 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1768563071; x=1769167871; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:from:to:cc:subject:date:message-id:reply-to; bh=HccSZY+rZTaO1CqKYtPWo1Xi+9vA2I6W+eqQUhoLZlo=; b=StWvrU1mxD+jc5lMegowlx9Z34xS3AxtdetZMm8gERggFfvP/lyHp7Xn2u9nou4S9T ueb7T03PN6fySIAmyayOhqS1pAkDrhXiAH6Y+4FNunPcJ6rsC+sA+QbJDwsCTvKgzVv0 l/Hd2A3a7OJmrSorHMkTvv4JEL80I8fZG57wxyva0CkFn86yakE0LYFy590Ad9HBXquT Nbg3Yu/PMzeD5BQNXJsmVaa4s4Pyp5wPObiBstKaVqY4Jze4pDv/8Fhxqe9Fvzm3/Oc4 i/5mgwbx7FMTjSpEyyJ3fVHVOQSQBOkQWHRtGExRwjfHAUPHUjzUVJKI2Nk5HO9qNhq2 /cuQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20230601; t=1768563071; x=1769167871; h=content-transfer-encoding:mime-version:message-id:date:subject:cc :to:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=HccSZY+rZTaO1CqKYtPWo1Xi+9vA2I6W+eqQUhoLZlo=; b=OvjEzOykx+KpS/BJnMB1yWfwra3JjVhQ1+mxvP4x2bZd25ErAB1wHAB+1ZrmeUMzSx CWVvnjseAXDaV8xIHVVc7wr1BTVTXmkeAE1FgTF+pSjwJg1Sknum/dPZwBnvE6kCILtw KUPBbqcdnOaXshw8ue58SKFri38vYR6kBIrus//tK+RjF7LQZgp0HK9UOvc1U8k1jvTE 8qMXmIzeHrR7Qi1uid1xPdNfR2Gbfs6dvh5SVZZZ8oNrz9KGkMaDZHMZSp4hSvY2Z6Ie ROGYBFdxAQk2tnaDWumZ5VdQREzOdxlNdL0w+xn9Nv4yjAZjZCNxNIS8qzVTjNcMHs7H x8Bg== X-Forwarded-Encrypted: i=1; AJvYcCVT1s3JVaDpDHPjGVN7n5sl1hRM+jC5vk1+oEtpiWV2CNX5t0q/SQZ+bcoKDBX6/gX31D0wKzNwb7BggQ8=@vger.kernel.org X-Gm-Message-State: AOJu0YzdaGR7rNcGfAvyRpqYGp9tOZAEUAPsiX3X6r3rRw4xvob0iuac FxPbccKUpdveYvigabCfM6kNZaAD3E4S1Hvlj32Zqhd6n063JTLzUrOg X-Gm-Gg: AY/fxX6tI2rmOLeZ/7SCp9uz1HydlUfwlPU0rey4adbEBM38SErIlEd0wzoYIPVZI6j TOmlnqVPkdWBiDeBjE5GMUuQb8rZS17rriJwcJ1ZiN8w8JJqJAUQB84A63uIihvJW5bVti98AHr 17TIQ84elM0240DLpdRqxgxULQqndk4UD7frpzm+n7shmDUBrtJqE3p9KAWxDIpWPa2zctw7r5I qFR349Jdfrz+Z5nU5IQ/JQXVv7s7wivD5c5eSXN/weQm+uScpdpQTJw+geO0Sm3zJSjlULkJde1 ezJDqjsHpv4YSYphLv3Qv6Jls5NMzWJvPXVQkLN5ZFyijnl2wbBWAiNap7ZyuKhqvuWswRuO5G0 s13ZJqp02RIPE31PlaUZOIkumMB0ez6ALvc77xso39qNNy0y5MkAWtSs4XT4dR+d1mZPYcSSpxW 4PfRownnUd2FcqiwngC/Gzj/8UIw== X-Received: by 2002:a17:907:d7cb:b0:b87:2f29:2060 with SMTP id a640c23a62f3a-b8792e32203mr260561766b.26.1768563070244; Fri, 16 Jan 2026 03:31:10 -0800 (PST) Received: from builder.. ([2001:9e8:f13f:1016:be24:11ff:fe30:5d85]) by smtp.gmail.com with ESMTPSA id a640c23a62f3a-b87959fbefdsm204116066b.55.2026.01.16.03.31.09 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 16 Jan 2026 03:31:09 -0800 (PST) From: Jonas Jelonek To: Russell King , Andrew Lunn , Heiner Kallweit , "David S . Miller" , Eric Dumazet , Jakub Kicinski , Paolo Abeni , Maxime Chevallier Cc: netdev@vger.kernel.org, linux-kernel@vger.kernel.org, =?UTF-8?q?Bj=C3=B8rn=20Mork?= , Jonas Jelonek Subject: [PATCH net-next v5] net: sfp: extend SMBus support Date: Fri, 16 Jan 2026 11:31:05 +0000 Message-ID: <20260116113105.244592-1-jelonek.jonas@gmail.com> X-Mailer: git-send-email 2.48.1 Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" 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 --- changes since v4: - made a more general approach, also covering word access v4: https://lore.kernel.org/netdev/20260109101321.2804-1-jelonek.jonas@gmai= l.com/ changes since v3: - fix formal issues v3: https://lore.kernel.org/netdev/20260105161242.578487-1-jelonek.jonas@gm= ail.com/ changes since v2: - fix previous attempt of v2 to fix return value v2: https://lore.kernel.org/netdev/20260105154653.575397-1-jelonek.jonas@gm= ail.com/ changes since v1: - return number of written bytes instead of zero v1: https://lore.kernel.org/netdev/20251228213331.472887-1-jelonek.jonas@gm= ail.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 #include #include +#include #include =20 #include "sfp.h" @@ -719,50 +720,103 @@ static int sfp_i2c_write(struct sfp *sfp, bool a2, u= 8 dev_addr, void *buf, return ret =3D=3D ARRAY_SIZE(msgs) ? len : 0; } =20 -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 =3D a2 ? 0x51 : 0x50; + size_t this_len, transferred; + u32 functionality; u8 *data =3D buf; int ret; =20 + functionality =3D i2c_get_functionality(sfp->i2c); + while (len) { - ret =3D i2c_smbus_xfer(sfp->i2c, bus_addr, 0, - I2C_SMBUS_READ, dev_addr, - I2C_SMBUS_BYTE_DATA, &smbus_data); + this_len =3D min(len, sfp->i2c_max_block_size); + + if (this_len > 2 && + functionality & I2C_FUNC_SMBUS_READ_I2C_BLOCK) { + smbus_data.block[0] =3D this_len; + ret =3D 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 =3D this_len; + } else if (this_len >=3D 2 && + functionality & I2C_FUNC_SMBUS_READ_WORD_DATA) { + ret =3D 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 =3D 2; + } else { + ret =3D i2c_smbus_xfer(sfp->i2c, bus_addr, 0, + I2C_SMBUS_READ, dev_addr, + I2C_SMBUS_BYTE_DATA, &smbus_data); + + *data =3D smbus_data.byte; + transferred =3D 1; + } + if (ret < 0) return ret; =20 - *data =3D smbus_data.byte; - - len--; - data++; - dev_addr++; + data +=3D transferred; + len -=3D transferred; + dev_addr +=3D transferred; } =20 return data - (u8 *)buf; } =20 -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 *bu= f, + size_t len) { union i2c_smbus_data smbus_data; u8 bus_addr =3D a2 ? 0x51 : 0x50; + size_t this_len, transferred; + u32 functionality; u8 *data =3D buf; int ret; =20 + functionality =3D i2c_get_functionality(sfp->i2c); + while (len) { - smbus_data.byte =3D *data; - ret =3D i2c_smbus_xfer(sfp->i2c, bus_addr, 0, - I2C_SMBUS_WRITE, dev_addr, - I2C_SMBUS_BYTE_DATA, &smbus_data); - if (ret) + this_len =3D min(len, sfp->i2c_max_block_size); + + if (this_len > 2 && + functionality & I2C_FUNC_SMBUS_WRITE_I2C_BLOCK) { + smbus_data.block[0] =3D this_len; + memcpy(&smbus_data.block[1], data, this_len); + + ret =3D i2c_smbus_xfer(sfp->i2c, bus_addr, 0, + I2C_SMBUS_WRITE, dev_addr, + I2C_SMBUS_WORD_DATA, &smbus_data); + transferred =3D this_len; + } else if (this_len >=3D 2 && + functionality & I2C_FUNC_SMBUS_WRITE_WORD_DATA) { + smbus_data.word =3D get_unaligned_le16(data); + ret =3D i2c_smbus_xfer(sfp->i2c, bus_addr, 0, + I2C_SMBUS_WRITE, dev_addr, + I2C_SMBUS_WORD_DATA, &smbus_data); + transferred =3D 2; + } else { + smbus_data.byte =3D *data; + ret =3D i2c_smbus_xfer(sfp->i2c, bus_addr, 0, + I2C_SMBUS_WRITE, dev_addr, + I2C_SMBUS_BYTE_DATA, &smbus_data); + transferred =3D 1; + } + + if (ret < 0) return ret; =20 - len--; - data++; - dev_addr++; + data +=3D transferred; + len -=3D transferred; + dev_addr +=3D transferred; } =20 return data - (u8 *)buf; @@ -770,21 +824,39 @@ static int sfp_smbus_byte_write(struct sfp *sfp, bool= a2, u8 dev_addr, =20 static int sfp_i2c_configure(struct sfp *sfp, struct i2c_adapter *i2c) { + size_t max_block_size; + u32 functionality; + sfp->i2c =3D i2c; + functionality =3D i2c_get_functionality(i2c); =20 - if (i2c_check_functionality(i2c, I2C_FUNC_I2C)) { + if (functionality & I2C_FUNC_I2C) { sfp->read =3D sfp_i2c_read; sfp->write =3D sfp_i2c_write; - sfp->i2c_max_block_size =3D SFP_EEPROM_BLOCK_SIZE; - } else if (i2c_check_functionality(i2c, I2C_FUNC_SMBUS_BYTE_DATA)) { - sfp->read =3D sfp_smbus_byte_read; - sfp->write =3D sfp_smbus_byte_write; - sfp->i2c_max_block_size =3D 1; + max_block_size =3D SFP_EEPROM_BLOCK_SIZE; + } else if (functionality & I2C_FUNC_SMBUS_BYTE_DATA) { + sfp->read =3D sfp_smbus_read; + sfp->write =3D sfp_smbus_write; + + if (functionality & I2C_FUNC_SMBUS_I2C_BLOCK) + max_block_size =3D SFP_EEPROM_BLOCK_SIZE; + else if (functionality & I2C_FUNC_SMBUS_WORD_DATA) + max_block_size =3D 2; + else + max_block_size =3D 1; + + if (i2c->quirks && i2c->quirks->max_read_len) + max_block_size =3D min(max_block_size, + i2c->quirks->max_read_len); + if (i2c->quirks && i2c->quirks->max_write_len) + max_block_size =3D min(max_block_size, + i2c->quirks->max_write_len); } else { sfp->i2c =3D NULL; return -EINVAL; } =20 + sfp->i2c_max_block_size =3D max_block_size; return 0; } =20 base-commit: 74ecff77dace0f9aead6aac852b57af5d4ad3b85 --=20 2.48.1