generic: ar8xxx: add optional gpio reset support
authorPetr Štetiar <ynezz@true.cz>
Mon, 20 May 2019 22:18:29 +0000 (00:18 +0200)
committerPetr Štetiar <ynezz@true.cz>
Fri, 31 May 2019 07:12:33 +0000 (09:12 +0200)
Some devices like Linksys EA8500 use designated GPIO to reset the switch
and if the switch isn't reset properly before first access, it can lead
to unusable switch after soft reboot of the device:

 libphy: GPIO Bitbanged MDIO: probed
 mdio_bus gpio-0: MDIO device at address 0 is missing.
 mdio_bus gpio-0: MDIO device at address 4 is missing.

Working case:

 libphy: GPIO Bitbanged MDIO: probed
 switch0: Atheros AR8337 rev. 2 switch registered on gpio-0

So this patch introduces optional reset GPIO which helps fixing this
problem.

Tested-by: Your Real Name <424778940z@gmail.com>
Reported-by: Your Real Name <424778940z@gmail.com>
Ref: https://github.com/openwrt/openwrt/pull/2047
Ref: https://bugs.openwrt.org/index.php?do=details&task_id=2168
Signed-off-by: Petr Štetiar <ynezz@true.cz>
target/linux/generic/files/drivers/net/phy/ar8216.c

index 683241cf1cee041ace2934b5963f63a68e44ec2f..f3053ccdd7c8161660ac88b4db5de660aa021a12 100644 (file)
@@ -23,6 +23,8 @@
 #include <linux/skbuff.h>
 #include <linux/netdevice.h>
 #include <linux/netlink.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
 #include <linux/of_device.h>
 #include <linux/of_mdio.h>
 #include <linux/of_net.h>
@@ -2750,6 +2752,41 @@ static const struct of_device_id ar8xxx_mdiodev_of_match[] = {
        { /* sentinel */ },
 };
 
+static void
+ar8xxx_gpio_reset(struct ar8xxx_priv *priv)
+{
+       int ret;
+       int gpio;
+       bool active_low;
+       enum of_gpio_flags flags;
+
+       gpio = of_get_named_gpio_flags(priv->pdev->of_node, "reset-gpio", 0, &flags);
+       if (!gpio_is_valid(gpio))
+               return;
+
+       active_low = flags & OF_GPIO_ACTIVE_LOW;
+       ret = devm_gpio_request_one(priv->pdev, gpio,
+                                   active_low ? GPIOF_OUT_INIT_LOW : GPIOF_OUT_INIT_HIGH,
+                                   "ar8xxx reset");
+       if (ret < 0) {
+               pr_err("ar8327: unable to claim reset-gpio: %d\n", ret);
+               return;
+       }
+
+       /* TODO: debug only, remove! */
+       {
+               int val = gpio_get_value_cansleep(gpio);
+               pr_info("ar8xxx: reset-gpio is %s, setting to %s after 15ms\n",
+                       val ? "HIGH" : "LOW",
+                       !!active_low ? "HIGH" : "LOW");
+       }
+
+       usleep_range(15 * 1000, 15 * 1000 + 1000);
+       gpio_set_value_cansleep(gpio, !!active_low);
+
+       pr_info("ar8xxx: gpio reset done\n");
+}
+
 static int
 ar8xxx_mdiodev_probe(struct mdio_device *mdiodev)
 {
@@ -2776,6 +2813,8 @@ ar8xxx_mdiodev_probe(struct mdio_device *mdiodev)
        if (ret)
                priv->mib_poll_interval = 0;
 
+       ar8xxx_gpio_reset(priv);
+
        ret = ar8xxx_read_id(priv);
        if (ret)
                goto free_priv;