Commit 9e330bf4 authored by David S. Miller's avatar David S. Miller
Browse files

Merge branch 'ethtool-master-slave'



Oleksij Rempel says:

====================
provide support for PHY master/slave configuration

changes v6:
- use NL_SET_ERR_MSG_ATTR in ethnl_update_linkmodes
- add sanity checks in the ioctl interface
- use bool for ethnl_validate_master_slave_cfg()

changes v5:
- set MASTER_SLAVE_CFG_UNSUPPORTED as default value
- send a netlink error message on validation error
- more code fixes

changes v4:
- rename port_mode to master_slave
- move validation code to net/ethtool/linkmodes.c
- add UNSUPPORTED state and avoid sending unsupported fields
- more formatting and naming fixes
- tja11xx: support only force mode
- tja11xx: mark state as unsupported

changes v3:
- provide separate field for config and state.
- make state rejected on set
- add validation

changes v2:
- change names. Use MASTER_PREFERRED instead of MULTIPORT
- configure master/slave only on request. Default configuration can be
  provided by PHY or eeprom
- status and configuration to the user space.
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 3d59a583 b883e47b
Loading
Loading
Loading
Loading
+19 −16
Original line number Diff line number Diff line
@@ -392,14 +392,16 @@ Request contents:

Kernel response contents:

  ====================================  ======  ==========================
  ==========================================  ======  ==========================
  ``ETHTOOL_A_LINKMODES_HEADER``              nested  reply header
  ``ETHTOOL_A_LINKMODES_AUTONEG``             u8      autonegotiation status
  ``ETHTOOL_A_LINKMODES_OURS``                bitset  advertised link modes
  ``ETHTOOL_A_LINKMODES_PEER``                bitset  partner link modes
  ``ETHTOOL_A_LINKMODES_SPEED``               u32     link speed (Mb/s)
  ``ETHTOOL_A_LINKMODES_DUPLEX``              u8      duplex mode
  ====================================  ======  ==========================
  ``ETHTOOL_A_LINKMODES_MASTER_SLAVE_CFG``    u8      Master/slave port mode
  ``ETHTOOL_A_LINKMODES_MASTER_SLAVE_STATE``  u8      Master/slave port state
  ==========================================  ======  ==========================

For ``ETHTOOL_A_LINKMODES_OURS``, value represents advertised modes and mask
represents supported modes. ``ETHTOOL_A_LINKMODES_PEER`` in the reply is a bit
@@ -414,14 +416,15 @@ LINKMODES_SET

Request contents:

  ====================================  ======  ==========================
  ==========================================  ======  ==========================
  ``ETHTOOL_A_LINKMODES_HEADER``              nested  request header
  ``ETHTOOL_A_LINKMODES_AUTONEG``             u8      autonegotiation status
  ``ETHTOOL_A_LINKMODES_OURS``                bitset  advertised link modes
  ``ETHTOOL_A_LINKMODES_PEER``                bitset  partner link modes
  ``ETHTOOL_A_LINKMODES_SPEED``               u32     link speed (Mb/s)
  ``ETHTOOL_A_LINKMODES_DUPLEX``              u8      duplex mode
  ====================================  ======  ==========================
  ``ETHTOOL_A_LINKMODES_MASTER_SLAVE_CFG``    u8      Master/slave port mode
  ==========================================  ======  ==========================

``ETHTOOL_A_LINKMODES_OURS`` bit set allows setting advertised link modes. If
autonegotiation is on (either set now or kept from before), advertised modes
+43 −0
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@
#define MII_ECTRL_WAKE_REQUEST		BIT(0)

#define MII_CFG1			18
#define MII_CFG1_MASTER_SLAVE		BIT(15)
#define MII_CFG1_AUTO_OP		BIT(14)
#define MII_CFG1_SLEEP_CONFIRM		BIT(6)
#define MII_CFG1_LED_MODE_MASK		GENMASK(5, 4)
@@ -167,6 +168,32 @@ static int tja11xx_soft_reset(struct phy_device *phydev)
	return genphy_soft_reset(phydev);
}

