#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
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,
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;
}
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;
/* 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;
}
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)) {
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);
}
/*
dm_pci_clrset_config16(dev, PCI_COMMAND, 0, PCI_COMMAND_MEMORY);
+ enetc_start_pcs(dev);
+ enetc_config_phy(dev);
+
return 0;
}
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) */
*/
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 */
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;
}
{
/* 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);
}
/*
.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,