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

Commit 1b8da103 authored by David S. Miller's avatar David S. Miller
Browse files

Merge branch 'ethtool-implement-Energy-Detect-Powerdown-support-via-phy-tunable'



Alexandru Ardelean says:

====================
ethtool: implement Energy Detect Powerdown support via phy-tunable

This changeset proposes a new control for PHY tunable to control Energy
Detect Power Down.

The `phy_tunable_id` has been named `ETHTOOL_PHY_EDPD` since it looks like
this feature is common across other PHYs (like EEE), and defining
`ETHTOOL_PHY_ENERGY_DETECT_POWER_DOWN` seems too long.

The way EDPD works, is that the RX block is put to a lower power mode,
except for link-pulse detection circuits. The TX block is also put to low
power mode, but the PHY wakes-up periodically to send link pulses, to avoid
lock-ups in case the other side is also in EDPD mode.

Currently, there are 2 PHY drivers that look like they could use this new
PHY tunable feature: the `adin` && `micrel` PHYs.

This series updates only the `adin` PHY driver to support this new feature,
as this chip has been tested. A change for `micrel` can be proposed after a
discussion of the PHY-tunable API is resolved.
====================

Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents 56a4e37e 65d7be09
Loading
Loading
Loading
Loading
+61 −0
Original line number Diff line number Diff line
@@ -26,6 +26,11 @@

#define ADIN1300_RX_ERR_CNT			0x0014

#define ADIN1300_PHY_CTRL_STATUS2		0x0015
#define   ADIN1300_NRG_PD_EN			BIT(3)
#define   ADIN1300_NRG_PD_TX_EN			BIT(2)
#define   ADIN1300_NRG_PD_STATUS		BIT(1)

#define ADIN1300_PHY_CTRL2			0x0016
#define   ADIN1300_DOWNSPEED_AN_100_EN		BIT(11)
#define   ADIN1300_DOWNSPEED_AN_10_EN		BIT(10)
@@ -328,12 +333,62 @@ static int adin_set_downshift(struct phy_device *phydev, u8 cnt)
			    ADIN1300_DOWNSPEEDS_EN);
}

static int adin_get_edpd(struct phy_device *phydev, u16 *tx_interval)
{
	int val;

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

	if (ADIN1300_NRG_PD_EN & val) {
		if (val & ADIN1300_NRG_PD_TX_EN)
			/* default is 1 second */
			*tx_interval = ETHTOOL_PHY_EDPD_DFLT_TX_MSECS;
		else
			*tx_interval = ETHTOOL_PHY_EDPD_NO_TX;
	} else {
		*tx_interval = ETHTOOL_PHY_EDPD_DISABLE;
	}

	return 0;
}

static int adin_set_edpd(struct phy_device *phydev, u16 tx_interval)
{
	u16 val;

	if (tx_interval == ETHTOOL_PHY_EDPD_DISABLE)
		return phy_clear_bits(phydev, ADIN1300_PHY_CTRL_STATUS2,
				(ADIN1300_NRG_PD_EN | ADIN1300_NRG_PD_TX_EN));

	val = ADIN1300_NRG_PD_EN;

	switch (tx_interval) {
	case 1000: /* 1 second */
		/* fallthrough */
	case ETHTOOL_PHY_EDPD_DFLT_TX_MSECS:
		val |= ADIN1300_NRG_PD_TX_EN;
		/* fallthrough */
	case ETHTOOL_PHY_EDPD_NO_TX:
		break;
	default:
		return -EINVAL;
	}

	return phy_modify(phydev, ADIN1300_PHY_CTRL_STATUS2,
			  (ADIN1300_NRG_PD_EN | ADIN1300_NRG_PD_TX_EN),
			  val);
}

