#include "enetc.h"
 #include <linux/tcp.h>
 #include <linux/udp.h>
-#include <linux/of_mdio.h>
 #include <linux/vmalloc.h>
 
 /* ENETC overhead: optional extension BD + 1 BD gap */
                enetc_rxbdr_wr(&priv->si->hw, i, ENETC_RBIER, 0);
 }
 
-static void adjust_link(struct net_device *ndev)
+static int enetc_phylink_connect(struct net_device *ndev)
 {
        struct enetc_ndev_priv *priv = netdev_priv(ndev);
-       struct phy_device *phydev = ndev->phydev;
-
-       if (priv->active_offloads & ENETC_F_QBV)
-               enetc_sched_speed_set(ndev);
-
-       phy_print_status(phydev);
-}
-
-static int enetc_phy_connect(struct net_device *ndev)
-{
-       struct enetc_ndev_priv *priv = netdev_priv(ndev);
-       struct phy_device *phydev;
        struct ethtool_eee edata;
+       int err;
 
-       if (!priv->phy_node)
+       if (!priv->phylink)
                return 0; /* phy-less mode */
 
-       phydev = of_phy_connect(ndev, priv->phy_node, &adjust_link,
-                               0, priv->if_mode);
-       if (!phydev) {
+       err = phylink_of_phy_connect(priv->phylink, priv->dev->of_node, 0);
+       if (err) {
                dev_err(&ndev->dev, "could not attach to PHY\n");
-               return -ENODEV;
+               return err;
        }
 
-       phy_attached_info(phydev);
-
        /* disable EEE autoneg, until ENETC driver supports it */
        memset(&edata, 0, sizeof(struct ethtool_eee));
-       phy_ethtool_set_eee(phydev, &edata);
+       phylink_ethtool_set_eee(priv->phylink, &edata);
 
        return 0;
 }
                enable_irq(irq);
        }
 
-       if (ndev->phydev)
-               phy_start(ndev->phydev);
+       if (priv->phylink)
+               phylink_start(priv->phylink);
        else
                netif_carrier_on(ndev);
 
        if (err)
                return err;
 
-       err = enetc_phy_connect(ndev);
+       err = enetc_phylink_connect(ndev);
        if (err)
                goto err_phy_connect;
 
 err_alloc_rx:
        enetc_free_tx_resources(priv);
 err_alloc_tx:
-       if (ndev->phydev)
-               phy_disconnect(ndev->phydev);
+       if (priv->phylink)
+               phylink_disconnect_phy(priv->phylink);
 err_phy_connect:
        enetc_free_irqs(priv);
 
                napi_disable(&priv->int_vector[i]->napi);
        }
 
-       if (ndev->phydev)
-               phy_stop(ndev->phydev);
+       if (priv->phylink)
+               phylink_stop(priv->phylink);
        else
                netif_carrier_off(ndev);
 
        enetc_stop(ndev);
        enetc_clear_bdrs(priv);
 
