diff options
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.patch | 165 |
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); + } + |