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

Merge branch 'ksz9477-eee-support'



Oleksij Rempel says:

====================
net: add EEE support for KSZ9477 switch family

changes v8:
- fix comment for linkmode_to_mii_eee_cap1_t() function
- add Acked-by: default avatarArun Ramadoss <arun.ramadoss@microchip.com>
- add Reviewed-by: default avatarAlexander Duyck <alexanderduyck@fb.com>

changes v7:
- update documentation for genphy_c45_eee_is_active()
- address review comments on "net: dsa: microchip: enable EEE support"
  patch

changes v6:
- split patch set and send only first 9 patches
- Add Reviewed-by: default avatarAndrew Lunn <andrew@lunn.ch>
- use 0xffff instead of GENMASK
- Document @supported_eee
- use "()" with function name in comments

changes v5:
- spell fixes
- move part of genphy_c45_read_eee_abilities() to
  genphy_c45_read_eee_cap1()
- validate MDIO_PCS_EEE_ABLE register against 0xffff val.
- rename *eee_100_10000* to *eee_cap1*
- use linkmode_intersects(phydev->supported, PHY_EEE_CAP1_FEATURES)
  instead of !linkmode_empty()
- add documentation to linkmode/register helpers

changes v4:
- remove following helpers:
  mmd_eee_cap_to_ethtool_sup_t
  mmd_eee_adv_to_ethtool_adv_t
  ethtool_adv_to_mmd_eee_adv_t
  and port drivers from this helpers to linkmode helpers.
- rebase against latest net-next
- port phy_init_eee() to genphy_c45_eee_is_active()

changes v3:
- rework some parts of EEE infrastructure and move it to c45 code.
- add supported_eee storage and start using it in EEE code and by the
  micrel driver.
- add EEE support for ar8035 PHY
- add SmartEEE support to FEC i.MX series.

changes v2:
- use phydev->supported instead of reading MII_BMSR regiaster
- fix @get_eee > @set_eee

With this patch series we provide EEE control for KSZ9477 family of
switches and
AR8035 with i.MX6 configuration.
According to my tests, on a system with KSZ8563 switch and 100Mbit idle
link,
we consume 0,192W less power per port if EEE is enabled.
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 79cdf17e 8b68710a
Loading
Loading
Loading
Loading
+66 −0
Original line number Original line Diff line number Diff line
@@ -2673,6 +2673,70 @@ static int ksz_max_mtu(struct dsa_switch *ds, int port)
	return -EOPNOTSUPP;
	return -EOPNOTSUPP;
}
}


static int ksz_validate_eee(struct dsa_switch *ds, int port)
{
	struct ksz_device *dev = ds->priv;

	if (!dev->info->internal_phy[port])
		return -EOPNOTSUPP;

	switch (dev->chip_id) {
	case KSZ8563_CHIP_ID:
	case KSZ9477_CHIP_ID:
	case KSZ9563_CHIP_ID:
	case KSZ9567_CHIP_ID:
	case KSZ9893_CHIP_ID:
	case KSZ9896_CHIP_ID:
	case KSZ9897_CHIP_ID:
		return 0;
	}

	return -EOPNOTSUPP;
}

static int ksz_get_mac_eee(struct dsa_switch *ds, int port,
			   struct ethtool_eee *e)
{
	int ret;

	ret = ksz_validate_eee(ds, port);
	if (ret)
		return ret;

	/* There is no documented control of Tx LPI configuration. */
	e->tx_lpi_enabled = true;

	/* There is no documented control of Tx LPI timer. According to tests
	 * Tx LPI timer seems to be set by default to minimal value.
	 */
	e->tx_lpi_timer = 0;

	return 0;
}

static int ksz_set_mac_eee(struct dsa_switch *ds, int port,
			   struct ethtool_eee *e)
{
	struct ksz_device *dev = ds->priv;
	int ret;

