Netdev List
 help / color / mirror / Atom feed
* [PATCH] w5100: restore GPIO-based link detection
@ 2026-08-04 14:38 Arthur Crépin Leblond
  2026-08-04 15:02 ` Arnd Bergmann
  2026-08-04 17:54 ` Andrew Lunn
  0 siblings, 2 replies; 8+ messages in thread
From: Arthur Crépin Leblond @ 2026-08-04 14:38 UTC (permalink / raw)
  To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
	Paolo Abeni, Rob Herring, Krzysztof Kozlowski, Conor Dooley
  Cc: Arnd Bergmann, netdev, devicetree, linux-kernel,
	Arthur Crépin Leblond

Commit dacf281771a9 ("w5100: remove unused gpio link detection")
dropped the link_gpio/link_irq handling on the grounds that no
devicetree user passed a "link" GPIO at the time.

Signed-off-by: Arthur Crépin Leblond <arthur@marmottus.net>
---
Hi,

I am using the W5500 on a Raspberry Pi and rely on the link GPIO for
the carrier detection.
If we could bring it back, I would greatly appreciate.

Thank you!

Arthur Crépin Leblond
---
 .../devicetree/bindings/net/wiznet,w5x00.txt       |  8 ++-
 drivers/net/ethernet/wiznet/w5100.c                | 84 ++++++++++++++++++++++
 2 files changed, 89 insertions(+), 3 deletions(-)

diff --git a/Documentation/devicetree/bindings/net/wiznet,w5x00.txt b/Documentation/devicetree/bindings/net/wiznet,w5x00.txt
index e9665798c4be..e97ce3cb9183 100644
--- a/Documentation/devicetree/bindings/net/wiznet,w5x00.txt
+++ b/Documentation/devicetree/bindings/net/wiznet,w5x00.txt
@@ -25,6 +25,7 @@ Optional properties:
   According to the w5500 datasheet, the chip allows a maximum of 80 MHz, however,
   board designs may need to limit this value.
 - local-mac-address: See ethernet.txt in the same directory.
+- link-gpios: a GPIO line used for the link detection interrupt
 
 
 Example (for Raspberry Pi with pin control stuff for GPIO irq):
@@ -38,13 +39,14 @@ Example (for Raspberry Pi with pin control stuff for GPIO irq):
 		interrupt-parent = <&gpio>;
 		interrupts = <25 IRQ_TYPE_EDGE_FALLING>;
 		spi-max-frequency = <30000000>;
+		link-gpios = <&gpio 4 GPIO_ACTIVE_HIGH>;
 	};
 };
 
 &gpio {
 	eth1_pins: eth1_pins {
-		brcm,pins = <25>;
-		brcm,function = <0>; /* in */
-		brcm,pull = <0>; /* none */
+		brcm,pins = <25 4>;
+		brcm,function = <0 0>; /* in */
+		brcm,pull = <0 0>; /* none */
 	};
 };
diff --git a/drivers/net/ethernet/wiznet/w5100.c b/drivers/net/ethernet/wiznet/w5100.c
index 53d8dc642fbd..c9d1891f37b9 100644
--- a/drivers/net/ethernet/wiznet/w5100.c
+++ b/drivers/net/ethernet/wiznet/w5100.c
@@ -22,6 +22,7 @@
 #include <linux/ioport.h>
 #include <linux/interrupt.h>
 #include <linux/irq.h>
+#include <linux/gpio/consumer.h>
 
 #include "w5100.h"
 
@@ -154,6 +155,8 @@ struct w5100_priv {
 	u16 s0_rx_buf_size;
 
 	int irq;
+	int link_irq;
+	struct gpio_desc *link_gpio;
 
 	struct napi_struct napi;
 	struct net_device *ndev;
@@ -414,6 +417,16 @@ static void w5100_get_drvinfo(struct net_device *ndev,
 		sizeof(info->bus_info));
 }
 
+static u32 w5100_get_link(struct net_device *ndev)
+{
+	struct w5100_priv *priv = netdev_priv(ndev);
+
+	if (priv->link_gpio)
+		return !!gpiod_get_value_cansleep(priv->link_gpio);
+
+	return 1;
+}
+
 static u32 w5100_get_msglevel(struct net_device *ndev)
 {
 	struct w5100_priv *priv = netdev_priv(ndev);
@@ -616,6 +629,24 @@ static irqreturn_t w5100_interrupt(int irq, void *ndev_instance)
 	return IRQ_HANDLED;
 }
 
