net: altera: tse: convert to phylink
authorMaxime Chevallier <maxime.chevallier@bootlin.com>
Fri, 2 Sep 2022 08:32:04 +0000 (10:32 +0200)
committerDavid S. Miller <davem@davemloft.net>
Mon, 5 Sep 2022 09:16:53 +0000 (10:16 +0100)
Convert the Altera Triple Speed Ethernet Controller to phylink.
This controller supports MII, GMII and RGMII with its MAC, and
SGMII + 1000BaseX through a small embedded PCS.

The PCS itself has a register set very similar to what is found in a
typical 802.3 ethernet PHY, but this register set memory-mapped instead
of lying on an mdio bus.

Signed-off-by: Maxime Chevallier <maxime.chevallier@bootlin.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ethernet/altera/Kconfig
drivers/net/ethernet/altera/altera_tse.h
drivers/net/ethernet/altera/altera_tse_ethtool.c
drivers/net/ethernet/altera/altera_tse_main.c

index 914e56b91467a59d4e36cc9dca21826a7c8f3636..dd7fd41ccde5bfd087f3850923bbdf3416037678 100644 (file)
@@ -3,6 +3,8 @@ config ALTERA_TSE
        tristate "Altera Triple-Speed Ethernet MAC support"
        depends on HAS_DMA
        select PHYLIB
+       select PHYLINK
+       select PCS_ALTERA_TSE
        help
          This driver supports the Altera Triple-Speed (TSE) Ethernet MAC.
 
index f17acfb579a0212c7abaa511e12446961a9f5085..db5eed06e92dfa6049894d4ef48804e563c6e8ca 100644 (file)
@@ -27,6 +27,7 @@
 #include <linux/list.h>
 #include <linux/netdevice.h>
 #include <linux/phy.h>
+#include <linux/phylink.h>
 
 #define ALTERA_TSE_SW_RESET_WATCHDOG_CNTR      10000
 #define ALTERA_TSE_MAC_FIFO_WIDTH              4       /* TX/RX FIFO width in
 #define MAC_CMDCFG_DISABLE_READ_TIMEOUT_GET(v) GET_BIT_VALUE(v, 27)
 #define MAC_CMDCFG_CNT_RESET_GET(v)            GET_BIT_VALUE(v, 31)
 
