diff options
author | Jonas Gorski <jonas.gorski@gmail.com> | 2016-09-29 12:16:51 +0200 |
---|---|---|
committer | Jonas Gorski <jonas.gorski@gmail.com> | 2016-09-29 13:47:18 +0200 |
commit | 4aa5d3e60d6007809a4b7fac9959645ef1600cf9 (patch) | |
tree | 7cb22196cbe77fccaec11c5606900a3713313225 /target/linux/mvebu/patches-4.4/134-net-mvneta-convert-to-phylink.patch | |
parent | a454756c913060977fd826ed421b9c7cfca3372e (diff) | |
download | upstream-4aa5d3e60d6007809a4b7fac9959645ef1600cf9.tar.gz upstream-4aa5d3e60d6007809a4b7fac9959645ef1600cf9.tar.bz2 upstream-4aa5d3e60d6007809a4b7fac9959645ef1600cf9.zip |
mvebu: add support for SFP
Add patches for SFP support and package it for ClearFog. Tested with a
Juniper SFP module.
Signed-off-by: Jonas Gorski <jonas.gorski@gmail.com>
Acked-by: Felix Fietkau <nbd@nbd.name>
Diffstat (limited to 'target/linux/mvebu/patches-4.4/134-net-mvneta-convert-to-phylink.patch')
-rw-r--r-- | target/linux/mvebu/patches-4.4/134-net-mvneta-convert-to-phylink.patch | 697 |
1 files changed, 697 insertions, 0 deletions
diff --git a/target/linux/mvebu/patches-4.4/134-net-mvneta-convert-to-phylink.patch b/target/linux/mvebu/patches-4.4/134-net-mvneta-convert-to-phylink.patch new file mode 100644 index 0000000000..fac283f3ee --- /dev/null +++ b/target/linux/mvebu/patches-4.4/134-net-mvneta-convert-to-phylink.patch @@ -0,0 +1,697 @@ +From e268be0ddc666f4a98db462cbed2a97637e82b5c Mon Sep 17 00:00:00 2001 +From: Russell King <rmk+kernel@arm.linux.org.uk> +Date: Wed, 16 Sep 2015 21:27:10 +0100 +Subject: [PATCH 722/744] net: mvneta: convert to phylink + +Convert mvneta to use phylink, which models the MAC to PHY link in +a generic, reusable form. + +Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk> +--- + drivers/net/ethernet/marvell/Kconfig | 2 +- + drivers/net/ethernet/marvell/mvneta.c | 451 +++++++++++++++++----------------- + 2 files changed, 227 insertions(+), 226 deletions(-) + +--- a/drivers/net/ethernet/marvell/Kconfig ++++ b/drivers/net/ethernet/marvell/Kconfig +@@ -58,7 +58,7 @@ config MVNETA + tristate "Marvell Armada 370/38x/XP network interface support" + depends on PLAT_ORION + select MVMDIO +- select FIXED_PHY ++ select PHYLINK + ---help--- + This driver supports the network interface units in the + Marvell ARMADA XP, ARMADA 370 and ARMADA 38x SoC family. +--- a/drivers/net/ethernet/marvell/mvneta.c ++++ b/drivers/net/ethernet/marvell/mvneta.c +@@ -28,6 +28,7 @@ + #include <linux/of_mdio.h> + #include <linux/of_net.h> + #include <linux/phy.h> ++#include <linux/phylink.h> + #include <linux/platform_device.h> + #include <linux/skbuff.h> + #include <net/hwbm.h> +@@ -188,6 +189,7 @@ + #define MVNETA_GMAC_CTRL_0 0x2c00 + #define MVNETA_GMAC_MAX_RX_SIZE_SHIFT 2 + #define MVNETA_GMAC_MAX_RX_SIZE_MASK 0x7ffc ++#define MVNETA_GMAC0_PORT_1000BASE_X BIT(1) + #define MVNETA_GMAC0_PORT_ENABLE BIT(0) + #define MVNETA_GMAC_CTRL_2 0x2c08 + #define MVNETA_GMAC2_INBAND_AN_ENABLE BIT(0) +@@ -203,13 +205,19 @@ + #define MVNETA_GMAC_TX_FLOW_CTRL_ENABLE BIT(5) + #define MVNETA_GMAC_RX_FLOW_CTRL_ACTIVE BIT(6) + #define MVNETA_GMAC_TX_FLOW_CTRL_ACTIVE BIT(7) ++#define MVNETA_GMAC_AN_COMPLETE BIT(11) ++#define MVNETA_GMAC_SYNC_OK BIT(14) + #define MVNETA_GMAC_AUTONEG_CONFIG 0x2c0c + #define MVNETA_GMAC_FORCE_LINK_DOWN BIT(0) + #define MVNETA_GMAC_FORCE_LINK_PASS BIT(1) + #define MVNETA_GMAC_INBAND_AN_ENABLE BIT(2) ++#define MVNETA_GMAC_AN_BYPASS_ENABLE BIT(3) ++#define MVNETA_GMAC_INBAND_RESTART_AN BIT(4) + #define MVNETA_GMAC_CONFIG_MII_SPEED BIT(5) + #define MVNETA_GMAC_CONFIG_GMII_SPEED BIT(6) + #define MVNETA_GMAC_AN_SPEED_EN BIT(7) ++#define MVNETA_GMAC_CONFIG_FLOW_CTRL BIT(8) ++#define MVNETA_GMAC_ADVERT_SYM_FLOW_CTRL BIT(9) + #define MVNETA_GMAC_AN_FLOW_CTRL_EN BIT(11) + #define MVNETA_GMAC_CONFIG_FULL_DUPLEX BIT(12) + #define MVNETA_GMAC_AN_DUPLEX_EN BIT(13) +@@ -396,15 +404,9 @@ struct mvneta_port { + u16 tx_ring_size; + u16 rx_ring_size; + +- struct mii_bus *mii_bus; +- struct phy_device *phy_dev; +- phy_interface_t phy_interface; +- struct device_node *phy_node; +- unsigned int link; +- unsigned int duplex; +- unsigned int speed; ++ struct device_node *dn; + unsigned int tx_csum_limit; +- unsigned int use_inband_status:1; ++ struct phylink *phylink; + + struct mvneta_bm *bm_priv; + struct mvneta_bm_pool *pool_long; +@@ -1236,44 +1238,6 @@ static void mvneta_set_other_mcast_table + mvreg_write(pp, MVNETA_DA_FILT_OTH_MCAST + offset, val); + } + +-static void mvneta_set_autoneg(struct mvneta_port *pp, int enable) +-{ +- u32 val; +- +- if (enable) { +- val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG); +- val &= ~(MVNETA_GMAC_FORCE_LINK_PASS | +- MVNETA_GMAC_FORCE_LINK_DOWN | +- MVNETA_GMAC_AN_FLOW_CTRL_EN); +- val |= MVNETA_GMAC_INBAND_AN_ENABLE | +- MVNETA_GMAC_AN_SPEED_EN | +- MVNETA_GMAC_AN_DUPLEX_EN; +- mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val); +- +- val = mvreg_read(pp, MVNETA_GMAC_CLOCK_DIVIDER); +- val |= MVNETA_GMAC_1MS_CLOCK_ENABLE; +- mvreg_write(pp, MVNETA_GMAC_CLOCK_DIVIDER, val); +- +- val = mvreg_read(pp, MVNETA_GMAC_CTRL_2); +- val |= MVNETA_GMAC2_INBAND_AN_ENABLE; +- mvreg_write(pp, MVNETA_GMAC_CTRL_2, val); +- } else { +- val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG); +- val &= ~(MVNETA_GMAC_INBAND_AN_ENABLE | +- MVNETA_GMAC_AN_SPEED_EN | +- MVNETA_GMAC_AN_DUPLEX_EN); +- mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val); +- +- val = mvreg_read(pp, MVNETA_GMAC_CLOCK_DIVIDER); +- val &= ~MVNETA_GMAC_1MS_CLOCK_ENABLE; +- mvreg_write(pp, MVNETA_GMAC_CLOCK_DIVIDER, val); +- +- val = mvreg_read(pp, MVNETA_GMAC_CTRL_2); +- val &= ~MVNETA_GMAC2_INBAND_AN_ENABLE; +- mvreg_write(pp, MVNETA_GMAC_CTRL_2, val); +- } +-} +- + static void mvneta_percpu_unmask_interrupt(void *arg) + { + struct mvneta_port *pp = arg; +@@ -1421,7 +1385,6 @@ static void mvneta_defaults_set(struct m + val &= ~MVNETA_PHY_POLLING_ENABLE; + mvreg_write(pp, MVNETA_UNIT_CONTROL, val); + +- mvneta_set_autoneg(pp, pp->use_inband_status); + mvneta_set_ucast_table(pp, -1); + mvneta_set_special_mcast_table(pp, -1); + mvneta_set_other_mcast_table(pp, -1); +@@ -2614,26 +2577,11 @@ static irqreturn_t mvneta_isr(int irq, v + return IRQ_HANDLED; + } + +-static int mvneta_fixed_link_update(struct mvneta_port *pp, +- struct phy_device *phy) ++static void mvneta_link_change(struct mvneta_port *pp) + { +- struct fixed_phy_status status; +- struct fixed_phy_status changed = {}; + u32 gmac_stat = mvreg_read(pp, MVNETA_GMAC_STATUS); + +- status.link = !!(gmac_stat & MVNETA_GMAC_LINK_UP); +- if (gmac_stat & MVNETA_GMAC_SPEED_1000) +- status.speed = SPEED_1000; +- else if (gmac_stat & MVNETA_GMAC_SPEED_100) +- status.speed = SPEED_100; +- else +- status.speed = SPEED_10; +- status.duplex = !!(gmac_stat & MVNETA_GMAC_FULL_DUPLEX); +- changed.link = 1; +- changed.speed = 1; +- changed.duplex = 1; +- fixed_phy_update_state(phy, &status, &changed); +- return 0; ++ phylink_mac_change(pp->phylink, !!(gmac_stat & MVNETA_GMAC_LINK_UP)); + } + + /* NAPI handler +@@ -2662,12 +2610,11 @@ static int mvneta_poll(struct napi_struc + u32 cause_misc = mvreg_read(pp, MVNETA_INTR_MISC_CAUSE); + + mvreg_write(pp, MVNETA_INTR_MISC_CAUSE, 0); +- if (pp->use_inband_status && (cause_misc & +- (MVNETA_CAUSE_PHY_STATUS_CHANGE | +- MVNETA_CAUSE_LINK_CHANGE | +- MVNETA_CAUSE_PSC_SYNC_CHANGE))) { +- mvneta_fixed_link_update(pp, pp->phy_dev); +- } ++ ++ if (cause_misc & (MVNETA_CAUSE_PHY_STATUS_CHANGE | ++ MVNETA_CAUSE_LINK_CHANGE | ++ MVNETA_CAUSE_PSC_SYNC_CHANGE)) ++ mvneta_link_change(pp); + } + + /* Release Tx descriptors */ +@@ -2983,7 +2930,7 @@ static void mvneta_start_dev(struct mvne + MVNETA_CAUSE_LINK_CHANGE | + MVNETA_CAUSE_PSC_SYNC_CHANGE); + +- phy_start(pp->phy_dev); ++ phylink_start(pp->phylink); + netif_tx_start_all_queues(pp->dev); + } + +@@ -2991,7 +2938,7 @@ static void mvneta_stop_dev(struct mvnet + { + unsigned int cpu; + +- phy_stop(pp->phy_dev); ++ phylink_stop(pp->phylink); + + for_each_online_cpu(cpu) { + struct mvneta_pcpu_port *port = per_cpu_ptr(pp->ports, cpu); +@@ -3161,99 +3108,219 @@ static int mvneta_set_mac_addr(struct ne + return 0; + } + +-static void mvneta_adjust_link(struct net_device *ndev) ++static int mvneta_mac_support(struct net_device *ndev, unsigned int mode, ++ struct phylink_link_state *state) ++{ ++ switch (mode) { ++ case MLO_AN_8023Z: ++ state->supported = SUPPORTED_1000baseT_Full | ++ SUPPORTED_Autoneg | SUPPORTED_Pause; ++ state->advertising = ADVERTISED_1000baseT_Full | ++ ADVERTISED_Autoneg | ADVERTISED_Pause; ++ state->an_enabled = 1; ++ break; ++ ++ case MLO_AN_FIXED: ++ break; ++ ++ default: ++ state->supported = PHY_10BT_FEATURES | ++ PHY_100BT_FEATURES | ++ SUPPORTED_1000baseT_Full | ++ SUPPORTED_Autoneg; ++ state->advertising = ADVERTISED_10baseT_Half | ++ ADVERTISED_10baseT_Full | ++ ADVERTISED_100baseT_Half | ++ ADVERTISED_100baseT_Full | ++ ADVERTISED_1000baseT_Full | ++ ADVERTISED_Autoneg; ++ state->an_enabled = 1; ++ break; ++ } ++ return 0; ++} ++ ++static int mvneta_mac_link_state(struct net_device *ndev, ++ struct phylink_link_state *state) + { + struct mvneta_port *pp = netdev_priv(ndev); +- struct phy_device *phydev = pp->phy_dev; +- int status_change = 0; ++ u32 gmac_stat; + +- if (phydev->link) { +- if ((pp->speed != phydev->speed) || +- (pp->duplex != phydev->duplex)) { +- u32 val; +- +- val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG); +- val &= ~(MVNETA_GMAC_CONFIG_MII_SPEED | +- MVNETA_GMAC_CONFIG_GMII_SPEED | +- MVNETA_GMAC_CONFIG_FULL_DUPLEX); +- +- if (phydev->duplex) +- val |= MVNETA_GMAC_CONFIG_FULL_DUPLEX; +- +- if (phydev->speed == SPEED_1000) +- val |= MVNETA_GMAC_CONFIG_GMII_SPEED; +- else if (phydev->speed == SPEED_100) +- val |= MVNETA_GMAC_CONFIG_MII_SPEED; ++ gmac_stat = mvreg_read(pp, MVNETA_GMAC_STATUS); + +- mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val); ++ if (gmac_stat & MVNETA_GMAC_SPEED_1000) ++ state->speed = SPEED_1000; ++ else if (gmac_stat & MVNETA_GMAC_SPEED_100) ++ state->speed = SPEED_100; ++ else ++ state->speed = SPEED_10; + +- pp->duplex = phydev->duplex; +- pp->speed = phydev->speed; +- } ++ state->an_complete = !!(gmac_stat & MVNETA_GMAC_AN_COMPLETE); ++ state->sync = !!(gmac_stat & MVNETA_GMAC_SYNC_OK); ++ state->link = !!(gmac_stat & MVNETA_GMAC_LINK_UP); ++ state->duplex = !!(gmac_stat & MVNETA_GMAC_FULL_DUPLEX); ++ ++ state->pause = 0; ++ if (gmac_stat & MVNETA_GMAC_RX_FLOW_CTRL_ENABLE) ++ state->pause |= MLO_PAUSE_RX; ++ if (gmac_stat & MVNETA_GMAC_TX_FLOW_CTRL_ENABLE) ++ state->pause |= MLO_PAUSE_TX; ++ ++ return 1; ++} ++ ++static void mvneta_mac_an_restart(struct net_device *ndev, unsigned int mode) ++{ ++ struct mvneta_port *pp = netdev_priv(ndev); ++ ++ if (mode == MLO_AN_8023Z) { ++ u32 gmac_an = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG); ++ ++ mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, ++ gmac_an | MVNETA_GMAC_INBAND_RESTART_AN); ++ mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, ++ gmac_an & ~MVNETA_GMAC_INBAND_RESTART_AN); + } ++} + +- if (phydev->link != pp->link) { +- if (!phydev->link) { +- pp->duplex = -1; +- pp->speed = 0; +- } ++static void mvneta_mac_config(struct net_device *ndev, unsigned int mode, ++ const struct phylink_link_state *state) ++{ ++ struct mvneta_port *pp = netdev_priv(ndev); ++ u32 new_ctrl0, gmac_ctrl0 = mvreg_read(pp, MVNETA_GMAC_CTRL_0); ++ u32 new_ctrl2, gmac_ctrl2 = mvreg_read(pp, MVNETA_GMAC_CTRL_2); ++ u32 new_clk, gmac_clk = mvreg_read(pp, MVNETA_GMAC_CLOCK_DIVIDER); ++ u32 new_an, gmac_an = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG); ++ ++ new_ctrl0 = gmac_ctrl0 & ~MVNETA_GMAC0_PORT_1000BASE_X; ++ new_ctrl2 = gmac_ctrl2 & ~MVNETA_GMAC2_INBAND_AN_ENABLE; ++ new_clk = gmac_clk & ~MVNETA_GMAC_1MS_CLOCK_ENABLE; ++ new_an = gmac_an & ~(MVNETA_GMAC_INBAND_AN_ENABLE | ++ MVNETA_GMAC_INBAND_RESTART_AN | ++ MVNETA_GMAC_CONFIG_MII_SPEED | ++ MVNETA_GMAC_CONFIG_GMII_SPEED | ++ MVNETA_GMAC_AN_SPEED_EN | ++ MVNETA_GMAC_ADVERT_SYM_FLOW_CTRL | ++ MVNETA_GMAC_CONFIG_FLOW_CTRL | ++ MVNETA_GMAC_AN_FLOW_CTRL_EN | ++ MVNETA_GMAC_CONFIG_FULL_DUPLEX | ++ MVNETA_GMAC_AN_DUPLEX_EN); ++ ++ if (state->advertising & ADVERTISED_Pause) ++ new_an |= MVNETA_GMAC_ADVERT_SYM_FLOW_CTRL; ++ ++ switch (mode) { ++ case MLO_AN_SGMII: ++ /* SGMII mode receives the state from the PHY */ ++ new_ctrl2 |= MVNETA_GMAC2_INBAND_AN_ENABLE; ++ new_clk |= MVNETA_GMAC_1MS_CLOCK_ENABLE; ++ new_an = (new_an & ~(MVNETA_GMAC_FORCE_LINK_DOWN | ++ MVNETA_GMAC_FORCE_LINK_PASS)) | ++ MVNETA_GMAC_INBAND_AN_ENABLE | ++ MVNETA_GMAC_AN_SPEED_EN | ++ MVNETA_GMAC_AN_DUPLEX_EN; ++ break; ++ ++ case MLO_AN_8023Z: ++ /* 802.3z negotiation - only 1000base-X */ ++ new_ctrl0 |= MVNETA_GMAC0_PORT_1000BASE_X; ++ new_clk |= MVNETA_GMAC_1MS_CLOCK_ENABLE; ++ new_an = (new_an & ~(MVNETA_GMAC_FORCE_LINK_DOWN | ++ MVNETA_GMAC_FORCE_LINK_PASS)) | ++ MVNETA_GMAC_INBAND_AN_ENABLE | ++ MVNETA_GMAC_CONFIG_GMII_SPEED | ++ /* The MAC only supports FD mode */ ++ MVNETA_GMAC_CONFIG_FULL_DUPLEX; ++ ++ if (state->an_enabled) ++ new_an |= MVNETA_GMAC_AN_FLOW_CTRL_EN; ++ break; + +- pp->link = phydev->link; +- status_change = 1; ++ default: ++ /* Phy or fixed speed */ ++ if (state->duplex) ++ new_an |= MVNETA_GMAC_CONFIG_FULL_DUPLEX; ++ ++ if (state->speed == SPEED_1000) ++ new_an |= MVNETA_GMAC_CONFIG_GMII_SPEED; ++ else if (state->speed == SPEED_100) ++ new_an |= MVNETA_GMAC_CONFIG_MII_SPEED; ++ break; + } + +- if (status_change) { +- if (phydev->link) { +- if (!pp->use_inband_status) { +- u32 val = mvreg_read(pp, +- MVNETA_GMAC_AUTONEG_CONFIG); +- val &= ~MVNETA_GMAC_FORCE_LINK_DOWN; +- val |= MVNETA_GMAC_FORCE_LINK_PASS; +- mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, +- val); +- } +- mvneta_port_up(pp); +- } else { +- if (!pp->use_inband_status) { +- u32 val = mvreg_read(pp, +- MVNETA_GMAC_AUTONEG_CONFIG); +- val &= ~MVNETA_GMAC_FORCE_LINK_PASS; +- val |= MVNETA_GMAC_FORCE_LINK_DOWN; +- mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, +- val); +- } +- mvneta_port_down(pp); +- } +- phy_print_status(phydev); ++ /* Armada 370 documentation says we can only change the port mode ++ * and in-band enable when the link is down, so force it down ++ * while making these changes. We also do this for GMAC_CTRL2 */ ++ if ((new_ctrl0 ^ gmac_ctrl0) & MVNETA_GMAC0_PORT_1000BASE_X || ++ (new_ctrl2 ^ gmac_ctrl2) & MVNETA_GMAC2_INBAND_AN_ENABLE || ++ (new_an ^ gmac_an) & MVNETA_GMAC_INBAND_AN_ENABLE) { ++ mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, ++ (gmac_an & ~MVNETA_GMAC_FORCE_LINK_PASS) | ++ MVNETA_GMAC_FORCE_LINK_DOWN); ++ } ++ ++ if (new_ctrl0 != gmac_ctrl0) ++ mvreg_write(pp, MVNETA_GMAC_CTRL_0, new_ctrl0); ++ if (new_ctrl2 != gmac_ctrl2) ++ mvreg_write(pp, MVNETA_GMAC_CTRL_2, new_ctrl2); ++ if (new_clk != gmac_clk) ++ mvreg_write(pp, MVNETA_GMAC_CLOCK_DIVIDER, new_clk); ++ if (new_an != gmac_an) ++ mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, new_an); ++} ++ ++static void mvneta_mac_link_down(struct net_device *ndev, unsigned int mode) ++{ ++ struct mvneta_port *pp = netdev_priv(ndev); ++ u32 val; ++ ++ mvneta_port_down(pp); ++ ++ if (mode == MLO_AN_PHY || mode == MLO_AN_FIXED) { ++ val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG); ++ val &= ~MVNETA_GMAC_FORCE_LINK_PASS; ++ val |= MVNETA_GMAC_FORCE_LINK_DOWN; ++ mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val); + } + } + +-static int mvneta_mdio_probe(struct mvneta_port *pp) ++static void mvneta_mac_link_up(struct net_device *ndev, unsigned int mode, ++ struct phy_device *phy) + { +- struct phy_device *phy_dev; ++ struct mvneta_port *pp = netdev_priv(ndev); ++ u32 val; ++ ++ if (mode == MLO_AN_PHY || mode == MLO_AN_FIXED) { ++ val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG); ++ val &= ~MVNETA_GMAC_FORCE_LINK_DOWN; ++ val |= MVNETA_GMAC_FORCE_LINK_PASS; ++ mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val); ++ } + +- phy_dev = of_phy_connect(pp->dev, pp->phy_node, mvneta_adjust_link, 0, +- pp->phy_interface); +- if (!phy_dev) { +- netdev_err(pp->dev, "could not find the PHY\n"); +- return -ENODEV; +- } +- +- phy_dev->supported &= PHY_GBIT_FEATURES; +- phy_dev->advertising = phy_dev->supported; +- +- pp->phy_dev = phy_dev; +- pp->link = 0; +- pp->duplex = 0; +- pp->speed = 0; ++ mvneta_port_up(pp); ++} + +- return 0; ++static const struct phylink_mac_ops mvneta_phylink_ops = { ++ .mac_get_support = mvneta_mac_support, ++ .mac_link_state = mvneta_mac_link_state, ++ .mac_an_restart = mvneta_mac_an_restart, ++ .mac_config = mvneta_mac_config, ++ .mac_link_down = mvneta_mac_link_down, ++ .mac_link_up = mvneta_mac_link_up, ++}; ++ ++static int mvneta_mdio_probe(struct mvneta_port *pp) ++{ ++ int err = phylink_of_phy_connect(pp->phylink, pp->dn); ++ if (err) ++ netdev_err(pp->dev, "could not attach PHY\n"); ++ ++ return err; + } + + static void mvneta_mdio_remove(struct mvneta_port *pp) + { +- phy_disconnect(pp->phy_dev); +- pp->phy_dev = NULL; ++ phylink_disconnect_phy(pp->phylink); + } + + /* Electing a CPU must be done in an atomic way: it should be done +@@ -3501,10 +3568,7 @@ static int mvneta_ioctl(struct net_devic + { + struct mvneta_port *pp = netdev_priv(dev); + +- if (!pp->phy_dev) +- return -ENOTSUPP; +- +- return phy_mii_ioctl(pp->phy_dev, ifr, cmd); ++ return phylink_mii_ioctl(pp->phylink, ifr, cmd); + } + + /* Ethtool methods */ +@@ -3514,54 +3578,15 @@ int mvneta_ethtool_get_settings(struct n + { + struct mvneta_port *pp = netdev_priv(dev); + +- if (!pp->phy_dev) +- return -ENODEV; +- +- return phy_ethtool_gset(pp->phy_dev, cmd); ++ return phylink_ethtool_get_settings(pp->phylink, cmd); + } + + /* Set settings (phy address, speed) for ethtools */ + int mvneta_ethtool_set_settings(struct net_device *dev, struct ethtool_cmd *cmd) + { + struct mvneta_port *pp = netdev_priv(dev); +- struct phy_device *phydev = pp->phy_dev; +- +- if (!phydev) +- return -ENODEV; + +- if ((cmd->autoneg == AUTONEG_ENABLE) != pp->use_inband_status) { +- u32 val; +- +- mvneta_set_autoneg(pp, cmd->autoneg == AUTONEG_ENABLE); +- +- if (cmd->autoneg == AUTONEG_DISABLE) { +- val = mvreg_read(pp, MVNETA_GMAC_AUTONEG_CONFIG); +- val &= ~(MVNETA_GMAC_CONFIG_MII_SPEED | +- MVNETA_GMAC_CONFIG_GMII_SPEED | +- MVNETA_GMAC_CONFIG_FULL_DUPLEX); +- +- if (phydev->duplex) +- val |= MVNETA_GMAC_CONFIG_FULL_DUPLEX; +- +- if (phydev->speed == SPEED_1000) +- val |= MVNETA_GMAC_CONFIG_GMII_SPEED; +- else if (phydev->speed == SPEED_100) +- val |= MVNETA_GMAC_CONFIG_MII_SPEED; +- +- mvreg_write(pp, MVNETA_GMAC_AUTONEG_CONFIG, val); +- } +- +- pp->use_inband_status = (cmd->autoneg == AUTONEG_ENABLE); +- netdev_info(pp->dev, "autoneg status set to %i\n", +- pp->use_inband_status); +- +- if (netif_running(dev)) { +- mvneta_port_down(pp); +- mvneta_port_up(pp); +- } +- } +- +- return phy_ethtool_sset(pp->phy_dev, cmd); ++ return phylink_ethtool_set_settings(pp->phylink, cmd); + } + + /* Set interrupt coalescing for ethtools */ +@@ -3669,7 +3694,8 @@ static void mvneta_ethtool_update_stats( + { + const struct mvneta_statistic *s; + void __iomem *base = pp->base; +- u32 high, low, val; ++ u32 high, low; ++ u64 val; + u64 val64; + int i; + +@@ -3964,14 +3990,13 @@ static int mvneta_probe(struct platform_ + const struct mbus_dram_target_info *dram_target_info; + struct resource *res; + struct device_node *dn = pdev->dev.of_node; +- struct device_node *phy_node; + struct device_node *bm_node; + struct mvneta_port *pp; + struct net_device *dev; ++ struct phylink *phylink; + const char *dt_mac_addr; + char hw_mac_addr[ETH_ALEN]; + const char *mac_from; +- const char *managed; + int tx_csum_limit; + int phy_mode; + int err; +@@ -3987,31 +4012,11 @@ static int mvneta_probe(struct platform_ + goto err_free_netdev; + } + +- phy_node = of_parse_phandle(dn, "phy", 0); +- if (!phy_node) { +- if (!of_phy_is_fixed_link(dn)) { +- dev_err(&pdev->dev, "no PHY specified\n"); +- err = -ENODEV; +- goto err_free_irq; +- } +- +- err = of_phy_register_fixed_link(dn); +- if (err < 0) { +- dev_err(&pdev->dev, "cannot register fixed PHY\n"); +- goto err_free_irq; +- } +- +- /* In the case of a fixed PHY, the DT node associated +- * to the PHY is the Ethernet MAC DT node. +- */ +- phy_node = of_node_get(dn); +- } +- + phy_mode = of_get_phy_mode(dn); + if (phy_mode < 0) { + dev_err(&pdev->dev, "incorrect phy-mode\n"); + err = -EINVAL; +- goto err_put_phy_node; ++ goto err_free_irq; + } + + dev->tx_queue_len = MVNETA_MAX_TXD; +@@ -4022,12 +4027,7 @@ static int mvneta_probe(struct platform_ + + pp = netdev_priv(dev); + spin_lock_init(&pp->lock); +- pp->phy_node = phy_node; +- pp->phy_interface = phy_mode; +- +- err = of_property_read_string(dn, "managed", &managed); +- pp->use_inband_status = (err == 0 && +- strcmp(managed, "in-band-status") == 0); ++ pp->dn = dn; + pp->cpu_notifier.notifier_call = mvneta_percpu_notifier; + + pp->rxq_def = rxq_def; +@@ -4037,7 +4037,7 @@ static int mvneta_probe(struct platform_ + pp->clk = devm_clk_get(&pdev->dev, NULL); + if (IS_ERR(pp->clk)) { + err = PTR_ERR(pp->clk); +- goto err_put_phy_node; ++ goto err_free_irq; + } + + clk_prepare_enable(pp->clk); +@@ -4140,6 +4140,14 @@ static int mvneta_probe(struct platform_ + dev->priv_flags |= IFF_UNICAST_FLT | IFF_LIVE_ADDR_CHANGE; + dev->gso_max_segs = MVNETA_MAX_TSO_SEGS; + ++ phylink = phylink_create(dev, dn, phy_mode, &mvneta_phylink_ops); ++ if (IS_ERR(phylink)) { ++ err = PTR_ERR(phylink); ++ goto err_free_stats; ++ } ++ ++ pp->phylink = phylink; ++ + err = register_netdev(dev); + if (err < 0) { + dev_err(&pdev->dev, "failed to register\n"); +@@ -4151,13 +4159,6 @@ static int mvneta_probe(struct platform_ + + platform_set_drvdata(pdev, pp->dev); + +- if (pp->use_inband_status) { +- struct phy_device *phy = of_phy_find_device(dn); +- +- mvneta_fixed_link_update(pp, phy); +- +- put_device(&phy->dev); +- } + + return 0; + +@@ -4169,13 +4170,13 @@ err_netdev: + 1 << pp->id); + } + err_free_stats: ++ if (pp->phylink) ++ phylink_destroy(pp->phylink); + free_percpu(pp->stats); + err_free_ports: + free_percpu(pp->ports); + err_clk: + clk_disable_unprepare(pp->clk); +-err_put_phy_node: +- of_node_put(phy_node); + err_free_irq: + irq_dispose_mapping(dev->irq); + err_free_netdev: +@@ -4194,7 +4195,7 @@ static int mvneta_remove(struct platform + free_percpu(pp->ports); + free_percpu(pp->stats); + irq_dispose_mapping(dev->irq); +- of_node_put(pp->phy_node); ++ phylink_destroy(pp->phylink); + free_netdev(dev); + + if (pp->bm_priv) { |