+static irqreturn_t w5100_detect_link(int irq, void *ndev_instance)
+{
+	struct net_device *ndev = ndev_instance;
+	struct w5100_priv *priv = netdev_priv(ndev);
+
+	if (netif_running(ndev)) {
+		if (gpiod_get_value(priv->link_gpio) != 0) {
+			netif_info(priv, link, ndev, "link is up\n");
+			netif_carrier_on(ndev);
+		} else {
+			netif_info(priv, link, ndev, "link is down\n");
+			netif_carrier_off(ndev);
+		}
+	}
+
+	return IRQ_HANDLED;
+}
+
 static void w5100_setrx_work(struct work_struct *work)
 {
 	struct w5100_priv *priv = container_of(work, struct w5100_priv,
@@ -659,6 +690,14 @@ static int w5100_open(struct net_device *ndev)
 	w5100_hw_start(priv);
 	napi_enable(&priv->napi);
 	netif_start_queue(ndev);
+
+	if (priv->link_gpio) {
+		if (gpiod_get_value_cansleep(priv->link_gpio) != 0)
+			netif_carrier_on(ndev);
+		else
+			netif_carrier_off(ndev);
+	}
+
 	return 0;
 }
 
@@ -678,6 +717,7 @@ static const struct ethtool_ops w5100_ethtool_ops = {
 	.get_drvinfo		= w5100_get_drvinfo,
 	.get_msglevel		= w5100_get_msglevel,
 	.set_msglevel		= w5100_set_msglevel,
+	.get_link		= w5100_get_link,
 	.get_regs_len		= w5100_get_regs_len,
 	.get_regs		= w5100_get_regs,
 };
@@ -751,6 +791,13 @@ int w5100_probe(struct device *dev, const struct w5100_ops *ops,
 	priv->ndev = ndev;
 	priv->ops = ops;
 	priv->irq = irq;
+	priv->link_gpio = gpiod_get_optional(dev, "link", GPIOD_IN);
+	if (IS_ERR(priv->link_gpio)) {
+		err = dev_err_probe(dev, PTR_ERR(priv->link_gpio),
+				    "failed to get link GPIO\n");
+		priv->link_gpio = NULL;
+		goto err_register;
+	}
 
 	ndev->netdev_ops = &w5100_netdev_ops;
 	ndev->ethtool_ops = &w5100_ethtool_ops;
@@ -803,13 +850,40 @@ int w5100_probe(struct device *dev, const struct w5100_ops *ops,
 	if (err)
 		goto err_hw;
 
+	if (priv->link_gpio) {
+		char *link_name = devm_kasprintf(dev, GFP_KERNEL, "%s-link",
+						 dev_name(dev));
+		if (!link_name) {
+			err = -ENOMEM;
+			goto err_gpio;
+		}
+
+		priv->link_irq = gpiod_to_irq(priv->link_gpio);
+		if (priv->link_irq < 0) {
+			err = priv->link_irq;
+			goto err_gpio;
+		}
+
+		err = request_any_context_irq(priv->link_irq, w5100_detect_link,
+					      IRQF_TRIGGER_RISING |
+						      IRQF_TRIGGER_FALLING,
+					      link_name, priv->ndev);
+		if (err < 0)
+			goto err_gpio;
+	}
+
 	return 0;
 
+err_gpio:
+	free_irq(priv->irq, ndev);
 err_hw:
 	destroy_workqueue(priv->xfer_wq);
 err_wq:
 	unregister_netdev(ndev);
 err_register:
+	if (priv->link_gpio)
+		gpiod_put(priv->link_gpio);
+
 	free_netdev(ndev);
 	return err;
 }
@@ -822,6 +896,10 @@ void w5100_remove(struct device *dev)
 
 	w5100_hw_reset(priv);
 	free_irq(priv->irq, ndev);
+	if (priv->link_gpio) {
+		free_irq(priv->link_irq, ndev);
+		gpiod_put(priv->link_gpio);
+	}
 
 	flush_work(&priv->setrx_work);
 	flush_work(&priv->restart_work);
@@ -840,6 +918,7 @@ static int w5100_suspend(struct device *dev)
 
 	if (netif_running(ndev)) {
 		netif_carrier_off(ndev);
+
 		netif_device_detach(ndev);
 
 		w5100_hw_close(priv);
@@ -857,7 +936,12 @@ static int w5100_resume(struct device *dev)
 		w5100_hw_start(priv);
 
 		netif_device_attach(ndev);
+
+		if (!priv->link_gpio ||
+		    gpiod_get_value_cansleep(priv->link_gpio) != 0)
+			netif_carrier_on(ndev);
 	}
+
 	return 0;
 }
 #endif /* CONFIG_PM_SLEEP */




^ permalink raw reply related	[flat|nested] 8+ messages in thread

end of thread, other threads:[~2026-08-05 10:46 UTC | newest]

Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-04 14:38 [PATCH] w5100: restore GPIO-based link detection Arthur Crépin Leblond
2026-08-04 15:02 ` Arnd Bergmann
2026-08-04 17:47   ` Arthur Crépin Leblond
2026-08-04 17:54 ` Andrew Lunn
2026-08-05  8:25   ` Arthur Crépin Leblond
2026-08-05  9:11     ` Arnd Bergmann
2026-08-05  9:44       ` Arthur Crépin Leblond
2026-08-05 10:46         ` Arnd Bergmann

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox