[PATCH v2 1/2] w5100: restore GPIO-based link detection
Arthur Crépin Leblond <[email protected]> Tue, 04 Aug 2026 19:40:56 +0200
| Newsgroups | gmane.linux.drivers.devicetree,gmane.linux.network,gmane.linux.kernel |
|---|---|
| Message-ID | <[email protected]> |
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 <[email protected]>
---
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