net: rtl8139: Move functions around
[oweals/u-boot.git] / drivers / net / fsl_enetc.c
index da533a1e5dbbf0269282cfeaa4d68c11d2088df8..bee73153d0efeaf5c1abe0506ae33023f392cfe0 100644 (file)
@@ -7,6 +7,7 @@
 #include <common.h>
 #include <dm.h>
 #include <errno.h>
+#include <malloc.h>
 #include <memalign.h>
 #include <asm/io.h>
 #include <pci.h>
 
 #include "fsl_enetc.h"
 
+#define ENETC_DRIVER_NAME      "enetc_eth"
+
+/*
+ * sets the MAC address in IERB registers, this setting is persistent and
+ * carried over to Linux.
+ */
+static void enetc_set_ierb_primary_mac(struct udevice *dev, int devfn,
+                                      const u8 *enetaddr)
+{
+#ifdef CONFIG_ARCH_LS1028A
+/*
+ * LS1028A is the only part with IERB at this time and there are plans to change
+ * its structure, keep this LS1028A specific for now
+ */
+#define IERB_BASE              0x1f0800000ULL
+#define IERB_PFMAC(pf, vf, n)  (IERB_BASE + 0x8000 + (pf) * 0x100 + (vf) * 8 \
+                                + (n) * 4)
+
+static int ierb_fn_to_pf[] = {0, 1, 2, -1, -1, -1, 3};
+
+       u16 lower = *(const u16 *)(enetaddr + 4);
+       u32 upper = *(const u32 *)enetaddr;
+
+       if (ierb_fn_to_pf[devfn] < 0)
+               return;
+
+       out_le32(IERB_PFMAC(ierb_fn_to_pf[devfn], 0, 0), upper);
+       out_le32(IERB_PFMAC(ierb_fn_to_pf[devfn], 0, 1), (u32)lower);
+#endif
+}
+
+/* sets up primary MAC addresses in DT/IERB */
+void fdt_fixup_enetc_mac(void *blob)
+{
+       struct pci_child_platdata *ppdata;
+       struct eth_pdata *pdata;
+       struct udevice *dev;
+       struct uclass *uc;
+       char path[256];
+       int offset;
+       int devfn;
+
+       uclass_get(UCLASS_ETH, &uc);
+       uclass_foreach_dev(dev, uc) {
+               if (!dev->driver || !dev->driver->name ||
+                   strcmp(dev->driver->name, ENETC_DRIVER_NAME))
+                       continue;
+
+               pdata = dev_get_platdata(dev);
+               ppdata = dev_get_parent_platdata(dev);
+               devfn = PCI_FUNC(ppdata->devfn);
+
+               enetc_set_ierb_primary_mac(dev, devfn, pdata->enetaddr);
+
+               snprintf(path, 256, "/soc/pcie@1f0000000/ethernet@%x,%x",
+                        PCI_DEV(ppdata->devfn), PCI_FUNC(ppdata->devfn));
+               offset = fdt_path_offset(blob, path);
+               if (offset < 0)
+                       continue;
+               fdt_setprop(blob, offset, "mac-address", pdata->enetaddr, 6);
+       }
+}
+
 /*
  * Bind the device:
  * - set a more explicit name on the interface
@@ -69,13 +133,25 @@ static bool enetc_has_imdio(struct udevice *dev)
 static int enetc_init_sgmii(struct udevice *dev)
 {
        struct enetc_priv *priv = dev_get_priv(dev);
+       bool is2500 = false;
+       u16 reg;
 
        if (!enetc_has_imdio(dev))
                return 0;
 
-       /* Set to SGMII mode, use AN */
+       if (priv->if_type == PHY_INTERFACE_MODE_SGMII_2500)
+               is2500 = true;
+
+       /*
+        * Set to SGMII mode, for 1Gbps enable AN, for 2.5Gbps set fixed speed.
+        * Although fixed speed is 1Gbps, we could be running at 2.5Gbps based
+        * on PLL configuration.  Setting 1G for 2.5G here is counter intuitive
+        * but intentional.
+        */
+       reg = ENETC_PCS_IF_MODE_SGMII;
+       reg |= is2500 ? ENETC_PCS_IF_MODE_SPEED_1G : ENETC_PCS_IF_MODE_SGMII_AN;
        enetc_mdio_write(&priv->imdio, ENETC_PCS_PHY_ADDR, MDIO_DEVAD_NONE,
-                        ENETC_PCS_IF_MODE, ENETC_PCS_IF_MODE_SGMII_AN);
+                        ENETC_PCS_IF_MODE, reg);
 
        /* Dev ability - SGMII */
        enetc_mdio_write(&priv->imdio, ENETC_PCS_PHY_ADDR, MDIO_DEVAD_NONE,
@@ -87,10 +163,11 @@ static int enetc_init_sgmii(struct udevice *dev)
        enetc_mdio_write(&priv->imdio, ENETC_PCS_PHY_ADDR, MDIO_DEVAD_NONE,
                         ENETC_PCS_LINK_TIMER2, ENETC_PCS_LINK_TIMER2_VAL);
 
+       reg = ENETC_PCS_CR_DEF_VAL;
+       reg |= is2500 ? ENETC_PCS_CR_RST : ENETC_PCS_CR_RESET_AN;
        /* restart PCS AN */
        enetc_mdio_write(&priv->imdio, ENETC_PCS_PHY_ADDR, MDIO_DEVAD_NONE,
-                        ENETC_PCS_CR,
-                        ENETC_PCS_CR_RESET_AN | ENETC_PCS_CR_DEF_VAL);
+                        ENETC_PCS_CR, reg);
 
        return 0;
 }
