summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRussell King <rmk+kernel@armlinux.org.uk>2016-12-06 16:22:21 +0000
committerRussell King <rmk+kernel@armlinux.org.uk>2019-07-09 12:20:56 +0100
commit47e28ad1439e1e77525b121ba1eafdfe6f76c4a8 (patch)
tree604acaf3da137e23d8f2e3f3e6e3339070c6e338
parente266e59e5e3ca08d61c60da42f50a8a04cfff99a (diff)
net: marvell: mvpp2x: start converting to phylink
Start converting mvpp2x to phylink - this is work in progress! Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
-rw-r--r--drivers/net/ethernet/marvell/Kconfig1
-rw-r--r--drivers/net/ethernet/marvell/mvpp2x/mv_pp2x.h3
-rw-r--r--drivers/net/ethernet/marvell/mvpp2x/mv_pp2x_ethtool.c73
-rw-r--r--drivers/net/ethernet/marvell/mvpp2x/mv_pp2x_main.c462
4 files changed, 515 insertions, 24 deletions
diff --git a/drivers/net/ethernet/marvell/Kconfig b/drivers/net/ethernet/marvell/Kconfig
index 330bb38be27d..85b528898298 100644
--- a/drivers/net/ethernet/marvell/Kconfig
+++ b/drivers/net/ethernet/marvell/Kconfig
@@ -95,6 +95,7 @@ config MVPP2X
depends on ARCH_MVEBU
select MVMDIO
select MVXMDIO
+ select PHYLINK
---help---
This driver supports the network interface units in the
Marvell ARMADA 375/70xx/80xx SoC series
diff --git a/drivers/net/ethernet/marvell/mvpp2x/mv_pp2x.h b/drivers/net/ethernet/marvell/mvpp2x/mv_pp2x.h
index fba29f1849f4..c2d0382894a9 100644
--- a/drivers/net/ethernet/marvell/mvpp2x/mv_pp2x.h
+++ b/drivers/net/ethernet/marvell/mvpp2x/mv_pp2x.h
@@ -28,6 +28,8 @@
#include "mv_pp2x_hw_type.h"
#include "mv_gop110_hw_type.h"
+struct phylink;
+
#define MVPP2_DRIVER_NAME "mvpp2x"
#define MVPP2_DRIVER_VERSION "1.0"
@@ -251,6 +253,7 @@ struct mv_mac_data {
int phy_addr;
phy_interface_t phy_mode; /* RXAUI, SGMII, etc. */
struct phy_device *phy_dev;
+ struct phylink *phylink;
struct device_node *phy_node;
int link_irq;
char irq_name[IRQ_NAME_SIZE];
diff --git a/drivers/net/ethernet/marvell/mvpp2x/mv_pp2x_ethtool.c b/drivers/net/ethernet/marvell/mvpp2x/mv_pp2x_ethtool.c
index d5cc5f5e6bee..c4a8316e4d63 100644
--- a/drivers/net/ethernet/marvell/mvpp2x/mv_pp2x_ethtool.c
+++ b/drivers/net/ethernet/marvell/mvpp2x/mv_pp2x_ethtool.c
@@ -32,7 +32,7 @@
#include <linux/of_mdio.h>
#include <linux/of_net.h>
#include <linux/of_address.h>
-#include <linux/phy.h>
+#include <linux/phylink.h>
#include <linux/clk.h>
#include <uapi/linux/ppp_defs.h>
#include <net/ip.h>
@@ -1239,7 +1239,76 @@ static const struct ethtool_ops mv_pp2x_eth_tool_ops = {
.set_link_ksettings = mv_pp2x_ethtool_set_link_ksettings,
};
+static int mv_pp2x_ethtool_pl_nway_reset(struct net_device *dev)
+{
+ struct mv_pp2x_port *port = netdev_priv(dev);
+
+ return phylink_ethtool_nway_reset(port->mac_data.phylink);
+}
+
+static void mv_pp2x_ethtool_pl_get_pauseparam(struct net_device *dev,
+ struct ethtool_pauseparam *pause)
+{
+ struct mv_pp2x_port *port = netdev_priv(dev);
+
+ phylink_ethtool_get_pauseparam(port->mac_data.phylink, pause);
+}
+
+static int mv_pp2x_ethtool_pl_set_pauseparam(struct net_device *dev,
+ struct ethtool_pauseparam *pause)
+{
+ struct mv_pp2x_port *port = netdev_priv(dev);
+
+ return phylink_ethtool_set_pauseparam(port->mac_data.phylink, pause);
+}
+
+static int mv_pp2x_ethtool_pl_get_link_ksettings(struct net_device *dev,
+ struct ethtool_link_ksettings *kset)
+{
+ struct mv_pp2x_port *port = netdev_priv(dev);
+
+ return phylink_ethtool_ksettings_get(port->mac_data.phylink, kset);
+}
+
+static int mv_pp2x_ethtool_pl_set_link_ksettings(struct net_device *dev,
+ const struct ethtool_link_ksettings *kset)
+{
+ struct mv_pp2x_port *port = netdev_priv(dev);
+
+ return phylink_ethtool_ksettings_set(port->mac_data.phylink, kset);
+}
+
+static const struct ethtool_ops mv_pp2x_eth_tool_phylink_ops = {
+ .get_link = ethtool_op_get_link,
+ .set_coalesce = mv_pp2x_ethtool_set_coalesce,
+ .get_coalesce = mv_pp2x_ethtool_get_coalesce,
+ .nway_reset = mv_pp2x_ethtool_pl_nway_reset,
+ .get_drvinfo = mv_pp2x_ethtool_get_drvinfo,
+ .get_ethtool_stats = mv_pp2x_eth_tool_get_ethtool_stats,
+ .get_sset_count = mv_pp2x_eth_tool_get_sset_count,
+ .get_strings = mv_pp2x_eth_tool_get_strings,
+ .get_ringparam = mv_pp2x_ethtool_get_ringparam,
+ .set_ringparam = mv_pp2x_ethtool_set_ringparam,
+ .get_pauseparam = mv_pp2x_ethtool_pl_get_pauseparam,
+ .set_pauseparam = mv_pp2x_ethtool_pl_set_pauseparam,
+ .get_rxfh_indir_size = mv_pp2x_ethtool_get_rxfh_indir_size,
+ .get_rxnfc = mv_pp2x_ethtool_get_rxnfc,
+ .set_rxnfc = mv_pp2x_ethtool_set_rxnfc,
+ .get_rxfh = mv_pp2x_ethtool_get_rxfh,
+ .set_rxfh = mv_pp2x_ethtool_set_rxfh,
+ .get_regs_len = mv_pp2x_ethtool_get_regs_len,
+ .get_regs = mv_pp2x_ethtool_get_regs,
+ .self_test = mv_pp2x_eth_tool_diag_test,
+ .get_link_ksettings = mv_pp2x_ethtool_pl_get_link_ksettings,
+ .set_link_ksettings = mv_pp2x_ethtool_pl_set_link_ksettings,
+};
+
void mv_pp2x_set_ethtool_ops(struct net_device *netdev)
{
- netdev->ethtool_ops = &mv_pp2x_eth_tool_ops;
+ struct mv_pp2x_port *port = netdev_priv(netdev);
+
+ if (port->mac_data.phylink)
+ netdev->ethtool_ops = &mv_pp2x_eth_tool_phylink_ops;
+ else
+ netdev->ethtool_ops = &mv_pp2x_eth_tool_ops;
}
diff --git a/drivers/net/ethernet/marvell/mvpp2x/mv_pp2x_main.c b/drivers/net/ethernet/marvell/mvpp2x/mv_pp2x_main.c
index c343f17d5e12..51cc42432fc7 100644
--- a/drivers/net/ethernet/marvell/mvpp2x/mv_pp2x_main.c
+++ b/drivers/net/ethernet/marvell/mvpp2x/mv_pp2x_main.c
@@ -38,7 +38,7 @@
#include <linux/of_device.h>
#include <linux/phy/phy.h>
-#include <linux/phy.h>
+#include <linux/phylink.h>
#include <linux/clk.h>
#include <linux/hrtimer.h>
#include <linux/ktime.h>
@@ -1523,6 +1523,21 @@ static irqreturn_t mv_pp2_link_change_isr(int irq, void *data)
pr_debug("%s cpu_id(%d) irq(%d) pp_port(%d)\n", __func__,
smp_processor_id(), irq, port->id);
+
+ if (port->mac_data.phylink) {
+ bool is_up;
+
+// mv_gop110_port_events_mask(&port->priv->hw.gop,
+// &port->mac_data);
+ mv_gop110_port_events_clear(&port->priv->hw.gop,
+ &port->mac_data);
+
+ is_up = mv_gop110_port_is_link_up(&port->priv->hw.gop,
+ &port->mac_data);
+ phylink_mac_change(port->mac_data.phylink, is_up);
+ return IRQ_HANDLED;
+ }
+
if (port->priv->pp2_version == PPV22) {
/* mask all events from this mac */
mv_gop110_port_events_mask(&port->priv->hw.gop, &port->mac_data);
@@ -1731,6 +1746,347 @@ static void mv_pp22_link_event(struct net_device *dev)
}
}
+
+static void mv_pp21_validate(struct net_device *dev, unsigned long *support,
+ struct phylink_link_state *state)
+{
+ __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
+
+ /* Allow all the expected bits */
+ phylink_set(mask, Autoneg);
+ phylink_set(mask, TP);
+ phylink_set(mask, AUI);
+ phylink_set(mask, MII);
+ phylink_set(mask, FIBRE);
+ phylink_set(mask, BNC);
+ phylink_set(mask, Backplane);
+
+ phylink_set(mask, Pause);
+ phylink_set(mask, Asym_Pause);
+
+ /* Half-duplex at speeds higher than 100Mbit are unsupported */
+ phylink_set(mask, 1000baseT_Full);
+ phylink_set(mask, 1000baseX_Full);
+
+ if (!phy_interface_mode_is_8023z(state->interface)) {
+ /* 10M and 100M are only supported in non-802.3z mode */
+ phylink_set(mask, 10baseT_Half);
+ phylink_set(mask, 10baseT_Full);
+ phylink_set(mask, 100baseT_Half);
+ phylink_set(mask, 100baseT_Full);
+ }
+
+ bitmap_and(support, support, mask, __ETHTOOL_LINK_MODE_MASK_NBITS);
+}
+
+static int mv_pp2_mac_link_state(struct net_device *dev,
+ struct phylink_link_state *state)
+{
+// struct mv_pp2x_port *port = netdev_priv(dev);
+ return 0;
+}
+
+static void mv_pp2_mac_an_restart(struct net_device *dev)
+{
+}
+
+static void mv_pp21_mac_config(struct net_device *dev, unsigned int mode,
+ const struct phylink_link_state *state)
+{
+ struct mv_pp2x_port *port = netdev_priv(dev);
+ u32 new_an, an = readl(port->base + MVPP2_GMAC_AUTONEG_CONFIG);
+
+ new_an = an & ~(MVPP2_GMAC_CONFIG_MII_SPEED |
+ MVPP2_GMAC_CONFIG_GMII_SPEED |
+ MVPP2_GMAC_AN_SPEED_EN |
+ MVPP2_GMAC_CONFIG_FULL_DUPLEX |
+ MVPP2_GMAC_AN_DUPLEX_EN);
+
+ if (state->duplex)
+ new_an |= MVPP2_GMAC_CONFIG_FULL_DUPLEX;
+
+ if (state->speed == SPEED_1000)
+ new_an |= MVPP2_GMAC_CONFIG_GMII_SPEED;
+ else if (state->speed == SPEED_100)
+ new_an |= MVPP2_GMAC_CONFIG_MII_SPEED;
+
+ if (an != new_an)
+ writel(new_an, port->base + MVPP2_GMAC_AUTONEG_CONFIG);
+
+ port->mac_data.speed = state->speed;
+ port->mac_data.duplex = state->duplex;
+}
+
+static void mv_pp21_mac_link_down(struct net_device *dev, unsigned int mode)
+{
+ struct mv_pp2x_port *port = netdev_priv(dev);
+
+ port->mac_data.link = 0;
+ mv_pp2x_ingress_disable(port);
+ mv_pp2x_egress_disable(port);
+}
+
+static void mv_pp21_mac_link_up(struct net_device *dev, unsigned int mode,
+ struct phy_device *phy)
+{
+ struct mv_pp2x_port *port = netdev_priv(dev);
+ u32 val;
+
+ port->mac_data.link = 1;
+ 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);
+
+ mv_pp2x_egress_enable(port);
+ mv_pp2x_ingress_enable(port);
+}
+
+static struct phylink_mac_ops mv_pp21_phylink_ops = {
+ .validate = mv_pp21_validate,
+ .mac_link_state = mv_pp2_mac_link_state,
+ .mac_an_restart = mv_pp2_mac_an_restart,
+ .mac_config = mv_pp21_mac_config,
+ .mac_link_down = mv_pp21_mac_link_down,
+ .mac_link_up = mv_pp21_mac_link_up,
+};
+
+static void mv_pp22_validate(struct net_device *dev, unsigned long *supported,
+ struct phylink_link_state *state)
+{
+ struct mv_pp2x_port *port = netdev_priv(dev);
+ __ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = { 0, };
+
+ /* Allow all the expected bits */
+ phylink_set(mask, Autoneg);
+ phylink_set_port_modes(mask);
+ phylink_set(mask, Pause);
+ phylink_set(mask, Asym_Pause);
+
+ switch (state->interface) {
+ case PHY_INTERFACE_MODE_NA:
+ case PHY_INTERFACE_MODE_XAUI:
+ case PHY_INTERFACE_MODE_RXAUI:
+ case PHY_INTERFACE_MODE_10GKR:
+ phylink_set(mask, 10000baseT_Full);
+ 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);
+
+ default:
+ /* 10M, 100M and 10G are only supported in non-802.3z mode */
+ phylink_set(mask, 10baseT_Half);
+ phylink_set(mask, 10baseT_Full);
+ phylink_set(mask, 100baseT_Half);
+ phylink_set(mask, 100baseT_Full);
+ phylink_set(mask, 10000baseT_Full);
+ /* fallthrough */
+ case PHY_INTERFACE_MODE_1000BASEX:
+ case PHY_INTERFACE_MODE_2500BASEX:
+ /* Half-duplex at speeds higher than 100Mbit are unsupported */
+ phylink_set(mask, 1000baseT_Full);
+ phylink_set(mask, 1000baseX_Full);
+
+ if (port->comphy)
+ phylink_set(mask, 2500baseX_Full);
+ break;
+ }
+
+ bitmap_and(supported, supported, mask,
+ __ETHTOOL_LINK_MODE_MASK_NBITS);
+ bitmap_and(state->advertising, state->advertising, mask,
+ __ETHTOOL_LINK_MODE_MASK_NBITS);
+}
+
+static int mv_pp22_mac_link_state(struct net_device *dev,
+ struct phylink_link_state *state)
+{
+ struct mv_pp2x_port *port = netdev_priv(dev);
+ struct gop_hw *gop = &port->priv->hw.gop;
+ struct mv_port_link_status pstatus;
+
+ pstatus.speed = MV_PORT_SPEED_1000;
+ pstatus.duplex = MV_PORT_DUPLEX_FULL;
+
+ mv_gop110_port_link_status(gop, &port->mac_data, &pstatus);
+
+ switch (pstatus.speed) {
+ case MV_PORT_SPEED_10:
+ state->speed = SPEED_10;
+ break;
+ case MV_PORT_SPEED_100:
+ state->speed = SPEED_100;
+ break;
+ case MV_PORT_SPEED_1000:
+ state->speed = SPEED_1000;
+ break;
+ case MV_PORT_SPEED_2500:
+ state->speed = SPEED_2500;
+ break;
+ case MV_PORT_SPEED_10000:
+ state->speed = SPEED_10000;
+ break;
+ default:
+ state->speed = SPEED_UNKNOWN;
+ break;
+ }
+ switch (pstatus.duplex) {
+ case MV_PORT_DUPLEX_HALF:
+ state->duplex = DUPLEX_HALF;
+ break;
+ case MV_PORT_DUPLEX_FULL:
+ state->duplex = DUPLEX_FULL;
+ break;
+ default:
+ state->duplex = DUPLEX_UNKNOWN;
+ break;
+ }
+
+ state->an_complete = 1;
+ state->link = pstatus.linkup;
+
+ state->pause = 0;
+ if (pstatus.rx_fc != MV_PORT_FC_DISABLE)
+ state->pause |= MLO_PAUSE_RX;
+ if (pstatus.tx_fc != MV_PORT_FC_DISABLE)
+ state->pause |= MLO_PAUSE_TX;
+
+ return 1;
+}
+
+static void mv_pp22_mac_an_restart(struct net_device *dev)
+{
+ struct mv_pp2x_port *port = netdev_priv(dev);
+
+ mv_gop110_autoneg_restart(&port->priv->hw.gop, &port->mac_data);
+}
+
+static void mv_pp22_mac_config_gop(struct net_device *dev, unsigned int mode,
+ const struct phylink_link_state *state)
+{
+ struct mv_pp2x_port *port = netdev_priv(dev);
+ struct gop_hw *gop = &port->priv->hw.gop;
+ void __iomem *base = gop->gop_110.gmac.base +
+ port->mac_data.gop_index * gop->gop_110.gmac.obj_size;
+ u32 old_ctl0, ctl0;
+ u32 old_ctl2, ctl2;
+ u32 old_ctl4, ctl4;
+ u32 old_an, an;
+
+ port->mac_data.speed = state->speed;
+ port->mac_data.duplex = state->duplex;
+
+ old_ctl0 = readl(base + 0x00);
+ old_ctl2 = readl(base + 0x08);
+ old_ctl4 = readl(base + 0x90);
+ old_an = readl(base + 0x0c);
+
+ ctl0 = old_ctl0;
+ ctl2 = old_ctl2;
+ ctl4 = old_ctl4 & ~(BIT(3) | BIT(4));
+ an = old_an & ~(BIT(5) | BIT(6) | BIT(7) | BIT(9) | BIT(10) | BIT(11));
+
+ if (phylink_test(state->advertising, Pause))
+ an |= BIT(9); /* ADV_PAUSE */
+ if (phylink_test(state->advertising, Asym_Pause))
+ an |= BIT(10); /* ADV_ASM_PAUSE */
+
+ if (!phylink_autoneg_inband(mode)) {
+ an = old_an;
+ } else if (state->interface == PHY_INTERFACE_MODE_SGMII) {
+ /* SGMII mode receives the state from the PHY */
+ ctl0 = old_ctl0 & ~BIT(1);
+ ctl2 = old_ctl2 | BIT(0); /* CTL2_SGMII_MODE */
+ an |= BIT(7) | /* EN_AN_SPEED */
+ BIT(13); /* EN_AN_FDX */
+ /* SGMII FC is out of band */
+ if (state->pause & MLO_PAUSE_TX)
+ ctl4 |= BIT(4);
+ if (state->pause & MLO_PAUSE_RX)
+ ctl4 |= BIT(3);
+ } else {
+ /* 802.3z negotiation - only 1000base-X */
+ ctl0 = old_ctl0 | BIT(1);
+ ctl2 = old_ctl2 & ~BIT(0); /* CTRL2_SGMII_MODE */
+ an |= BIT(6); /* SET_GMII_SPEED */
+ if (state->pause & MLO_PAUSE_AN && state->an_enabled)
+ an |= BIT(11); /* EN_FC_AN */
+ }
+
+ if ((old_ctl0 ^ ctl0) & BIT(1) ||
+ (old_ctl2 ^ ctl2) & BIT(0)) {
+ mv_gop110_port_disable(gop, &port->mac_data);
+ writel(ctl0, base + 0x00);
+ writel(ctl2, base + 0x08);
+ writel(ctl4, base + 0x90);
+ writel(an | 1, base + 0x0c);
+ mv_gop110_port_enable(gop, &port->mac_data);
+ } else {
+ if (old_ctl0 != ctl0)
+ writel(ctl0, base + 0x00);
+ if (old_ctl2 != ctl2)
+ writel(ctl2, base + 0x08);
+ if (old_ctl4 != ctl4)
+ writel(ctl4, base + 0x90);
+ if (old_an != an)
+ writel(an, base + 0x0c);
+ }
+}
+
+static void mv_pp22_mac_config(struct net_device *dev, unsigned int mode,
+ const struct phylink_link_state *state)
+{
+ struct mv_pp2x_port *port = netdev_priv(dev);
+
+ switch (port->mac_data.phy_mode) {
+ case PHY_INTERFACE_MODE_RGMII:
+ case PHY_INTERFACE_MODE_SGMII:
+ case PHY_INTERFACE_MODE_QSGMII:
+ mv_pp22_mac_config_gop(dev, mode, state);
+ break;
+
+ default:
+ break;
+ }
+}
+
+static void mv_pp22_mac_link_down(struct net_device *dev, unsigned int mode,
+ phy_interface_t interface)
+{
+ struct mv_pp2x_port *port = netdev_priv(dev);
+
+ port->mac_data.link = 0;
+ mv_pp2x_ingress_disable(port);
+ mv_pp2x_egress_disable(port);
+ port->mac_data.flags &= ~MV_EMAC_F_LINK_UP;
+}
+
+static void mv_pp22_mac_link_up(struct net_device *dev, unsigned int mode,
+ phy_interface_t interface,
+ struct phy_device *phy)
+{
+ struct mv_pp2x_port *port = netdev_priv(dev);
+
+ port->mac_data.link = 1;
+ mv_gop110_port_events_mask(&port->priv->hw.gop, &port->mac_data);
+ mv_pp2x_egress_enable(port);
+ mv_pp2x_ingress_enable(port);
+ mv_gop110_port_events_unmask(&port->priv->hw.gop, &port->mac_data);
+ port->mac_data.flags |= MV_EMAC_F_LINK_UP;
+}
+
+static struct phylink_mac_ops mv_pp22_phylink_ops = {
+ .validate = mv_pp22_validate,
+ .mac_link_state = mv_pp22_mac_link_state,
+ .mac_an_restart = mv_pp22_mac_an_restart,
+ .mac_config = mv_pp22_mac_config,
+ .mac_link_down = mv_pp22_mac_link_down,
+ .mac_link_up = mv_pp22_mac_link_up,
+};
+
+
void mv_pp2_link_change_tasklet(unsigned long data)
{
struct net_device *dev = (struct net_device *)data;
@@ -3253,7 +3609,9 @@ void mv_pp2x_start_dev(struct mv_pp2x_port *port)
mv_gop110_port_enable(gop, mac);
}
- if (port->mac_data.phy_dev) {
+ if (port->mac_data.phylink) {
+ phylink_start(port->mac_data.phylink);
+ } else if (port->mac_data.phy_dev) {
phy_start(port->mac_data.phy_dev);
} else {
mv_pp22_dev_link_event(port->dev);
@@ -3261,11 +3619,14 @@ void mv_pp2x_start_dev(struct mv_pp2x_port *port)
(unsigned long)(port->dev));
}
- if (port->mac_data.phy_dev)
+ if (port->mac_data.phy_dev || port->mac_data.phylink)
netif_tx_start_all_queues(port->dev);
- mv_pp2x_egress_enable(port);
- mv_pp2x_ingress_enable(port);
+ if (!port->mac_data.phylink) {
+ mv_pp2x_egress_enable(port);
+ mv_pp2x_ingress_enable(port);
+ }
+
/* Unmask link_event */
if (port->priv->pp2_version == PPV22) {
mv_gop110_port_events_unmask(gop, mac);
@@ -3279,20 +3640,29 @@ void mv_pp2x_stop_dev(struct mv_pp2x_port *port)
struct gop_hw *gop = &port->priv->hw.gop;
struct mv_mac_data *mac = &port->mac_data;
- /* Stop new packets from arriving to RXQs */
- mv_pp2x_ingress_disable(port);
+ if (port->mac_data.phylink) {
+ phylink_stop(port->mac_data.phylink);
- mdelay(10);
+ /* Disable interrupts on all CPUs */
+ mv_pp2x_port_interrupts_disable(port);
+ mv_pp2x_port_napi_disable(port);
+ netif_tx_stop_all_queues(port->dev);
+ } else {
+ /* Stop new packets from arriving to RXQs */
+ mv_pp2x_ingress_disable(port);
- /* Disable interrupts on all CPUs */
- mv_pp2x_port_interrupts_disable(port);
+ mdelay(10);
- mv_pp2x_port_napi_disable(port);
+ /* Disable interrupts on all CPUs */
+ mv_pp2x_port_interrupts_disable(port);
- netif_carrier_off(port->dev);
- netif_tx_stop_all_queues(port->dev);
+ mv_pp2x_port_napi_disable(port);
- mv_pp2x_egress_disable(port);
+ netif_carrier_off(port->dev);
+ netif_tx_stop_all_queues(port->dev);
+
+ mv_pp2x_egress_disable(port);
+ }
if (port->comphy)
phy_power_off(port->comphy);
@@ -3306,10 +3676,12 @@ void mv_pp2x_stop_dev(struct mv_pp2x_port *port)
port->mac_data.flags &= ~MV_EMAC_F_PORT_UP;
}
- if (port->mac_data.phy_dev)
- phy_stop(port->mac_data.phy_dev);
- else
- tasklet_kill(&port->link_change_tasklet);
+ if (!port->mac_data.phylink) {
+ if (port->mac_data.phy_dev)
+ phy_stop(port->mac_data.phy_dev);
+ else
+ tasklet_kill(&port->link_change_tasklet);
+ }
}
/* Return positive if MTU is valid */
@@ -3421,7 +3793,9 @@ static int mv_pp2x_phy_connect(struct mv_pp2x_port *port)
static void mv_pp2x_phy_disconnect(struct mv_pp2x_port *port)
{
- if (port->mac_data.phy_dev) {
+ if (port->mac_data.phylink) {
+ phylink_disconnect_phy(port->mac_data.phylink);
+ } else if (port->mac_data.phy_dev) {
phy_disconnect(port->mac_data.phy_dev);
port->mac_data.phy_dev = NULL;
}
@@ -3849,6 +4223,9 @@ static int mv_pp2x_ioctl(struct net_device *dev, struct ifreq *ifr, int cmd)
struct mv_pp2x_port *port = netdev_priv(dev);
int ret = 0;
+ if (port->mac_data.phylink)
+ return phylink_mii_ioctl(port->mac_data.phylink, ifr, cmd);
+
if (!port->mac_data.phy_dev)
return -ENOTSUPP;
ret = phy_mii_ioctl(port->mac_data.phy_dev, ifr, cmd);
@@ -3903,13 +4280,14 @@ static int mv_pp2x_netdev_set_features(struct net_device *dev,
}
u16 mv_pp2x_select_queue(struct net_device *dev, struct sk_buff *skb,
- void *accel_priv, select_queue_fallback_t fallback)
+ struct net_device *sb_dev,
+ select_queue_fallback_t fallback)
{
if (skb->queue_mapping)
return skb->napi_id - 1;
- return fallback(dev, skb) % mv_pp2x_txq_number;
+ return fallback(dev, skb, NULL) % mv_pp2x_txq_number;
}
/* Device ops */
@@ -4443,7 +4821,43 @@ static int mv_pp2x_port_probe(struct platform_device *pdev,
port->comphy = comphy;
}
- if (port->mac_data.phy_node) {
+ if (port->mac_data.phy_mode == PHY_INTERFACE_MODE_SGMII ||
+ port->mac_data.phy_mode == PHY_INTERFACE_MODE_10GKR) {
+ struct phylink *pl;
+ const struct phylink_mac_ops *ops;
+
+ if (port->priv->pp2_version == PPV21)
+ ops = &mv_pp21_phylink_ops;
+ else
+ ops = &mv_pp22_phylink_ops;
+
+ pl = phylink_create(dev, of_fwnode_handle(emac_node),
+ port->mac_data.phy_mode, ops);
+ if (IS_ERR(pl)) {
+ dev_err(port->dev->dev.parent,
+ "port %d: could not create phylink: %d\n",
+ port->id, err);
+ err = PTR_ERR(pl);
+ goto err_free_netdev;
+ }
+
+ port->mac_data.phylink = pl;
+
+ if (port->mac_data.phy_node) {
+ int err = phylink_of_phy_connect(port->mac_data.phylink,
+ emac_node, 0);
+ if (err) {
+ dev_err(port->dev->dev.parent,
+ "port %d: could not attach PHY: %d\n",
+ port->id, err);
+ goto err_free_netdev;
+ }
+
+ port->mac_data.link = 0;
+ port->mac_data.duplex = 0;
+ port->mac_data.speed = 0;
+ }
+ } else if (port->mac_data.phy_node) {
err = mv_pp2x_phy_connect(port);
if (err < 0)
goto err_free_netdev;
@@ -4643,6 +5057,8 @@ err_free_stats:
err_free_irq:
mv_pp2x_port_irqs_dispose_mapping(port);
err_free_netdev:
+ if (port->mac_data.phylink)
+ phylink_destroy(port->mac_data.phylink);
free_netdev(dev);
dev_err(&pdev->dev, "%s failed for port_id(%d)\n", __func__, id);
@@ -4667,6 +5083,8 @@ static void mv_pp2x_port_remove(struct mv_pp2x_port *port)
for (i = 0; i < port->num_tx_queues; i++)
free_percpu(port->txqs[i]->pcpu);
mv_pp2x_port_irqs_dispose_mapping(port);
+ if (port->mac_data.phylink)
+ phylink_destroy(port->mac_data.phylink);
free_netdev(port->dev);
}