static int tja11xx_config_aneg(struct phy_device *phydev)
{
	u16 ctl = 0;
	int ret;

	switch (phydev->master_slave_set) {
	case MASTER_SLAVE_CFG_MASTER_FORCE:
		ctl |= MII_CFG1_MASTER_SLAVE;
		break;
	case MASTER_SLAVE_CFG_SLAVE_FORCE:
		break;
	case MASTER_SLAVE_CFG_UNKNOWN:
	case MASTER_SLAVE_CFG_UNSUPPORTED:
		return 0;
	default:
		phydev_warn(phydev, "Unsupported Master/Slave mode\n");
		return -ENOTSUPP;
	}

	ret = phy_modify_changed(phydev, MII_CFG1, MII_CFG1_MASTER_SLAVE, ctl);
	if (ret < 0)
		return ret;

	return __genphy_config_aneg(phydev, ret);
}

static int tja11xx_config_init(struct phy_device *phydev)
{
	int ret;
@@ -224,10 +251,22 @@ static int tja11xx_read_status(struct phy_device *phydev)
{
	int ret;

	phydev->master_slave_get = MASTER_SLAVE_CFG_UNKNOWN;
	phydev->master_slave_state = MASTER_SLAVE_STATE_UNSUPPORTED;

	ret = genphy_update_link(phydev);
	if (ret)
		return ret;

	ret = phy_read(phydev, MII_CFG1);
	if (ret < 0)
		return ret;

	if (ret & MII_CFG1_MASTER_SLAVE)
		phydev->master_slave_get = MASTER_SLAVE_CFG_MASTER_FORCE;
	else
		phydev->master_slave_get = MASTER_SLAVE_CFG_SLAVE_FORCE;

	if (phydev->link) {
		ret = phy_read(phydev, MII_COMMSTAT);
		if (ret < 0)
@@ -504,6 +543,7 @@ static struct phy_driver tja11xx_driver[] = {
		.features       = PHY_BASIC_T1_FEATURES,
		.probe		= tja11xx_probe,
		.soft_reset	= tja11xx_soft_reset,
		.config_aneg	= tja11xx_config_aneg,
		.config_init	= tja11xx_config_init,
		.read_status	= tja11xx_read_status,
		.suspend	= genphy_suspend,
@@ -519,6 +559,7 @@ static struct phy_driver tja11xx_driver[] = {
		.features       = PHY_BASIC_T1_FEATURES,
		.probe		= tja11xx_probe,
		.soft_reset	= tja11xx_soft_reset,
		.config_aneg	= tja11xx_config_aneg,
		.config_init	= tja11xx_config_init,
		.read_status	= tja11xx_read_status,
		.suspend	= genphy_suspend,
@@ -533,6 +574,7 @@ static struct phy_driver tja11xx_driver[] = {
		.features       = PHY_BASIC_T1_FEATURES,
		.probe		= tja1102_p0_probe,
		.soft_reset	= tja11xx_soft_reset,
		.config_aneg	= tja11xx_config_aneg,
		.config_init	= tja11xx_config_init,
		.read_status	= tja11xx_read_status,
		.match_phy_device = tja1102_p0_match_phy_device,
@@ -551,6 +593,7 @@ static struct phy_driver tja11xx_driver[] = {
		.features       = PHY_BASIC_T1_FEATURES,
		/* currently no probe for Port 1 is need */
		.soft_reset	= tja11xx_soft_reset,
		.config_aneg	= tja11xx_config_aneg,
		.config_init	= tja11xx_config_init,
		.read_status	= tja11xx_read_status,
		.match_phy_device = tja1102_p1_match_phy_device,
+3 −1
Original line number Diff line number Diff line
@@ -295,7 +295,7 @@ int phy_ethtool_ksettings_set(struct phy_device *phydev,
			 phydev->advertising, autoneg == AUTONEG_ENABLE);

	phydev->duplex = duplex;

	phydev->master_slave_set = cmd->base.master_slave_cfg;
	phydev->mdix_ctrl = cmd->base.eth_tp_mdix_ctrl;

	/* Restart the PHY */
@@ -314,6 +314,8 @@ void phy_ethtool_ksettings_get(struct phy_device *phydev,

	cmd->base.speed = phydev->speed;
	cmd->base.duplex = phydev->duplex;
	cmd->base.master_slave_cfg = phydev->master_slave_get;
	cmd->base.master_slave_state = phydev->master_slave_state;
	if (phydev->interface == PHY_INTERFACE_MODE_MOCA)
		cmd->base.port = PORT_BNC;
	else
+94 −0
Original line number Diff line number Diff line
@@ -1913,6 +1913,90 @@ int genphy_setup_forced(struct phy_device *phydev)
}
EXPORT_SYMBOL(genphy_setup_forced);

static int genphy_setup_master_slave(struct phy_device *phydev)
{
	u16 ctl = 0;

	if (!phydev->is_gigabit_capable)
		return 0;

	switch (phydev->master_slave_set) {
	case MASTER_SLAVE_CFG_MASTER_PREFERRED:
		ctl |= CTL1000_PREFER_MASTER;
		break;
	case MASTER_SLAVE_CFG_SLAVE_PREFERRED:
		break;
	case MASTER_SLAVE_CFG_MASTER_FORCE:
		ctl |= CTL1000_AS_MASTER;
		/* fallthrough */
	case MASTER_SLAVE_CFG_SLAVE_FORCE:
		ctl |= CTL1000_ENABLE_MASTER;
		break;
	case MASTER_SLAVE_CFG_UNKNOWN:
	case MASTER_SLAVE_CFG_UNSUPPORTED:
		return 0;
	default:
		phydev_warn(phydev, "Unsupported Master/Slave mode\n");
		return -EOPNOTSUPP;
	}

	return phy_modify_changed(phydev, MII_CTRL1000,
				  (CTL1000_ENABLE_MASTER | CTL1000_AS_MASTER |
				   CTL1000_PREFER_MASTER), ctl);
}

static int genphy_read_master_slave(struct phy_device *phydev)
{
	int cfg, state;
	u16 val;

	if (!phydev->is_gigabit_capable) {
		phydev->master_slave_get = MASTER_SLAVE_CFG_UNSUPPORTED;
		phydev->master_slave_state = MASTER_SLAVE_STATE_UNSUPPORTED;
		return 0;
	}

	phydev->master_slave_get = MASTER_SLAVE_CFG_UNKNOWN;
	phydev->master_slave_state = MASTER_SLAVE_STATE_UNKNOWN;

	val = phy_read(phydev, MII_CTRL1000);
	if (val < 0)
		return val;

	if (val & CTL1000_ENABLE_MASTER) {
		if (val & CTL1000_AS_MASTER)
			cfg = MASTER_SLAVE_CFG_MASTER_FORCE;
		else
			cfg = MASTER_SLAVE_CFG_SLAVE_FORCE;
	} else {
		if (val & CTL1000_PREFER_MASTER)
			cfg = MASTER_SLAVE_CFG_MASTER_PREFERRED;
		else
			cfg = MASTER_SLAVE_CFG_SLAVE_PREFERRED;
	}

	val = phy_read(phydev, MII_STAT1000);
	if (val < 0)
		return val;

	if (val & LPA_1000MSFAIL) {
		state = MASTER_SLAVE_STATE_ERR;
	} else if (phydev->link) {
		/* this bits are valid only for active link */
		if (val & LPA_1000MSRES)
			state = MASTER_SLAVE_STATE_MASTER;
		else
			state = MASTER_SLAVE_STATE_SLAVE;
	} else {
		state = MASTER_SLAVE_STATE_UNKNOWN;
	}

	phydev->master_slave_get = cfg;
	phydev->master_slave_state = state;

	return 0;
}

/**
 * genphy_restart_aneg - Enable and Restart Autonegotiation
 * @phydev: target phy_device struct
@@ -1971,6 +2055,12 @@ int __genphy_config_aneg(struct phy_device *phydev, bool changed)
	if (genphy_config_eee_advert(phydev))
		changed = true;

	err = genphy_setup_master_slave(phydev);
	if (err < 0)
		return err;
	else if (err)
		changed = true;

	if (AUTONEG_ENABLE != phydev->autoneg)
		return genphy_setup_forced(phydev);

@@ -2205,6 +2295,10 @@ int genphy_read_status(struct phy_device *phydev)
	phydev->pause = 0;
	phydev->asym_pause = 0;

	err = genphy_read_master_slave(phydev);
	if (err < 0)
		return err;

	err = genphy_read_lpa(phydev);
	if (err < 0)
		return err;
+3 −0
Original line number Diff line number Diff line
@@ -477,6 +477,9 @@ struct phy_device {
	int duplex;
	int pause;
	int asym_pause;
	u8 master_slave_get;
	u8 master_slave_set;
	u8 master_slave_state;

	/* Union of PHY and Attached devices' supported link modes */
	/* See ethtool.h for more info */
Loading