[PATCH net-next 06/13] net: sfp: Add a sfp-bus ops when connecting a module without PHY

Maxime Chevallier posted 13 patches 1 week, 5 days ago
There is a newer version of this series
[PATCH net-next 06/13] net: sfp: Add a sfp-bus ops when connecting a module without PHY
Posted by Maxime Chevallier 1 week, 5 days ago
The SFP bus infrastructure notifies its upstream when a PHY device was
discovered on the module. However, we don't have any indication when a
module with no PHY was inserted, except for the .insert() and .start()
notifications.

We want to keep track of the SFP module's capabilities using phy_port.
When the module contains an embedded PHY, the PHY driver will expose a
phy_port for it. However when there's no PHY, we have no hook to
populate the phy_port, so let's introduce one. It is called when we
have parsed the module's caps, applied the fixups, tried and failed to
probe for a PHY.

This will allow the bus' upstream to create the corresponding port, and
register it to the topology.

Signed-off-by: Maxime Chevallier <maxime.chevallier@bootlin.com>
---
 drivers/net/phy/sfp-bus.c | 20 ++++++++++++++++++++
 drivers/net/phy/sfp.c     | 12 ++++++++++++
 drivers/net/phy/sfp.h     |  2 ++
 include/linux/sfp.h       |  5 +++++
 4 files changed, 39 insertions(+)

diff --git a/drivers/net/phy/sfp-bus.c b/drivers/net/phy/sfp-bus.c
index b945d75966d5..a3d9e28badd0 100644
--- a/drivers/net/phy/sfp-bus.c
+++ b/drivers/net/phy/sfp-bus.c
@@ -753,6 +753,26 @@ void sfp_remove_phy(struct sfp_bus *bus)
 }
 EXPORT_SYMBOL_GPL(sfp_remove_phy);
 
