summaryrefslogtreecommitdiffstats
path: root/target/linux/mvebu/patches-4.4/031-mvneta-implement-ethtool-autonegotiation-control.patch
diff options
context:
space:
mode:
Diffstat (limited to 'target/linux/mvebu/patches-4.4/031-mvneta-implement-ethtool-autonegotiation-control.patch')
-rw-r--r--target/linux/mvebu/patches-4.4/031-mvneta-implement-ethtool-autonegotiation-control.patch165
1 files changed, 165 insertions, 0 deletions
diff --git a/target/linux/mvebu/patches-4.4/031-mvneta-implement-ethtool-autonegotiation-control.patch b/target/linux/mvebu/patches-4.4/031-mvneta-implement-ethtool-autonegotiation-control.patch
new file mode 100644
index 0000000000..626b511c45
--- /dev/null
+++ b/target/linux/mvebu/patches-4.4/031-mvneta-implement-ethtool-autonegotiation-control.patch
@@ -0,0 +1,165 @@
+From: Stas Sergeev <stsp@list.ru>
+Date: Wed, 2 Dec 2015 20:35:11 +0300
+Subject: [PATCH] mvneta: implement ethtool autonegotiation control
+
+This patch allows to do
+ethtool -s eth0 autoneg off
+ethtool -s eth0 autoneg on
+to disable or enable autonegotiation at run-time.
+Without that functionality, the only way to control the autonegotiation
+is to modify the device tree.
+
+This is needed if you plan to use the same kernel with
+different ethernet switches, the ones that support the in-band
+status and the ones that not.
+
+CC: Thomas Petazzoni <thomas.petazzoni@free-electrons.com>
+CC: netdev@vger.kernel.org
+CC: linux-kernel@vger.kernel.org
+
+Signed-off-by: Stas Sergeev <stsp@users.sourceforge.net>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+---
+
+--- a/drivers/net/ethernet/marvell/mvneta.c
++++ b/drivers/net/ethernet/marvell/mvneta.c
+@@ -371,7 +371,7 @@ struct mvneta_port {
+ unsigned int duplex;
+ unsigned int speed;
+ unsigned int tx_csum_limit;
+- int use_inband_status:1;
++ unsigned int use_inband_status:1;
+
+ u64 ethtool_stats[ARRAY_SIZE(mvneta_statistics)];
+ };
+@@ -973,6 +973,44 @@ 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);
++ }
++}
++
+ /* This method sets defaults to the NETA port:
+ * Clears interrupt Cause and Mask registers.
+ * Clears all MAC tables.
+@@ -1058,39 +1096,7 @@ static void mvneta_defaults_set(struct m
+ val &= ~MVNETA_PHY_POLLING_ENABLE;
+ mvreg_write(pp, MVNETA_UNIT_CONTROL, val);
+
+- if (pp->use_inband_status) {
+- 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);
+- }
+-
++ 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);
+@@ -2956,10 +2962,43 @@ int mvneta_ethtool_get_settings(struct n
+ 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 (!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);
+ }
+