-/* SGMII PCS register addresses
- */
-#define SGMII_PCS_SCRATCH      0x10
-#define SGMII_PCS_REV          0x11
-#define SGMII_PCS_LINK_TIMER_0 0x12
-#define SGMII_PCS_LINK_TIMER_1 0x13
-#define SGMII_PCS_IF_MODE      0x14
-#define SGMII_PCS_DIS_READ_TO  0x15
-#define SGMII_PCS_READ_TO      0x16
-#define SGMII_PCS_SW_RESET_TIMEOUT 100 /* usecs */
-
 /* MDIO registers within MAC register Space
  */
 struct altera_tse_mdio {
@@ -423,6 +413,9 @@ struct altera_tse_private {
        void __iomem *tx_dma_csr;
        void __iomem *tx_dma_desc;
 
+       /* SGMII PCS address space */
+       void __iomem *pcs_base;
+
        /* Rx buffers queue */
        struct tse_buffer *rx_ring;
        u32 rx_cons;
@@ -480,6 +473,10 @@ struct altera_tse_private {
        u32 msg_enable;
 
        struct altera_dmaops *dmaops;
+
+       struct phylink *phylink;
+       struct phylink_config phylink_config;
+       struct phylink_pcs *pcs;
 };
 
 /* Function prototypes
index f0b11a2786442854e45691f9cd57de5f2686c4f4..81313c85833eb6a30f0d6ffa19a5797c22d0376c 100644 (file)
@@ -221,6 +221,22 @@ static void tse_get_regs(struct net_device *dev, struct ethtool_regs *regs,
                buf[i] = csrrd32(priv->mac_dev, i * 4);
 }
 
+static int tse_ethtool_set_link_ksettings(struct net_device *dev,
+                                         const struct ethtool_link_ksettings *cmd)
+{
+       struct altera_tse_private *priv = netdev_priv(dev);
+
+       return phylink_ethtool_ksettings_set(priv->phylink, cmd);
+}
+
+static int tse_ethtool_get_link_ksettings(struct net_device *dev,
+                                         struct ethtool_link_ksettings *cmd)
+{
+       struct altera_tse_private *priv = netdev_priv(dev);
+
+       return phylink_ethtool_ksettings_get(priv->phylink, cmd);
+}
+
 static const struct ethtool_ops tse_ethtool_ops = {
        .get_drvinfo = tse_get_drvinfo,
        .get_regs_len = tse_reglen,
@@ -231,8 +247,8 @@ static const struct ethtool_ops tse_ethtool_ops = {
        .get_ethtool_stats = tse_fill_stats,
        .get_msglevel = tse_get_msglevel,
        .set_msglevel = tse_set_msglevel,
-       .get_link_ksettings = phy_ethtool_get_link_ksettings,
-       .set_link_ksettings = phy_ethtool_set_link_ksettings,
+       .get_link_ksettings = tse_ethtool_get_link_ksettings,
+       .set_link_ksettings = tse_ethtool_set_link_ksettings,
        .get_ts_info = ethtool_op_get_ts_info,
 };
 
index 930afc9ec833d772807b98006194c4702355ca29..89ae6d1623aaaf82117e380a2bbf55d265f942af 100644 (file)
@@ -32,6 +32,7 @@
 #include <linux/of_mdio.h>
 #include <linux/of_net.h>
 #include <linux/of_platform.h>
+#include <linux/pcs-altera-tse.h>
 #include <linux/phy.h>
 #include <linux/platform_device.h>
 #include <linux/skbuff.h>
@@ -86,27 +87,6 @@ static inline u32 tse_tx_avail(struct altera_tse_private *priv)
        return priv->tx_cons + priv->tx_ring_size - priv->tx_prod - 1;
 }
 
-/* PCS Register read/write functions
- */
-static u16 sgmii_pcs_read(struct altera_tse_private *priv, int regnum)
-{
-       return csrrd32(priv->mac_dev,
-                      tse_csroffs(mdio_phy0) + regnum * 4) & 0xffff;
-}
-
-static void sgmii_pcs_write(struct altera_tse_private *priv, int regnum,
-                               u16 value)
-{
-       csrwr32(value, priv->mac_dev, tse_csroffs(mdio_phy0) + regnum * 4);
-}
-
-/* Check PCS scratch memory */
-static int sgmii_pcs_scratch_test(struct altera_tse_private *priv, u16 value)
-{
-       sgmii_pcs_write(priv, SGMII_PCS_SCRATCH, value);
-       return (sgmii_pcs_read(priv, SGMII_PCS_SCRATCH) == value);
-}
-
 /* MDIO specific functions
  */
 static int altera_tse_mdio_read(struct mii_bus *bus, int mii_id, int regnum)
@@ -620,117 +600,6 @@ out:
        return ret;
 }
 
-/* Called every time the controller might need to be made
- * aware of new link state.  The PHY code conveys this
- * information through variables in the phydev structure, and this
- * function converts those variables into the appropriate
- * register values, and can bring down the device if needed.
- */
-static void altera_tse_adjust_link(struct net_device *dev)
-{
-       struct altera_tse_private *priv = netdev_priv(dev);
-       struct phy_device *phydev = dev->phydev;
-       int new_state = 0;
-
-       /* only change config if there is a link */
-       spin_lock(&priv->mac_cfg_lock);
-       if (phydev->link) {
-               /* Read old config */
-               u32 cfg_reg = ioread32(&priv->mac_dev->command_config);
-
-               /* Check duplex */
-               if (phydev->duplex != priv->oldduplex) {
-                       new_state = 1;
-                       if (!(phydev->duplex))
-                               cfg_reg |= MAC_CMDCFG_HD_ENA;
-                       else
-                               cfg_reg &= ~MAC_CMDCFG_HD_ENA;
-
-                       netdev_dbg(priv->dev, "%s: Link duplex = 0x%x\n",
-                                  dev->name, phydev->duplex);
-
-                       priv->oldduplex = phydev->duplex;
-               }
-
-               /* Check speed */
-               if (phydev->speed != priv->oldspeed) {
-                       new_state = 1;
-                       switch (phydev->speed) {
-                       case 1000:
-                               cfg_reg |= MAC_CMDCFG_ETH_SPEED;
-                               cfg_reg &= ~MAC_CMDCFG_ENA_10;
-                               break;
-                       case 100:
-                               cfg_reg &= ~MAC_CMDCFG_ETH_SPEED;
-                               cfg_reg &= ~MAC_CMDCFG_ENA_10;
-                               break;
-                       case 10:
-                               cfg_reg &= ~MAC_CMDCFG_ETH_SPEED;
-                               cfg_reg |= MAC_CMDCFG_ENA_10;
-                               break;
-                       default:
-                               if (netif_msg_link(priv))
-                                       netdev_warn(dev, "Speed (%d) is not 10/100/1000!\n",
-                                                   phydev->speed);
-                               break;
-                       }
-                       priv->oldspeed = phydev->speed;
-               }
-               iowrite32(cfg_reg, &priv->mac_dev->command_config);
-
-               if (!priv->oldlink) {
-                       new_state = 1;
-                       priv->oldlink = 1;
-               }
-       } else if (priv->oldlink) {
-               new_state = 1;
-               priv->oldlink = 0;
-               priv->oldspeed = 0;
-               priv->oldduplex = -1;
-       }
-
-       if (new_state && netif_msg_link(priv))
-               phy_print_status(phydev);
-
-       spin_unlock(&priv->mac_cfg_lock);
-}
-static struct phy_device *connect_local_phy(struct net_device *dev)
-{
-       struct altera_tse_private *priv = netdev_priv(dev);
-       char phy_id_fmt[MII_BUS_ID_SIZE + 3];
-       struct phy_device *phydev = NULL;
-
-       if (priv->phy_addr != POLL_PHY) {
-               snprintf(phy_id_fmt, MII_BUS_ID_SIZE + 3, PHY_ID_FMT,
-                        priv->mdio->id, priv->phy_addr);
-
-               netdev_dbg(dev, "trying to attach to %s\n", phy_id_fmt);
-
-               phydev = phy_connect(dev, phy_id_fmt, &altera_tse_adjust_link,
-                                    priv->phy_iface);
-               if (IS_ERR(phydev)) {
-                       netdev_err(dev, "Could not attach to PHY\n");
-                       phydev = NULL;
-               }
-
-       } else {
-               int ret;
-               phydev = phy_find_first(priv->mdio);
-               if (phydev == NULL) {
-                       netdev_err(dev, "No PHY found\n");
-                       return phydev;
-               }
-
-               ret = phy_connect_direct(dev, phydev, &altera_tse_adjust_link,
-                               priv->phy_iface);
-               if (ret != 0) {
-                       netdev_err(dev, "Could not attach to PHY\n");
-                       phydev = NULL;
-               }
-       }
-       return phydev;
-}
-
 static int altera_tse_phy_get_addr_mdio_create(struct net_device *dev)
 {
        struct altera_tse_private *priv = netdev_priv(dev);
@@ -769,91 +638,6 @@ static int altera_tse_phy_get_addr_mdio_create(struct net_device *dev)
        return 0;
 }
 
-/* Initialize driver's PHY state, and attach to the PHY
- */
-static int init_phy(struct net_device *dev)
-{
-       struct altera_tse_private *priv = netdev_priv(dev);
-       struct device_node *phynode;
-       struct phy_device *phydev;
-       bool fixed_link = false;
-       int rc = 0;
-
-       /* Avoid init phy in case of no phy present */
-       if (!priv->phy_iface)
-               return 0;
-
-       priv->oldlink = 0;
-       priv->oldspeed = 0;
-       priv->oldduplex = -1;
-
-       phynode = of_parse_phandle(priv->device->of_node, "phy-handle", 0);
-
-       if (!phynode) {
-               /* check if a fixed-link is defined in device-tree */
-               if (of_phy_is_fixed_link(priv->device->of_node)) {
-                       rc = of_phy_register_fixed_link(priv->device->of_node);
-                       if (rc < 0) {
-                               netdev_err(dev, "cannot register fixed PHY\n");
-                               return rc;
-                       }
-
-                       /* In the case of a fixed PHY, the DT node associated
-                        * to the PHY is the Ethernet MAC DT node.
-                        */
-                       phynode = of_node_get(priv->device->of_node);
-                       fixed_link = true;
-
-                       netdev_dbg(dev, "fixed-link detected\n");
-                       phydev = of_phy_connect(dev, phynode,
-                                               &altera_tse_adjust_link,
-                                               0, priv->phy_iface);
-               } else {
-                       netdev_dbg(dev, "no phy-handle found\n");
-                       if (!priv->mdio) {
-                               netdev_err(dev, "No phy-handle nor local mdio specified\n");
-                               return -ENODEV;
-                       }
-                       phydev = connect_local_phy(dev);
-               }
-       } else {
-               netdev_dbg(dev, "phy-handle found\n");
-               phydev = of_phy_connect(dev, phynode,
-                       &altera_tse_adjust_link, 0, priv->phy_iface);
-       }
-       of_node_put(phynode);
-
-       if (!phydev) {
-               netdev_err(dev, "Could not find the PHY\n");
-               if (fixed_link)
-                       of_phy_deregister_fixed_link(priv->device->of_node);
-               return -ENODEV;
-       }
-
-       /* Stop Advertising 1000BASE Capability if interface is not GMII
-        */
-       if ((priv->phy_iface == PHY_INTERFACE_MODE_MII) ||
-           (priv->phy_iface == PHY_INTERFACE_MODE_RMII))
-               phy_set_max_speed(phydev, SPEED_100);
-
-       /* Broken HW is sometimes missing the pull-up resistor on the
-        * MDIO line, which results in reads to non-existent devices returning
-        * 0 rather than 0xffff. Catch this here and treat 0 as a non-existent
-        * device as well. If a fixed-link is used the phy_id is always 0.
-        * Note: phydev->phy_id is the result of reading the UID PHY registers.
-        */
-       if ((phydev->phy_id == 0) && !fixed_link) {
-               netdev_err(dev, "Bad PHY UID 0x%08x\n", phydev->phy_id);
-               phy_disconnect(phydev);
-               return -ENODEV;
-       }
-
-       netdev_dbg(dev, "attached to PHY %d UID 0x%08x Link = %d\n",
-                  phydev->mdio.addr, phydev->phy_id, phydev->link);
-
-       return 0;
-}
-
 static void tse_update_mac_addr(struct altera_tse_private *priv, const u8 *addr)
 {
        u32 msb;
@@ -1088,66 +872,6 @@ static void tse_set_rx_mode(struct net_device *dev)
        spin_unlock(&priv->mac_cfg_lock);
 }
 
-/* Initialise (if necessary) the SGMII PCS component
- */
-static int init_sgmii_pcs(struct net_device *dev)
-{
-       struct altera_tse_private *priv = netdev_priv(dev);
-       int n;
-       unsigned int tmp_reg = 0;
-
-       if (priv->phy_iface != PHY_INTERFACE_MODE_SGMII)
-               return 0; /* Nothing to do, not in SGMII mode */
-
-       /* The TSE SGMII PCS block looks a little like a PHY, it is
-        * mapped into the zeroth MDIO space of the MAC and it has
-        * ID registers like a PHY would.  Sadly this is often
-        * configured to zeroes, so don't be surprised if it does
-        * show 0x00000000.
-        */
-
-       if (sgmii_pcs_scratch_test(priv, 0x0000) &&
-               sgmii_pcs_scratch_test(priv, 0xffff) &&
-               sgmii_pcs_scratch_test(priv, 0xa5a5) &&
-               sgmii_pcs_scratch_test(priv, 0x5a5a)) {
-               netdev_info(dev, "PCS PHY ID: 0x%04x%04x\n",
-                               sgmii_pcs_read(priv, MII_PHYSID1),
-                               sgmii_pcs_read(priv, MII_PHYSID2));
-       } else {
-               netdev_err(dev, "SGMII PCS Scratch memory test failed.\n");
-               return -ENOMEM;
-       }
-
-       /* Starting on page 5-29 of the MegaCore Function User Guide
-        * Set SGMII Link timer to 1.6ms
-        */
-       sgmii_pcs_write(priv, SGMII_PCS_LINK_TIMER_0, 0x0D40);
-       sgmii_pcs_write(priv, SGMII_PCS_LINK_TIMER_1, 0x03);
-
-       /* Enable SGMII Interface and Enable SGMII Auto Negotiation */
-       sgmii_pcs_write(priv, SGMII_PCS_IF_MODE, 0x3);
-
-       /* Enable Autonegotiation */
-       tmp_reg = sgmii_pcs_read(priv, MII_BMCR);
-       tmp_reg |= (BMCR_SPEED1000 | BMCR_FULLDPLX | BMCR_ANENABLE);
-       sgmii_pcs_write(priv, MII_BMCR, tmp_reg);
-
-       /* Reset PCS block */
-       tmp_reg |= BMCR_RESET;
-       sgmii_pcs_write(priv, MII_BMCR, tmp_reg);
-       for (n = 0; n < SGMII_PCS_SW_RESET_TIMEOUT; n++) {
-               if (!(sgmii_pcs_read(priv, MII_BMCR) & BMCR_RESET)) {
-                       netdev_info(dev, "SGMII PCS block initialised OK\n");
-                       return 0;
-               }
-               udelay(1);
-       }
-
-       /* We failed to reset the block, return a timeout */
-       netdev_err(dev, "SGMII PCS block reset failed.\n");
-       return -ETIMEDOUT;
-}
-
 /* Open and initialize the interface
  */
 static int tse_open(struct net_device *dev)
@@ -1172,14 +896,6 @@ static int tse_open(struct net_device *dev)
                netdev_warn(dev, "TSE revision %x\n", priv->revision);
 
        spin_lock(&priv->mac_cfg_lock);
-       /* no-op if MAC not operating in SGMII mode*/
-       ret = init_sgmii_pcs(dev);
-       if (ret) {
-               netdev_err(dev,
-                          "Cannot init the SGMII PCS (error: %d)\n", ret);
-               spin_unlock(&priv->mac_cfg_lock);
-               goto phy_error;
-       }
 
        ret = reset_mac(priv);
        /* Note that reset_mac will fail if the clocks are gated by the PHY
@@ -1237,8 +953,12 @@ static int tse_open(struct net_device *dev)
 
        spin_unlock_irqrestore(&priv->rxdma_irq_lock, flags);
 
-       if (dev->phydev)
-               phy_start(dev->phydev);
+       ret = phylink_of_phy_connect(priv->phylink, priv->device->of_node, 0);
+       if (ret) {
+               netdev_err(dev, "could not connect phylink (%d)\n", ret);
+               goto tx_request_irq_error;
+       }
+       phylink_start(priv->phylink);
 
        napi_enable(&priv->napi);
        netif_start_queue(dev);
@@ -1269,10 +989,7 @@ static int tse_shutdown(struct net_device *dev)
        unsigned long int flags;
        int ret;
 
-       /* Stop the PHY */
-       if (dev->phydev)
-               phy_stop(dev->phydev);
-
+       phylink_stop(priv->phylink);
        netif_stop_queue(dev);
        napi_disable(&priv->napi);
 
@@ -1318,6 +1035,74 @@ static struct net_device_ops altera_tse_netdev_ops = {
        .ndo_validate_addr      = eth_validate_addr,
 };
 
+static void alt_tse_mac_an_restart(struct phylink_config *config)
+{
+}
+
+static void alt_tse_mac_config(struct phylink_config *config, unsigned int mode,
+                              const struct phylink_link_state *state)
+{
+       struct net_device *ndev = to_net_dev(config->dev);
+       struct altera_tse_private *priv = netdev_priv(ndev);
+
+       spin_lock(&priv->mac_cfg_lock);
+       reset_mac(priv);
+       tse_set_mac(priv, true);
+       spin_unlock(&priv->mac_cfg_lock);
+}
+
+static void alt_tse_mac_link_down(struct phylink_config *config,
+                                 unsigned int mode, phy_interface_t interface)
+{
+}
+
+static void alt_tse_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)
+{
+       struct net_device *ndev = to_net_dev(config->dev);
+       struct altera_tse_private *priv = netdev_priv(ndev);
+       u32 ctrl;
+
+       ctrl = csrrd32(priv->mac_dev, tse_csroffs(command_config));
+       ctrl &= ~(MAC_CMDCFG_ENA_10 | MAC_CMDCFG_ETH_SPEED | MAC_CMDCFG_HD_ENA);
+
+       if (duplex == DUPLEX_HALF)
+               ctrl |= MAC_CMDCFG_HD_ENA;
+
+       if (speed == SPEED_1000)
+               ctrl |= MAC_CMDCFG_ETH_SPEED;
+       else if (speed == SPEED_10)
+               ctrl |= MAC_CMDCFG_ENA_10;
+
+       spin_lock(&priv->mac_cfg_lock);
+       csrwr32(ctrl, priv->mac_dev, tse_csroffs(command_config));
+       spin_unlock(&priv->mac_cfg_lock);
+}
+
+static struct phylink_pcs *alt_tse_select_pcs(struct phylink_config *config,
+                                             phy_interface_t interface)
+{
+       struct net_device *ndev = to_net_dev(config->dev);
+       struct altera_tse_private *priv = netdev_priv(ndev);
+
+       if (interface == PHY_INTERFACE_MODE_SGMII ||
+           interface == PHY_INTERFACE_MODE_1000BASEX)
+               return priv->pcs;
+       else
+               return NULL;
+}
+
+static const struct phylink_mac_ops alt_tse_phylink_ops = {
+       .validate = phylink_generic_validate,
+       .mac_an_restart = alt_tse_mac_an_restart,
+       .mac_config = alt_tse_mac_config,
+       .mac_link_down = alt_tse_mac_link_down,
+       .mac_link_up = alt_tse_mac_link_up,
+       .mac_select_pcs = alt_tse_select_pcs,
+};
+
 static int request_and_map(struct platform_device *pdev, const char *name,
                           struct resource **res, void __iomem **ptr)
 {
@@ -1355,8 +1140,10 @@ static int altera_tse_probe(struct platform_device *pdev)
        struct altera_tse_private *priv;
        struct resource *control_port;
        struct resource *dma_res;
+       struct resource *pcs_res;
        struct net_device *ndev;
        void __iomem *descmap;
+       int pcs_reg_width = 2;
        int ret = -ENODEV;
 
        ndev = alloc_etherdev(sizeof(struct altera_tse_private));
@@ -1468,6 +1255,17 @@ static int altera_tse_probe(struct platform_device *pdev)
        if (ret)
                goto err_free_netdev;
 
+       /* SGMII PCS address space. The location can vary depending on how the
+        * IP is integrated. We can have a resource dedicated to it at a specific
+        * address space, but if it's not the case, we fallback to the mdiophy0
+        * from the MAC's address space
+        */
+       ret = request_and_map(pdev, "pcs", &pcs_res,
+                             &priv->pcs_base);
+       if (ret) {
+               priv->pcs_base = priv->mac_dev + tse_csroffs(mdio_phy0);
+               pcs_reg_width = 4;
+       }
 
        /* Rx IRQ */
        priv->rx_irq = platform_get_irq_byname(pdev, "rx_irq");
@@ -1591,11 +1389,31 @@ static int altera_tse_probe(struct platform_device *pdev)
                         (unsigned long) control_port->start, priv->rx_irq,
                         priv->tx_irq);
 
