diff --git a/drivers/net/fsl_enetc.c b/drivers/net/fsl_enetc.c index f14b484b26e..da533a1e5db 100644 --- a/drivers/net/fsl_enetc.c +++ b/drivers/net/fsl_enetc.c @@ -39,6 +39,152 @@ static int enetc_bind(struct udevice *dev) return 0; } +/* MDIO wrappers, we're using these to drive internal MDIO to get to serdes */ +static int enetc_mdio_read(struct mii_dev *bus, int addr, int devad, int reg) +{ + struct enetc_mdio_priv priv; + + priv.regs_base = bus->priv; + return enetc_mdio_read_priv(&priv, addr, devad, reg); +} + +static int enetc_mdio_write(struct mii_dev *bus, int addr, int devad, int reg, + u16 val) +{ + struct enetc_mdio_priv priv; + + priv.regs_base = bus->priv; + return enetc_mdio_write_priv(&priv, addr, devad, reg, val); +} + +/* only interfaces that can pin out through serdes have internal MDIO */ +static bool enetc_has_imdio(struct udevice *dev) +{ + struct enetc_priv *priv = dev_get_priv(dev); + + return !!(priv->imdio.priv); +} + +/* set up serdes for SGMII */ +static int enetc_init_sgmii(struct udevice *dev) +{ + struct enetc_priv *priv = dev_get_priv(dev); + + if (!enetc_has_imdio(dev)) + return 0; + + /* Set to SGMII mode, use AN */ + enetc_mdio_write(&priv->imdio, ENETC_PCS_PHY_ADDR, MDIO_DEVAD_NONE, + ENETC_PCS_IF_MODE, ENETC_PCS_IF_MODE_SGMII_AN); + + /* Dev ability - SGMII */ + enetc_mdio_write(&priv->imdio, ENETC_PCS_PHY_ADDR, MDIO_DEVAD_NONE, + ENETC_PCS_DEV_ABILITY, ENETC_PCS_DEV_ABILITY_SGMII); + + /* Adjust link timer for SGMII */ + enetc_mdio_write(&priv->imdio, ENETC_PCS_PHY_ADDR, MDIO_DEVAD_NONE, + ENETC_PCS_LINK_TIMER1, ENETC_PCS_LINK_TIMER1_VAL); + enetc_mdio_write(&priv->imdio, ENETC_PCS_PHY_ADDR, MDIO_DEVAD_NONE, + ENETC_PCS_LINK_TIMER2, ENETC_PCS_LINK_TIMER2_VAL); + + /* 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); + + return 0; +} + +/* set up MAC for RGMII */ +static int enetc_init_rgmii(struct udevice *dev) +{ + struct enetc_priv *priv = dev_get_priv(dev); + u32 if_mode; + + /* enable RGMII AN */ + if_mode = enetc_read_port(priv, ENETC_PM_IF_MODE); + if_mode |= ENETC_PM_IF_MODE_AN_ENA; + enetc_write_port(priv, ENETC_PM_IF_MODE, if_mode); + + return 0; +} + +/* set up MAC and serdes for SXGMII */ +static int enetc_init_sxgmii(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); + + if (!enetc_has_imdio(dev)) + return 0; + + /* Dev ability - SXGMII */ + enetc_mdio_write(&priv->imdio, ENETC_PCS_PHY_ADDR, ENETC_PCS_DEVAD_REPL, + ENETC_PCS_DEV_ABILITY, ENETC_PCS_DEV_ABILITY_SXGMII); + + /* 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); + + return 0; +} + +/* Apply protocol specific configuration to MAC, serdes as needed */ +static void enetc_start_pcs(struct udevice *dev) +{ + struct enetc_priv *priv = dev_get_priv(dev); + const char *if_str; + + priv->if_type = PHY_INTERFACE_MODE_NONE; + + /* check internal mdio capability, not all ports need it */ + 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 (!ofnode_valid(dev->node)) { + enetc_dbg(dev, "no enetc ofnode found, skipping PCS set-up\n"); + return; + } + + if_str = ofnode_read_string(dev->node, "phy-mode"); + if (if_str) + priv->if_type = phy_get_interface_by_name(if_str); + else + enetc_dbg(dev, + "phy-mode property not found, defaulting to SGMII\n"); + if (priv->if_type < 0) + priv->if_type = PHY_INTERFACE_MODE_NONE; + + switch (priv->if_type) { + case PHY_INTERFACE_MODE_SGMII: + enetc_init_sgmii(dev); + break; + case PHY_INTERFACE_MODE_RGMII: + enetc_init_rgmii(dev); + break; + case PHY_INTERFACE_MODE_XGMII: + enetc_init_sxgmii(dev); + break; + }; +} + /* Configure the actual/external ethernet PHY, if one is found */ static void enetc_start_phy(struct udevice *dev) { @@ -303,7 +449,7 @@ static int enetc_start(struct udevice *dev) enetc_setup_tx_bdr(dev); enetc_setup_rx_bdr(dev); - priv->if_type = PHY_INTERFACE_MODE_NONE; + enetc_start_pcs(dev); enetc_start_phy(dev); return 0; diff --git a/drivers/net/fsl_enetc.h b/drivers/net/fsl_enetc.h index fbb9dfa15e4..7ac7c1fefea 100644 --- a/drivers/net/fsl_enetc.h +++ b/drivers/net/fsl_enetc.h @@ -61,6 +61,8 @@ enum enetc_bdr_type {TX, RX}; #define ENETC_PSIPMMR 0x0018 #define ENETC_PSIPMAR0 0x0100 #define ENETC_PSIPMAR1 0x0104 +#define ENETC_PCAPR0 0x0900 +#define ENETC_PCAPRO_MDIO BIT(11) #define ENETC_PSICFGR(n) (0x0940 + (n) * 0x10) #define ENETC_PSICFGR_SET_TXBDR(val) ((val) & 0xff) #define ENETC_PSICFGR_SET_RXBDR(val) (((val) & 0xff) << 16) @@ -70,6 +72,11 @@ enum enetc_bdr_type {TX, RX}; #define ENETC_PM_CC_RX_TX_EN 0x8813 #define ENETC_PM_MAXFRM 0x8014 #define ENETC_RX_MAXFRM_SIZE PKTSIZE_ALIGN +#define ENETC_PM_IMDIO_BASE 0x8030 +#define ENETC_PM_IF_MODE 0x8300 +#define ENETC_PM_IF_MODE_RG BIT(2) +#define ENETC_PM_IF_MODE_AN_ENA BIT(15) +#define ENETC_PM_IF_IFMODE_MASK GENMASK(1, 0) /* buffer descriptors count must be multiple of 8 and aligned to 128 bytes */ #define ENETC_BD_CNT CONFIG_SYS_RX_ETH_BUFFER @@ -146,6 +153,7 @@ struct enetc_priv { struct bd_ring rx_bdr; int if_type; + struct mii_dev imdio; }; /* register accessors */ @@ -168,6 +176,27 @@ struct enetc_priv { #define enetc_bdr_write(priv, t, n, off, val) \ enetc_write(priv, ENETC_BDR(t, n, off), val) +/* PCS / internal SoC PHY ID, it defaults to 0 on all interfaces */ +#define ENETC_PCS_PHY_ADDR 0 + +/* PCS registers */ +#define ENETC_PCS_CR 0x00 +#define ENETC_PCS_CR_RESET_AN 0x1200 +#define ENETC_PCS_CR_DEF_VAL 0x0140 +#define ENETC_PCS_CR_LANE_RESET 0x8000 +#define ENETC_PCS_DEV_ABILITY 0x04 +#define ENETC_PCS_DEV_ABILITY_SGMII 0x4001 +#define ENETC_PCS_DEV_ABILITY_SXGMII 0x5001 +#define ENETC_PCS_LINK_TIMER1 0x12 +#define ENETC_PCS_LINK_TIMER1_VAL 0x06a0 +#define ENETC_PCS_LINK_TIMER2 0x13 +#define ENETC_PCS_LINK_TIMER2_VAL 0x0003 +#define ENETC_PCS_IF_MODE 0x14 +#define ENETC_PCS_IF_MODE_SGMII_AN 0x0003 + +/* PCS replicator block for USXGMII */ +#define ENETC_PCS_DEVAD_REPL 0x1f + /* ENETC external MDIO registers */ #define ENETC_MDIO_BASE 0x1c00 #define ENETC_MDIO_CFG 0x00 @@ -186,4 +215,13 @@ struct enetc_mdio_priv { void *regs_base; }; +/* + * these functions are implemented by ENETC_MDIO and are re-used by ENETC driver + * to drive serdes / internal SoC PHYs + */ +int enetc_mdio_read_priv(struct enetc_mdio_priv *priv, int addr, int devad, + int reg); +int enetc_mdio_write_priv(struct enetc_mdio_priv *priv, int addr, int devad, + int reg, u16 val); + #endif /* _ENETC_H */ diff --git a/drivers/net/fsl_enetc_mdio.c b/drivers/net/fsl_enetc_mdio.c index 46afac06d7c..60d21537b80 100644 --- a/drivers/net/fsl_enetc_mdio.c +++ b/drivers/net/fsl_enetc_mdio.c @@ -21,8 +21,8 @@ static void enetc_mdio_wait_bsy(struct enetc_mdio_priv *priv) cpu_relax(); } -static int enetc_mdio_read_priv(struct enetc_mdio_priv *priv, int addr, - int devad, int reg) +int enetc_mdio_read_priv(struct enetc_mdio_priv *priv, int addr, int devad, + int reg) { if (devad == MDIO_DEVAD_NONE) enetc_write(priv, ENETC_MDIO_CFG, ENETC_EMDIO_CFG_C22); @@ -51,8 +51,8 @@ static int enetc_mdio_read_priv(struct enetc_mdio_priv *priv, int addr, return enetc_read(priv, ENETC_MDIO_DATA); } -static int enetc_mdio_write_priv(struct enetc_mdio_priv *priv, int addr, - int devad, int reg, u16 val) +int enetc_mdio_write_priv(struct enetc_mdio_priv *priv, int addr, int devad, + int reg, u16 val) { if (devad == MDIO_DEVAD_NONE) enetc_write(priv, ENETC_MDIO_CFG, ENETC_EMDIO_CFG_C22);