[PATCH v2 1/2] w5100: restore GPIO-based link detection
From: Arthur Crépin Leblond <hidden>
Date: 2026-08-04 17:58:29
Also in:
linux-devicetree, lkml
Subsystem:
networking drivers, the rest · Maintainers:
Andrew Lunn, "David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, Linus Torvalds
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.
This isn't a plain revert of that removal. The link GPIO is now
bound from the device tree link-gpios property using the resource
managed GPIO descriptors.
The link IRQ is also requested with devm_request_threaded_irq()
instead of request_any_context_irq(), so it is released automatically
on driver removal or probe failure.
Signed-off-by: Arthur Crépin Leblond <redacted>
---
drivers/net/ethernet/wiznet/w5100.c | 71 +++++++++++++++++++++++++++++++++++++
1 file changed, 71 insertions(+)
diff --git a/drivers/net/ethernet/wiznet/w5100.c b/drivers/net/ethernet/wiznet/w5100.c
index 53d8dc642fbd..1410a9fcfa92 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,7 @@ struct w5100_priv { u16 s0_rx_buf_size; int irq; + struct gpio_desc *link_gpio; struct napi_struct napi; struct net_device *ndev;
@@ -414,6 +416,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 +628,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 +689,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 +716,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 +790,13 @@ int w5100_probe(struct device *dev, const struct w5100_ops *ops, priv->ndev = ndev; priv->ops = ops; priv->irq = irq; + priv->link_gpio = devm_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,8 +849,29 @@ int w5100_probe(struct device *dev, const struct w5100_ops *ops, if (err) goto err_hw; + if (priv->link_gpio) { + int link_irq = gpiod_to_irq(priv->link_gpio); + + if (link_irq < 0) { + err = dev_err_probe(dev, link_irq, + "No corresponding irq for link gpio\n"); + goto err_gpio; + } + + err = devm_request_threaded_irq(dev, link_irq, NULL, + w5100_detect_link, + IRQF_TRIGGER_RISING | + IRQF_TRIGGER_FALLING | + IRQF_ONESHOT, + "w5100-link", 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:
@@ -857,6 +924,10 @@ 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; }
--
2.55.0