diff options
-rw-r--r-- | drivers/net/phy/adin.c | 61 |
1 files changed, 61 insertions, 0 deletions
diff --git a/drivers/net/phy/adin.c b/drivers/net/phy/adin.c index 4dec83df048d..cf5a391c93e6 100644 --- a/drivers/net/phy/adin.c +++ b/drivers/net/phy/adin.c @@ -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)); |