	ret = ksz_validate_eee(ds, port);
	if (ret)
		return ret;

	if (!e->tx_lpi_enabled) {
		dev_err(dev->dev, "Disabling EEE Tx LPI is not supported\n");
		return -EINVAL;
	}

	if (e->tx_lpi_timer) {
		dev_err(dev->dev, "Setting EEE Tx LPI timer is not supported\n");
		return -EINVAL;
	}

	return 0;
}

static void ksz_set_xmii(struct ksz_device *dev, int port,
static void ksz_set_xmii(struct ksz_device *dev, int port,
			 phy_interface_t interface)
			 phy_interface_t interface)
{
{
@@ -3130,6 +3194,8 @@ static const struct dsa_switch_ops ksz_switch_ops = {
	.port_txtstamp		= ksz_port_txtstamp,
	.port_txtstamp		= ksz_port_txtstamp,
	.port_rxtstamp		= ksz_port_rxtstamp,
	.port_rxtstamp		= ksz_port_rxtstamp,
	.port_setup_tc		= ksz_setup_tc,
	.port_setup_tc		= ksz_setup_tc,
	.get_mac_eee		= ksz_get_mac_eee,
	.set_mac_eee		= ksz_set_mac_eee,
};
};


struct ksz_device *ksz_switch_alloc(struct device *base, void *priv)
struct ksz_device *ksz_switch_alloc(struct device *base, void *priv)
+21 −0
Original line number Original line Diff line number Diff line
@@ -1392,6 +1392,26 @@ static int ksz9131_config_aneg(struct phy_device *phydev)
	return genphy_config_aneg(phydev);
	return genphy_config_aneg(phydev);
}
}


static int ksz9477_get_features(struct phy_device *phydev)
{
	int ret;

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

	/* The "EEE control and capability 1" (Register 3.20) seems to be
	 * influenced by the "EEE advertisement 1" (Register 7.60). Changes
	 * on the 7.60 will affect 3.20. So, we need to construct our own list
	 * of caps.
	 * KSZ8563R should have 100BaseTX/Full only.
	 */
	linkmode_and(phydev->supported_eee, phydev->supported,
		     PHY_EEE_CAP1_FEATURES);

	return 0;
}

#define KSZ8873MLL_GLOBAL_CONTROL_4	0x06
#define KSZ8873MLL_GLOBAL_CONTROL_4	0x06
#define KSZ8873MLL_GLOBAL_CONTROL_4_DUPLEX	BIT(6)
#define KSZ8873MLL_GLOBAL_CONTROL_4_DUPLEX	BIT(6)
#define KSZ8873MLL_GLOBAL_CONTROL_4_SPEED	BIT(4)
#define KSZ8873MLL_GLOBAL_CONTROL_4_SPEED	BIT(4)
@@ -4172,6 +4192,7 @@ static struct phy_driver ksphy_driver[] = {
	.handle_interrupt = kszphy_handle_interrupt,
	.handle_interrupt = kszphy_handle_interrupt,
	.suspend	= genphy_suspend,
	.suspend	= genphy_suspend,
	.resume		= genphy_resume,
	.resume		= genphy_resume,
	.get_features	= ksz9477_get_features,
} };
} };


module_phy_driver(ksphy_driver);
module_phy_driver(ksphy_driver);
+318 −1
Original line number Original line Diff line number Diff line
@@ -262,7 +262,11 @@ int genphy_c45_an_config_aneg(struct phy_device *phydev)
	linkmode_and(phydev->advertising, phydev->advertising,
	linkmode_and(phydev->advertising, phydev->advertising,
		     phydev->supported);
		     phydev->supported);


	changed = genphy_config_eee_advert(phydev);
	ret = genphy_c45_write_eee_adv(phydev, phydev->supported_eee);
	if (ret < 0)
		return ret;
	else if (ret)
		changed = true;


	if (genphy_c45_baset1_able(phydev))
	if (genphy_c45_baset1_able(phydev))
		return genphy_c45_baset1_an_config_aneg(phydev);
		return genphy_c45_baset1_an_config_aneg(phydev);
