On Thu, Jul 09, 2020 at 11:35:25PM +0200, Michael Walle wrote:
> ENETC has ethernet MACs capable of SGMII, 2500BaseX and USXGMII. But in
> order to use these protocols some SerDes configurations need to be
> performed. The SerDes is configurable via an internal PCS PHY which is
> connected to an internal MDIO bus at address 0.
> 
> This patch basically removes the dependency on bootloader regarding
> SerDes initialization.
> 
> Signed-off-by: Michael Walle <mich...@walle.cc>
> Reviewed-by: Claudiu Manoil <claudiu.man...@nxp.com>
> ---

As mentioned earlier, this works for SGMII. For USXGMII, it would also
need this patch to work (something which is also true of Felix):

https://patchwork.ozlabs.org/project/netdev/patch/20200712164815.1763532-1-olte...@gmail.com/

Considering that patch as external to this series:

Tested-by: Vladimir Oltean <vladimir.olt...@nxp.com>

>  .../net/ethernet/freescale/enetc/enetc_hw.h   |   3 +
>  .../net/ethernet/freescale/enetc/enetc_pf.c   | 135 ++++++++++++++++++
>  .../net/ethernet/freescale/enetc/enetc_pf.h   |   2 +
>  3 files changed, 140 insertions(+)
> 
> diff --git a/drivers/net/ethernet/freescale/enetc/enetc_hw.h 
> b/drivers/net/ethernet/freescale/enetc/enetc_hw.h
> index fc357bc56835..135bf46354ea 100644
> --- a/drivers/net/ethernet/freescale/enetc/enetc_hw.h
> +++ b/drivers/net/ethernet/freescale/enetc/enetc_hw.h
> @@ -224,6 +224,9 @@ enum enetc_bdr_type {TX, RX};
>  #define ENETC_PM0_MAXFRM     0x8014
>  #define ENETC_SET_TX_MTU(val)        ((val) << 16)
>  #define ENETC_SET_MAXFRM(val)        ((val) & 0xffff)
> +
> +#define ENETC_PM_IMDIO_BASE  0x8030
> +
>  #define ENETC_PM0_IF_MODE    0x8300
>  #define ENETC_PMO_IFM_RG     BIT(2)
>  #define ENETC_PM0_IFM_RLP    (BIT(5) | BIT(11))
> diff --git a/drivers/net/ethernet/freescale/enetc/enetc_pf.c 
> b/drivers/net/ethernet/freescale/enetc/enetc_pf.c
> index 4fac57dbb3c8..662740874841 100644
> --- a/drivers/net/ethernet/freescale/enetc/enetc_pf.c
> +++ b/drivers/net/ethernet/freescale/enetc/enetc_pf.c
> @@ -1,6 +1,7 @@
>  // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
>  /* Copyright 2017-2019 NXP */
>  
> +#include <linux/mdio.h>
>  #include <linux/module.h>
>  #include <linux/fsl/enetc_mdio.h>
>  #include <linux/of_mdio.h>
> @@ -833,6 +834,135 @@ static void enetc_of_put_phy(struct enetc_ndev_priv 
> *priv)
>               of_node_put(priv->phy_node);
>  }
>  
> +static int enetc_imdio_init(struct enetc_pf *pf, bool is_c45)
> +{
> +     struct device *dev = &pf->si->pdev->dev;
> +     struct enetc_mdio_priv *mdio_priv;
> +     struct phy_device *pcs;
> +     struct mii_bus *bus;
> +     int err;
> +
> +     bus = mdiobus_alloc_size(sizeof(*mdio_priv));
> +     if (!bus)
> +             return -ENOMEM;
> +
> +     bus->name = "Freescale ENETC internal MDIO Bus";
> +     bus->read = enetc_mdio_read;
> +     bus->write = enetc_mdio_write;
> +     bus->parent = dev;
> +     bus->phy_mask = ~0;
> +     mdio_priv = bus->priv;
> +     mdio_priv->hw = &pf->si->hw;
> +     mdio_priv->mdio_base = ENETC_PM_IMDIO_BASE;
> +     snprintf(bus->id, MII_BUS_ID_SIZE, "%s-imdio", dev_name(dev));
> +
> +     err = mdiobus_register(bus);
> +     if (err) {
> +             dev_err(dev, "cannot register internal MDIO bus (%d)\n", err);
> +             goto free_mdio_bus;
> +     }
> +
> +     pcs = get_phy_device(bus, 0, is_c45);
> +     if (IS_ERR(pcs)) {
> +             err = PTR_ERR(pcs);
> +             dev_err(dev, "cannot get internal PCS PHY (%d)\n", err);
> +             goto unregister_mdiobus;
> +     }
> +
> +     pf->imdio = bus;
> +     pf->pcs = pcs;
> +
> +     return 0;
> +
> +unregister_mdiobus:
> +     mdiobus_unregister(bus);
> +free_mdio_bus:
> +     mdiobus_free(bus);
> +     return err;
> +}
> +
> +static void enetc_imdio_remove(struct enetc_pf *pf)
> +{
> +     if (pf->pcs)
> +             put_device(&pf->pcs->mdio.dev);
> +     if (pf->imdio) {
> +             mdiobus_unregister(pf->imdio);
> +             mdiobus_free(pf->imdio);
> +     }
> +}
> +
> +static void enetc_configure_sgmii(struct phy_device *pcs)
> +{
> +     /* SGMII spec requires tx_config_Reg[15:0] to be exactly 0x4001
> +      * for the MAC PCS in order to acknowledge the AN.
> +      */
> +     phy_write(pcs, MII_ADVERTISE, ADVERTISE_SGMII | ADVERTISE_LPACK);
> +
> +     phy_write(pcs, ENETC_PCS_IF_MODE,
> +               ENETC_PCS_IF_MODE_SGMII_EN |
> +               ENETC_PCS_IF_MODE_USE_SGMII_AN);
> +
> +     /* Adjust link timer for SGMII */
> +     phy_write(pcs, ENETC_PCS_LINK_TIMER1, ENETC_PCS_LINK_TIMER1_VAL);
> +     phy_write(pcs, ENETC_PCS_LINK_TIMER2, ENETC_PCS_LINK_TIMER2_VAL);
> +
> +     phy_write(pcs, MII_BMCR, BMCR_ANRESTART | BMCR_ANENABLE);
> +}
> +
> +static void enetc_configure_2500basex(struct phy_device *pcs)
> +{
> +     phy_write(pcs, ENETC_PCS_IF_MODE,
> +               ENETC_PCS_IF_MODE_SGMII_EN |
> +               ENETC_PCS_IF_MODE_SGMII_SPEED(ENETC_PCS_SPEED_2500));
> +
> +     phy_write(pcs, MII_BMCR, BMCR_SPEED1000 | BMCR_FULLDPLX | BMCR_RESET);
> +}
> +
> +static void enetc_configure_usxgmii(struct phy_device *pcs)
> +{
> +     /* Configure device ability for the USXGMII Replicator */
> +     phy_write_mmd(pcs, MDIO_MMD_VEND2, MII_ADVERTISE,
> +                   ADVERTISE_SGMII | ADVERTISE_LPACK |
> +                   MDIO_LPA_USXGMII_FULL_DUPLEX);
> +
> +     /* Restart PCS AN */
> +     phy_write_mmd(pcs, MDIO_MMD_VEND2, MII_BMCR,
> +                   BMCR_RESET | BMCR_ANENABLE | BMCR_ANRESTART);
> +}
> +
> +static int enetc_configure_serdes(struct enetc_ndev_priv *priv)
> +{
> +     bool is_c45 = priv->if_mode == PHY_INTERFACE_MODE_USXGMII;
> +     struct enetc_pf *pf = enetc_si_priv(priv->si);
> +     int err;
> +
> +     if (priv->if_mode != PHY_INTERFACE_MODE_SGMII &&
> +         priv->if_mode != PHY_INTERFACE_MODE_2500BASEX &&
> +         priv->if_mode != PHY_INTERFACE_MODE_USXGMII)
> +             return 0;
> +
> +     err = enetc_imdio_init(pf, is_c45);
> +     if (err)
> +             return err;
> +
> +     switch (priv->if_mode) {
> +     case PHY_INTERFACE_MODE_SGMII:
> +             enetc_configure_sgmii(pf->pcs);
> +             break;
> +     case PHY_INTERFACE_MODE_2500BASEX:
> +             enetc_configure_2500basex(pf->pcs);
> +             break;
> +     case PHY_INTERFACE_MODE_USXGMII:
> +             enetc_configure_usxgmii(pf->pcs);
> +             break;
> +     default:
> +             dev_err(&pf->si->pdev->dev, "Unsupported link mode %s\n",
> +                     phy_modes(priv->if_mode));
> +     }
> +
> +     return 0;
> +}
> +
>  static int enetc_pf_probe(struct pci_dev *pdev,
>                         const struct pci_device_id *ent)
>  {
> @@ -897,6 +1027,10 @@ static int enetc_pf_probe(struct pci_dev *pdev,
>       if (err)
>               dev_warn(&pdev->dev, "Fallback to PHY-less operation\n");
>  
> +     err = enetc_configure_serdes(priv);
> +     if (err)
> +             dev_warn(&pdev->dev, "Attempted SerDes config but failed\n");
> +
>       err = register_netdev(ndev);
>       if (err)
>               goto err_reg_netdev;
> @@ -932,6 +1066,7 @@ static void enetc_pf_remove(struct pci_dev *pdev)
>       priv = netdev_priv(si->ndev);
>       unregister_netdev(si->ndev);
>  
> +     enetc_imdio_remove(pf);
>       enetc_mdio_remove(pf);
>       enetc_of_put_phy(priv);
>  
> diff --git a/drivers/net/ethernet/freescale/enetc/enetc_pf.h 
> b/drivers/net/ethernet/freescale/enetc/enetc_pf.h
> index 59e65a6f6c3e..2cb922b59f46 100644
> --- a/drivers/net/ethernet/freescale/enetc/enetc_pf.h
> +++ b/drivers/net/ethernet/freescale/enetc/enetc_pf.h
> @@ -44,6 +44,8 @@ struct enetc_pf {
>       DECLARE_BITMAP(active_vlans, VLAN_N_VID);
>  
>       struct mii_bus *mdio; /* saved for cleanup */
> +     struct mii_bus *imdio;
> +     struct phy_device *pcs;
>  };
>  
>  int enetc_msg_psi_init(struct enetc_pf *pf);
> -- 
> 2.20.1
> 

Reply via email to