+int sfp_module_connect_nophy(struct sfp_bus *bus)
+{
+	const struct sfp_upstream_ops *ops = sfp_get_upstream_ops(bus);
+
+	if (ops && ops->connect_nophy)
+		return ops->connect_nophy(bus->upstream);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(sfp_module_connect_nophy);
+
+void sfp_module_disconnect_nophy(struct sfp_bus *bus)
+{
+	const struct sfp_upstream_ops *ops = sfp_get_upstream_ops(bus);
+
+	if (ops && ops->disconnect_nophy)
+		ops->disconnect_nophy(bus->upstream);
+}
+EXPORT_SYMBOL_GPL(sfp_module_disconnect_nophy);
+
 void sfp_link_up(struct sfp_bus *bus)
 {
 	const struct sfp_upstream_ops *ops = sfp_get_upstream_ops(bus);
diff --git a/drivers/net/phy/sfp.c b/drivers/net/phy/sfp.c
index 47f095bd91ce..0f6357a98787 100644
--- a/drivers/net/phy/sfp.c
+++ b/drivers/net/phy/sfp.c
@@ -1875,6 +1875,8 @@ static void sfp_sm_mod_next(struct sfp *sfp, unsigned int state,
 
 static void sfp_sm_phy_detach(struct sfp *sfp)
 {
+	if (!sfp->mod_phy)
+		sfp_module_disconnect_nophy(sfp->sfp_bus);
 	sfp_remove_phy(sfp->sfp_bus);
 	phy_device_remove(sfp->mod_phy);
 	phy_device_free(sfp->mod_phy);
@@ -1918,6 +1920,11 @@ static int sfp_sm_probe_phy(struct sfp *sfp, int addr, bool is_c45)
 	return 0;
 }
 
+static int sfp_sm_connect_nophy(struct sfp *sfp)
+{
+	return sfp_module_connect_nophy(sfp->sfp_bus);
+}
+
 static void sfp_sm_link_up(struct sfp *sfp)
 {
 	sfp_link_up(sfp->sfp_bus);
@@ -2749,6 +2756,11 @@ static void sfp_sm_main(struct sfp *sfp, unsigned int event)
 			sfp_sm_next(sfp, SFP_S_FAIL, 0);
 			break;
 		}
+		if (!sfp->mod_phy) {
+			ret = sfp_sm_connect_nophy(sfp);
+			if (ret)
+				sfp_sm_next(sfp, SFP_S_FAIL, 0);
+		}
 		if (sfp_module_start(sfp->sfp_bus)) {
 			sfp_sm_next(sfp, SFP_S_FAIL, 0);
 			break;
diff --git a/drivers/net/phy/sfp.h b/drivers/net/phy/sfp.h
index 879dff7afe6a..02f3814aac84 100644
--- a/drivers/net/phy/sfp.h
+++ b/drivers/net/phy/sfp.h
@@ -37,6 +37,8 @@ int sfp_module_insert(struct sfp_bus *bus, const struct sfp_eeprom_id *id,
 void sfp_module_remove(struct sfp_bus *bus);
 int sfp_module_start(struct sfp_bus *bus);
 void sfp_module_stop(struct sfp_bus *bus);
+int sfp_module_connect_nophy(struct sfp_bus *bus);
+void sfp_module_disconnect_nophy(struct sfp_bus *bus);
 struct sfp_bus *sfp_register_socket(struct device *dev, struct sfp *sfp,
 				    const struct sfp_socket_ops *ops);
 void sfp_unregister_socket(struct sfp_bus *bus);
diff --git a/include/linux/sfp.h b/include/linux/sfp.h
index 5c71945a5e4d..20c9f66c1080 100644
--- a/include/linux/sfp.h
+++ b/include/linux/sfp.h
@@ -561,6 +561,9 @@ struct sfp_module_caps {
  *   on the module.
  * @disconnect_phy: called when a module with an I2C accessible PHY has
  *   been removed.
+ * @connect_nophy: called when it was established that the connected module
+ *		   doesn't habe an I2C accessible.
+ * @disconnect_nophy: called when the PHY-less module has been removed.
  */
 struct sfp_upstream_ops {
 	void (*attach)(void *priv, struct sfp_bus *bus);
@@ -573,6 +576,8 @@ struct sfp_upstream_ops {
 	void (*link_up)(void *priv);
 	int (*connect_phy)(void *priv, struct phy_device *);
 	void (*disconnect_phy)(void *priv, struct phy_device *);
+	int (*connect_nophy)(void *priv);
+	void (*disconnect_nophy)(void *priv);
 };
 
 #if IS_ENABLED(CONFIG_SFP)
-- 
2.49.0
Re: [PATCH net-next 06/13] net: sfp: Add a sfp-bus ops when connecting a module without PHY
Posted by Russell King (Oracle) 1 week, 2 days ago
On Tue, Jan 27, 2026 at 02:41:54PM +0100, Maxime Chevallier wrote:
> The SFP bus infrastructure notifies its upstream when a PHY device was
> discovered on the module. However, we don't have any indication when a
> module with no PHY was inserted, except for the .insert() and .start()
> notifications.

That's the way you tell - if you get the .start() callback but you
haven't had a PHY connected, that means there's no PHY.

There should be no need to add this callback, since you shouldn't be
thinking that the module is fully initialised until you have received
the .start() callback.

-- 
RMK's Patch system: https://www.armlinux.org.uk/developer/patches/
FTTP is here! 80Mbps down 10Mbps up. Decent connectivity at last!
Re: [PATCH net-next 06/13] net: sfp: Add a sfp-bus ops when connecting a module without PHY
Posted by Maxime Chevallier 1 week, 2 days ago
Hi Russell,

On 30/01/2026 17:19, Russell King (Oracle) wrote:
> On Tue, Jan 27, 2026 at 02:41:54PM +0100, Maxime Chevallier wrote:
>> The SFP bus infrastructure notifies its upstream when a PHY device was
>> discovered on the module. However, we don't have any indication when a
>> module with no PHY was inserted, except for the .insert() and .start()
>> notifications.
> 
> That's the way you tell - if you get the .start() callback but you
> haven't had a PHY connected, that means there's no PHY.
> 
> There should be no need to add this callback, since you shouldn't be
> thinking that the module is fully initialised until you have received
> the .start() callback.
> 

Ok fair point, thanks for the feedback ! I was about to send V3, but
I'll implement something that doesn't require this new callback then.

Thanks for taking a look :)

Maxime