@@ -661,6 +665,199 @@ int genphy_c45_read_mdix(struct phy_device *phydev)
}
}
EXPORT_SYMBOL_GPL(genphy_c45_read_mdix);
EXPORT_SYMBOL_GPL(genphy_c45_read_mdix);


/**
 * genphy_c45_write_eee_adv - write advertised EEE link modes
 * @phydev: target phy_device struct
 * @adv: the linkmode advertisement settings
 */
int genphy_c45_write_eee_adv(struct phy_device *phydev, unsigned long *adv)
{
	int val, changed;

	if (linkmode_intersects(phydev->supported, PHY_EEE_CAP1_FEATURES)) {
		val = linkmode_to_mii_eee_cap1_t(adv);

		/* In eee_broken_modes are stored MDIO_AN_EEE_ADV specific raw
		 * register values.
		 */
		val &= ~phydev->eee_broken_modes;

		/* IEEE 802.3-2018 45.2.7.13 EEE advertisement 1
		 * (Register 7.60)
		 */
		val = phy_modify_mmd_changed(phydev, MDIO_MMD_AN,
					     MDIO_AN_EEE_ADV,
					     MDIO_EEE_100TX | MDIO_EEE_1000T |
					     MDIO_EEE_10GT | MDIO_EEE_1000KX |
					     MDIO_EEE_10GKX4 | MDIO_EEE_10GKR,
					     val);
		if (val < 0)
			return val;
		if (val > 0)
			changed = 1;
	}

	if (linkmode_test_bit(ETHTOOL_LINK_MODE_10baseT1L_Full_BIT,
			      phydev->supported_eee)) {
		val = linkmode_adv_to_mii_10base_t1_t(adv);
		/* IEEE 802.3cg-2019 45.2.7.25 10BASE-T1 AN control register
		 * (Register 7.526)
		 */
		val = phy_modify_mmd_changed(phydev, MDIO_MMD_AN,
					     MDIO_AN_10BT1_AN_CTRL,
					     MDIO_AN_10BT1_AN_CTRL_ADV_EEE_T1L,
					     val);
		if (val < 0)
			return val;
		if (val > 0)
			changed = 1;
	}

	return changed;
}

/**
 * genphy_c45_read_eee_adv - read advertised EEE link modes
 * @phydev: target phy_device struct
 * @adv: the linkmode advertisement status
 */
static int genphy_c45_read_eee_adv(struct phy_device *phydev,
				   unsigned long *adv)
{
	int val;

	if (linkmode_intersects(phydev->supported, PHY_EEE_CAP1_FEATURES)) {
		/* IEEE 802.3-2018 45.2.7.13 EEE advertisement 1
		 * (Register 7.60)
		 */
		val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV);
		if (val < 0)
			return val;

		mii_eee_cap1_mod_linkmode_t(adv, val);
	}

	if (linkmode_test_bit(ETHTOOL_LINK_MODE_10baseT1L_Full_BIT,
			      phydev->supported_eee)) {
		/* IEEE 802.3cg-2019 45.2.7.25 10BASE-T1 AN control register
		 * (Register 7.526)
		 */
		val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_10BT1_AN_CTRL);
		if (val < 0)
			return val;

		mii_10base_t1_adv_mod_linkmode_t(adv, val);
	}

	return 0;
}

/**
 * genphy_c45_read_eee_lpa - read advertised LP EEE link modes
 * @phydev: target phy_device struct
 * @lpa: the linkmode LP advertisement status
 */
