net: fec: add fuse check
[oweals/u-boot.git] / drivers / net / fec_mxc.c
index dac07b6e34d24c26c8b39fcc13e631d5bd372f36..3046305cfb65b827f306223af81f20392e699c14 100644 (file)
@@ -8,13 +8,15 @@
  */
 
 #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 <power/regulator.h>
 
 #include <asm/io.h>
 #include <linux/errno.h>
@@ -122,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)
 {
        /*
@@ -139,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
@@ -456,6 +503,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);
 
@@ -566,7 +623,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);
@@ -1147,6 +1204,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.
@@ -1226,36 +1290,53 @@ 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;
 
+       addr = device_get_phy_addr(dev);
 #ifdef CONFIG_FEC_MXC_PHYADDR
-       mask = 1 << 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;
 }
 
-#ifdef CONFIG_DM_GPIO
+#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);
-               udelay(priv->reset_delay);
+               mdelay(priv->reset_delay);
                dm_gpio_set_value(&priv->phy_reset_gpio, 0);
+               if (priv->reset_post_delay)
+                       mdelay(priv->reset_post_delay);
        }
 }
 #endif
@@ -1268,11 +1349,84 @@ 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) {
+                       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_GPIO
+#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. */
@@ -1301,8 +1455,27 @@ static int fecmxc_probe(struct udevice *dev)
        }
 
        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;
@@ -1327,6 +1500,11 @@ 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;
 }
 
@@ -1350,32 +1528,45 @@ static int fecmxc_ofdata_to_platdata(struct udevice *dev)
                return -EINVAL;
        }
 
-#ifdef CONFIG_DM_GPIO
+#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) {
-               ret = dev_read_u32_array(dev, "phy-reset-duration",
-                                        &priv->reset_delay, 1);
-       } else if (ret == -ENOENT) {
-               priv->reset_delay = 1000;
-               ret = 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("FEX MXC: gpio reset timeout should be less the 1000\n");
-               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 ret;
+       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" },
        { }
 };