Re: [PATCH net-next] net: mvpp2: phylink support

From: Marcin Wojtas
Date: Fri Sep 22 2017 - 03:56:18 EST


Hi Antoine,

You can add
Tested-by: Marcin Wojtas <mw@xxxxxxxxxxxx>

Best regards,
Marcin

2017-09-21 15:45 GMT+02:00 Antoine Tenart <antoine.tenart@xxxxxxxxxxxxxxxxxx>:
> Convert the PPv2 driver to use phylink, which models the MAC to PHY
> link. The phylink support is made such a way the GoP link IRQ can still
> be used: the two modes are incompatible and the GoP link IRQ will be
> used if no PHY is described in the device tree. This is the same
> behaviour as before.
>
> Signed-off-by: Antoine Tenart <antoine.tenart@xxxxxxxxxxxxxxxxxx>
> ---
> drivers/net/ethernet/marvell/Kconfig | 1 +
> drivers/net/ethernet/marvell/mvpp2.c | 502 +++++++++++++++++++++--------------
> 2 files changed, 303 insertions(+), 200 deletions(-)
>
> diff --git a/drivers/net/ethernet/marvell/Kconfig b/drivers/net/ethernet/marvell/Kconfig
> index da6fb825afea..991138b8dfba 100644
> --- a/drivers/net/ethernet/marvell/Kconfig
> +++ b/drivers/net/ethernet/marvell/Kconfig
> @@ -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.
> diff --git a/drivers/net/ethernet/marvell/mvpp2.c b/drivers/net/ethernet/marvell/mvpp2.c
> index 8041d692db3c..5fb7e76ee128 100644
> --- a/drivers/net/ethernet/marvell/mvpp2.c
> +++ b/drivers/net/ethernet/marvell/mvpp2.c
> @@ -28,6 +28,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>
> @@ -341,6 +342,7 @@
> #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)
> @@ -350,6 +352,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
> @@ -878,12 +886,11 @@ struct mvpp2_port {
> u16 rx_ring_size;
> struct mvpp2_pcpu_stats __percpu *stats;
>
> + 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;
> @@ -4716,13 +4723,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;
> @@ -4778,10 +4786,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;
> @@ -5860,111 +5864,6 @@ static irqreturn_t mvpp2_link_status_isr(int irq, void *dev_id)
> 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;
> @@ -6582,7 +6481,6 @@ static int mvpp2_poll(struct napi_struct *napi, int budget)
> /* Set hw internals when starting port */
> static void mvpp2_start_dev(struct mvpp2_port *port)
> {
> - struct net_device *ndev = port->dev;
> int i;
>
> if (port->gop_id == 0 &&
> @@ -6607,15 +6505,14 @@ static void mvpp2_start_dev(struct mvpp2_port *port)
>
> mvpp2_port_mii_set(port);
> mvpp2_port_enable(port);
> - if (ndev->phydev)
> - phy_start(ndev->phydev);
> + if (port->phylink)
> + phylink_start(port->phylink);
> 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 */
> @@ -6634,8 +6531,8 @@ static void mvpp2_stop_dev(struct mvpp2_port *port)
>
> 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);
> }
>
> @@ -6688,40 +6585,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;
> @@ -6765,7 +6628,6 @@ static void mvpp2_irqs_deinit(struct mvpp2_port *port)
> static int mvpp2_open(struct net_device *dev)
> {
> struct mvpp2_port *port = netdev_priv(dev);
> - struct mvpp2 *priv = port->priv;
> unsigned char mac_bcast[ETH_ALEN] = {
> 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
> int err;
> @@ -6811,7 +6673,16 @@ static int mvpp2_open(struct net_device *dev)
> goto err_cleanup_txqs;
> }
>
> - if (priv->hw_version == MVPP22 && !port->phy_node && port->link_irq) {
> + /* In default link is down */
> + netif_carrier_off(port->dev);
> +
> + if (port->phylink) {
> + err = phylink_of_phy_connect(port->phylink, port->of_node);
> + if (err) {
> + netdev_err(port->dev, "could not attach PHY (%d)\n", err);
> + goto err_free_irq;
> + }
> + } else if (port->link_irq) {
> err = request_irq(port->link_irq, mvpp2_link_status_isr, 0,
> dev->name, port);
> if (err) {
> @@ -6821,15 +6692,11 @@ static int mvpp2_open(struct net_device *dev)
> }
>
> mvpp22_gop_setup_irq(port);
> + } else {
> + netdev_err(dev, "cannot use phylink or GoP link IRQ\n");
> + goto err_free_irq;
> }
>
> - /* In default link is down */
> - netif_carrier_off(port->dev);
> -
> - err = mvpp2_phy_connect(port);
> - if (err < 0)
> - goto err_free_link_irq;
> -
> /* Unmask interrupts on all CPUs */
> on_each_cpu(mvpp2_interrupts_unmask, port, 1);
> mvpp2_shared_interrupt_mask_unmask(port, false);
> @@ -6838,9 +6705,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:
> @@ -6854,17 +6718,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);
> + if (port->phylink)
> + phylink_disconnect_phy(port->phylink);
>
> /* 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->link_irq)
> free_irq(port->link_irq, port);
>
> mvpp2_irqs_deinit(port);
> @@ -7029,20 +6893,26 @@ 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);
> }
>
> /* 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)
> @@ -7170,6 +7040,50 @@ static int mvpp2_ethtool_set_ringparam(struct net_device *dev,
> 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 = {
> @@ -7184,15 +7098,17 @@ 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_drvinfo = mvpp2_ethtool_get_drvinfo,
> .get_ringparam = mvpp2_ethtool_get_ringparam,
> .set_ringparam = mvpp2_ethtool_set_ringparam,
> - .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
> @@ -7492,17 +7408,185 @@ 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_port_modes(mask);
> +
> + phylink_set(mask, Autoneg);
> + phylink_set(mask, Pause);
> + phylink_set(mask, Asym_Pause);
> +
> + phylink_set(mask, 10baseT_Half);
> + phylink_set(mask, 10baseT_Full);
> + phylink_set(mask, 100baseT_Half);
> + phylink_set(mask, 100baseT_Full);
> + phylink_set(mask, 1000baseT_Half);
> + phylink_set(mask, 1000baseT_Full);
> + phylink_set(mask, 1000baseX_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 int mvpp2_phylink_mac_link_state(struct net_device *dev,
> + struct phylink_link_state *state)
> +{
> + struct mvpp2_port *port = netdev_priv(dev);
> + u32 val;
> +
> + if (!phy_interface_mode_is_rgmii(port->phy_interface) &&
> + port->phy_interface != PHY_INTERFACE_MODE_SGMII)
> + return 0;
> +
> + 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
> + state->speed = (val & MVPP2_GMAC_STATUS0_MII_SPEED) ?
> + SPEED_100 : 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;
> +
> + return 1;
> +}
> +
> +static void mvpp2_mac_an_restart(struct net_device *dev)
> +{
> + struct mvpp2_port *port = netdev_priv(dev);
> + u32 val;
> +
> + if (!phy_interface_mode_is_rgmii(port->phy_interface) &&
> + port->phy_interface != PHY_INTERFACE_MODE_SGMII)
> + return;
> +
> + val = readl(port->base + MVPP2_GMAC_AUTONEG_CONFIG);
> + val |= MVPP2_GMAC_IN_BAND_RESTART_AN;
> + writel(val, 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);
> + u32 val;
> +
> + /* 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 = state->interface;
> + mvpp22_comphy_init(port);
> +
> + /* gop/mac reconfiguration */
> + mvpp22_gop_init(port);
> + mvpp2_port_mii_set(port);
> +
> + if (!phy_interface_mode_is_rgmii(port->phy_interface) &&
> + 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 (state->duplex)
> + val |= MVPP2_GMAC_CONFIG_FULL_DUPLEX;
> +
> + if (state->speed == SPEED_1000)
> + val |= MVPP2_GMAC_CONFIG_GMII_SPEED;
> + else if (state->speed == SPEED_100)
> + val |= MVPP2_GMAC_CONFIG_MII_SPEED;
> +
> + writel(val, port->base + MVPP2_GMAC_AUTONEG_CONFIG);
> +
> + if (port->priv->hw_version == MVPP21 && port->flags & MVPP2_F_LOOPBACK)
> + mvpp2_port_loopback_set(port, state);
> +}
> +
> +static void mvpp2_mac_link_down(struct net_device *dev, unsigned int mode)
> +{
> + struct mvpp2_port *port = netdev_priv(dev);
> + u32 val;
> +
> + 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);
> +
> + if (!phylink_autoneg_inband(mode)) {
> + 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);
> + }
> +}
> +
> +static void mvpp2_mac_link_up(struct net_device *dev, unsigned int mode,
> + struct phy_device *phy)
> +{
> + struct mvpp2_port *port = netdev_priv(dev);
> + u32 val;
> +
> + if (!phylink_autoneg_inband(mode)) {
> + val = readl(port->base + MVPP2_GMAC_AUTONEG_CONFIG);
> + val &= ~MVPP2_GMAC_FORCE_LINK_DOWN;
> + val |= MVPP2_GMAC_FORCE_LINK_PASS;
> + 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);
> +}
> +
> +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_down = mvpp2_mac_link_down,
> + .mac_link_up = mvpp2_mac_link_up,
> +};
> +
> /* Ports initialization */
> static int mvpp2_port_probe(struct platform_device *pdev,
> struct device_node *port_node,
> struct mvpp2 *priv)
> {
> - struct device_node *phy_node;
> struct phy *comphy;
> struct mvpp2_port *port;
> struct mvpp2_port_pcpu *port_pcpu;
> struct net_device *dev;
> struct resource *res;
> + struct phylink *phylink;
> char *mac_from = "";
> unsigned int ntxqs, nrxqs;
> bool has_tx_irqs;
> @@ -7526,7 +7610,6 @@ static int mvpp2_port_probe(struct platform_device *pdev,
> if (!dev)
> return -ENOMEM;
>
> - phy_node = of_parse_phandle(port_node, "phy", 0);
> phy_mode = of_get_phy_mode(port_node);
> if (phy_mode < 0) {
> dev_err(&pdev->dev, "incorrect phy mode\n");
> @@ -7565,15 +7648,6 @@ static int mvpp2_port_probe(struct platform_device *pdev,
> if (err)
> goto err_free_netdev;
>
> - port->link_irq = of_irq_get_byname(port_node, "link");
> - if (port->link_irq == -EPROBE_DEFER) {
> - err = -EPROBE_DEFER;
> - goto err_deinit_qvecs;
> - }
> - if (port->link_irq <= 0)
> - /* the link irq is optional */
> - port->link_irq = 0;
> -
> if (of_property_read_bool(port_node, "marvell,loopback"))
> port->flags |= MVPP2_F_LOOPBACK;
>
> @@ -7583,7 +7657,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;
>
> @@ -7592,7 +7666,7 @@ static int mvpp2_port_probe(struct platform_device *pdev,
> port->base = devm_ioremap_resource(&pdev->dev, res);
> if (IS_ERR(port->base)) {
> err = PTR_ERR(port->base);
> - goto err_free_irq;
> + goto err_deinit_qvecs;
> }
> } else {
> if (of_property_read_u32(port_node, "gop-port-id",
> @@ -7609,7 +7683,7 @@ static int mvpp2_port_probe(struct platform_device *pdev,
> port->stats = netdev_alloc_pcpu_stats(struct mvpp2_pcpu_stats);
> if (!port->stats) {
> err = -ENOMEM;
> - goto err_free_irq;
> + goto err_deinit_qvecs;
> }
>
> mvpp2_port_copy_mac_addr(dev, priv, port_node, &mac_from);
> @@ -7662,16 +7736,47 @@ static int mvpp2_port_probe(struct platform_device *pdev,
> /* 9676 == 9700 - 20 and rounding to 8 */
> dev->max_mtu = 9676;
>
> + /* The PHY node is optional. If not present the GoP link IRQ will be
> + * used to handle link updates. Otherwise use phylink.
> + */
> + if (of_find_property(port_node, "phy", NULL)) {
> + phylink = phylink_create(dev, port_node, phy_mode,
> + &mvpp2_phylink_ops);
> + if (IS_ERR(phylink)) {
> + err = PTR_ERR(phylink);
> + goto err_free_port_pcpu;
> + }
> + port->phylink = phylink;
> + port->link_irq = 0;
> + } else {
> + port->phylink = NULL;
> + if (priv->hw_version == MVPP22) {
> + port->link_irq = of_irq_get_byname(port_node, "link");
> + if (port->link_irq == -EPROBE_DEFER) {
> + err = -EPROBE_DEFER;
> + goto err_free_port_pcpu;
> + }
> + if (port->link_irq <= 0)
> + /* the link irq is optional */
> + port->link_irq = 0;
> + }
> + }
> +
> err = register_netdev(dev);
> if (err < 0) {
> dev_err(&pdev->dev, "failed to register netdev\n");
> - goto err_free_port_pcpu;
> + goto err_phylink_irq;
> }
> netdev_info(dev, "Using %s mac address %pM\n", mac_from, dev->dev_addr);
>
> priv->port_list[id] = port;
> return 0;
>
> +err_phylink_irq:
> + if (port->phylink)
> + phylink_destroy(port->phylink);
> + else if (port->link_irq)
> + irq_dispose_mapping(port->link_irq);
> err_free_port_pcpu:
> free_percpu(port->pcpu);
> err_free_txq_pcpu:
> @@ -7679,13 +7784,9 @@ static int mvpp2_port_probe(struct platform_device *pdev,
> free_percpu(port->txqs[i]->pcpu);
> err_free_stats:
> free_percpu(port->stats);
> -err_free_irq:
> - if (port->link_irq)
> - irq_dispose_mapping(port->link_irq);
> err_deinit_qvecs:
> mvpp2_queue_vectors_deinit(port);
> err_free_netdev:
> - of_node_put(phy_node);
> free_netdev(dev);
> return err;
> }
> @@ -7696,7 +7797,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++)
> --
> 2.13.5
>