-       if (ndev->phydev)
-               phy_disconnect(ndev->phydev);
+       if (priv->phylink)
+               phylink_disconnect_phy(priv->phylink);
        enetc_free_rxtx_rings(priv);
        enetc_free_rx_resources(priv);
        enetc_free_tx_resources(priv);
 
 int enetc_ioctl(struct net_device *ndev, struct ifreq *rq, int cmd)
 {
+       struct enetc_ndev_priv *priv = netdev_priv(ndev);
 #ifdef CONFIG_FSL_ENETC_PTP_CLOCK
        if (cmd == SIOCSHWTSTAMP)
                return enetc_hwtstamp_set(ndev, rq);
                return enetc_hwtstamp_get(ndev, rq);
 #endif
 
-       if (!ndev->phydev)
+       if (!priv->phylink)
                return -EOPNOTSUPP;
-       return phy_mii_ioctl(ndev->phydev, rq, cmd);
+
+       return phylink_mii_ioctl(priv->phylink, rq, cmd);
 }
 
 int enetc_alloc_msix(struct enetc_ndev_priv *priv)
 
 
 static void enetc_mac_config(struct enetc_hw *hw, phy_interface_t phy_mode)
 {
-       u32 val;
-
        /* set auto-speed for RGMII */
        if (enetc_port_rd(hw, ENETC_PM0_IF_MODE) & ENETC_PMO_IFM_RG ||
            phy_interface_mode_is_rgmii(phy_mode))
 
        if (phy_mode == PHY_INTERFACE_MODE_USXGMII)
                enetc_port_wr(hw, ENETC_PM0_IF_MODE, ENETC_PM0_IFM_XGMII);
+}
+
+static void enetc_mac_enable(struct enetc_hw *hw, bool en)
+{
+       u32 val = enetc_port_rd(hw, ENETC_PM0_CMD_CFG);
 
-       /* enable Rx and Tx */
-       val = enetc_port_rd(hw, ENETC_PM0_CMD_CFG);
-       enetc_port_wr(hw, ENETC_PM0_CMD_CFG,
-                     val | ENETC_PM0_TX_EN | ENETC_PM0_RX_EN);
+       val &= ~(ENETC_PM0_TX_EN | ENETC_PM0_RX_EN);
+       val |= en ? (ENETC_PM0_TX_EN | ENETC_PM0_RX_EN) : 0;
 
-       enetc_port_wr(hw, ENETC_PM1_CMD_CFG,
-                     val | ENETC_PM0_TX_EN | ENETC_PM0_RX_EN);
+       enetc_port_wr(hw, ENETC_PM0_CMD_CFG, val);
+       enetc_port_wr(hw, ENETC_PM1_CMD_CFG, val);
 }
 
 static void enetc_configure_port_pmac(struct enetc_hw *hw)
                mdiobus_unregister(pf->mdio);
 }
 
-static int enetc_of_get_phy(struct enetc_pf *pf)
-{
-       struct device *dev = &pf->si->pdev->dev;
-       struct device_node *np = dev->of_node;
-       int err;
-
-       pf->phy_node = of_parse_phandle(np, "phy-handle", 0);
-       if (!pf->phy_node) {
-               if (!of_phy_is_fixed_link(np)) {
-                       dev_dbg(dev, "PHY not specified\n");
-                       return 0;
-               }
-
-               err = of_phy_register_fixed_link(np);
-               if (err < 0) {
-                       dev_err(dev, "fixed link registration failed\n");
-                       return err;
-               }
-
-               pf->phy_node = of_node_get(np);
-       }
-
-       err = of_get_phy_mode(np, &pf->if_mode);
-       if (err) {
-               dev_err(dev, "missing phy type\n");
-               of_node_put(pf->phy_node);
-               if (of_phy_is_fixed_link(np))
-                       of_phy_deregister_fixed_link(np);
-
-               return -EINVAL;
-       }
-
-       return 0;
-}
-
-static void enetc_of_put_phy(struct enetc_pf *pf)
-{
-       struct device_node *np = pf->si->pdev->dev.of_node;
-
-       if (np && of_phy_is_fixed_link(np))
-               of_phy_deregister_fixed_link(np);
-       if (pf->phy_node)
-               of_node_put(pf->phy_node);
-}
-
 static int enetc_imdio_create(struct enetc_pf *pf)
 {
        struct device *dev = &pf->si->pdev->dev;
        struct enetc_mdio_priv *mdio_priv;
-       struct phy_device *pcs;
+       struct lynx_pcs *pcs_lynx;
+       struct mdio_device *pcs;
        struct mii_bus *bus;
        int err;
 
                goto free_mdio_bus;
        }
 
-       pcs = get_phy_device(bus, 0, pf->if_mode == PHY_INTERFACE_MODE_USXGMII);
+       pcs = mdio_device_create(bus, 0);
        if (IS_ERR(pcs)) {
                err = PTR_ERR(pcs);
-               dev_err(dev, "cannot get internal PCS PHY (%d)\n", err);
+               dev_err(dev, "cannot create pcs (%d)\n", err);
+               goto unregister_mdiobus;
+       }
+
+       pcs_lynx = lynx_pcs_create(pcs);
+       if (!pcs_lynx) {
+               mdio_device_free(pcs);
+               err = -ENOMEM;
+               dev_err(dev, "cannot create lynx pcs (%d)\n", err);
                goto unregister_mdiobus;
        }
 
        pf->imdio = bus;