static int genphy_c45_read_eee_lpa(struct phy_device *phydev,
				   unsigned long *lpa)
{
	int val;

	if (linkmode_intersects(phydev->supported, PHY_EEE_CAP1_FEATURES)) {
		/* IEEE 802.3-2018 45.2.7.14 EEE link partner ability 1
		 * (Register 7.61)
		 */
		val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_LPABLE);
		if (val < 0)
			return val;

		mii_eee_cap1_mod_linkmode_t(lpa, val);
	}

	if (linkmode_test_bit(ETHTOOL_LINK_MODE_10baseT1L_Full_BIT,
			      phydev->supported_eee)) {
		/* IEEE 802.3cg-2019 45.2.7.26 10BASE-T1 AN status register
		 * (Register 7.527)
		 */
		val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_10BT1_AN_STAT);
		if (val < 0)
			return val;

		mii_10base_t1_adv_mod_linkmode_t(lpa, val);
	}

	return 0;
}

/**
 * genphy_c45_read_eee_cap1 - read supported EEE link modes from register 3.20
 * @phydev: target phy_device struct
 */
static int genphy_c45_read_eee_cap1(struct phy_device *phydev)
{
	int val;

	/* IEEE 802.3-2018 45.2.3.10 EEE control and capability 1
	 * (Register 3.20)
	 */
	val = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_PCS_EEE_ABLE);
	if (val < 0)
		return val;

	/* The 802.3 2018 standard says the top 2 bits are reserved and should
	 * read as 0. Also, it seems unlikely anybody will build a PHY which
	 * supports 100GBASE-R deep sleep all the way down to 100BASE-TX EEE.
	 * If MDIO_PCS_EEE_ABLE is 0xffff assume EEE is not supported.
	 */
	if (val == 0xffff)
		return 0;

	mii_eee_cap1_mod_linkmode_t(phydev->supported_eee, val);

	/* Some buggy devices indicate EEE link modes in MDIO_PCS_EEE_ABLE
	 * which they don't support as indicated by BMSR, ESTATUS etc.
	 */
	linkmode_and(phydev->supported_eee, phydev->supported_eee,
		     phydev->supported);

	return 0;
}

/**
 * genphy_c45_read_eee_abilities - read supported EEE link modes
 * @phydev: target phy_device struct
 */
int genphy_c45_read_eee_abilities(struct phy_device *phydev)
{
	int val;

	/* There is not indicator whether optional register
	 * "EEE control and capability 1" (3.20) is supported. Read it only
	 * on devices with appropriate linkmodes.
	 */
	if (linkmode_intersects(phydev->supported, PHY_EEE_CAP1_FEATURES)) {
		val = genphy_c45_read_eee_cap1(phydev);
		if (val)
			return val;
	}

	if (linkmode_test_bit(ETHTOOL_LINK_MODE_10baseT1L_Full_BIT,
			      phydev->supported)) {
		/* IEEE 802.3cg-2019 45.2.1.186b 10BASE-T1L PMA status register
		 * (Register 1.2295)
		 */
		val = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_PMA_10T1L_STAT);
		if (val < 0)
			return val;

		linkmode_mod_bit(ETHTOOL_LINK_MODE_10baseT1L_Full_BIT,
				 phydev->supported_eee,
				 val & MDIO_PMA_10T1L_STAT_EEE);
	}

	return 0;
}
EXPORT_SYMBOL_GPL(genphy_c45_read_eee_abilities);

/**
/**
 * genphy_c45_pma_read_abilities - read supported link modes from PMA
 * genphy_c45_pma_read_abilities - read supported link modes from PMA
 * @phydev: target phy_device struct
 * @phydev: target phy_device struct
@@ -775,6 +972,11 @@ int genphy_c45_pma_read_abilities(struct phy_device *phydev)
		}
		}
	}
	}


	/* This is optional functionality. If not supported, we may get an error
	 * which should be ignored.
	 */
	genphy_c45_read_eee_abilities(phydev);

	return 0;
	return 0;
}
}
EXPORT_SYMBOL_GPL(genphy_c45_pma_read_abilities);
EXPORT_SYMBOL_GPL(genphy_c45_pma_read_abilities);
@@ -1124,6 +1326,121 @@ int genphy_c45_plca_get_status(struct phy_device *phydev,
}
}
EXPORT_SYMBOL_GPL(genphy_c45_plca_get_status);
EXPORT_SYMBOL_GPL(genphy_c45_plca_get_status);


