[PATCH] w5100: restore GPIO-based link detection

Arthur Crépin Leblond <[email protected]>
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.

Signed-off-by: Arthur Crépin Leblond <[email protected]>
---
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 */
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.