@@ -166,8 +166,18 @@
#define AT803X_MMD3_SMARTEEE_CTL1 0x805b
#define AT803X_MMD3_SMARTEEE_CTL2 0x805c
+#define AT803X_MMD3_SMARTEEE_LPI_TIME_LOW GENMASK(15, 0)
+#define AT803X_MMD3_SMARTEEE_LPI_TIME_15_0 GENMASK(15, 0)
#define AT803X_MMD3_SMARTEEE_CTL3 0x805d
#define AT803X_MMD3_SMARTEEE_CTL3_LPI_EN BIT(8)
+#define AT803X_MMD3_SMARTEEE_LPI_TIME_HIGH GENMASK(7, 0)
+#define AT803X_MMD3_SMARTEEE_LPI_TIME_23_16 GENMASK(23, 16)
+/* Tx LPI timer resolution */
+#define AT803X_MMD3_SMARTEEE_LPI_TIME_RESOL_NS 163840
+#define AT803X_MMD3_SMARTEEE_LPI_TIME_MAX_US \
+ ((GENMASK(23, 0) * AT803X_MMD3_SMARTEEE_LPI_TIME_RESOL_NS) / \
+ NSEC_PER_USEC)
+#define AT803X_MMD3_SMARTEEE_LPI_TIME_DEF_US 335544
#define ATH9331_PHY_ID 0x004dd041
#define ATH8030_PHY_ID 0x004dd076
@@ -951,17 +961,26 @@ static int at803x_get_features(struct phy_device *phydev)
return 0;
}
-static int at803x_smarteee_config(struct phy_device *phydev)
+static int at803x_smarteee_config(struct phy_device *phydev, bool enable,
+ u32 tx_lpi_timer_us)
{
struct at803x_priv *priv = phydev->priv;
+ u64 tx_lpi_timer_raw;
+ u64 tx_lpi_timer_ns;
u16 mask = 0, val = 0;
int ret;
- if (priv->flags & AT803X_DISABLE_SMARTEEE)
+ if (priv->flags & AT803X_DISABLE_SMARTEEE || !enable)
return phy_modify_mmd(phydev, MDIO_MMD_PCS,
AT803X_MMD3_SMARTEEE_CTL3,
AT803X_MMD3_SMARTEEE_CTL3_LPI_EN, 0);
+ if (tx_lpi_timer_us > AT803X_MMD3_SMARTEEE_LPI_TIME_MAX_US) {
+ phydev_err(phydev, "Max LPI timer is %lu microsecs\n",
+ AT803X_MMD3_SMARTEEE_LPI_TIME_MAX_US);
+ return -EINVAL;
+ }
+
if (priv->smarteee_lpi_tw_1g) {
mask |= 0xff00;
val |= priv->smarteee_lpi_tw_1g << 8;
@@ -978,9 +997,27 @@ static int at803x_smarteee_config(struct phy_device *phydev)
if (ret)
return ret;
+ tx_lpi_timer_ns = tx_lpi_timer_us * NSEC_PER_USEC;
+ tx_lpi_timer_raw =
+ DIV_ROUND_CLOSEST_ULL(tx_lpi_timer_ns,
+ AT803X_MMD3_SMARTEEE_LPI_TIME_RESOL_NS);
+ val = FIELD_PREP(AT803X_MMD3_SMARTEEE_LPI_TIME_LOW,
+ FIELD_GET(AT803X_MMD3_SMARTEEE_LPI_TIME_15_0,
+ tx_lpi_timer_raw));
+
+ ret = phy_write_mmd(phydev, MDIO_MMD_PCS, AT803X_MMD3_SMARTEEE_CTL2,
+ val);
+ if (ret)
+ return ret;
+
+ val = AT803X_MMD3_SMARTEEE_CTL3_LPI_EN |
+ FIELD_PREP(AT803X_MMD3_SMARTEEE_LPI_TIME_HIGH,
+ FIELD_GET(AT803X_MMD3_SMARTEEE_LPI_TIME_23_16,
+ tx_lpi_timer_raw));
+
return phy_modify_mmd(phydev, MDIO_MMD_PCS, AT803X_MMD3_SMARTEEE_CTL3,
- AT803X_MMD3_SMARTEEE_CTL3_LPI_EN,
- AT803X_MMD3_SMARTEEE_CTL3_LPI_EN);
+ AT803X_MMD3_SMARTEEE_CTL3_LPI_EN |
+ AT803X_MMD3_SMARTEEE_LPI_TIME_HIGH, val);
}
static int at803x_clk_out_config(struct phy_device *phydev)
@@ -1067,7 +1104,8 @@ static int at803x_config_init(struct phy_device *phydev)
if (ret < 0)
return ret;
- ret = at803x_smarteee_config(phydev);
+ ret = at803x_smarteee_config(phydev, true,
+ AT803X_MMD3_SMARTEEE_LPI_TIME_DEF_US);
if (ret < 0)
return ret;
@@ -1612,6 +1650,65 @@ static int at803x_cable_test_start(struct phy_device *phydev)
return 0;
}
+static int at803x_get_eee(struct phy_device *phydev, struct ethtool_eee *data)
+{
+ struct at803x_priv *priv = phydev->priv;
+ u32 tx_timer_raw;
+ u64 tx_timer_ns;
+ int ret;
+
+ /* If SmartEEE is not enabled, it is expected that tx_lpi_* fields
+ * are processed by the MAC driver.
+ */
+ if (priv->flags & AT803X_DISABLE_SMARTEEE)
+ return genphy_c45_ethtool_get_eee(phydev, data);
+
+ ret = phy_read_mmd(phydev, MDIO_MMD_PCS,
+ AT803X_MMD3_SMARTEEE_CTL2);
+ tx_timer_raw = FIELD_PREP(AT803X_MMD3_SMARTEEE_LPI_TIME_15_0,
+ FIELD_GET(AT803X_MMD3_SMARTEEE_LPI_TIME_LOW,
+ ret));
+ if (ret < 0)
+ return ret;
+
+ ret = phy_read_mmd(phydev, MDIO_MMD_PCS,
+ AT803X_MMD3_SMARTEEE_CTL3);
+ if (ret < 0)
+ return ret;
+
+ tx_timer_raw |= FIELD_PREP(AT803X_MMD3_SMARTEEE_LPI_TIME_23_16,
+ FIELD_GET(AT803X_MMD3_SMARTEEE_LPI_TIME_HIGH,
+ ret));
+ tx_timer_ns = tx_timer_raw * AT803X_MMD3_SMARTEEE_LPI_TIME_RESOL_NS;
+ data->tx_lpi_timer = DIV_ROUND_CLOSEST_ULL(tx_timer_ns, NSEC_PER_USEC);
+
+ data->tx_lpi_enabled = !!(ret & AT803X_MMD3_SMARTEEE_CTL3_LPI_EN);
+
+ return genphy_c45_ethtool_get_eee(phydev, data);
+}
+
+static int at803x_set_eee(struct phy_device *phydev, struct ethtool_eee *data)
+{
+ struct at803x_priv *priv = phydev->priv;
+ int ret;
+
+ /* If SmartEEE is not enabled, it is expected that tx_lpi_* fields
+ * are processed by the MAC driver.
+ */
+ if (priv->flags & AT803X_DISABLE_SMARTEEE)
+ return genphy_c45_ethtool_set_eee(phydev, data);
+
+ /* Changing Tx LPI on/off or Tx LPI timer settings
+ * do not require link reset.
+ */
+ ret = at803x_smarteee_config(phydev, data->tx_lpi_enabled,
+ data->tx_lpi_timer);
+ if (ret)
+ return ret;
+
+ return genphy_c45_ethtool_set_eee(phydev, data);
+}
+
static int qca83xx_config_init(struct phy_device *phydev)
{
u8 switch_revision;
@@ -2038,6 +2135,8 @@ static struct phy_driver at803x_driver[] = {
.set_tunable = at803x_set_tunable,
.cable_test_start = at803x_cable_test_start,
.cable_test_get_status = at803x_cable_test_get_status,
+ .get_eee = at803x_get_eee,
+ .set_eee = at803x_set_eee,
}, {
/* Qualcomm Atheros AR8030 */
.phy_id = ATH8030_PHY_ID,
If AR8035 PHY is used with a MAC without EEE support (iMX6, etc), then we need to process ethtool_eee::tx_lpi_timer and tx_lpi_enabled by the PHY driver. So, add get/set_eee support for this functionality. Signed-off-by: Oleksij Rempel <o.rempel@pengutronix.de> --- drivers/net/phy/at803x.c | 109 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 104 insertions(+), 5 deletions(-)