@@ -109,16 +186,34 @@ static int enetc_init_rgmii(struct udevice *dev)
        return 0;
 }
 
-/* set up MAC and serdes for SXGMII */
-static int enetc_init_sxgmii(struct udevice *dev)
+/* set up MAC configuration for the given interface type */
+static void enetc_setup_mac_iface(struct udevice *dev)
 {
        struct enetc_priv *priv = dev_get_priv(dev);
        u32 if_mode;
 
-       /* set ifmode to (US)XGMII */
-       if_mode = enetc_read_port(priv, ENETC_PM_IF_MODE);
-       if_mode &= ~ENETC_PM_IF_IFMODE_MASK;
-       enetc_write_port(priv, ENETC_PM_IF_MODE, if_mode);
+       switch (priv->if_type) {
+       case PHY_INTERFACE_MODE_RGMII:
+       case PHY_INTERFACE_MODE_RGMII_ID:
+       case PHY_INTERFACE_MODE_RGMII_RXID:
+       case PHY_INTERFACE_MODE_RGMII_TXID:
+               enetc_init_rgmii(dev);
+               break;
+       case PHY_INTERFACE_MODE_XGMII:
+       case PHY_INTERFACE_MODE_USXGMII:
+       case PHY_INTERFACE_MODE_XFI:
+               /* set ifmode to (US)XGMII */
+               if_mode = enetc_read_port(priv, ENETC_PM_IF_MODE);
+               if_mode &= ~ENETC_PM_IF_IFMODE_MASK;
+               enetc_write_port(priv, ENETC_PM_IF_MODE, if_mode);
+               break;
+       };
+}
+
+/* set up serdes for SXGMII */
+static int enetc_init_sxgmii(struct udevice *dev)
+{
+       struct enetc_priv *priv = dev_get_priv(dev);
 
        if (!enetc_has_imdio(dev))
                return 0;
@@ -130,7 +225,7 @@ static int enetc_init_sxgmii(struct udevice *dev)
        /* Restart PCS AN */
        enetc_mdio_write(&priv->imdio, ENETC_PCS_PHY_ADDR, ENETC_PCS_DEVAD_REPL,
                         ENETC_PCS_CR,
-                        ENETC_PCS_CR_LANE_RESET | ENETC_PCS_CR_RESET_AN);
+                        ENETC_PCS_CR_RST | ENETC_PCS_CR_RESET_AN);
 
        return 0;
 }
@@ -143,19 +238,14 @@ static void enetc_start_pcs(struct udevice *dev)
 
        priv->if_type = PHY_INTERFACE_MODE_NONE;
 