static int adin_get_tunable(struct phy_device *phydev,
			    struct ethtool_tunable *tuna, void *data)
{
	switch (tuna->id) {
	case ETHTOOL_PHY_DOWNSHIFT:
		return adin_get_downshift(phydev, data);
	case ETHTOOL_PHY_EDPD:
		return adin_get_edpd(phydev, data);
	default:
		return -EOPNOTSUPP;
	}
@@ -345,6 +400,8 @@ static int adin_set_tunable(struct phy_device *phydev,
	switch (tuna->id) {
	case ETHTOOL_PHY_DOWNSHIFT:
		return adin_set_downshift(phydev, *(const u8 *)data);
	case ETHTOOL_PHY_EDPD:
		return adin_set_edpd(phydev, *(const u16 *)data);
	default:
		return -EOPNOTSUPP;
	}
@@ -368,6 +425,10 @@ static int adin_config_init(struct phy_device *phydev)
	if (rc < 0)
		return rc;

	rc = adin_set_edpd(phydev, ETHTOOL_PHY_EDPD_DFLT_TX_MSECS);
	if (rc < 0)
		return rc;

	phydev_dbg(phydev, "PHY is using mode '%s'\n",
		   phy_modes(phydev->interface));

+22 −0
Original line number Diff line number Diff line
@@ -259,10 +259,32 @@ struct ethtool_tunable {
#define ETHTOOL_PHY_FAST_LINK_DOWN_ON	0
#define ETHTOOL_PHY_FAST_LINK_DOWN_OFF	0xff

/* Energy Detect Power Down (EDPD) is a feature supported by some PHYs, where
 * the PHY's RX & TX blocks are put into a low-power mode when there is no
 * link detected (typically cable is un-plugged). For RX, only a minimal
 * link-detection is available, and for TX the PHY wakes up to send link pulses
 * to avoid any lock-ups in case the peer PHY may also be running in EDPD mode.
 *
 * Some PHYs may support configuration of the wake-up interval for TX pulses,
 * and some PHYs may support only disabling TX pulses entirely. For the latter
 * a special value is required (ETHTOOL_PHY_EDPD_NO_TX) so that this can be
 * configured from userspace (should the user want it).
 *
 * The interval units for TX wake-up are in milliseconds, since this should
 * cover a reasonable range of intervals:
 *  - from 1 millisecond, which does not sound like much of a power-saver
 *  - to ~65 seconds which is quite a lot to wait for a link to come up when
 *    plugging a cable
 */
#define ETHTOOL_PHY_EDPD_DFLT_TX_MSECS		0xffff
#define ETHTOOL_PHY_EDPD_NO_TX			0xfffe
#define ETHTOOL_PHY_EDPD_DISABLE		0

enum phy_tunable_id {
	ETHTOOL_PHY_ID_UNSPEC,
	ETHTOOL_PHY_DOWNSHIFT,
	ETHTOOL_PHY_FAST_LINK_DOWN,
	ETHTOOL_PHY_EDPD,
	/*
	 * Add your fresh new phy tunable attribute above and remember to update
	 * phy_tunable_strings[] in net/core/ethtool.c
+6 −0
Original line number Diff line number Diff line
@@ -133,6 +133,7 @@ phy_tunable_strings[__ETHTOOL_PHY_TUNABLE_COUNT][ETH_GSTRING_LEN] = {
	[ETHTOOL_ID_UNSPEC]     = "Unspec",
	[ETHTOOL_PHY_DOWNSHIFT]	= "phy-downshift",
	[ETHTOOL_PHY_FAST_LINK_DOWN] = "phy-fast-link-down",
	[ETHTOOL_PHY_EDPD]	= "phy-energy-detect-power-down",
};

static int ethtool_get_features(struct net_device *dev, void __user *useraddr)
@@ -2451,6 +2452,11 @@ static int ethtool_phy_tunable_valid(const struct ethtool_tunable *tuna)
		    tuna->type_id != ETHTOOL_TUNABLE_U8)
			return -EINVAL;
		break;
	case ETHTOOL_PHY_EDPD:
		if (tuna->len != sizeof(u16) ||
		    tuna->type_id != ETHTOOL_TUNABLE_U16)
			return -EINVAL;
		break;
	default:
		return -EINVAL;
	}