/**
 * genphy_c45_eee_is_active - get EEE status
 * @phydev: target phy_device struct
 * @adv: variable to store advertised linkmodes
 * @lp: variable to store LP advertised linkmodes
 * @is_enabled: variable to store EEE enabled/disabled configuration value
 *
 * Description: this function will read local and link partner PHY
 * advertisements. Compare them return current EEE state.
 */
int genphy_c45_eee_is_active(struct phy_device *phydev, unsigned long *adv,
			     unsigned long *lp, bool *is_enabled)
{
	__ETHTOOL_DECLARE_LINK_MODE_MASK(tmp_adv) = {};
	__ETHTOOL_DECLARE_LINK_MODE_MASK(tmp_lp) = {};
	__ETHTOOL_DECLARE_LINK_MODE_MASK(common);
	bool eee_enabled, eee_active;
	int ret;

	ret = genphy_c45_read_eee_adv(phydev, tmp_adv);
	if (ret)
		return ret;

	ret = genphy_c45_read_eee_lpa(phydev, tmp_lp);
	if (ret)
		return ret;

	eee_enabled = !linkmode_empty(tmp_adv);
	linkmode_and(common, tmp_adv, tmp_lp);
	if (eee_enabled && !linkmode_empty(common))
		eee_active = phy_check_valid(phydev->speed, phydev->duplex,
					     common);
	else
		eee_active = false;

	if (adv)
		linkmode_copy(adv, tmp_adv);
	if (lp)
		linkmode_copy(lp, tmp_lp);
	if (is_enabled)
		*is_enabled = eee_enabled;

	return eee_active;
}
EXPORT_SYMBOL(genphy_c45_eee_is_active);

/**
 * genphy_c45_ethtool_get_eee - get EEE supported and status
 * @phydev: target phy_device struct
 * @data: ethtool_eee data
 *
 * Description: it reports the Supported/Advertisement/LP Advertisement
 * capabilities.
 */
int genphy_c45_ethtool_get_eee(struct phy_device *phydev,
			       struct ethtool_eee *data)
{
	__ETHTOOL_DECLARE_LINK_MODE_MASK(adv) = {};
	__ETHTOOL_DECLARE_LINK_MODE_MASK(lp) = {};
	bool overflow = false, is_enabled;
	int ret;

	ret = genphy_c45_eee_is_active(phydev, adv, lp, &is_enabled);
	if (ret < 0)
		return ret;

	data->eee_enabled = is_enabled;
	data->eee_active = ret;

	if (!ethtool_convert_link_mode_to_legacy_u32(&data->supported,
						     phydev->supported_eee))
		overflow = true;
	if (!ethtool_convert_link_mode_to_legacy_u32(&data->advertised, adv))
		overflow = true;
	if (!ethtool_convert_link_mode_to_legacy_u32(&data->lp_advertised, lp))
		overflow = true;

	if (overflow)
		phydev_warn(phydev, "Not all supported or advertised EEE link modes were passed to the user space\n");

	return 0;
}
EXPORT_SYMBOL(genphy_c45_ethtool_get_eee);

/**
 * genphy_c45_ethtool_set_eee - get EEE supported and status
 * @phydev: target phy_device struct
 * @data: ethtool_eee data
 *
 * Description: it reportes the Supported/Advertisement/LP Advertisement
 * capabilities.
 */
int genphy_c45_ethtool_set_eee(struct phy_device *phydev,
			       struct ethtool_eee *data)
{
	__ETHTOOL_DECLARE_LINK_MODE_MASK(adv) = {};
	int ret;