-       /* check internal mdio capability, not all ports need it */
+       /* register internal MDIO for debug purposes */
        if (enetc_read_port(priv, ENETC_PCAPR0) & ENETC_PCAPRO_MDIO) {
-               /*
-                * set up internal MDIO, this is part of ETH PCI function and is
-                * used to access serdes / internal SoC PHYs.
-                * We don't currently register it as a MDIO bus as it goes away
-                * when the interface is removed, so it can't practically be
-                * used in the console.
-                */
                priv->imdio.read = enetc_mdio_read;
                priv->imdio.write = enetc_mdio_write;
                priv->imdio.priv = priv->port_regs + ENETC_PM_IMDIO_BASE;
                strncpy(priv->imdio.name, dev->name, MDIO_NAME_LEN);
+               if (!miiphy_get_dev_by_name(priv->imdio.name))
+                       mdio_register(&priv->imdio);
        }
 
        if (!ofnode_valid(dev->node)) {
@@ -174,68 +264,33 @@ static void enetc_start_pcs(struct udevice *dev)
 
        switch (priv->if_type) {
        case PHY_INTERFACE_MODE_SGMII:
+       case PHY_INTERFACE_MODE_SGMII_2500:
                enetc_init_sgmii(dev);
                break;
-       case PHY_INTERFACE_MODE_RGMII:
-               enetc_init_rgmii(dev);
-               break;
        case PHY_INTERFACE_MODE_XGMII:
+       case PHY_INTERFACE_MODE_USXGMII:
+       case PHY_INTERFACE_MODE_XFI:
                enetc_init_sxgmii(dev);
                break;
        };
 }
 
 /* Configure the actual/external ethernet PHY, if one is found */
-static void enetc_start_phy(struct udevice *dev)
+static void enetc_config_phy(struct udevice *dev)
 {
        struct enetc_priv *priv = dev_get_priv(dev);
-       struct udevice *miidev;
-       struct phy_device *phy;
-       u32 phandle, phy_id;
-       ofnode phy_node;
        int supported;
 
-       if (!ofnode_valid(dev->node)) {
-               enetc_dbg(dev, "no enetc ofnode found, skipping PHY set-up\n");
-               return;
-       }
+       priv->phy = dm_eth_phy_connect(dev);
 
-       if (ofnode_read_u32(dev->node, "phy-handle", &phandle)) {
-               enetc_dbg(dev, "phy-handle not found, skipping PHY set-up\n");
+       if (!priv->phy)
                return;
-       }
 
-       phy_node = ofnode_get_by_phandle(phandle);
-       if (!ofnode_valid(phy_node)) {
-               enetc_dbg(dev, "invalid phy node, skipping PHY set-up\n");
-               return;
-       }
-       enetc_dbg(dev, "phy node: %s\n", ofnode_get_name(phy_node));
-
-       if (ofnode_read_u32(phy_node, "reg", &phy_id)) {
-               enetc_dbg(dev,
-                         "missing reg in PHY node, skipping PHY set-up\n");
-               return;
-       }
+       supported = PHY_GBIT_FEATURES | SUPPORTED_2500baseX_Full;
+       priv->phy->supported &= supported;
+       priv->phy->advertising &= supported;
 
-       if (uclass_get_device_by_ofnode(UCLASS_MDIO,
-                                       ofnode_get_parent(phy_node),
-                                       &miidev)) {
-               enetc_dbg(dev, "can't find MDIO bus for node %s\n",
-                         ofnode_get_name(ofnode_get_parent(phy_node)));
-               return;
-       }
-
-       phy = dm_mdio_phy_connect(miidev, phy_id, dev, priv->if_type);
-       if (!phy) {
-               enetc_dbg(dev, "dm_mdio_phy_connect returned null\n");
-               return;
-       }
-
-       supported = GENMASK(6, 0); /* speeds up to 1G & AN */
-       phy->advertising = phy->supported & supported;
-       phy_config(phy);
-       phy_startup(phy);
+       phy_config(priv->phy);
 }
 
 /*
@@ -274,6 +329,9 @@ static int enetc_probe(struct udevice *dev)
 
        dm_pci_clrset_config16(dev, PCI_COMMAND, 0, PCI_COMMAND_MEMORY);
 
+       enetc_start_pcs(dev);
+       enetc_config_phy(dev);
+
        return 0;
 }
 
@@ -291,14 +349,57 @@ static int enetc_remove(struct udevice *dev)
        return 0;
 }
 
-/* ENETC Port MAC address registers, accepts big-endian format */
-static void enetc_set_primary_mac_addr(struct enetc_priv *priv, const u8 *addr)
+/*
+ * LS1028A is the only part with IERB at this time and there are plans to
+ * change its structure, keep this LS1028A specific for now.
+ */
+#define LS1028A_IERB_BASE              0x1f0800000ULL
+#define LS1028A_IERB_PSIPMAR0(pf, vf)  (LS1028A_IERB_BASE + 0x8000 \
+                                        + (pf) * 0x100 + (vf) * 8)
+#define LS1028A_IERB_PSIPMAR1(pf, vf)  (LS1028A_IERB_PSIPMAR0(pf, vf) + 4)
+
+static int enetc_ls1028a_write_hwaddr(struct udevice *dev)
+{
+       struct pci_child_platdata *ppdata = dev_get_parent_platdata(dev);
+       const int devfn_to_pf[] = {0, 1, 2, -1, -1, -1, 3};
+       struct eth_pdata *plat = dev_get_platdata(dev);
+       int devfn = PCI_FUNC(ppdata->devfn);
+       u8 *addr = plat->enetaddr;
+       u32 lower, upper;
+       int pf;
+
+       if (devfn >= ARRAY_SIZE(devfn_to_pf))
+               return 0;
+
+       pf = devfn_to_pf[devfn];
+       if (pf < 0)
+               return 0;
+
+       lower = *(const u16 *)(addr + 4);
+       upper = *(const u32 *)addr;
+
+       out_le32(LS1028A_IERB_PSIPMAR0(pf, 0), upper);
+       out_le32(LS1028A_IERB_PSIPMAR1(pf, 0), lower);
+
+       return 0;
+}
+
+static int enetc_write_hwaddr(struct udevice *dev)
 {
+       struct eth_pdata *plat = dev_get_platdata(dev);
+       struct enetc_priv *priv = dev_get_priv(dev);
+       u8 *addr = plat->enetaddr;
+
+       if (IS_ENABLED(CONFIG_ARCH_LS1028A))
+               return enetc_ls1028a_write_hwaddr(dev);
+
        u16 lower = *(const u16 *)(addr + 4);
        u32 upper = *(const u32 *)addr;
 
        enetc_write_port(priv, ENETC_PSIPMAR0, upper);
        enetc_write_port(priv, ENETC_PSIPMAR1, lower);
+
+       return 0;
 }
 
 /* Configure port parameters (# of rings, frame size, enable port) */
