#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
        /* 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;
        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;
         (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
        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;
            (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);
 }
 
 /* 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;
        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;
                }
        }
 
+       if (port->phylink) {
+               phylink_mac_change(port->phylink, link);
+               goto handled;
+       }
+
        if (!netif_running(dev) || !event)
                goto 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;
        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 ||
                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);
 
        /* 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);
 }
 
        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;
        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);
                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) {
                }
 
                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);
 
        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:
 {
        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);
 
 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)
 
 /* 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)
        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 = {
 };
 
 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,
        .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
        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;
        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");
        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;
        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;
 
 
        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);
        /* 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);
 
 
        return 0;
 
+err_phylink:
+       if (port->phylink)
+               phylink_destroy(port->phylink);
 err_free_port_pcpu:
        free_percpu(port->pcpu);
 err_free_txq_pcpu:
 err_deinit_qvecs:
        mvpp2_queue_vectors_deinit(port);
 err_free_netdev:
-       of_node_put(phy_node);
        free_netdev(dev);
        return err;
 }
        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++)