summaryrefslogtreecommitdiffstats
path: root/drivers/net/phy
diff options
context:
space:
mode:
authorSam Ravnborg <sam@ravnborg.org>2018-08-22 20:36:50 +0200
committerSascha Hauer <s.hauer@pengutronix.de>2018-08-24 10:03:17 +0200
commit8fd3bb0f22847dfa8890d28af07becec5b61492d (patch)
tree9b46d3da7be79765fea8ba257a0b85128b814170 /drivers/net/phy
parent9f5267107dc83e45795d74e488cb0b68ef88378d (diff)
downloadbarebox-8fd3bb0f22847dfa8890d28af07becec5b61492d.tar.gz
barebox-8fd3bb0f22847dfa8890d28af07becec5b61492d.tar.xz
phylib: add support for reset-gpios
Add minimal support for reset-gpios in the PHY node. Example DT that uses this: macb0: ethernet@fffbc000 { phy-mode = "rmii"; #address-cells = <1>; #size-cells = <0>; ethphy0: ethernet-phy@1 { reg = <3>; reset-gpios = <&pioE 17 GPIO_ACTIVE_LOW>; reset-assert-us = <1000>; reset-deassert-us = <2000>; }; }; The reset is required to get the Davicom PHY operational on my proprietary board, and is assumed relevant for other boards too. The PHY is reset when we read the info from DT, before the phy_id is retreived. The bindings are documented in dts/Bindings/net/phy.txt. A side-effect of this patch is that we may see phy devices created from the DT due to the extra call to: of_mdiobus_register() with the ethernet node as argument. The logic to determine if a child node is a PHY node is a simpler version compared to the one used in the kernel. The kernel have a whitelist of compatible strings that is not included in the barebox version. They can be added later if needed. Signed-off-by: Sam Ravnborg <sam@ravnborg.org> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Diffstat (limited to 'drivers/net/phy')
-rw-r--r--drivers/net/phy/mdio_bus.c96
1 files changed, 95 insertions, 1 deletions
diff --git a/drivers/net/phy/mdio_bus.c b/drivers/net/phy/mdio_bus.c
index 5d4218f7c0..02e47f6a14 100644
--- a/drivers/net/phy/mdio_bus.c
+++ b/drivers/net/phy/mdio_bus.c
@@ -17,14 +17,19 @@
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include <common.h>
+#include <of_gpio.h>
#include <driver.h>
#include <init.h>
+#include <gpio.h>
#include <clock.h>
#include <net.h>
#include <errno.h>
#include <linux/phy.h>
#include <linux/err.h>
+#define DEFAULT_GPIO_RESET_ASSERT 1000 /* us */
+#define DEFAULT_GPIO_RESET_DEASSERT 1000 /* us */
+
LIST_HEAD(mii_bus_list);
int mdiobus_detect(struct device_d *dev)
@@ -82,6 +87,82 @@ static int of_mdiobus_register_phy(struct mii_bus *mdio, struct device_node *chi
return 0;
}
+/*
+ * Node is considered a PHY node if:
+ * o Compatible string of "ethernet-phy-idX.X"
+ * o Compatible string of "ethernet-phy-ieee802.3-c45"
+ * o Compatible string of "ethernet-phy-ieee802.3-c22"
+ * o No compatibility string
+ *
+ * A device which is not a phy is expected to have a compatible string
+ * indicating what sort of device it is.
+ */
+static bool of_mdiobus_child_is_phy(struct device_node *np)
+{
+ struct property *prop;
+ const char *cp;
+
+ of_property_for_each_string(np, "compatible", prop, cp) {
+ if (!strncmp(cp, "ethernet-phy-id", strlen("ethernet-phy-id")))
+ return true;
+ }
+
+ if (of_device_is_compatible(np, "ethernet-phy-ieee802.3-c45"))
+ return true;
+
+ if (of_device_is_compatible(np, "ethernet-phy-ieee802.3-c22"))
+ return true;
+
+ if (!of_find_property(np, "compatible", NULL))
+ return true;
+
+ return false;
+}
+
+/*
+ * Reset the PHY, based on DT info
+ *
+ * If np is a phy node, and the phy node contains a reset-gpios property
+ * then reset the phy.
+ */
+static void of_mdiobus_reset_phy(struct mii_bus *bus, struct device_node *np)
+{
+ enum of_gpio_flags of_flags;
+ u32 reset_deassert_delay;
+ u32 reset_assert_delay;
+ unsigned long flags;
+ int gpio;
+ int ret;
+
+ gpio = of_get_named_gpio_flags(np, "reset-gpios", 0, &of_flags);
+ if (!gpio_is_valid(gpio))
+ return;
+
+ flags = GPIOF_OUT_INIT_INACTIVE;
+ if (of_flags & OF_GPIO_ACTIVE_LOW)
+ flags |= GPIOF_ACTIVE_LOW;
+
+ ret = gpio_request_one(gpio, flags, np->name);
+ if (ret) {
+ dev_err(&bus->dev, "failed to request reset gpio for: %s\n",
+ np->name);
+ return;
+ }
+
+ reset_assert_delay = DEFAULT_GPIO_RESET_ASSERT;
+ of_property_read_u32(np, "reset-assert-us", &reset_assert_delay);
+
+ reset_deassert_delay = DEFAULT_GPIO_RESET_DEASSERT;
+ of_property_read_u32(np, "reset-deassert-us", &reset_deassert_delay);
+
+ /* reset the PHY */
+ dev_dbg(&bus->dev, "reset PHY with GPIO: %d\n", gpio);
+ gpio_set_active(gpio, 1);
+ udelay(reset_assert_delay);
+ gpio_set_active(gpio, 0);
+ udelay(reset_deassert_delay);
+}
+
/**
* of_mdiobus_register - Register mii_bus and create PHYs from the device tree
* @mdio: pointer to mii_bus structure
@@ -111,6 +192,10 @@ static int of_mdiobus_register(struct mii_bus *mdio, struct device_node *np)
continue;
}
+ if (!of_mdiobus_child_is_phy(child))
+ continue;
+
+ of_mdiobus_reset_phy(mdio, child);
of_mdiobus_register_phy(mdio, child, addr);
}
@@ -154,8 +239,17 @@ int mdiobus_register(struct mii_bus *bus)
pr_info("%s: probed\n", dev_name(&bus->dev));
- if (bus->dev.device_node)
+ if (bus->dev.device_node) {
+ /* Register PHY's as child node to mdio node */
of_mdiobus_register(bus, bus->dev.device_node);
+ }
+ else if (bus->parent->device_node) {
+ /*
+ * Register PHY's as child node to the ethernet node,
+ * if there was no mdio node
+ */
+ of_mdiobus_register(bus, bus->parent->device_node);
+ }
return 0;
}