	if (data->eee_enabled) {
		if (data->advertised)
			adv[0] = data->advertised;
		else
			linkmode_copy(adv, phydev->supported_eee);
	}

	ret = genphy_c45_write_eee_adv(phydev, adv);
	if (ret < 0)
		return ret;
	if (ret > 0)
		return phy_restart_aneg(phydev);

	return 0;
}
EXPORT_SYMBOL(genphy_c45_ethtool_set_eee);

struct phy_driver genphy_c45_driver = {
struct phy_driver genphy_c45_driver = {
	.phy_id         = 0xffffffff,
	.phy_id         = 0xffffffff,
	.phy_id_mask    = 0xffffffff,
	.phy_id_mask    = 0xffffffff,
+18 −135
Original line number Original line Diff line number Diff line
@@ -242,11 +242,11 @@ unsigned int phy_supported_speeds(struct phy_device *phy,
 *
 *
 * Description: Returns true if there is a valid setting, false otherwise.
 * Description: Returns true if there is a valid setting, false otherwise.
 */
 */
static inline bool phy_check_valid(int speed, int duplex,
bool phy_check_valid(int speed, int duplex, unsigned long *features)
				   unsigned long *features)
{
{
	return !!phy_lookup_setting(speed, duplex, features, true);
	return !!phy_lookup_setting(speed, duplex, features, true);
}
}
EXPORT_SYMBOL(phy_check_valid);


/**
/**
 * phy_sanitize_settings - make sure the PHY is set to supported speed and duplex
 * phy_sanitize_settings - make sure the PHY is set to supported speed and duplex
@@ -1457,30 +1457,6 @@ void phy_mac_interrupt(struct phy_device *phydev)
}
}
EXPORT_SYMBOL(phy_mac_interrupt);
EXPORT_SYMBOL(phy_mac_interrupt);


static void mmd_eee_adv_to_linkmode(unsigned long *advertising, u16 eee_adv)
{
	linkmode_zero(advertising);

	if (eee_adv & MDIO_EEE_100TX)
		linkmode_set_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT,
				 advertising);
	if (eee_adv & MDIO_EEE_1000T)
		linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
				 advertising);
	if (eee_adv & MDIO_EEE_10GT)
		linkmode_set_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT,
				 advertising);
	if (eee_adv & MDIO_EEE_1000KX)
		linkmode_set_bit(ETHTOOL_LINK_MODE_1000baseKX_Full_BIT,
				 advertising);
	if (eee_adv & MDIO_EEE_10GKX4)
		linkmode_set_bit(ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT,
				 advertising);
	if (eee_adv & MDIO_EEE_10GKR)
		linkmode_set_bit(ETHTOOL_LINK_MODE_10000baseKR_Full_BIT,
				 advertising);
}

/**
/**
 * phy_init_eee - init and check the EEE feature
 * phy_init_eee - init and check the EEE feature
 * @phydev: target phy_device struct
 * @phydev: target phy_device struct
@@ -1493,62 +1469,25 @@ static void mmd_eee_adv_to_linkmode(unsigned long *advertising, u16 eee_adv)
 */
 */
int phy_init_eee(struct phy_device *phydev, bool clk_stop_enable)
int phy_init_eee(struct phy_device *phydev, bool clk_stop_enable)
{
{
	int ret;

	if (!phydev->drv)
	if (!phydev->drv)
		return -EIO;
		return -EIO;


	/* According to 802.3az,the EEE is supported only in full duplex-mode.
	ret = genphy_c45_eee_is_active(phydev, NULL, NULL, NULL);
	 */
	if (ret < 0)
	if (phydev->duplex == DUPLEX_FULL) {
		return ret;
		__ETHTOOL_DECLARE_LINK_MODE_MASK(common);
	if (!ret)
		__ETHTOOL_DECLARE_LINK_MODE_MASK(lp);
		return -EPROTONOSUPPORT;
		__ETHTOOL_DECLARE_LINK_MODE_MASK(adv);
		int eee_lp, eee_cap, eee_adv;
		int status;
		u32 cap;

		/* Read phy status to properly get the right settings */
		status = phy_read_status(phydev);
		if (status)
			return status;

		/* First check if the EEE ability is supported */
		eee_cap = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_PCS_EEE_ABLE);
		if (eee_cap <= 0)
			goto eee_exit_err;

		cap = mmd_eee_cap_to_ethtool_sup_t(eee_cap);
		if (!cap)
			goto eee_exit_err;

		/* Check which link settings negotiated and verify it in
		 * the EEE advertising registers.
		 */
		eee_lp = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_LPABLE);
		if (eee_lp <= 0)
			goto eee_exit_err;

		eee_adv = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV);
		if (eee_adv <= 0)
			goto eee_exit_err;

		mmd_eee_adv_to_linkmode(adv, eee_adv);
		mmd_eee_adv_to_linkmode(lp, eee_lp);
		linkmode_and(common, adv, lp);

		if (!phy_check_valid(phydev->speed, phydev->duplex, common))
			goto eee_exit_err;


	if (clk_stop_enable)
	if (clk_stop_enable)
		/* Configure the PHY to stop receiving xMII
		/* Configure the PHY to stop receiving xMII
		 * clock while it is signaling LPI.
		 * clock while it is signaling LPI.
		 */
		 */
			phy_set_bits_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1,
		ret = phy_set_bits_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1,
				       MDIO_PCS_CTRL1_CLKSTOP_EN);
				       MDIO_PCS_CTRL1_CLKSTOP_EN);


