net: mvpp2: phylink support
authorAntoine Tenart <antoine.tenart@bootlin.com>
Thu, 17 May 2018 08:29:31 +0000 (10:29 +0200)
committerDavid S. Miller <davem@davemloft.net>
Thu, 17 May 2018 20:11:39 +0000 (16:11 -0400)
Convert the PPv2 driver to implement phylink helpers, and use phylink in
DT mode. The other mode supported is ACPI, which will need further work
in order to be entirely compatible with phylink.

The MAC and GoP configuration functions were completely moved to fit
into the phylink helpers. When a PHY is always present between the MAC
and the physical port, phylink only is used, but when this is not the
case (the MAC directly is connected to the physical port) the link IRQ
is used to detect changes in the link state and call phylink_mac_change.

The ACPI mode do not uses phylink as of now, and the changes shouldn't
impact its use.

Signed-off-by: Antoine Tenart <antoine.tenart@bootlin.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/marvell/Kconfig
drivers/net/ethernet/marvell/mvpp2.c

index ebe5c91489355c9e8ae3cb71cb1731a7db354094..cc2f7701e71e1b033c4bd7ceb78c970351f4d9ee 100644 (file)
@@ -86,6 +86,7 @@ config MVPP2
        depends on ARCH_MVEBU || COMPILE_TEST
        depends on HAS_DMA
        select MVMDIO
+       select PHYLINK
        ---help---
          This driver supports the network interface units in the
          Marvell ARMADA 375, 7K and 8K SoCs.
index 77dd91e3d962acced0640638ce4efa213621a385..60093f1e6297b86d6acacdd66b6f84ecba288a0b 100644 (file)
@@ -29,6 +29,7 @@
 #include <linux/of_address.h>
 #include <linux/of_device.h>
 #include <linux/phy.h>
+#include <linux/phylink.h>
 #include <linux/phy/phy.h>
 #include <linux/clk.h>
 #include <linux/hrtimer.h>
 #define     MVPP2_GMAC_FORCE_LINK_PASS         BIT(1)
 #define     MVPP2_GMAC_IN_BAND_AUTONEG         BIT(2)
 #define     MVPP2_GMAC_IN_BAND_AUTONEG_BYPASS  BIT(3)
+#define     MVPP2_GMAC_IN_BAND_RESTART_AN      BIT(4)
 #define     MVPP2_GMAC_CONFIG_MII_SPEED        BIT(5)
 #define     MVPP2_GMAC_CONFIG_GMII_SPEED       BIT(6)
 #define     MVPP2_GMAC_AN_SPEED_EN             BIT(7)
 #define     MVPP2_GMAC_FC_ADV_EN               BIT(9)
+#define     MVPP2_GMAC_FC_ADV_ASM_EN           BIT(10)
 #define     MVPP2_GMAC_FLOW_CTRL_AUTONEG       BIT(11)
 #define     MVPP2_GMAC_CONFIG_FULL_DUPLEX      BIT(12)
 #define     MVPP2_GMAC_AN_DUPLEX_EN            BIT(13)
 #define MVPP2_GMAC_STATUS0                     0x10
 #define     MVPP2_GMAC_STATUS0_LINK_UP         BIT(0)
+#define     MVPP2_GMAC_STATUS0_GMII_SPEED      BIT(1)
+#define     MVPP2_GMAC_STATUS0_MII_SPEED       BIT(2)
+#define     MVPP2_GMAC_STATUS0_FULL_DUPLEX     BIT(3)
+#define     MVPP2_GMAC_STATUS0_RX_PAUSE                BIT(6)
+#define     MVPP2_GMAC_STATUS0_TX_PAUSE                BIT(7)
+#define     MVPP2_GMAC_STATUS0_AN_COMPLETE     BIT(11)
 #define MVPP2_GMAC_PORT_FIFO_CFG_1_REG         0x1c
 #define     MVPP2_GMAC_TX_FIFO_MIN_TH_OFFS     6
 #define     MVPP2_GMAC_TX_FIFO_MIN_TH_ALL_MASK 0x1fc0
 #define     MVPP22_GMAC_INT_MASK_LINK_STAT     BIT(1)
 #define MVPP22_GMAC_CTRL_4_REG                 0x90
 #define     MVPP22_CTRL4_EXT_PIN_GMII_SEL      BIT(0)
+#define     MVPP22_CTRL4_RX_FC_EN              BIT(3)
+#define     MVPP22_CTRL4_TX_FC_EN              BIT(4)
 #define     MVPP22_CTRL4_DP_CLK_SEL            BIT(5)
 #define     MVPP22_CTRL4_SYNC_BYPASS_DIS       BIT(6)
 #define     MVPP22_CTRL4_QSGMII_BYPASS_ACTIVE  BIT(7)
 #define     MVPP22_XLG_CTRL0_PORT_EN           BIT(0)
 #define     MVPP22_XLG_CTRL0_MAC_RESET_DIS     BIT(1)
 #define     MVPP22_XLG_CTRL0_RX_FLOW_CTRL_EN   BIT(7)
+#define     MVPP22_XLG_CTRL0_TX_FLOW_CTRL_EN   BIT(8)
 #define     MVPP22_XLG_CTRL0_MIB_CNT_DIS       BIT(14)
 #define MVPP22_XLG_CTRL1_REG                   0x104
 #define     MVPP22_XLG_CTRL1_FRAMESIZELIMIT_OFFS       0
 #define     MVPP22_XLG_CTRL4_FWD_FC            BIT(5)
 #define     MVPP22_XLG_CTRL4_FWD_PFC           BIT(6)
 #define     MVPP22_XLG_CTRL4_MACMODSELECT_GMAC BIT(12)
+#define     MVPP22_XLG_CTRL4_EN_IDLE_CHECK     BIT(14)
 
 /* SMI registers. PPv2.2 only, relative to priv->iface_base. */
 #define MVPP22_SMI_MISC_CFG_REG                        0x1204
