net: Add eth phy generic driver for shared MDIO
authorYe Li <ye.li@nxp.com>
Sun, 3 May 2020 14:41:14 +0000 (22:41 +0800)
committerStefano Babic <sbabic@denx.de>
Sun, 10 May 2020 11:24:48 +0000 (13:24 +0200)
For dual ethernet controllers, the HW design may connect ETH phys to
one MDIO ports. So two different ethernet drivers have to share MDIO bus.
Since two ethernet drivers are independent, we can't ensure their probe
order.

To resolve this problem, introduce an eth phy generic driver and uclass.

After eth-uclass binds, we search the mdio node and binds the phy node
with the eth-phy-generic driver.

When one eth driver get its phy device, the parent of phy device will
probe prior than phy device. So this ensure the eth driver ownes the
MDIO bus will be probed before using its MDIO.

Signed-off-by: Ye Li <ye.li@nxp.com>
Signed-off-by: Peng Fan <peng.fan@nxp.com>
drivers/net/Kconfig
drivers/net/Makefile
drivers/net/eth-phy-uclass.c [new file with mode: 0644]
include/dm/uclass-id.h
include/eth_phy.h [new file with mode: 0644]
net/eth-uclass.c

index a2587a29e16513aada0ccdd8faf89cfd5538fcf5..38f2bd6637decc0e7e178dc1d14c57509a1cbe16 100644 (file)
@@ -57,6 +57,12 @@ config MDIO_MUX_SANDBOX
 
          This driver is used for testing in test/dm/mdio.c
 
+config DM_ETH_PHY
+       bool "Enable Driver Model for Ethernet Generic PHY drivers"
+       depends on DM
+       help
+         Enable driver model for Ethernet Generic PHY .
+
 menuconfig NETDEVICES
        bool "Network device support"
        depends on NET
index 6d9b8772b1a5fcc3b6bf1cc39d391daf55162eef..383ed1c64f7b9e368474d5afdde35b4d95b2ccc8 100644 (file)
@@ -20,6 +20,7 @@ obj-$(CONFIG_ETH_DESIGNWARE) += designware.o
 obj-$(CONFIG_ETH_DESIGNWARE_SOCFPGA) += dwmac_socfpga.o
 obj-$(CONFIG_DRIVER_DM9000) += dm9000x.o
 obj-$(CONFIG_DNET) += dnet.o
+obj-$(CONFIG_DM_ETH_PHY) += eth-phy-uclass.o
 obj-$(CONFIG_E1000) += e1000.o
 obj-$(CONFIG_E1000_SPI) += e1000_spi.o
 obj-$(CONFIG_EEPRO100) += eepro100.o