		return 0; /* EEE supported */
	return ret < 0 ? ret : 0;
	}
eee_exit_err:
	return -EPROTONOSUPPORT;
}
}
EXPORT_SYMBOL(phy_init_eee);
EXPORT_SYMBOL(phy_init_eee);


@@ -1578,33 +1517,10 @@ EXPORT_SYMBOL(phy_get_eee_err);
 */
 */
int phy_ethtool_get_eee(struct phy_device *phydev, struct ethtool_eee *data)
int phy_ethtool_get_eee(struct phy_device *phydev, struct ethtool_eee *data)
{
{
	int val;

	if (!phydev->drv)
	if (!phydev->drv)
		return -EIO;
		return -EIO;


	/* Get Supported EEE */
	return genphy_c45_ethtool_get_eee(phydev, data);
	val = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_PCS_EEE_ABLE);
	if (val < 0)
		return val;
	data->supported = mmd_eee_cap_to_ethtool_sup_t(val);

	/* Get advertisement EEE */
	val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV);
	if (val < 0)
		return val;
	data->advertised = mmd_eee_adv_to_ethtool_adv_t(val);
	data->eee_enabled = !!data->advertised;

	/* Get LP advertisement EEE */
	val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_LPABLE);
	if (val < 0)
		return val;
	data->lp_advertised = mmd_eee_adv_to_ethtool_adv_t(val);

	data->eee_active = !!(data->advertised & data->lp_advertised);

	return 0;
}
}
EXPORT_SYMBOL(phy_ethtool_get_eee);
EXPORT_SYMBOL(phy_ethtool_get_eee);


@@ -1617,43 +1533,10 @@ EXPORT_SYMBOL(phy_ethtool_get_eee);
 */
 */
