net: fec: Allow the PHY node to be retrieved
[oweals/u-boot.git] / drivers / net / fec_mxc.c
index f991b40b385a712179fa9911e648c2c9c8d6e84d..992180df869bc163bed99eb371b7ef4688e74ab3 100644 (file)
@@ -8,13 +8,17 @@
  */
 
 #include <common.h>
+#include <cpu_func.h>
 #include <dm.h>
-#include <environment.h>
+#include <env.h>
+#include <log.h>
 #include <malloc.h>
 #include <memalign.h>
 #include <miiphy.h>
 #include <net.h>
 #include <netdev.h>
+#include <asm/cache.h>
+#include <linux/delay.h>
 #include <power/regulator.h>
 
 #include <asm/io.h>
@@ -27,6 +31,7 @@
 #include <asm-generic/gpio.h>
 
 #include "fec_mxc.h"
+#include <eth_phy.h>
 
 DECLARE_GLOBAL_DATA_PTR;
 
@@ -123,30 +128,38 @@ static int fec_mdio_read(struct ethernet_regs *eth, uint8_t phyaddr,
        return val;
 }
 
+#ifndef imx_get_fecclk
+u32 __weak imx_get_fecclk(void)
+{
+       return 0;
+}
+#endif
+
 static int fec_get_clk_rate(void *udev, int idx)
 {
-#if IS_ENABLED(CONFIG_IMX8)
        struct fec_priv *fec;
        struct udevice *dev;
        int ret;
 
-       dev = udev;
-       if (!dev) {
-               ret = uclass_get_device(UCLASS_ETH, idx, &dev);
-               if (ret < 0) {
-                       debug("Can't get FEC udev: %d\n", ret);
-                       return ret;
+       if (IS_ENABLED(CONFIG_IMX8) ||
+           CONFIG_IS_ENABLED(CLK_CCF)) {
+               dev = udev;
+               if (!dev) {
+                       ret = uclass_get_device(UCLASS_ETH, idx, &dev);
+                       if (ret < 0) {
+                               debug("Can't get FEC udev: %d\n", ret);
+                               return ret;
+                       }
                }
-       }
 
-       fec = dev_get_priv(dev);
-       if (fec)
-               return fec->clk_rate;
+               fec = dev_get_priv(dev);
+               if (fec)
+                       return fec->clk_rate;
 
-       return -EINVAL;
-#else
-       return imx_get_fecclk();
-#endif
+               return -EINVAL;
+       } else {
+               return imx_get_fecclk();
+       }
 }
 
 static void fec_mii_setspeed(struct ethernet_regs *eth)
@@ -494,6 +507,16 @@ static int fec_open(struct eth_device *edev)
        writel(readl(&fec->eth->ecntrl) | FEC_ECNTRL_ETHER_EN,
               &fec->eth->ecntrl);
 
+#ifdef FEC_ENET_ENABLE_TXC_DELAY
+       writel(readl(&fec->eth->ecntrl) | FEC_ECNTRL_TXC_DLY,
+              &fec->eth->ecntrl);
+#endif
+
+#ifdef FEC_ENET_ENABLE_RXC_DELAY
+       writel(readl(&fec->eth->ecntrl) | FEC_ECNTRL_RXC_DLY,
+              &fec->eth->ecntrl);
+#endif
+
 #if defined(CONFIG_MX25) || defined(CONFIG_MX53) || defined(CONFIG_MX6SL)
        udelay(100);
 
@@ -604,7 +627,7 @@ static int fec_init(struct eth_device *dev, bd_t *bd)
        writel(0x00000000, &fec->eth->gaddr2);
 
        /* Do not access reserved register */
-       if (!is_mx6ul() && !is_mx6ull() && !is_imx8m()) {
+       if (!is_mx6ul() && !is_mx6ull() && !is_imx8() && !is_imx8m()) {
                /* clear MIB RAM */
                for (i = mib_ptr; i <= mib_ptr + 0xfc; i += 4)
                        writel(0, i);
@@ -1185,6 +1208,13 @@ int fecmxc_initialize_multi(bd_t *bd, int dev_id, int phy_id, uint32_t addr)
 #endif
        int ret;
 
+       if (CONFIG_IS_ENABLED(IMX_MODULE_FUSE)) {
+               if (enet_fused((ulong)addr)) {
+                       printf("SoC fuse indicates Ethernet@0x%x is unavailable.\n", addr);
+                       return -ENODEV;
+               }
+       }
+
 #ifdef CONFIG_FEC_MXC_MDIO_BASE
        /*
         * The i.MX28 has two ethernet interfaces, but they are not equal.
@@ -1264,7 +1294,7 @@ static const struct eth_ops fecmxc_ops = {
        .read_rom_hwaddr        = fecmxc_read_rom_hwaddr,
 };
 
-static int device_get_phy_addr(struct udevice *dev)
+static int device_get_phy_addr(struct fec_priv *priv, struct udevice *dev)
 {
        struct ofnode_phandle_args phandle_args;
        int reg;
@@ -1275,6 +1305,8 @@ static int device_get_phy_addr(struct udevice *dev)
                return -ENODEV;
        }
 
+       priv->phy_of_node = phandle_args.node;
+
        reg = ofnode_read_u32_default(phandle_args.node, "reg", 0);
 
        return reg;
@@ -1285,7 +1317,7 @@ static int fec_phy_init(struct fec_priv *priv, struct udevice *dev)
        struct phy_device *phydev;
        int addr;
 
-       addr = device_get_phy_addr(dev);
+       addr = device_get_phy_addr(priv, dev);
 #ifdef CONFIG_FEC_MXC_PHYADDR
        addr = CONFIG_FEC_MXC_PHYADDR;
 #endif
@@ -1295,12 +1327,13 @@ static int fec_phy_init(struct fec_priv *priv, struct udevice *dev)
                return -ENODEV;
 
        priv->phydev = phydev;
+       priv->phydev->node = priv->phy_of_node;
        phy_config(phydev);
 
        return 0;
 }
 
-#ifdef CONFIG_DM_GPIO
+#if CONFIG_IS_ENABLED(DM_GPIO)
 /* FEC GPIO reset */
 static void fec_gpio_reset(struct fec_priv *priv)
 {
@@ -1309,6 +1342,8 @@ static void fec_gpio_reset(struct fec_priv *priv)
                dm_gpio_set_value(&priv->phy_reset_gpio, 1);
                mdelay(priv->reset_delay);
                dm_gpio_set_value(&priv->phy_reset_gpio, 0);
+               if (priv->reset_post_delay)
+                       mdelay(priv->reset_post_delay);
        }
 }
 #endif
@@ -1321,6 +1356,13 @@ static int fecmxc_probe(struct udevice *dev)
        uint32_t start;
        int ret;
 
+       if (CONFIG_IS_ENABLED(IMX_MODULE_FUSE)) {
+               if (enet_fused((ulong)priv->eth)) {
+                       printf("SoC fuse indicates Ethernet@0x%lx is unavailable.\n", (ulong)priv->eth);
+                       return -ENODEV;
+               }
+       }
+
        if (IS_ENABLED(CONFIG_IMX8)) {
                ret = clk_get_by_name(dev, "ipg", &priv->ipg_clk);
                if (ret < 0) {
@@ -1333,6 +1375,47 @@ static int fecmxc_probe(struct udevice *dev)
                        return ret;
                }
 
+               priv->clk_rate = clk_get_rate(&priv->ipg_clk);
+       } else if (CONFIG_IS_ENABLED(CLK_CCF)) {
+               ret = clk_get_by_name(dev, "ipg", &priv->ipg_clk);
+               if (ret < 0) {
+                       debug("Can't get FEC ipg clk: %d\n", ret);
+                       return ret;
+               }
+               ret = clk_enable(&priv->ipg_clk);
+               if(ret)
+                       return ret;
+
+               ret = clk_get_by_name(dev, "ahb", &priv->ahb_clk);
+               if (ret < 0) {
+                       debug("Can't get FEC ahb clk: %d\n", ret);
+                       return ret;
+               }
+               ret = clk_enable(&priv->ahb_clk);
+               if (ret)
+                       return ret;
+
+               ret = clk_get_by_name(dev, "enet_out", &priv->clk_enet_out);
+               if (!ret) {
+                       ret = clk_enable(&priv->clk_enet_out);
+                       if (ret)
+                               return ret;
+               }
+
+               ret = clk_get_by_name(dev, "enet_clk_ref", &priv->clk_ref);
+               if (!ret) {
+                       ret = clk_enable(&priv->clk_ref);
+                       if (ret)
+                               return ret;
+               }
+
+               ret = clk_get_by_name(dev, "ptp", &priv->clk_ptp);
+               if (!ret) {
+                       ret = clk_enable(&priv->clk_ptp);
+                       if (ret)
+                               return ret;
+               }
+
                priv->clk_rate = clk_get_rate(&priv->ipg_clk);
        }
 
@@ -1350,7 +1433,7 @@ static int fecmxc_probe(struct udevice *dev)
        }
 #endif
 
-#ifdef CONFIG_DM_GPIO
+#if CONFIG_IS_ENABLED(DM_GPIO)
        fec_gpio_reset(priv);
 #endif
        /* Reset chip. */
@@ -1368,16 +1451,27 @@ static int fecmxc_probe(struct udevice *dev)
        fec_reg_setup(priv);
 
        priv->dev_id = dev->seq;
+
+#ifdef CONFIG_DM_ETH_PHY
+       bus = eth_phy_get_mdio_bus(dev);
+#endif
+
+       if (!bus) {
 #ifdef CONFIG_FEC_MXC_MDIO_BASE
-       bus = fec_get_miibus((ulong)CONFIG_FEC_MXC_MDIO_BASE, dev->seq);
+               bus = fec_get_miibus((ulong)CONFIG_FEC_MXC_MDIO_BASE, dev->seq);
 #else
-       bus = fec_get_miibus((ulong)priv->eth, dev->seq);
+               bus = fec_get_miibus((ulong)priv->eth, dev->seq);
 #endif
+       }
        if (!bus) {
                ret = -ENOMEM;
                goto err_mii;
        }
 
+#ifdef CONFIG_DM_ETH_PHY
+       eth_phy_set_mdio_bus(dev, bus);
+#endif
+
        priv->bus = bus;
        priv->interface = pdata->phy_interface;
        switch (priv->interface) {
@@ -1456,7 +1550,7 @@ static int fecmxc_ofdata_to_platdata(struct udevice *dev)
        device_get_supply_regulator(dev, "phy-supply", &priv->phy_supply);
 #endif
 
-#ifdef CONFIG_DM_GPIO
+#if CONFIG_IS_ENABLED(DM_GPIO)
        ret = gpio_request_by_name(dev, "phy-reset-gpios", 0,
                                   &priv->phy_reset_gpio, GPIOD_IS_OUT);
        if (ret < 0)
@@ -1468,18 +1562,29 @@ static int fecmxc_ofdata_to_platdata(struct udevice *dev)
                /* property value wrong, use default value */
                priv->reset_delay = 1;
        }
+
+       priv->reset_post_delay = dev_read_u32_default(dev,
+                                                     "phy-reset-post-delay",
+                                                     0);
+       if (priv->reset_post_delay > 1000) {
+               printf("FEC MXC: phy reset post delay should be <= 1000ms\n");
+               /* property value wrong, use default value */
+               priv->reset_post_delay = 0;
+       }
 #endif
 
        return 0;
 }
 
 static const struct udevice_id fecmxc_ids[] = {
+       { .compatible = "fsl,imx28-fec" },
        { .compatible = "fsl,imx6q-fec" },
        { .compatible = "fsl,imx6sl-fec" },
        { .compatible = "fsl,imx6sx-fec" },
        { .compatible = "fsl,imx6ul-fec" },
        { .compatible = "fsl,imx53-fec" },
        { .compatible = "fsl,imx7d-fec" },
+       { .compatible = "fsl,mvf600-fec" },
        { }
 };