-       pf->pcs = pcs;
+       pf->pcs = pcs_lynx;
 
        return 0;
 
 
 static void enetc_imdio_remove(struct enetc_pf *pf)
 {
-       if (pf->pcs)
-               put_device(&pf->pcs->mdio.dev);
+       if (pf->pcs) {
+               mdio_device_free(pf->pcs->mdio);
+               lynx_pcs_destroy(pf->pcs);
+       }
        if (pf->imdio) {
                mdiobus_unregister(pf->imdio);
                mdiobus_free(pf->imdio);
        enetc_imdio_remove(pf);
 }
 
-static void enetc_configure_sgmii(struct phy_device *pcs)
+static void enetc_pl_mac_validate(struct phylink_config *config,
+                                 unsigned long *supported,
+                                 struct phylink_link_state *state)
 {
-       /* 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);
+       __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
+
+       if (state->interface != PHY_INTERFACE_MODE_NA &&
+           state->interface != PHY_INTERFACE_MODE_INTERNAL &&
+           state->interface != PHY_INTERFACE_MODE_SGMII &&
+           state->interface != PHY_INTERFACE_MODE_2500BASEX &&
+           state->interface != PHY_INTERFACE_MODE_USXGMII &&
+           !phy_interface_mode_is_rgmii(state->interface)) {
+               bitmap_zero(supported, __ETHTOOL_LINK_MODE_MASK_NBITS);
+               return;
+       }
 
-       phy_write(pcs, ENETC_PCS_IF_MODE,
-                 ENETC_PCS_IF_MODE_SGMII_EN |
-                 ENETC_PCS_IF_MODE_USE_SGMII_AN);
+       phylink_set_port_modes(mask);
+       phylink_set(mask, Autoneg);
+       phylink_set(mask, Pause);
+       phylink_set(mask, Asym_Pause);
+       phylink_set(mask, 10baseT_Half);
+       phylink_set(mask, 10baseT_Full);
+       phylink_set(mask, 100baseT_Half);
+       phylink_set(mask, 100baseT_Full);
+       phylink_set(mask, 100baseT_Half);
+       phylink_set(mask, 1000baseT_Half);
+       phylink_set(mask, 1000baseT_Full);
+
+       if (state->interface == PHY_INTERFACE_MODE_INTERNAL ||
+           state->interface == PHY_INTERFACE_MODE_2500BASEX ||
+           state->interface == PHY_INTERFACE_MODE_USXGMII) {
+               phylink_set(mask, 2500baseT_Full);
+               phylink_set(mask, 2500baseX_Full);
+       }
+
+       bitmap_and(supported, supported, mask,
+                  __ETHTOOL_LINK_MODE_MASK_NBITS);
+       bitmap_and(state->advertising, state->advertising, mask,
+                  __ETHTOOL_LINK_MODE_MASK_NBITS);
+}
 
-       /* 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);
+static void enetc_pl_mac_config(struct phylink_config *config,
+                               unsigned int mode,
+                               const struct phylink_link_state *state)
+{
+       struct enetc_pf *pf = phylink_to_enetc_pf(config);
+       struct enetc_ndev_priv *priv;
 
-       phy_write(pcs, MII_BMCR, BMCR_ANRESTART | BMCR_ANENABLE);
+       enetc_mac_config(&pf->si->hw, state->interface);
+
+       priv = netdev_priv(pf->si->ndev);
+       if (pf->pcs)
+               phylink_set_pcs(priv->phylink, &pf->pcs->pcs);
 }
 
-static void enetc_configure_2500basex(struct phy_device *pcs)
+static void enetc_pl_mac_link_up(struct phylink_config *config,
+                                struct phy_device *phy, unsigned int mode,
+                                phy_interface_t interface, int speed,
+                                int duplex, bool tx_pause, bool rx_pause)
 {
-       phy_write(pcs, ENETC_PCS_IF_MODE,
-                 ENETC_PCS_IF_MODE_SGMII_EN |
-                 ENETC_PCS_IF_MODE_SGMII_SPEED(ENETC_PCS_SPEED_2500));
+       struct enetc_pf *pf = phylink_to_enetc_pf(config);
+       struct enetc_ndev_priv *priv;
+
+       priv = netdev_priv(pf->si->ndev);
+       if (priv->active_offloads & ENETC_F_QBV)
+               enetc_sched_speed_set(priv, speed);
 
-       phy_write(pcs, MII_BMCR, BMCR_SPEED1000 | BMCR_FULLDPLX | BMCR_RESET);
+       enetc_mac_enable(&pf->si->hw, true);
 }
 
-static void enetc_configure_usxgmii(struct phy_device *pcs)
+static void enetc_pl_mac_link_down(struct phylink_config *config,
+                                  unsigned int mode,
+                                  phy_interface_t interface)
 {
-       /* Configure device ability for the USXGMII Replicator */
-       phy_write_mmd(pcs, MDIO_MMD_VEND2, MII_ADVERTISE,
-                     ADVERTISE_SGMII | ADVERTISE_LPACK |
-                     MDIO_USXGMII_FULL_DUPLEX);
-
-       /* Restart PCS AN */
-       phy_write_mmd(pcs, MDIO_MMD_VEND2, MII_BMCR,
-                     BMCR_RESET | BMCR_ANENABLE | BMCR_ANRESTART);
+       struct enetc_pf *pf = phylink_to_enetc_pf(config);
+
+       enetc_mac_enable(&pf->si->hw, false);
 }
 
-static void enetc_configure_serdes(struct enetc_pf *pf)
+static const struct phylink_mac_ops enetc_mac_phylink_ops = {
+       .validate = enetc_pl_mac_validate,
+       .mac_config = enetc_pl_mac_config,
+       .mac_link_up = enetc_pl_mac_link_up,
+       .mac_link_down = enetc_pl_mac_link_down,
+};
+
+static int enetc_phylink_create(struct enetc_ndev_priv *priv)
 {
-       switch (pf->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_dbg(&pf->si->pdev->dev, "Unsupported link mode %s\n",
-                       phy_modes(pf->if_mode));
+       struct enetc_pf *pf = enetc_si_priv(priv->si);
+       struct device *dev = &pf->si->pdev->dev;
+       struct phylink *phylink;
+       int err;
+
+       pf->phylink_config.dev = &priv->ndev->dev;
+       pf->phylink_config.type = PHYLINK_NETDEV;
+
+       phylink = phylink_create(&pf->phylink_config,
+                                of_fwnode_handle(dev->of_node),
+                                pf->if_mode, &enetc_mac_phylink_ops);
+       if (IS_ERR(phylink)) {
+               err = PTR_ERR(phylink);
+               return err;
        }
+
+       priv->phylink = phylink;
+
+       return 0;
+}
+
+static void enetc_phylink_destroy(struct enetc_ndev_priv *priv)
+{
+       if (priv->phylink)
+               phylink_destroy(priv->phylink);
 }
 
 static int enetc_pf_probe(struct pci_dev *pdev,
                goto err_alloc_msix;
        }
 
-       err = enetc_of_get_phy(pf);
-       if (err)
-               goto err_of_get_phy;
-
-       if (pf->phy_node) {
-               priv->phy_node = pf->phy_node;
-               priv->if_mode = pf->if_mode;
-
+       if (!of_get_phy_mode(pdev->dev.of_node, &pf->if_mode)) {
                err = enetc_mdiobus_create(pf);
                if (err)
                        goto err_mdiobus_create;
 
-               if (enetc_port_has_pcs(pf))
-                       enetc_configure_serdes(pf);
-
-               enetc_mac_config(&pf->si->hw, pf->if_mode);
+               err = enetc_phylink_create(priv);
+               if (err)
+                       goto err_phylink_create;
        }
 
        err = register_netdev(ndev);
        if (err)
                goto err_reg_netdev;
 
-       netif_carrier_off(ndev);
-
        return 0;
 
 err_reg_netdev:
+       enetc_phylink_destroy(priv);
+err_phylink_create:
        enetc_mdiobus_destroy(pf);
 err_mdiobus_create:
-       enetc_of_put_phy(pf);
-err_of_get_phy:
        enetc_free_msix(priv);
 err_alloc_msix:
        enetc_free_si_resources(priv);
        struct enetc_ndev_priv *priv;
 
        priv = netdev_priv(si->ndev);
+       enetc_phylink_destroy(priv);
        enetc_mdiobus_destroy(pf);
-       enetc_of_put_phy(pf);
 
        if (pf->num_vfs)
                enetc_sriov_configure(pdev, 0);