Merge branch 'next' of https://gitlab.denx.de/u-boot/custodians/u-boot-x86 into next
[oweals/u-boot.git] / drivers / net / fec_mxc.c
index 74d36f479ec9b59301cb2002c94e783986c0e564..bc5b63d78814979347988ea85fd08ea4223209fd 100644 (file)
@@ -1,22 +1,22 @@
+// SPDX-License-Identifier: GPL-2.0+
 /*
  * (C) Copyright 2009 Ilya Yanok, Emcraft Systems Ltd <yanok@emcraft.com>
  * (C) Copyright 2008,2009 Eric Jarrige <eric.jarrige@armadeus.org>
  * (C) Copyright 2008 Armadeus Systems nc
  * (C) Copyright 2007 Pengutronix, Sascha Hauer <s.hauer@pengutronix.de>
  * (C) Copyright 2007 Pengutronix, Juergen Beisert <j.beisert@pengutronix.de>
- *
- * SPDX-License-Identifier:    GPL-2.0+
  */
 
 #include <common.h>
+#include <cpu_func.h>
 #include <dm.h>
-#include <environment.h>
+#include <env.h>
 #include <malloc.h>
 #include <memalign.h>
 #include <miiphy.h>
 #include <net.h>
 #include <netdev.h>
-#include "fec_mxc.h"
+#include <power/regulator.h>
 
 #include <asm/io.h>
 #include <linux/errno.h>
@@ -25,6 +25,9 @@
 #include <asm/arch/clock.h>
 #include <asm/arch/imx-regs.h>
 #include <asm/mach-imx/sys_proto.h>
+#include <asm-generic/gpio.h>
+
+#include "fec_mxc.h"
 
 DECLARE_GLOBAL_DATA_PTR;
 
@@ -121,6 +124,40 @@ 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)
+{
+       struct fec_priv *fec;
+       struct udevice *dev;
+       int 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;
+
+               return -EINVAL;
+       } else {
+               return imx_get_fecclk();
+       }
+}
+
 static void fec_mii_setspeed(struct ethernet_regs *eth)
 {
        /*
@@ -138,9 +175,20 @@ static void fec_mii_setspeed(struct ethernet_regs *eth)
         * Given that ceil(clkrate / 5000000) <= 64, the calculation for
         * holdtime cannot result in a value greater than 3.
         */
-       u32 pclk = imx_get_fecclk();
-       u32 speed = DIV_ROUND_UP(pclk, 5000000);
-       u32 hold = DIV_ROUND_UP(pclk, 100000000) - 1;
+       u32 pclk;
+       u32 speed;
+       u32 hold;
+       int ret;
+
+       ret = fec_get_clk_rate(NULL, 0);
+       if (ret < 0) {
+               printf("Can't find FEC0 clk rate: %d\n", ret);
+               return;
+       }
+       pclk = ret;
+       speed = DIV_ROUND_UP(pclk, 5000000);
+       hold = DIV_ROUND_UP(pclk, 100000000) - 1;
+
 #ifdef FEC_QUIRK_ENET_MAC
        speed--;
 #endif
@@ -565,7 +613,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_mx8m()) {
+       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);
@@ -1146,12 +1194,12 @@ int fecmxc_initialize_multi(bd_t *bd, int dev_id, int phy_id, uint32_t addr)
 #endif
        int ret;
 
-#ifdef CONFIG_MX28
+#ifdef CONFIG_FEC_MXC_MDIO_BASE
        /*
         * The i.MX28 has two ethernet interfaces, but they are not equal.
         * Only the first one can access the MDIO bus.
         */
-       base_mii = MXS_ENET0_BASE;
+       base_mii = CONFIG_FEC_MXC_MDIO_BASE;
 #else
        base_mii = addr;
 #endif
@@ -1225,40 +1273,138 @@ static const struct eth_ops fecmxc_ops = {
        .read_rom_hwaddr        = fecmxc_read_rom_hwaddr,
 };
 
+static int device_get_phy_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;
+}
+
 static int fec_phy_init(struct fec_priv *priv, struct udevice *dev)
 {
        struct phy_device *phydev;
-       int mask = 0xffffffff;
+       int addr;
 
-#ifdef CONFIG_PHYLIB
-       mask = 1 << CONFIG_FEC_MXC_PHYADDR;
+       addr = device_get_phy_addr(dev);
+#ifdef CONFIG_FEC_MXC_PHYADDR
+       addr = CONFIG_FEC_MXC_PHYADDR;
 #endif
 
-       phydev = phy_find_by_mask(priv->bus, mask, priv->interface);
+       phydev = phy_connect(priv->bus, addr, dev, priv->interface);
        if (!phydev)
                return -ENODEV;
 
-       phy_connect_dev(phydev, dev);
-
        priv->phydev = phydev;
        phy_config(phydev);
 
        return 0;
 }
 