diff --git a/drivers/net/eth-phy-uclass.c b/drivers/net/eth-phy-uclass.c
new file mode 100644 (file)
index 0000000..b383f45
--- /dev/null
@@ -0,0 +1,122 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2020 NXP
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <net.h>
+#include <dm/device-internal.h>
+#include <dm/uclass-internal.h>
+#include <dm/lists.h>
+
+struct eth_phy_device_priv {
+       struct mii_dev *mdio_bus;
+};
+
+int eth_phy_binds_nodes(struct udevice *eth_dev)
+{
+       ofnode mdio_node, phy_node;
+       const char *node_name;
+       int ret;
+
+       mdio_node = dev_read_subnode(eth_dev, "mdio");
+       if (!ofnode_valid(mdio_node)) {
+               debug("%s: %s mdio subnode not found!", __func__,
+                     eth_dev->name);
+               return -ENXIO;
+       }
+
+       ofnode_for_each_subnode(phy_node, mdio_node) {
+               node_name = ofnode_get_name(phy_node);
+
+               debug("* Found child node: '%s'\n", node_name);
+
+               ret = device_bind_driver_to_node(eth_dev,
+                                                "eth_phy_generic_drv",
+                                                node_name, phy_node, NULL);
+               if (ret) {
+                       debug("  - Eth phy binding error: %d\n", ret);
+                       continue;
+               }
+
+               debug("  - bound phy device: '%s'\n", node_name);
+       }
+
+       return 0;
+}
+
+int eth_phy_set_mdio_bus(struct udevice *eth_dev, struct mii_dev *mdio_bus)
+{
+       struct udevice *dev;
+       struct eth_phy_device_priv *uc_priv;
+
+       for (uclass_first_device(UCLASS_ETH_PHY, &dev); dev;
+            uclass_next_device(&dev)) {
+               if (dev->parent == eth_dev) {
+                       uc_priv = (struct eth_phy_device_priv *)(dev->uclass_priv);
+
+                       if (!uc_priv->mdio_bus)
+                               uc_priv->mdio_bus = mdio_bus;
+               }
+       }
+
+       return 0;
+}
+
+struct mii_dev *eth_phy_get_mdio_bus(struct udevice *eth_dev)
+{
+       int ret;
+       struct udevice *phy_dev;
+       struct eth_phy_device_priv *uc_priv;
+
+       /* Will probe the parent of phy device, then phy device */
+       ret = uclass_get_device_by_phandle(UCLASS_ETH_PHY, eth_dev,
+                                          "phy-handle", &phy_dev);
+       if (!ret) {
+               if (eth_dev != phy_dev->parent) {
+                       /*
+                        * phy_dev is shared and controlled by
+                        * other eth controller
+                        */
+                       uc_priv = (struct eth_phy_device_priv *)(phy_dev->uclass_priv);
+                       if (uc_priv->mdio_bus)
+                               printf("Get shared mii bus on %s\n", eth_dev->name);
+                       else
+                               printf("Can't get shared mii bus on %s\n", eth_dev->name);
+
+                       return uc_priv->mdio_bus;
+               }
+       } else {
+               printf("FEC: can't find phy-handle\n");
+       }
+
+       return NULL;
+}
+
+int eth_phy_get_addr(struct udevice *dev)
+{
+       struct ofnode_phandle_args phandle_args;
+       int reg;
+
+       if (dev_read_phandle_with_args(dev, "phy-handle", NULL, 0, 0,
+                                      &phandle_args)) {
+               debug("Failed to find phy-handle");
+               return -ENODEV;
+       }
+
+       reg = ofnode_read_u32_default(phandle_args.node, "reg", 0);
+
+       return reg;
+}
+
+UCLASS_DRIVER(eth_phy_generic) = {
+       .id             = UCLASS_ETH_PHY,
+       .name           = "eth_phy_generic",
+       .per_device_auto_alloc_size = sizeof(struct eth_phy_device_priv),
+};
+
+U_BOOT_DRIVER(eth_phy_generic_drv) = {
+       .name           = "eth_phy_generic_drv",
+       .id             = UCLASS_ETH_PHY,
+};
index 37ada51f9f7ae7ed80b316c952b5ecbbf914cfac..7837d459f18c8a09d0b4c8ed74d02653ea716cfb 100644 (file)
@@ -47,6 +47,7 @@ enum uclass_id {
        UCLASS_DMA,             /* Direct Memory Access */
        UCLASS_EFI,             /* EFI managed devices */
        UCLASS_ETH,             /* Ethernet device */
+       UCLASS_ETH_PHY,         /* Ethernet PHY device */
        UCLASS_FIRMWARE,        /* Firmware */
        UCLASS_FS_FIRMWARE_LOADER,              /* Generic loader */
        UCLASS_GPIO,            /* Bank of general-purpose I/O pins */
diff --git a/include/eth_phy.h b/include/eth_phy.h
new file mode 100644 (file)
index 0000000..19c4965
--- /dev/null
@@ -0,0 +1,17 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright 2020 NXP
+ */
+
+#ifndef _eth_phy_h_
+#define _eth_phy_h_
+
+#include <dm.h>
+#include <phy.h>
+
+int eth_phy_binds_nodes(struct udevice *eth_dev);
+int eth_phy_set_mdio_bus(struct udevice *eth_dev, struct mii_dev *mdio_bus);
+struct mii_dev *eth_phy_get_mdio_bus(struct udevice *eth_dev);
+int eth_phy_get_addr(struct udevice *dev);
+
+#endif
index 950a48d520db93a63b152cb07cd113f97e92b22c..48560d59f6608ff9da4c7c6312776d9047005a9e 100644 (file)
@@ -13,6 +13,7 @@
 #include <dm/uclass-internal.h>
 #include <net/pcap.h>
 #include "eth_internal.h"
+#include <eth_phy.h>
 
 DECLARE_GLOBAL_DATA_PTR;
 
@@ -461,6 +462,10 @@ static int eth_post_bind(struct udevice *dev)
                return -EINVAL;
        }
 
+#ifdef CONFIG_DM_ETH_PHY
+       eth_phy_binds_nodes(dev);
+#endif
+
        return 0;
 }