int phy_ethtool_set_eee(struct phy_device *phydev, struct ethtool_eee *data)
int phy_ethtool_set_eee(struct phy_device *phydev, struct ethtool_eee *data)
{
{
	int cap, old_adv, adv = 0, ret;

	if (!phydev->drv)
	if (!phydev->drv)
		return -EIO;
		return -EIO;


	/* Get Supported EEE */
	return genphy_c45_ethtool_set_eee(phydev, data);
	cap = phy_read_mmd(phydev, MDIO_MMD_PCS, MDIO_PCS_EEE_ABLE);
	if (cap < 0)
		return cap;

	old_adv = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV);
	if (old_adv < 0)
		return old_adv;

	if (data->eee_enabled) {
		adv = !data->advertised ? cap :
		      ethtool_adv_to_mmd_eee_adv_t(data->advertised) & cap;
		/* Mask prohibited EEE modes */
		adv &= ~phydev->eee_broken_modes;
	}

	if (old_adv != adv) {
		ret = phy_write_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV, adv);
		if (ret < 0)
			return ret;

		/* Restart autonegotiation so the new modes get sent to the
		 * link partner.
		 */
		if (phydev->autoneg == AUTONEG_ENABLE) {
			ret = phy_restart_aneg(phydev);
			if (ret < 0)
				return ret;
		}
	}

	return 0;
}
}
EXPORT_SYMBOL(phy_ethtool_set_eee);
EXPORT_SYMBOL(phy_ethtool_set_eee);


+25 −1
Original line number Original line Diff line number Diff line
@@ -132,6 +132,18 @@ static const int phy_10gbit_full_features_array[] = {
	ETHTOOL_LINK_MODE_10000baseT_Full_BIT,
	ETHTOOL_LINK_MODE_10000baseT_Full_BIT,
};
};


static const int phy_eee_cap1_features_array[] = {
	ETHTOOL_LINK_MODE_100baseT_Full_BIT,
	ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
	ETHTOOL_LINK_MODE_10000baseT_Full_BIT,
	ETHTOOL_LINK_MODE_1000baseKX_Full_BIT,
	ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT,
	ETHTOOL_LINK_MODE_10000baseKR_Full_BIT,
};

__ETHTOOL_DECLARE_LINK_MODE_MASK(phy_eee_cap1_features) __ro_after_init;
EXPORT_SYMBOL_GPL(phy_eee_cap1_features);

static void features_init(void)
static void features_init(void)
{
{
	/* 10/100 half/full*/
	/* 10/100 half/full*/
@@ -213,6 +225,10 @@ static void features_init(void)
	linkmode_set_bit_array(phy_10gbit_fec_features_array,
	linkmode_set_bit_array(phy_10gbit_fec_features_array,
			       ARRAY_SIZE(phy_10gbit_fec_features_array),
			       ARRAY_SIZE(phy_10gbit_fec_features_array),
			       phy_10gbit_fec_features);
			       phy_10gbit_fec_features);
	linkmode_set_bit_array(phy_eee_cap1_features_array,
			       ARRAY_SIZE(phy_eee_cap1_features_array),
			       phy_eee_cap1_features);

}
}


void phy_device_free(struct phy_device *phydev)
void phy_device_free(struct phy_device *phydev)
@@ -2215,7 +2231,10 @@ int __genphy_config_aneg(struct phy_device *phydev, bool changed)
{
{
	int err;
	int err;


	if (genphy_config_eee_advert(phydev))
	err = genphy_c45_write_eee_adv(phydev, phydev->supported_eee);
	if (err < 0)
		return err;
	else if (err)
		changed = true;
		changed = true;


	err = genphy_setup_master_slave(phydev);
	err = genphy_setup_master_slave(phydev);
@@ -2637,6 +2656,11 @@ int genphy_read_abilities(struct phy_device *phydev)
				 phydev->supported, val & ESTATUS_1000_XFULL);
				 phydev->supported, val & ESTATUS_1000_XFULL);
	}
	}


	/* This is optional functionality. If not supported, we may get an error
	 * which should be ignored.
	 */
	genphy_c45_read_eee_abilities(phydev);

	return 0;
	return 0;
}
}
EXPORT_SYMBOL(genphy_read_abilities);
EXPORT_SYMBOL(genphy_read_abilities);
Loading