+#if CONFIG_IS_ENABLED(DM_GPIO)
+/* FEC GPIO reset */
+static void fec_gpio_reset(struct fec_priv *priv)
+{
+       debug("fec_gpio_reset: fec_gpio_reset(dev)\n");
+       if (dm_gpio_is_valid(&priv->phy_reset_gpio)) {
+               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
+
 static int fecmxc_probe(struct udevice *dev)
 {
        struct eth_pdata *pdata = dev_get_platdata(dev);
        struct fec_priv *priv = dev_get_priv(dev);
        struct mii_dev *bus = NULL;
-       int dev_id = -1;
        uint32_t start;
        int ret;
 
+       if (IS_ENABLED(CONFIG_IMX8)) {
+               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 < 0) {
+                       debug("Can't enable FEC ipg clk: %d\n", ret);
+                       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);
+       }
+
        ret = fec_alloc_descs(priv);
        if (ret)
                return ret;
 
+#ifdef CONFIG_DM_REGULATOR
+       if (priv->phy_supply) {
+               ret = regulator_set_enable(priv->phy_supply, true);
+               if (ret) {
+                       printf("%s: Error enabling phy supply\n", dev->name);
+                       return ret;
+               }
+       }
+#endif
+
+#if CONFIG_IS_ENABLED(DM_GPIO)
+       fec_gpio_reset(priv);
+#endif
        /* Reset chip. */
        writel(readl(&priv->eth->ecntrl) | FEC_ECNTRL_RESET,
               &priv->eth->ecntrl);
@@ -1272,29 +1418,51 @@ static int fecmxc_probe(struct udevice *dev)
        }
 
        fec_reg_setup(priv);
-       priv->dev_id = (dev_id == -1) ? 0 : dev_id;
 
-       bus = fec_get_miibus((ulong)priv->eth, dev_id);
+       priv->dev_id = dev->seq;
+#ifdef CONFIG_FEC_MXC_MDIO_BASE
+       bus = fec_get_miibus((ulong)CONFIG_FEC_MXC_MDIO_BASE, dev->seq);
+#else
+       bus = fec_get_miibus((ulong)priv->eth, dev->seq);
+#endif
        if (!bus) {
                ret = -ENOMEM;
                goto err_mii;
        }
 
        priv->bus = bus;
-       priv->xcv_type = CONFIG_FEC_XCV_TYPE;
        priv->interface = pdata->phy_interface;
+       switch (priv->interface) {
+       case PHY_INTERFACE_MODE_MII:
+               priv->xcv_type = MII100;
+               break;
+       case PHY_INTERFACE_MODE_RMII:
+               priv->xcv_type = RMII;
+               break;
+       case PHY_INTERFACE_MODE_RGMII:
+       case PHY_INTERFACE_MODE_RGMII_ID:
+       case PHY_INTERFACE_MODE_RGMII_RXID:
+       case PHY_INTERFACE_MODE_RGMII_TXID:
+               priv->xcv_type = RGMII;
+               break;
+       default:
+               priv->xcv_type = CONFIG_FEC_XCV_TYPE;
+               printf("Unsupported interface type %d defaulting to %d\n",
+                      priv->interface, priv->xcv_type);
+               break;
+       }
+
        ret = fec_phy_init(priv, dev);
        if (ret)
                goto err_phy;
 
        return 0;
 
-err_timeout:
-       free(priv->phydev);
 err_phy:
        mdio_unregister(bus);
        free(bus);
 err_mii:
+err_timeout:
        fec_free_descs(priv);
        return ret;
 }
@@ -1308,11 +1476,17 @@ static int fecmxc_remove(struct udevice *dev)
        mdio_unregister(priv->bus);
        mdio_free(priv->bus);
 
+#ifdef CONFIG_DM_REGULATOR
+       if (priv->phy_supply)
+               regulator_set_enable(priv->phy_supply, false);
+#endif
+
        return 0;
 }
 
 static int fecmxc_ofdata_to_platdata(struct udevice *dev)
 {
+       int ret = 0;
        struct eth_pdata *pdata = dev_get_platdata(dev);
        struct fec_priv *priv = dev_get_priv(dev);
        const char *phy_mode;
@@ -1330,16 +1504,45 @@ static int fecmxc_ofdata_to_platdata(struct udevice *dev)
                return -EINVAL;
        }
 
-       /* TODO
-        * Need to get the reset-gpio and related properties from DT
-        * and implemet the enet reset code on .probe call
-        */
+#ifdef CONFIG_DM_REGULATOR
+       device_get_supply_regulator(dev, "phy-supply", &priv->phy_supply);
+#endif
+
+#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)
+               return 0; /* property is optional, don't return error! */
+
+       priv->reset_delay = dev_read_u32_default(dev, "phy-reset-duration", 1);
+       if (priv->reset_delay > 1000) {
+               printf("FEC MXC: phy reset duration should be <= 1000ms\n");
+               /* 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" },
        { }
 };