@@ -1017,6 +1030,9 @@ struct mvpp2_port {
        /* Firmware node associated to the port */
        struct fwnode_handle *fwnode;
 
+       /* Is a PHY always connected to the port */
+       bool has_phy;
+
        /* Per-port registers' base address */
        void __iomem *base;
        void __iomem *stats_base;
@@ -1044,12 +1060,11 @@ struct mvpp2_port {
        struct mutex gather_stats_lock;
        struct delayed_work stats_work;
 
+       struct device_node *of_node;
+
        phy_interface_t phy_interface;
-       struct device_node *phy_node;
+       struct phylink *phylink;
        struct phy *comphy;
-       unsigned int link;
-       unsigned int duplex;
-       unsigned int speed;
 
        struct mvpp2_bm_pool *pool_long;
        struct mvpp2_bm_pool *pool_short;
@@ -1338,6 +1353,12 @@ struct mvpp2_bm_pool {
         (addr) < (txq_pcpu)->tso_headers_dma + \
         (txq_pcpu)->size * TSO_HEADER_SIZE)
 
+/* The prototype is added here to be used in start_dev when using ACPI. This
+ * will be removed once phylink is used for all modes (dt+ACPI).
+ */
+static void mvpp2_mac_config(struct net_device *dev, unsigned int mode,
+                            const struct phylink_link_state *state);
+
 /* Queue modes */
 #define MVPP2_QDIST_SINGLE_MODE        0
 #define MVPP2_QDIST_MULTI_MODE 1
@@ -4969,133 +4990,6 @@ static int mvpp22_comphy_init(struct mvpp2_port *port)
        return phy_power_on(port->comphy);
 }
 
-static void mvpp2_port_mii_gmac_configure_mode(struct mvpp2_port *port)
-{
-       u32 val;
-
-       if (port->phy_interface == PHY_INTERFACE_MODE_SGMII) {
-               val = readl(port->base + MVPP22_GMAC_CTRL_4_REG);
-               val |= MVPP22_CTRL4_SYNC_BYPASS_DIS | MVPP22_CTRL4_DP_CLK_SEL |
-                      MVPP22_CTRL4_QSGMII_BYPASS_ACTIVE;
-               val &= ~MVPP22_CTRL4_EXT_PIN_GMII_SEL;
-               writel(val, port->base + MVPP22_GMAC_CTRL_4_REG);
-       } else if (phy_interface_mode_is_rgmii(port->phy_interface)) {
-               val = readl(port->base + MVPP22_GMAC_CTRL_4_REG);
-               val |= MVPP22_CTRL4_EXT_PIN_GMII_SEL |
-                      MVPP22_CTRL4_SYNC_BYPASS_DIS |
-                      MVPP22_CTRL4_QSGMII_BYPASS_ACTIVE;
-               val &= ~MVPP22_CTRL4_DP_CLK_SEL;
-               writel(val, port->base + MVPP22_GMAC_CTRL_4_REG);
-       }
-
-       /* The port is connected to a copper PHY */
-       val = readl(port->base + MVPP2_GMAC_CTRL_0_REG);
-       val &= ~MVPP2_GMAC_PORT_TYPE_MASK;
-       writel(val, port->base + MVPP2_GMAC_CTRL_0_REG);
-
-       val = readl(port->base + MVPP2_GMAC_AUTONEG_CONFIG);
-       val |= MVPP2_GMAC_IN_BAND_AUTONEG_BYPASS |
-              MVPP2_GMAC_AN_SPEED_EN | MVPP2_GMAC_FLOW_CTRL_AUTONEG |
-              MVPP2_GMAC_AN_DUPLEX_EN;
-       if (port->phy_interface == PHY_INTERFACE_MODE_SGMII)
-               val |= MVPP2_GMAC_IN_BAND_AUTONEG;
-       writel(val, port->base + MVPP2_GMAC_AUTONEG_CONFIG);
-}
-
-static void mvpp2_port_mii_gmac_configure(struct mvpp2_port *port)
-{
-       u32 val;
-
-       /* Force link down */
-       val = readl(port->base + MVPP2_GMAC_AUTONEG_CONFIG);
-       val &= ~MVPP2_GMAC_FORCE_LINK_PASS;
-       val |= MVPP2_GMAC_FORCE_LINK_DOWN;
-       writel(val, port->base + MVPP2_GMAC_AUTONEG_CONFIG);
-
-       /* Set the GMAC in a reset state */
-       val = readl(port->base + MVPP2_GMAC_CTRL_2_REG);
-       val |= MVPP2_GMAC_PORT_RESET_MASK;
-       writel(val, port->base + MVPP2_GMAC_CTRL_2_REG);
-
-       /* Configure the PCS and in-band AN */
-       val = readl(port->base + MVPP2_GMAC_CTRL_2_REG);
-       if (port->phy_interface == PHY_INTERFACE_MODE_SGMII) {
-               val |= MVPP2_GMAC_INBAND_AN_MASK | MVPP2_GMAC_PCS_ENABLE_MASK;
-       } else if (phy_interface_mode_is_rgmii(port->phy_interface)) {
-               val &= ~MVPP2_GMAC_PCS_ENABLE_MASK;
-       }
-       writel(val, port->base + MVPP2_GMAC_CTRL_2_REG);
-
-       mvpp2_port_mii_gmac_configure_mode(port);
-
-       /* Unset the GMAC reset state */
-       val = readl(port->base + MVPP2_GMAC_CTRL_2_REG);
-       val &= ~MVPP2_GMAC_PORT_RESET_MASK;
-       writel(val, port->base + MVPP2_GMAC_CTRL_2_REG);
-
-       /* Stop forcing link down */
-       val = readl(port->base + MVPP2_GMAC_AUTONEG_CONFIG);
-       val &= ~MVPP2_GMAC_FORCE_LINK_DOWN;
-       writel(val, port->base + MVPP2_GMAC_AUTONEG_CONFIG);
-}
-
-static void mvpp2_port_mii_xlg_configure(struct mvpp2_port *port)
-{
-       u32 val;
-
-       if (port->gop_id != 0)
-               return;
-
-       val = readl(port->base + MVPP22_XLG_CTRL0_REG);
-       val |= MVPP22_XLG_CTRL0_RX_FLOW_CTRL_EN;
-       writel(val, port->base + MVPP22_XLG_CTRL0_REG);
-
-       val = readl(port->base + MVPP22_XLG_CTRL4_REG);
-       val &= ~MVPP22_XLG_CTRL4_MACMODSELECT_GMAC;
-       val |= MVPP22_XLG_CTRL4_FWD_FC | MVPP22_XLG_CTRL4_FWD_PFC;
-       writel(val, port->base + MVPP22_XLG_CTRL4_REG);
-}
-
-static void mvpp22_port_mii_set(struct mvpp2_port *port)
-{
-       u32 val;
-
-       /* Only GOP port 0 has an XLG MAC */
-       if (port->gop_id == 0) {
-               val = readl(port->base + MVPP22_XLG_CTRL3_REG);
-               val &= ~MVPP22_XLG_CTRL3_MACMODESELECT_MASK;
-
-               if (port->phy_interface == PHY_INTERFACE_MODE_XAUI ||
-                   port->phy_interface == PHY_INTERFACE_MODE_10GKR)
-                       val |= MVPP22_XLG_CTRL3_MACMODESELECT_10G;
-               else
-                       val |= MVPP22_XLG_CTRL3_MACMODESELECT_GMAC;
-
-               writel(val, port->base + MVPP22_XLG_CTRL3_REG);
-       }
-}
-
-static void mvpp2_port_mii_set(struct mvpp2_port *port)
-{
-       if (port->priv->hw_version == MVPP22)
-               mvpp22_port_mii_set(port);
-
-       if (phy_interface_mode_is_rgmii(port->phy_interface) ||
-           port->phy_interface == PHY_INTERFACE_MODE_SGMII)
-               mvpp2_port_mii_gmac_configure(port);
-       else if (port->phy_interface == PHY_INTERFACE_MODE_10GKR)
-               mvpp2_port_mii_xlg_configure(port);
-}
-
-static void mvpp2_port_fc_adv_enable(struct mvpp2_port *port)
-{
-       u32 val;
-
-       val = readl(port->base + MVPP2_GMAC_AUTONEG_CONFIG);
-       val |= MVPP2_GMAC_FC_ADV_EN;
-       writel(val, port->base + MVPP2_GMAC_AUTONEG_CONFIG);
-}
-
 static void mvpp2_port_enable(struct mvpp2_port *port)
 {
        u32 val;
@@ -5126,8 +5020,11 @@ static void mvpp2_port_disable(struct mvpp2_port *port)
            (port->phy_interface == PHY_INTERFACE_MODE_XAUI ||
             port->phy_interface == PHY_INTERFACE_MODE_10GKR)) {
                val = readl(port->base + MVPP22_XLG_CTRL0_REG);
-               val &= ~(MVPP22_XLG_CTRL0_PORT_EN |
-                        MVPP22_XLG_CTRL0_MAC_RESET_DIS);
+               val &= ~MVPP22_XLG_CTRL0_PORT_EN;
+               writel(val, port->base + MVPP22_XLG_CTRL0_REG);
+
+               /* Disable & reset should be done separately */
+               val &= ~MVPP22_XLG_CTRL0_MAC_RESET_DIS;
                writel(val, port->base + MVPP22_XLG_CTRL0_REG);
        } else {
                val = readl(port->base + MVPP2_GMAC_CTRL_0_REG);
@@ -5147,13 +5044,14 @@ static void mvpp2_port_periodic_xon_disable(struct mvpp2_port *port)
 }
 
 /* Configure loopback port */
-static void mvpp2_port_loopback_set(struct mvpp2_port *port)
+static void mvpp2_port_loopback_set(struct mvpp2_port *port,
+                                   const struct phylink_link_state *state)
 {
        u32 val;
 
        val = readl(port->base + MVPP2_GMAC_CTRL_1_REG);
 
-       if (port->speed == 1000)
+       if (state->speed == 1000)
                val |= MVPP2_GMAC_GMII_LB_EN_MASK;
        else
                val &= ~MVPP2_GMAC_GMII_LB_EN_MASK;
@@ -5331,10 +5229,6 @@ static void mvpp2_defaults_set(struct mvpp2_port *port)
        int tx_port_num, val, queue, ptxq, lrxq;
 
        if (port->priv->hw_version == MVPP21) {
-               /* Configure port to loopback if needed */
-               if (port->flags & MVPP2_F_LOOPBACK)
-                       mvpp2_port_loopback_set(port);
-
                /* Update TX FIFO MIN Threshold */
                val = readl(port->base + MVPP2_GMAC_PORT_FIFO_CFG_1_REG);
                val &= ~MVPP2_GMAC_TX_FIFO_MIN_TH_ALL_MASK;
@@ -6382,6 +6276,11 @@ static irqreturn_t mvpp2_link_status_isr(int irq, void *dev_id)
                }
        }
 
+       if (port->phylink) {
+               phylink_mac_change(port->phylink, link);
+               goto handled;
+       }
+
        if (!netif_running(dev) || !event)
                goto handled;
 
@@ -6406,111 +6305,6 @@ handled:
        return IRQ_HANDLED;
 }
 
