Commit 022c3f87 authored by Oleksij Rempel's avatar Oleksij Rempel Committed by David S. Miller

net: phy: add genphy_c45_ethtool_get/set_eee() support

Add replacement for phy_ethtool_get/set_eee() functions.

Current phy_ethtool_get/set_eee() implementation is great and it is
possible to make it even better:
- this functionality is for devices implementing parts of IEEE 802.3
  specification beyond Clause 22. The better place for this code is
  phy-c45.c
- currently it is able to do read/write operations on PHYs with
  different abilities to not existing registers. It is better to
  use stored supported_eee abilities to avoid false read/write
  operations.
- the eee_active detection will provide wrong results on not supported
  link modes. It is better to validate speed/duplex properties against
  supported EEE link modes.
- it is able to support only limited amount of link modes. We have more
  EEE link modes...

By refactoring this code I address most of this point except of the last
one. Adding additional EEE link modes will need more work.
Signed-off-by: default avatarOleksij Rempel <o.rempel@pengutronix.de>
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parent cf9f6079
......@@ -661,6 +661,129 @@ int genphy_c45_read_mdix(struct phy_device *phydev)
}
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
......@@ -1194,6 +1317,121 @@ int genphy_c45_plca_get_status(struct phy_device *phydev,
}
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 = {
.phy_id = 0xffffffff,
.phy_id_mask = 0xffffffff,
......
......@@ -428,6 +428,64 @@ static inline void mii_eee_cap1_mod_linkmode_t(unsigned long *adv, u32 val)
adv, val & MDIO_EEE_10GKR);
}
/**
* linkmode_to_mii_eee_cap1_t()
* @adv: the linkmode advertisement settings
*
* A function that translates linkmode to value for IEEE 802.3-2018 45.2.7.13
* "EEE advertisement 1" register (7.60)
*/
static inline u32 linkmode_to_mii_eee_cap1_t(unsigned long *adv)
{
u32 result = 0;
if (linkmode_test_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, adv))
result |= MDIO_EEE_100TX;
if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, adv))
result |= MDIO_EEE_1000T;
if (linkmode_test_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT, adv))
result |= MDIO_EEE_10GT;
if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseKX_Full_BIT, adv))
result |= MDIO_EEE_1000KX;
if (linkmode_test_bit(ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT, adv))
result |= MDIO_EEE_10GKX4;
if (linkmode_test_bit(ETHTOOL_LINK_MODE_10000baseKR_Full_BIT, adv))
result |= MDIO_EEE_10GKR;
return result;
}
/**
* mii_10base_t1_adv_mod_linkmode_t()
* @adv: linkmode advertisement settings
* @val: register value
*
* A function that translates IEEE 802.3cg-2019 45.2.7.26 "10BASE-T1 AN status"
* register (7.527) value to the linkmode.
*/
static inline void mii_10base_t1_adv_mod_linkmode_t(unsigned long *adv, u16 val)
{
linkmode_mod_bit(ETHTOOL_LINK_MODE_10baseT1L_Full_BIT,
adv, val & MDIO_AN_10BT1_AN_CTRL_ADV_EEE_T1L);
}
/**
* linkmode_adv_to_mii_10base_t1_t()
* @adv: linkmode advertisement settings
*
* A function that translates the linkmode to IEEE 802.3cg-2019 45.2.7.25
* "10BASE-T1 AN control" register (7.526) value.
*/
static inline u32 linkmode_adv_to_mii_10base_t1_t(unsigned long *adv)
{
u32 result = 0;
if (linkmode_test_bit(ETHTOOL_LINK_MODE_10baseT1L_Full_BIT, adv))
result |= MDIO_AN_10BT1_AN_CTRL_ADV_EEE_T1L;
return result;
}
int __mdiobus_read(struct mii_bus *bus, int addr, u32 regnum);
int __mdiobus_write(struct mii_bus *bus, int addr, u32 regnum, u16 val);
int __mdiobus_modify_changed(struct mii_bus *bus, int addr, u32 regnum,
......
......@@ -1758,6 +1758,13 @@ int genphy_c45_plca_set_cfg(struct phy_device *phydev,
const struct phy_plca_cfg *plca_cfg);
int genphy_c45_plca_get_status(struct phy_device *phydev,
struct phy_plca_status *plca_st);
int genphy_c45_eee_is_active(struct phy_device *phydev, unsigned long *adv,
unsigned long *lp, bool *is_enabled);
int genphy_c45_ethtool_get_eee(struct phy_device *phydev,
struct ethtool_eee *data);
int genphy_c45_ethtool_set_eee(struct phy_device *phydev,
struct ethtool_eee *data);
int genphy_c45_write_eee_adv(struct phy_device *phydev, unsigned long *adv);
/* Generic C45 PHY driver */
extern struct phy_driver genphy_c45_driver;
......
......@@ -79,6 +79,8 @@
#define MDIO_AN_T1_LP_L 517 /* BASE-T1 AN LP Base Page ability register [15:0] */
#define MDIO_AN_T1_LP_M 518 /* BASE-T1 AN LP Base Page ability register [31:16] */
#define MDIO_AN_T1_LP_H 519 /* BASE-T1 AN LP Base Page ability register [47:32] */
#define MDIO_AN_10BT1_AN_CTRL 526 /* 10BASE-T1 AN control register */
#define MDIO_AN_10BT1_AN_STAT 527 /* 10BASE-T1 AN status register */
#define MDIO_PMA_PMD_BT1_CTRL 2100 /* BASE-T1 PMA/PMD control register */
/* LASI (Link Alarm Status Interrupt) registers, defined by XENPAK MSA. */
......@@ -340,6 +342,12 @@
#define MDIO_AN_T1_LP_H_10L_TX_HI_REQ 0x1000 /* 10BASE-T1L High Level LP Transmit Request */
#define MDIO_AN_T1_LP_H_10L_TX_HI 0x2000 /* 10BASE-T1L High Level LP Transmit Ability */
/* 10BASE-T1 AN control register */
#define MDIO_AN_10BT1_AN_CTRL_ADV_EEE_T1L 0x4000 /* 10BASE-T1L EEE ability advertisement */
/* 10BASE-T1 AN status register */
#define MDIO_AN_10BT1_AN_STAT_LPA_EEE_T1L 0x4000 /* 10BASE-T1L LP EEE ability advertisement */
/* BASE-T1 PMA/PMD control register */
#define MDIO_PMA_PMD_BT1_CTRL_CFG_MST 0x4000 /* MASTER-SLAVE config value */
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment