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 --- 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 #include #include +#include #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 */