-static void mvpp2_gmac_set_autoneg(struct mvpp2_port *port,
-                                  struct phy_device *phydev)
-{
-       u32 val;
-
-       if (port->phy_interface != PHY_INTERFACE_MODE_RGMII &&
-           port->phy_interface != PHY_INTERFACE_MODE_RGMII_ID &&
-           port->phy_interface != PHY_INTERFACE_MODE_RGMII_RXID &&
-           port->phy_interface != PHY_INTERFACE_MODE_RGMII_TXID &&
-           port->phy_interface != PHY_INTERFACE_MODE_SGMII)
-               return;
-
-       val = readl(port->base + MVPP2_GMAC_AUTONEG_CONFIG);
-       val &= ~(MVPP2_GMAC_CONFIG_MII_SPEED |
-                MVPP2_GMAC_CONFIG_GMII_SPEED |
-                MVPP2_GMAC_CONFIG_FULL_DUPLEX |
-                MVPP2_GMAC_AN_SPEED_EN |
-                MVPP2_GMAC_AN_DUPLEX_EN);
-
-       if (phydev->duplex)
-               val |= MVPP2_GMAC_CONFIG_FULL_DUPLEX;
-
-       if (phydev->speed == SPEED_1000)
-               val |= MVPP2_GMAC_CONFIG_GMII_SPEED;
-       else if (phydev->speed == SPEED_100)
-               val |= MVPP2_GMAC_CONFIG_MII_SPEED;
-
-       writel(val, port->base + MVPP2_GMAC_AUTONEG_CONFIG);
-}
-
-/* Adjust link */
-static void mvpp2_link_event(struct net_device *dev)
-{
-       struct mvpp2_port *port = netdev_priv(dev);
-       struct phy_device *phydev = dev->phydev;
-       bool link_reconfigured = false;
-       u32 val;
-
-       if (phydev->link) {
-               if (port->phy_interface != phydev->interface && port->comphy) {
-                       /* disable current port for reconfiguration */
-                       mvpp2_interrupts_disable(port);
-                       netif_carrier_off(port->dev);
-                       mvpp2_port_disable(port);
-                       phy_power_off(port->comphy);
-
-                       /* comphy reconfiguration */
-                       port->phy_interface = phydev->interface;
-                       mvpp22_comphy_init(port);
-
-                       /* gop/mac reconfiguration */
-                       mvpp22_gop_init(port);
-                       mvpp2_port_mii_set(port);
-
-                       link_reconfigured = true;
-               }
-
-               if ((port->speed != phydev->speed) ||
-                   (port->duplex != phydev->duplex)) {
-                       mvpp2_gmac_set_autoneg(port, phydev);
-
-                       port->duplex = phydev->duplex;
-                       port->speed  = phydev->speed;
-               }
-       }
-
-       if (phydev->link != port->link || link_reconfigured) {
-               port->link = phydev->link;
-
-               if (phydev->link) {
-                       if (port->phy_interface == PHY_INTERFACE_MODE_RGMII ||
-                           port->phy_interface == PHY_INTERFACE_MODE_RGMII_ID ||
-                           port->phy_interface == PHY_INTERFACE_MODE_RGMII_RXID ||
-                           port->phy_interface == PHY_INTERFACE_MODE_RGMII_TXID ||
-                           port->phy_interface == PHY_INTERFACE_MODE_SGMII) {
-                               val = readl(port->base + MVPP2_GMAC_AUTONEG_CONFIG);
-                               val |= (MVPP2_GMAC_FORCE_LINK_PASS |
-                                       MVPP2_GMAC_FORCE_LINK_DOWN);
-                               writel(val, port->base + MVPP2_GMAC_AUTONEG_CONFIG);
-                       }
-
-                       mvpp2_interrupts_enable(port);
-                       mvpp2_port_enable(port);
-
-                       mvpp2_egress_enable(port);
-                       mvpp2_ingress_enable(port);
-                       netif_carrier_on(dev);
-                       netif_tx_wake_all_queues(dev);
-               } else {
-                       port->duplex = -1;
-                       port->speed = 0;
-
-                       netif_tx_stop_all_queues(dev);
-                       netif_carrier_off(dev);
-                       mvpp2_ingress_disable(port);
-                       mvpp2_egress_disable(port);
-
-                       mvpp2_port_disable(port);
-                       mvpp2_interrupts_disable(port);
-               }
-
-               phy_print_status(phydev);
-       }
-}
-
 static void mvpp2_timer_set(struct mvpp2_port_pcpu *port_pcpu)
 {
        ktime_t interval;
@@ -7118,11 +6912,29 @@ static int mvpp2_poll(struct napi_struct *napi, int budget)
        return rx_done;
 }
 