@@ -429,7 +530,6 @@ static void enetc_setup_rx_bdr(struct udevice *dev)
  */
 static int enetc_start(struct udevice *dev)
 {
-       struct eth_pdata *plat = dev_get_platdata(dev);
        struct enetc_priv *priv = dev_get_priv(dev);
 
        /* reset and enable the PCI device */
@@ -437,20 +537,16 @@ static int enetc_start(struct udevice *dev)
        dm_pci_clrset_config16(dev, PCI_COMMAND, 0,
                               PCI_COMMAND_MEMORY | PCI_COMMAND_MASTER);
 
-       if (!is_valid_ethaddr(plat->enetaddr)) {
-               enetc_dbg(dev, "invalid MAC address, generate random ...\n");
-               net_random_ethaddr(plat->enetaddr);
-       }
-       enetc_set_primary_mac_addr(priv, plat->enetaddr);
-
        enetc_enable_si_port(priv);
 
        /* setup Tx/Rx buffer descriptors */
        enetc_setup_tx_bdr(dev);
        enetc_setup_rx_bdr(dev);
 
-       enetc_start_pcs(dev);
-       enetc_start_phy(dev);
+       enetc_setup_mac_iface(dev);
+
+       if (priv->phy)
+               phy_startup(priv->phy);
 
        return 0;
 }
@@ -464,6 +560,10 @@ static void enetc_stop(struct udevice *dev)
 {
        /* FLR is sufficient to quiesce the device */
        dm_pci_flr(dev);
+       /* leave the BARs accessible after we stop, this is needed to use
+        * internal MDIO in command line.
+        */
+       dm_pci_clrset_config16(dev, PCI_COMMAND, 0, PCI_COMMAND_MEMORY);
 }
 
 /*
@@ -562,10 +662,11 @@ static const struct eth_ops enetc_ops = {
        .send   = enetc_send,
        .recv   = enetc_recv,
        .stop   = enetc_stop,
+       .write_hwaddr = enetc_write_hwaddr,
 };
 
 U_BOOT_DRIVER(eth_enetc) = {
-       .name   = "enetc_eth",
+       .name   = ENETC_DRIVER_NAME,
        .id     = UCLASS_ETH,
        .bind   = enetc_bind,
        .probe  = enetc_probe,