-       ret = init_phy(ndev);
-       if (ret != 0) {
-               netdev_err(ndev, "Cannot attach to PHY (error: %d)\n", ret);
+       priv->pcs = alt_tse_pcs_create(ndev, priv->pcs_base, pcs_reg_width);
+
+       priv->phylink_config.dev = &ndev->dev;
+       priv->phylink_config.type = PHYLINK_NETDEV;
+       priv->phylink_config.mac_capabilities = MAC_SYM_PAUSE | MAC_10 |
+                                               MAC_100 | MAC_1000FD;
+
+       phy_interface_set_rgmii(priv->phylink_config.supported_interfaces);
+       __set_bit(PHY_INTERFACE_MODE_MII,
+                 priv->phylink_config.supported_interfaces);
+       __set_bit(PHY_INTERFACE_MODE_GMII,
+                 priv->phylink_config.supported_interfaces);
+       __set_bit(PHY_INTERFACE_MODE_SGMII,
+                 priv->phylink_config.supported_interfaces);
+       __set_bit(PHY_INTERFACE_MODE_1000BASEX,
+                 priv->phylink_config.supported_interfaces);
+
+       priv->phylink = phylink_create(&priv->phylink_config,
+                                      of_fwnode_handle(priv->device->of_node),
+                                      priv->phy_iface, &alt_tse_phylink_ops);
+       if (IS_ERR(priv->phylink)) {
+               dev_err(&pdev->dev, "failed to create phylink\n");
                goto err_init_phy;
        }
+
        return 0;
 
 err_init_phy:
@@ -1615,16 +1433,10 @@ static int altera_tse_remove(struct platform_device *pdev)
        struct net_device *ndev = platform_get_drvdata(pdev);
        struct altera_tse_private *priv = netdev_priv(ndev);
 
-       if (ndev->phydev) {
-               phy_disconnect(ndev->phydev);
-
-               if (of_phy_is_fixed_link(priv->device->of_node))
-                       of_phy_deregister_fixed_link(priv->device->of_node);
-       }
-
        platform_set_drvdata(pdev, NULL);
        altera_tse_mdio_destroy(ndev);
        unregister_netdev(ndev);
+       phylink_destroy(priv->phylink);
        free_netdev(ndev);
 
        return 0;