-/* Set hw internals when starting port */
-static void mvpp2_start_dev(struct mvpp2_port *port)
+static void mvpp22_mode_reconfigure(struct mvpp2_port *port)
 {
-       struct net_device *ndev = port->dev;
-       int i;
+       u32 ctrl3;
+
+       /* comphy reconfiguration */
+       mvpp22_comphy_init(port);
+
+       /* gop reconfiguration */
+       mvpp22_gop_init(port);
+
+       /* Only GOP port 0 has an XLG MAC */
+       if (port->gop_id == 0) {
+               ctrl3 = readl(port->base + MVPP22_XLG_CTRL3_REG);
+               ctrl3 &= ~MVPP22_XLG_CTRL3_MACMODESELECT_MASK;
+
+               if (port->phy_interface == PHY_INTERFACE_MODE_XAUI ||
+                   port->phy_interface == PHY_INTERFACE_MODE_10GKR)
+                       ctrl3 |= MVPP22_XLG_CTRL3_MACMODESELECT_10G;
+               else
+                       ctrl3 |= MVPP22_XLG_CTRL3_MACMODESELECT_GMAC;
+
+               writel(ctrl3, port->base + MVPP22_XLG_CTRL3_REG);
+       }
 
        if (port->gop_id == 0 &&
            (port->phy_interface == PHY_INTERFACE_MODE_XAUI ||
@@ -7130,6 +6942,12 @@ static void mvpp2_start_dev(struct mvpp2_port *port)
                mvpp2_xlg_max_rx_size_set(port);
        else
                mvpp2_gmac_max_rx_size_set(port);
+}
+
+/* Set hw internals when starting port */
+static void mvpp2_start_dev(struct mvpp2_port *port)
+{
+       int i;
 
        mvpp2_txp_max_tx_size_set(port);
 
@@ -7139,42 +6957,39 @@ static void mvpp2_start_dev(struct mvpp2_port *port)
        /* Enable interrupts on all CPUs */
        mvpp2_interrupts_enable(port);
 
-       if (port->priv->hw_version == MVPP22) {
-               mvpp22_comphy_init(port);
-               mvpp22_gop_init(port);
+       if (port->priv->hw_version == MVPP22)
+               mvpp22_mode_reconfigure(port);
+
+       if (port->phylink) {
+               phylink_start(port->phylink);
+       } else {
+               /* Phylink isn't used as of now for ACPI, so the MAC has to be
+                * configured manually when the interface is started. This will
+                * be removed as soon as the phylink ACPI support lands in.
+                */
+               struct phylink_link_state state = {
+                       .interface = port->phy_interface,
+                       .link = 1,
+               };
+               mvpp2_mac_config(port->dev, MLO_AN_INBAND, &state);
        }
 
-       mvpp2_port_mii_set(port);
-       mvpp2_port_enable(port);
-       if (ndev->phydev)
-               phy_start(ndev->phydev);
        netif_tx_start_all_queues(port->dev);
 }
 
 /* Set hw internals when stopping port */
 static void mvpp2_stop_dev(struct mvpp2_port *port)
 {
-       struct net_device *ndev = port->dev;
        int i;
 
-       /* Stop new packets from arriving to RXQs */
-       mvpp2_ingress_disable(port);
-
-       mdelay(10);
-
        /* Disable interrupts on all CPUs */
        mvpp2_interrupts_disable(port);
 
        for (i = 0; i < port->nqvecs; i++)
                napi_disable(&port->qvecs[i].napi);
 
-       netif_carrier_off(port->dev);
-       netif_tx_stop_all_queues(port->dev);
-
-       mvpp2_egress_disable(port);
-       mvpp2_port_disable(port);
-       if (ndev->phydev)
-               phy_stop(ndev->phydev);
+       if (port->phylink)
+               phylink_stop(port->phylink);
        phy_power_off(port->comphy);
 }
 
@@ -7233,40 +7048,6 @@ static void mvpp21_get_mac_address(struct mvpp2_port *port, unsigned char *addr)
        addr[5] = (mac_addr_l >> MVPP2_GMAC_SA_LOW_OFFS) & 0xFF;
 }
 
-static int mvpp2_phy_connect(struct mvpp2_port *port)
-{
-       struct phy_device *phy_dev;
-
-       /* No PHY is attached */
-       if (!port->phy_node)
-               return 0;
-
-       phy_dev = of_phy_connect(port->dev, port->phy_node, mvpp2_link_event, 0,
-                                port->phy_interface);
-       if (!phy_dev) {
-               netdev_err(port->dev, "cannot connect to phy\n");
-               return -ENODEV;
-       }
-       phy_dev->supported &= PHY_GBIT_FEATURES;
-       phy_dev->advertising = phy_dev->supported;
-
-       port->link    = 0;
-       port->duplex  = 0;
-       port->speed   = 0;
-
-       return 0;
-}
-
-static void mvpp2_phy_disconnect(struct mvpp2_port *port)
-{
-       struct net_device *ndev = port->dev;
-
-       if (!ndev->phydev)
-               return;
-
-       phy_disconnect(ndev->phydev);
-}
-
 static int mvpp2_irqs_init(struct mvpp2_port *port)
 {
        int err, i;
@@ -7350,6 +7131,7 @@ static int mvpp2_open(struct net_device *dev)
        struct mvpp2 *priv = port->priv;
        unsigned char mac_bcast[ETH_ALEN] = {
                        0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+       bool valid = false;
        int err;
 
        err = mvpp2_prs_mac_da_accept(port, mac_bcast, true);
@@ -7392,7 +7174,19 @@ static int mvpp2_open(struct net_device *dev)
                goto err_cleanup_txqs;
        }
 
-       if (priv->hw_version == MVPP22 && !port->phy_node && port->link_irq) {
+       /* Phylink isn't supported yet in ACPI mode */
+       if (port->of_node) {
+               err = phylink_of_phy_connect(port->phylink, port->of_node, 0);
+               if (err) {
+                       netdev_err(port->dev, "could not attach PHY (%d)\n",
+                                  err);
+                       goto err_free_irq;
+               }
+
+               valid = true;
+       }
+
+       if (priv->hw_version == MVPP22 && port->link_irq && !port->phylink) {
                err = request_irq(port->link_irq, mvpp2_link_status_isr, 0,
                                  dev->name, port);
                if (err) {
@@ -7402,14 +7196,20 @@ static int mvpp2_open(struct net_device *dev)
                }
 
                mvpp22_gop_setup_irq(port);
-       }
 
-       /* In default link is down */
-       netif_carrier_off(port->dev);
+               /* In default link is down */
+               netif_carrier_off(port->dev);
 
-       err = mvpp2_phy_connect(port);
-       if (err < 0)
-               goto err_free_link_irq;
+               valid = true;
+       } else {
+               port->link_irq = 0;
+       }
+
+       if (!valid) {
+               netdev_err(port->dev,
+                          "invalid configuration: no dt or link IRQ");
+               goto err_free_irq;
+       }
 
        /* Unmask interrupts on all CPUs */
        on_each_cpu(mvpp2_interrupts_unmask, port, 1);
@@ -7426,9 +7226,6 @@ static int mvpp2_open(struct net_device *dev)
 
        return 0;
 
-err_free_link_irq:
-       if (priv->hw_version == MVPP22 && !port->phy_node && port->link_irq)
-               free_irq(port->link_irq, port);
 err_free_irq:
        mvpp2_irqs_deinit(port);
 err_cleanup_txqs:
@@ -7442,17 +7239,17 @@ static int mvpp2_stop(struct net_device *dev)
 {
        struct mvpp2_port *port = netdev_priv(dev);
        struct mvpp2_port_pcpu *port_pcpu;
-       struct mvpp2 *priv = port->priv;
        int cpu;
 
        mvpp2_stop_dev(port);
-       mvpp2_phy_disconnect(port);
 
        /* Mask interrupts on all CPUs */
        on_each_cpu(mvpp2_interrupts_mask, port, 1);
        mvpp2_shared_interrupt_mask_unmask(port, true);
 
-       if (priv->hw_version == MVPP22 && !port->phy_node && port->link_irq)
+       if (port->phylink)
+               phylink_disconnect_phy(port->phylink);
+       if (port->link_irq)
                free_irq(port->link_irq, port);
 
        mvpp2_irqs_deinit(port);
@@ -7658,16 +7455,12 @@ mvpp2_get_stats64(struct net_device *dev, struct rtnl_link_stats64 *stats)
 
 static int mvpp2_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
 {
-       int ret;
+       struct mvpp2_port *port = netdev_priv(dev);
 
-       if (!dev->phydev)
+       if (!port->phylink)
                return -ENOTSUPP;
 
-       ret = phy_mii_ioctl(dev->phydev, ifr, cmd);
-       if (!ret)
-               mvpp2_link_event(dev);
-
-       return ret;
+       return phylink_mii_ioctl(port->phylink, ifr, cmd);
 }
 
 static int mvpp2_vlan_rx_add_vid(struct net_device *dev, __be16 proto, u16 vid)
@@ -7714,6 +7507,16 @@ static int mvpp2_set_features(struct net_device *dev,
 
 /* Ethtool methods */
 
+static int mvpp2_ethtool_nway_reset(struct net_device *dev)
+{
+       struct mvpp2_port *port = netdev_priv(dev);
+
+       if (!port->phylink)
+               return -ENOTSUPP;
+
+       return phylink_ethtool_nway_reset(port->phylink);
+}
+
 /* Set interrupt coalescing for ethtools */
 static int mvpp2_ethtool_set_coalesce(struct net_device *dev,
                                      struct ethtool_coalesce *c)
@@ -7842,6 +7645,50 @@ err_out:
        return err;
 }
 
+static void mvpp2_ethtool_get_pause_param(struct net_device *dev,
+                                         struct ethtool_pauseparam *pause)
+{
+       struct mvpp2_port *port = netdev_priv(dev);
+
+       if (!port->phylink)
+               return;
+
+       phylink_ethtool_get_pauseparam(port->phylink, pause);
+}
+
+static int mvpp2_ethtool_set_pause_param(struct net_device *dev,
+                                        struct ethtool_pauseparam *pause)
+{
+       struct mvpp2_port *port = netdev_priv(dev);
+
+       if (!port->phylink)
+               return -ENOTSUPP;
+
+       return phylink_ethtool_set_pauseparam(port->phylink, pause);
+}
+
+static int mvpp2_ethtool_get_link_ksettings(struct net_device *dev,
+                                           struct ethtool_link_ksettings *cmd)
+{
+       struct mvpp2_port *port = netdev_priv(dev);
+
+       if (!port->phylink)
+               return -ENOTSUPP;
+
+       return phylink_ethtool_ksettings_get(port->phylink, cmd);
+}
+
+static int mvpp2_ethtool_set_link_ksettings(struct net_device *dev,
+                                           const struct ethtool_link_ksettings *cmd)
+{
+       struct mvpp2_port *port = netdev_priv(dev);
+
+       if (!port->phylink)
+               return -ENOTSUPP;
+
+       return phylink_ethtool_ksettings_set(port->phylink, cmd);
+}
+
 /* Device ops */
 
 static const struct net_device_ops mvpp2_netdev_ops = {
@@ -7859,7 +7706,7 @@ static const struct net_device_ops mvpp2_netdev_ops = {
 };
 
 static const struct ethtool_ops mvpp2_eth_tool_ops = {
-       .nway_reset             = phy_ethtool_nway_reset,
+       .nway_reset             = mvpp2_ethtool_nway_reset,
        .get_link               = ethtool_op_get_link,
        .set_coalesce           = mvpp2_ethtool_set_coalesce,
        .get_coalesce           = mvpp2_ethtool_get_coalesce,
@@ -7869,8 +7716,10 @@ static const struct ethtool_ops mvpp2_eth_tool_ops = {
        .get_strings            = mvpp2_ethtool_get_strings,
        .get_ethtool_stats      = mvpp2_ethtool_get_stats,
        .get_sset_count         = mvpp2_ethtool_get_sset_count,
-       .get_link_ksettings     = phy_ethtool_get_link_ksettings,
-       .set_link_ksettings     = phy_ethtool_set_link_ksettings,
+       .get_pauseparam         = mvpp2_ethtool_get_pause_param,
+       .set_pauseparam         = mvpp2_ethtool_set_pause_param,
+       .get_link_ksettings     = mvpp2_ethtool_get_link_ksettings,
+       .set_link_ksettings     = mvpp2_ethtool_set_link_ksettings,
 };
 
 /* Used for PPv2.1, or PPv2.2 with the old Device Tree binding that
@@ -8172,18 +8021,330 @@ static void mvpp2_port_copy_mac_addr(struct net_device *dev, struct mvpp2 *priv,
        eth_hw_addr_random(dev);
 }
 
+static void mvpp2_phylink_validate(struct net_device *dev,
+                                  unsigned long *supported,
+                                  struct phylink_link_state *state)
+{
+       __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
+
+       phylink_set(mask, Autoneg);
+       phylink_set_port_modes(mask);
+       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, 1000baseT_Full);
+       phylink_set(mask, 10000baseT_Full);
+
+       if (state->interface == PHY_INTERFACE_MODE_10GKR) {
+               phylink_set(mask, 10000baseCR_Full);
+               phylink_set(mask, 10000baseSR_Full);
+               phylink_set(mask, 10000baseLR_Full);
+               phylink_set(mask, 10000baseLRM_Full);
+               phylink_set(mask, 10000baseER_Full);
+               phylink_set(mask, 10000baseKR_Full);
+       }
+
+       bitmap_and(supported, supported, mask, __ETHTOOL_LINK_MODE_MASK_NBITS);
+       bitmap_and(state->advertising, state->advertising, mask,
+                  __ETHTOOL_LINK_MODE_MASK_NBITS);
+}
+
+static void mvpp22_xlg_link_state(struct mvpp2_port *port,
+                                 struct phylink_link_state *state)
+{
+       u32 val;
+
+       state->speed = SPEED_10000;
+       state->duplex = 1;
+       state->an_complete = 1;
+
+       val = readl(port->base + MVPP22_XLG_STATUS);
+       state->link = !!(val & MVPP22_XLG_STATUS_LINK_UP);
+
+       state->pause = 0;
+       val = readl(port->base + MVPP22_XLG_CTRL0_REG);
+       if (val & MVPP22_XLG_CTRL0_TX_FLOW_CTRL_EN)
+               state->pause |= MLO_PAUSE_TX;
+       if (val & MVPP22_XLG_CTRL0_RX_FLOW_CTRL_EN)
+               state->pause |= MLO_PAUSE_RX;
+}
+
+static void mvpp2_gmac_link_state(struct mvpp2_port *port,
+                                 struct phylink_link_state *state)
+{
+       u32 val;
+
+       val = readl(port->base + MVPP2_GMAC_STATUS0);
+
+       state->an_complete = !!(val & MVPP2_GMAC_STATUS0_AN_COMPLETE);
+       state->link = !!(val & MVPP2_GMAC_STATUS0_LINK_UP);
+       state->duplex = !!(val & MVPP2_GMAC_STATUS0_FULL_DUPLEX);
+
+       if (val & MVPP2_GMAC_STATUS0_GMII_SPEED)
+               state->speed = SPEED_1000;
+       else if (val & MVPP2_GMAC_STATUS0_MII_SPEED)
+               state->speed = SPEED_100;
+       else
+               state->speed = SPEED_10;
+
+       state->pause = 0;
+       if (val & MVPP2_GMAC_STATUS0_RX_PAUSE)
+               state->pause |= MLO_PAUSE_RX;
+       if (val & MVPP2_GMAC_STATUS0_TX_PAUSE)
+               state->pause |= MLO_PAUSE_TX;
+}
+
+static int mvpp2_phylink_mac_link_state(struct net_device *dev,
+                                       struct phylink_link_state *state)
+{
+       struct mvpp2_port *port = netdev_priv(dev);
+
+       if (port->priv->hw_version == MVPP22 && port->gop_id == 0) {
+               u32 mode = readl(port->base + MVPP22_XLG_CTRL3_REG);
+               mode &= MVPP22_XLG_CTRL3_MACMODESELECT_MASK;
+
+               if (mode == MVPP22_XLG_CTRL3_MACMODESELECT_10G) {
+                       mvpp22_xlg_link_state(port, state);
+                       return 1;
+               }
+       }
+
+       mvpp2_gmac_link_state(port, state);
+       return 1;
+}
+
+static void mvpp2_mac_an_restart(struct net_device *dev)
+{
+       struct mvpp2_port *port = netdev_priv(dev);
+       u32 val;
+
+       if (port->phy_interface != PHY_INTERFACE_MODE_SGMII)
+               return;
+
+       val = readl(port->base + MVPP2_GMAC_AUTONEG_CONFIG);
+       /* The RESTART_AN bit is cleared by the h/w after restarting the AN
+        * process.
+        */
+       val |= MVPP2_GMAC_IN_BAND_RESTART_AN | MVPP2_GMAC_IN_BAND_AUTONEG;
+       writel(val, port->base + MVPP2_GMAC_AUTONEG_CONFIG);
+}
+
+static void mvpp2_xlg_config(struct mvpp2_port *port, unsigned int mode,
+                            const struct phylink_link_state *state)
+{
+       u32 ctrl0, ctrl4;
+
+       ctrl0 = readl(port->base + MVPP22_XLG_CTRL0_REG);
+       ctrl4 = readl(port->base + MVPP22_XLG_CTRL4_REG);
+
+       if (state->pause & MLO_PAUSE_TX)
+               ctrl0 |= MVPP22_XLG_CTRL0_TX_FLOW_CTRL_EN;
+       if (state->pause & MLO_PAUSE_RX)
+               ctrl0 |= MVPP22_XLG_CTRL0_RX_FLOW_CTRL_EN;
+
+       ctrl4 &= ~MVPP22_XLG_CTRL4_MACMODSELECT_GMAC;
+       ctrl4 |= MVPP22_XLG_CTRL4_FWD_FC | MVPP22_XLG_CTRL4_FWD_PFC |
+                MVPP22_XLG_CTRL4_EN_IDLE_CHECK;
+
+       writel(ctrl0, port->base + MVPP22_XLG_CTRL0_REG);
+       writel(ctrl4, port->base + MVPP22_XLG_CTRL4_REG);
+}
+
+static void mvpp2_gmac_config(struct mvpp2_port *port, unsigned int mode,
+                             const struct phylink_link_state *state)
+{
+       u32 an, ctrl0, ctrl2, ctrl4;
+
+       an = readl(port->base + MVPP2_GMAC_AUTONEG_CONFIG);
+       ctrl0 = readl(port->base + MVPP2_GMAC_CTRL_0_REG);
+       ctrl2 = readl(port->base + MVPP2_GMAC_CTRL_2_REG);
+       ctrl4 = readl(port->base + MVPP22_GMAC_CTRL_4_REG);
+
+       /* Force link down */
+       an &= ~MVPP2_GMAC_FORCE_LINK_PASS;
+       an |= MVPP2_GMAC_FORCE_LINK_DOWN;
+       writel(an, port->base + MVPP2_GMAC_AUTONEG_CONFIG);
+
+       /* Set the GMAC in a reset state */
+       ctrl2 |= MVPP2_GMAC_PORT_RESET_MASK;
+       writel(ctrl2, port->base + MVPP2_GMAC_CTRL_2_REG);
+
+       an &= ~(MVPP2_GMAC_CONFIG_MII_SPEED | MVPP2_GMAC_CONFIG_GMII_SPEED |
+               MVPP2_GMAC_AN_SPEED_EN | MVPP2_GMAC_FC_ADV_EN |
+               MVPP2_GMAC_FC_ADV_ASM_EN | MVPP2_GMAC_FLOW_CTRL_AUTONEG |
+               MVPP2_GMAC_CONFIG_FULL_DUPLEX | MVPP2_GMAC_AN_DUPLEX_EN |
+               MVPP2_GMAC_FORCE_LINK_DOWN);
+       ctrl0 &= ~MVPP2_GMAC_PORT_TYPE_MASK;
+       ctrl2 &= ~(MVPP2_GMAC_PORT_RESET_MASK | MVPP2_GMAC_PCS_ENABLE_MASK);
+
+       if (!phy_interface_mode_is_rgmii(state->interface))
+               an |= MVPP2_GMAC_AN_SPEED_EN | MVPP2_GMAC_FLOW_CTRL_AUTONEG;
+
+       if (state->duplex)
+               an |= MVPP2_GMAC_CONFIG_FULL_DUPLEX;
+       if (phylink_test(state->advertising, Pause))
+               an |= MVPP2_GMAC_FC_ADV_EN;
+       if (phylink_test(state->advertising, Asym_Pause))
+               an |= MVPP2_GMAC_FC_ADV_ASM_EN;
+
+       if (state->interface == PHY_INTERFACE_MODE_SGMII) {
+               an |= MVPP2_GMAC_IN_BAND_AUTONEG;
+               ctrl2 |= MVPP2_GMAC_INBAND_AN_MASK | MVPP2_GMAC_PCS_ENABLE_MASK;
+
+               ctrl4 &= ~(MVPP22_CTRL4_EXT_PIN_GMII_SEL |
+                          MVPP22_CTRL4_RX_FC_EN | MVPP22_CTRL4_TX_FC_EN);
+               ctrl4 |= MVPP22_CTRL4_SYNC_BYPASS_DIS |
+                        MVPP22_CTRL4_DP_CLK_SEL |
+                        MVPP22_CTRL4_QSGMII_BYPASS_ACTIVE;
+
+               if (state->pause & MLO_PAUSE_TX)
+                       ctrl4 |= MVPP22_CTRL4_TX_FC_EN;
+               if (state->pause & MLO_PAUSE_RX)
+                       ctrl4 |= MVPP22_CTRL4_RX_FC_EN;
+       } else if (phy_interface_mode_is_rgmii(state->interface)) {
+               an |= MVPP2_GMAC_IN_BAND_AUTONEG_BYPASS;
+
+               if (state->speed == SPEED_1000)
+                       an |= MVPP2_GMAC_CONFIG_GMII_SPEED;
+               else if (state->speed == SPEED_100)
+                       an |= MVPP2_GMAC_CONFIG_MII_SPEED;
+
+               ctrl4 &= ~MVPP22_CTRL4_DP_CLK_SEL;
+               ctrl4 |= MVPP22_CTRL4_EXT_PIN_GMII_SEL |
+                        MVPP22_CTRL4_SYNC_BYPASS_DIS |
+                        MVPP22_CTRL4_QSGMII_BYPASS_ACTIVE;
+       }
+
+       writel(ctrl0, port->base + MVPP2_GMAC_CTRL_0_REG);
+       writel(ctrl2, port->base + MVPP2_GMAC_CTRL_2_REG);
+       writel(ctrl4, port->base + MVPP22_GMAC_CTRL_4_REG);
+       writel(an, port->base + MVPP2_GMAC_AUTONEG_CONFIG);
+}
+
+static void mvpp2_mac_config(struct net_device *dev, unsigned int mode,
+                            const struct phylink_link_state *state)
+{
+       struct mvpp2_port *port = netdev_priv(dev);
+
+       /* Check for invalid configuration */
+       if (state->interface == PHY_INTERFACE_MODE_10GKR && port->gop_id != 0) {
+               netdev_err(dev, "Invalid mode on %s\n", dev->name);
+               return;
+       }
+
+       netif_tx_stop_all_queues(port->dev);
+       if (!port->has_phy)
+               netif_carrier_off(port->dev);
+
+       /* Make sure the port is disabled when reconfiguring the mode */
+       mvpp2_port_disable(port);
+
+       if (port->priv->hw_version == MVPP22 &&
+           port->phy_interface != state->interface) {
+               port->phy_interface = state->interface;
+
+               /* Reconfigure the serdes lanes */
+               phy_power_off(port->comphy);
+               mvpp22_mode_reconfigure(port);
+       }
+
+       /* mac (re)configuration */
+       if (state->interface == PHY_INTERFACE_MODE_10GKR)
+               mvpp2_xlg_config(port, mode, state);
+       else if (phy_interface_mode_is_rgmii(state->interface) ||
+                state->interface == PHY_INTERFACE_MODE_SGMII)
+               mvpp2_gmac_config(port, mode, state);
+
+       if (port->priv->hw_version == MVPP21 && port->flags & MVPP2_F_LOOPBACK)
+               mvpp2_port_loopback_set(port, state);
+
+       /* If the port already was up, make sure it's still in the same state */
+       if (state->link || !port->has_phy) {
+               mvpp2_port_enable(port);
+
+               mvpp2_egress_enable(port);
+               mvpp2_ingress_enable(port);
+               if (!port->has_phy)
+                       netif_carrier_on(dev);
+               netif_tx_wake_all_queues(dev);
+       }
+}
+
+static void mvpp2_mac_link_up(struct net_device *dev, unsigned int mode,
+                             phy_interface_t interface, struct phy_device *phy)
+{
+       struct mvpp2_port *port = netdev_priv(dev);
+       u32 val;
+
+       if (!phylink_autoneg_inband(mode) &&
+           interface != PHY_INTERFACE_MODE_10GKR) {
+               val = readl(port->base + MVPP2_GMAC_AUTONEG_CONFIG);
+               val &= ~MVPP2_GMAC_FORCE_LINK_DOWN;
+               if (phy_interface_mode_is_rgmii(interface))
+                       val |= MVPP2_GMAC_FORCE_LINK_PASS;
+               writel(val, port->base + MVPP2_GMAC_AUTONEG_CONFIG);
+       }
+
+       mvpp2_port_enable(port);
+
+       mvpp2_egress_enable(port);
+       mvpp2_ingress_enable(port);
+       netif_tx_wake_all_queues(dev);
+}
+
+static void mvpp2_mac_link_down(struct net_device *dev, unsigned int mode,
+                               phy_interface_t interface)
+{
+       struct mvpp2_port *port = netdev_priv(dev);
+       u32 val;
+
+       if (!phylink_autoneg_inband(mode) &&
+           interface != PHY_INTERFACE_MODE_10GKR) {
+               val = readl(port->base + MVPP2_GMAC_AUTONEG_CONFIG);
+               val &= ~MVPP2_GMAC_FORCE_LINK_PASS;
+               val |= MVPP2_GMAC_FORCE_LINK_DOWN;
+               writel(val, port->base + MVPP2_GMAC_AUTONEG_CONFIG);
+       }
+
+       netif_tx_stop_all_queues(dev);
+       mvpp2_egress_disable(port);
+       mvpp2_ingress_disable(port);
+
+       /* When using link interrupts to notify phylink of a MAC state change,
+        * we do not want the port to be disabled (we want to receive further
+        * interrupts, to be notified when the port will have a link later).
+        */
+       if (!port->has_phy)
+               return;
+
+       mvpp2_port_disable(port);
+}
+
+static const struct phylink_mac_ops mvpp2_phylink_ops = {
+       .validate = mvpp2_phylink_validate,
+       .mac_link_state = mvpp2_phylink_mac_link_state,
+       .mac_an_restart = mvpp2_mac_an_restart,
+       .mac_config = mvpp2_mac_config,
+       .mac_link_up = mvpp2_mac_link_up,
+       .mac_link_down = mvpp2_mac_link_down,
+};
+
 /* Ports initialization */
 static int mvpp2_port_probe(struct platform_device *pdev,
                            struct fwnode_handle *port_fwnode,
                            struct mvpp2 *priv)
 {
-       struct device_node *phy_node;
        struct phy *comphy = NULL;
        struct mvpp2_port *port;
        struct mvpp2_port_pcpu *port_pcpu;
        struct device_node *port_node = to_of_node(port_fwnode);
        struct net_device *dev;
        struct resource *res;
+       struct phylink *phylink;
        char *mac_from = "";
        unsigned int ntxqs, nrxqs;
        bool has_tx_irqs;
@@ -8212,11 +8373,6 @@ static int mvpp2_port_probe(struct platform_device *pdev,
        if (!dev)
                return -ENOMEM;
 
-       if (port_node)
-               phy_node = of_parse_phandle(port_node, "phy", 0);
-       else
-               phy_node = NULL;
-
        phy_mode = fwnode_get_phy_mode(port_fwnode);
        if (phy_mode < 0) {
                dev_err(&pdev->dev, "incorrect phy mode\n");
@@ -8249,6 +8405,7 @@ static int mvpp2_port_probe(struct platform_device *pdev,
        port = netdev_priv(dev);
        port->dev = dev;
        port->fwnode = port_fwnode;
+       port->has_phy = !!of_find_property(port_node, "phy", NULL);
        port->ntxqs = ntxqs;
        port->nrxqs = nrxqs;
        port->priv = priv;
@@ -8279,7 +8436,7 @@ static int mvpp2_port_probe(struct platform_device *pdev,
        else
                port->first_rxq = port->id * priv->max_port_rxqs;
 
-       port->phy_node = phy_node;
+       port->of_node = port_node;
        port->phy_interface = phy_mode;
        port->comphy = comphy;
 
@@ -8340,9 +8497,6 @@ static int mvpp2_port_probe(struct platform_device *pdev,
 
        mvpp2_port_periodic_xon_disable(port);
 
-       if (priv->hw_version == MVPP21)
-               mvpp2_port_fc_adv_enable(port);
-
        mvpp2_port_reset(port);
 
        port->pcpu = alloc_percpu(struct mvpp2_port_pcpu);
@@ -8386,10 +8540,23 @@ static int mvpp2_port_probe(struct platform_device *pdev,
        /* 9704 == 9728 - 20 and rounding to 8 */
        dev->max_mtu = MVPP2_BM_JUMBO_PKT_SIZE;
 
+       /* Phylink isn't used w/ ACPI as of now */
+       if (port_node) {
+               phylink = phylink_create(dev, port_fwnode, phy_mode,
+                                        &mvpp2_phylink_ops);
+               if (IS_ERR(phylink)) {
+                       err = PTR_ERR(phylink);
+                       goto err_free_port_pcpu;
+               }
+               port->phylink = phylink;
+       } else {
+               port->phylink = NULL;
+       }
+
        err = register_netdev(dev);
        if (err < 0) {
                dev_err(&pdev->dev, "failed to register netdev\n");
-               goto err_free_port_pcpu;
+               goto err_phylink;
        }
        netdev_info(dev, "Using %s mac address %pM\n", mac_from, dev->dev_addr);
 
@@ -8397,6 +8564,9 @@ static int mvpp2_port_probe(struct platform_device *pdev,
 
        return 0;
 
+err_phylink:
+       if (port->phylink)
+               phylink_destroy(port->phylink);
 err_free_port_pcpu:
        free_percpu(port->pcpu);
 err_free_txq_pcpu:
@@ -8410,7 +8580,6 @@ err_free_irq:
 err_deinit_qvecs:
        mvpp2_queue_vectors_deinit(port);
 err_free_netdev:
-       of_node_put(phy_node);
        free_netdev(dev);
        return err;
 }
@@ -8421,7 +8590,8 @@ static void mvpp2_port_remove(struct mvpp2_port *port)
        int i;
 
        unregister_netdev(port->dev);
-       of_node_put(port->phy_node);
+       if (port->phylink)
+               phylink_destroy(port->phylink);
        free_percpu(port->pcpu);
        free_percpu(port->stats);
        for (i = 0; i < port->ntxqs; i++)