123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165 |
- 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);
- }
-
|