Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 13722bbe authored by Woojung Huh's avatar Woojung Huh Committed by David S. Miller
Browse files

smsc95xx: Add mdix control via ethtool



Add mdix control through ethtool.

Signed-off-by: default avatarWoojung Huh <Woojung.huh@gmail.com>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent 273bf288
Loading
Loading
Loading
Loading
+106 −3
Original line number Diff line number Diff line
@@ -33,7 +33,7 @@
#include "smsc95xx.h"

#define SMSC_CHIPNAME			"smsc95xx"
#define SMSC_DRIVER_VERSION		"1.0.4"
#define SMSC_DRIVER_VERSION		"1.0.5"
#define HS_USB_PKT_SIZE			(512)
#define FS_USB_PKT_SIZE			(64)
#define DEFAULT_HS_BURST_CAP_SIZE	(16 * 1024 + 5 * HS_USB_PKT_SIZE)
@@ -64,6 +64,7 @@
#define CARRIER_CHECK_DELAY (2 * HZ)

struct smsc95xx_priv {
	u32 chip_id;
	u32 mac_cr;
	u32 hash_hi;
	u32 hash_lo;
@@ -71,6 +72,7 @@ struct smsc95xx_priv {
	spinlock_t mac_cr_lock;
	u8 features;
	u8 suspend_flags;
	u8 mdix_ctrl;
	bool link_ok;
	struct delayed_work carrier_check;
	struct usbnet *dev;
@@ -782,14 +784,113 @@ static int smsc95xx_ethtool_set_wol(struct net_device *net,
	return ret;
}

static int get_mdix_status(struct net_device *net)
{
	struct usbnet *dev = netdev_priv(net);
	u32 val;
	int buf;

	buf = smsc95xx_mdio_read(dev->net, dev->mii.phy_id, SPECIAL_CTRL_STS);
	if (buf & SPECIAL_CTRL_STS_OVRRD_AMDIX_) {
		if (buf & SPECIAL_CTRL_STS_AMDIX_ENABLE_)
			return ETH_TP_MDI_AUTO;
		else if (buf & SPECIAL_CTRL_STS_AMDIX_STATE_)
			return ETH_TP_MDI_X;
	} else {
		buf = smsc95xx_read_reg(dev, STRAP_STATUS, &val);
		if (val & STRAP_STATUS_AMDIX_EN_)
			return ETH_TP_MDI_AUTO;
	}

	return ETH_TP_MDI;
}

static void set_mdix_status(struct net_device *net, __u8 mdix_ctrl)
{
	struct usbnet *dev = netdev_priv(net);
	struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]);
	int buf;

	if ((pdata->chip_id == ID_REV_CHIP_ID_9500A_) ||
	    (pdata->chip_id == ID_REV_CHIP_ID_9530_) ||
	    (pdata->chip_id == ID_REV_CHIP_ID_89530_) ||
	    (pdata->chip_id == ID_REV_CHIP_ID_9730_)) {
		/* Extend Manual AutoMDIX timer for 9500A/9500Ai */
		buf = smsc95xx_mdio_read(dev->net, dev->mii.phy_id,
					 PHY_EDPD_CONFIG);
		buf |= PHY_EDPD_CONFIG_EXT_CROSSOVER_;
		smsc95xx_mdio_write(dev->net, dev->mii.phy_id,
				    PHY_EDPD_CONFIG, buf);
	}

	if (mdix_ctrl == ETH_TP_MDI) {
		buf = smsc95xx_mdio_read(dev->net, dev->mii.phy_id,
					 SPECIAL_CTRL_STS);
		buf |= SPECIAL_CTRL_STS_OVRRD_AMDIX_;
		buf &= ~(SPECIAL_CTRL_STS_AMDIX_ENABLE_ |
			 SPECIAL_CTRL_STS_AMDIX_STATE_);
		smsc95xx_mdio_write(dev->net, dev->mii.phy_id,
				    SPECIAL_CTRL_STS, buf);
	} else if (mdix_ctrl == ETH_TP_MDI_X) {
		buf = smsc95xx_mdio_read(dev->net, dev->mii.phy_id,
					 SPECIAL_CTRL_STS);
		buf |= SPECIAL_CTRL_STS_OVRRD_AMDIX_;
		buf &= ~(SPECIAL_CTRL_STS_AMDIX_ENABLE_ |
			 SPECIAL_CTRL_STS_AMDIX_STATE_);
		buf |= SPECIAL_CTRL_STS_AMDIX_STATE_;
		smsc95xx_mdio_write(dev->net, dev->mii.phy_id,
				    SPECIAL_CTRL_STS, buf);
	} else if (mdix_ctrl == ETH_TP_MDI_AUTO) {
		buf = smsc95xx_mdio_read(dev->net, dev->mii.phy_id,
					 SPECIAL_CTRL_STS);
		buf &= ~SPECIAL_CTRL_STS_OVRRD_AMDIX_;
		buf &= ~(SPECIAL_CTRL_STS_AMDIX_ENABLE_ |
			 SPECIAL_CTRL_STS_AMDIX_STATE_);
		buf |= SPECIAL_CTRL_STS_AMDIX_ENABLE_;
		smsc95xx_mdio_write(dev->net, dev->mii.phy_id,
				    SPECIAL_CTRL_STS, buf);
	}
	pdata->mdix_ctrl = mdix_ctrl;
}

static int smsc95xx_get_settings(struct net_device *net,
				 struct ethtool_cmd *cmd)
{
	struct usbnet *dev = netdev_priv(net);
	struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]);
	int retval;

	retval = usbnet_get_settings(net, cmd);

	cmd->eth_tp_mdix = pdata->mdix_ctrl;
	cmd->eth_tp_mdix_ctrl = pdata->mdix_ctrl;

	return retval;
}

static int smsc95xx_set_settings(struct net_device *net,
				 struct ethtool_cmd *cmd)
{
	struct usbnet *dev = netdev_priv(net);
	struct smsc95xx_priv *pdata = (struct smsc95xx_priv *)(dev->data[0]);
	int retval;

	if (pdata->mdix_ctrl != cmd->eth_tp_mdix_ctrl)
		set_mdix_status(net, cmd->eth_tp_mdix_ctrl);

	retval = usbnet_set_settings(net, cmd);

	return retval;
}

static const struct ethtool_ops smsc95xx_ethtool_ops = {
	.get_link	= usbnet_get_link,
	.nway_reset	= usbnet_nway_reset,
	.get_drvinfo	= usbnet_get_drvinfo,
	.get_msglevel	= usbnet_get_msglevel,
	.set_msglevel	= usbnet_set_msglevel,
	.get_settings	= usbnet_get_settings,
	.set_settings	= usbnet_set_settings,
	.get_settings	= smsc95xx_get_settings,
	.set_settings	= smsc95xx_set_settings,
	.get_eeprom_len	= smsc95xx_ethtool_get_eeprom_len,
	.get_eeprom	= smsc95xx_ethtool_get_eeprom,
	.set_eeprom	= smsc95xx_ethtool_set_eeprom,
@@ -1194,6 +1295,8 @@ static int smsc95xx_bind(struct usbnet *dev, struct usb_interface *intf)
	if (ret < 0)
		return ret;
	val >>= 16;
	pdata->chip_id = val;
	pdata->mdix_ctrl = get_mdix_status(dev->net);

	if ((val == ID_REV_CHIP_ID_9500A_) || (val == ID_REV_CHIP_ID_9530_) ||
	    (val == ID_REV_CHIP_ID_89530_) || (val == ID_REV_CHIP_ID_9730_))