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

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: Arun Ramadoss <arun.ramadoss@microchip.com>
- add Reviewed-by: Alexander 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: Andrew 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
...@@ -2673,6 +2673,70 @@ static int ksz_max_mtu(struct dsa_switch *ds, int port) ...@@ -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 = { ...@@ -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)
......
...@@ -1392,6 +1392,26 @@ static int ksz9131_config_aneg(struct phy_device *phydev) ...@@ -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[] = { ...@@ -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);
......
...@@ -262,7 +262,11 @@ int genphy_c45_an_config_aneg(struct phy_device *phydev) ...@@ -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) ...@@ -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) ...@@ -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, ...@@ -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,
......
...@@ -242,11 +242,11 @@ unsigned int phy_supported_speeds(struct phy_device *phy, ...@@ -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) ...@@ -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) ...@@ -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); ...@@ -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); ...@@ -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);
......
...@@ -132,6 +132,18 @@ static const int phy_10gbit_full_features_array[] = { ...@@ -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) ...@@ -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) ...@@ -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) ...@@ -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);
......
...@@ -402,6 +402,90 @@ static inline u32 linkmode_adv_to_mii_t1_adv_m_t(unsigned long *advertising) ...@@ -402,6 +402,90 @@ static inline u32 linkmode_adv_to_mii_t1_adv_m_t(unsigned long *advertising)
return result; return result;
} }
/**
* mii_eee_cap1_mod_linkmode_t()
* @adv: target the linkmode advertisement settings
* @val: register value
*
* A function that translates value of following registers to the linkmode:
* IEEE 802.3-2018 45.2.3.10 "EEE control and capability 1" register (3.20)
* IEEE 802.3-2018 45.2.7.13 "EEE advertisement 1" register (7.60)
* IEEE 802.3-2018 45.2.7.14 "EEE "link partner ability 1 register (7.61)
*/
static inline void mii_eee_cap1_mod_linkmode_t(unsigned long *adv, u32 val)
{
linkmode_mod_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT,
adv, val & MDIO_EEE_100TX);
linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
adv, val & MDIO_EEE_1000T);
linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT,
adv, val & MDIO_EEE_10GT);
linkmode_mod_bit(ETHTOOL_LINK_MODE_1000baseKX_Full_BIT,
adv, val & MDIO_EEE_1000KX);
linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT,
adv, val & MDIO_EEE_10GKX4);
linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseKR_Full_BIT,
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_read(struct mii_bus *bus, int addr, u32 regnum);
int __mdiobus_write(struct mii_bus *bus, int addr, u32 regnum, u16 val); 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, int __mdiobus_modify_changed(struct mii_bus *bus, int addr, u32 regnum,
......
...@@ -52,6 +52,7 @@ extern __ETHTOOL_DECLARE_LINK_MODE_MASK(phy_gbit_all_ports_features) __ro_after_ ...@@ -52,6 +52,7 @@ extern __ETHTOOL_DECLARE_LINK_MODE_MASK(phy_gbit_all_ports_features) __ro_after_
extern __ETHTOOL_DECLARE_LINK_MODE_MASK(phy_10gbit_features) __ro_after_init; extern __ETHTOOL_DECLARE_LINK_MODE_MASK(phy_10gbit_features) __ro_after_init;
extern __ETHTOOL_DECLARE_LINK_MODE_MASK(phy_10gbit_fec_features) __ro_after_init; extern __ETHTOOL_DECLARE_LINK_MODE_MASK(phy_10gbit_fec_features) __ro_after_init;
extern __ETHTOOL_DECLARE_LINK_MODE_MASK(phy_10gbit_full_features) __ro_after_init; extern __ETHTOOL_DECLARE_LINK_MODE_MASK(phy_10gbit_full_features) __ro_after_init;
extern __ETHTOOL_DECLARE_LINK_MODE_MASK(phy_eee_cap1_features) __ro_after_init;
#define PHY_BASIC_FEATURES ((unsigned long *)&phy_basic_features) #define PHY_BASIC_FEATURES ((unsigned long *)&phy_basic_features)
#define PHY_BASIC_T1_FEATURES ((unsigned long *)&phy_basic_t1_features) #define PHY_BASIC_T1_FEATURES ((unsigned long *)&phy_basic_t1_features)
...@@ -62,6 +63,7 @@ extern __ETHTOOL_DECLARE_LINK_MODE_MASK(phy_10gbit_full_features) __ro_after_ini ...@@ -62,6 +63,7 @@ extern __ETHTOOL_DECLARE_LINK_MODE_MASK(phy_10gbit_full_features) __ro_after_ini
#define PHY_10GBIT_FEATURES ((unsigned long *)&phy_10gbit_features) #define PHY_10GBIT_FEATURES ((unsigned long *)&phy_10gbit_features)
#define PHY_10GBIT_FEC_FEATURES ((unsigned long *)&phy_10gbit_fec_features) #define PHY_10GBIT_FEC_FEATURES ((unsigned long *)&phy_10gbit_fec_features)
#define PHY_10GBIT_FULL_FEATURES ((unsigned long *)&phy_10gbit_full_features) #define PHY_10GBIT_FULL_FEATURES ((unsigned long *)&phy_10gbit_full_features)
#define PHY_EEE_CAP1_FEATURES ((unsigned long *)&phy_eee_cap1_features)
extern const int phy_basic_ports_array[3]; extern const int phy_basic_ports_array[3];
extern const int phy_fibre_port_array[1]; extern const int phy_fibre_port_array[1];
...@@ -572,6 +574,7 @@ struct macsec_ops; ...@@ -572,6 +574,7 @@ struct macsec_ops;
* @supported: Combined MAC/PHY supported linkmodes * @supported: Combined MAC/PHY supported linkmodes
* @advertising: Currently advertised linkmodes * @advertising: Currently advertised linkmodes
* @adv_old: Saved advertised while power saving for WoL * @adv_old: Saved advertised while power saving for WoL
* @supported_eee: supported PHY EEE linkmodes
* @lp_advertising: Current link partner advertised linkmodes * @lp_advertising: Current link partner advertised linkmodes
* @host_interfaces: PHY interface modes supported by host * @host_interfaces: PHY interface modes supported by host
* @eee_broken_modes: Energy efficient ethernet modes which should be prohibited * @eee_broken_modes: Energy efficient ethernet modes which should be prohibited
...@@ -676,6 +679,8 @@ struct phy_device { ...@@ -676,6 +679,8 @@ struct phy_device {
__ETHTOOL_DECLARE_LINK_MODE_MASK(lp_advertising); __ETHTOOL_DECLARE_LINK_MODE_MASK(lp_advertising);
/* used with phy_speed_down */ /* used with phy_speed_down */
__ETHTOOL_DECLARE_LINK_MODE_MASK(adv_old); __ETHTOOL_DECLARE_LINK_MODE_MASK(adv_old);
/* used for eee validation */
__ETHTOOL_DECLARE_LINK_MODE_MASK(supported_eee);
/* Host supported PHY interface types. Should be ignored if empty. */ /* Host supported PHY interface types. Should be ignored if empty. */
DECLARE_PHY_INTERFACE_MASK(host_interfaces); DECLARE_PHY_INTERFACE_MASK(host_interfaces);
...@@ -1614,6 +1619,7 @@ int phy_start_aneg(struct phy_device *phydev); ...@@ -1614,6 +1619,7 @@ int phy_start_aneg(struct phy_device *phydev);
int phy_aneg_done(struct phy_device *phydev); int phy_aneg_done(struct phy_device *phydev);
int phy_speed_down(struct phy_device *phydev, bool sync); int phy_speed_down(struct phy_device *phydev, bool sync);
int phy_speed_up(struct phy_device *phydev); int phy_speed_up(struct phy_device *phydev);
bool phy_check_valid(int speed, int duplex, unsigned long *features);
int phy_restart_aneg(struct phy_device *phydev); int phy_restart_aneg(struct phy_device *phydev);
int phy_reset_after_clk_enable(struct phy_device *phydev); int phy_reset_after_clk_enable(struct phy_device *phydev);
...@@ -1737,6 +1743,7 @@ int genphy_c45_an_config_aneg(struct phy_device *phydev); ...@@ -1737,6 +1743,7 @@ int genphy_c45_an_config_aneg(struct phy_device *phydev);
int genphy_c45_an_disable_aneg(struct phy_device *phydev); int genphy_c45_an_disable_aneg(struct phy_device *phydev);
int genphy_c45_read_mdix(struct phy_device *phydev); int genphy_c45_read_mdix(struct phy_device *phydev);
int genphy_c45_pma_read_abilities(struct phy_device *phydev); int genphy_c45_pma_read_abilities(struct phy_device *phydev);
int genphy_c45_read_eee_abilities(struct phy_device *phydev);
int genphy_c45_pma_baset1_read_master_slave(struct phy_device *phydev); int genphy_c45_pma_baset1_read_master_slave(struct phy_device *phydev);
int genphy_c45_read_status(struct phy_device *phydev); int genphy_c45_read_status(struct phy_device *phydev);
int genphy_c45_baset1_read_status(struct phy_device *phydev); int genphy_c45_baset1_read_status(struct phy_device *phydev);
...@@ -1751,6 +1758,13 @@ int genphy_c45_plca_set_cfg(struct phy_device *phydev, ...@@ -1751,6 +1758,13 @@ int genphy_c45_plca_set_cfg(struct phy_device *phydev,
const struct phy_plca_cfg *plca_cfg); const struct phy_plca_cfg *plca_cfg);
int genphy_c45_plca_get_status(struct phy_device *phydev, int genphy_c45_plca_get_status(struct phy_device *phydev,
struct phy_plca_status *plca_st); 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 */ /* Generic C45 PHY driver */
extern struct phy_driver genphy_c45_driver; extern struct phy_driver genphy_c45_driver;
......
...@@ -79,6 +79,8 @@ ...@@ -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_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_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_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 */ #define MDIO_PMA_PMD_BT1_CTRL 2100 /* BASE-T1 PMA/PMD control register */
/* LASI (Link Alarm Status Interrupt) registers, defined by XENPAK MSA. */ /* LASI (Link Alarm Status Interrupt) registers, defined by XENPAK MSA. */
...@@ -340,6 +342,12 @@ ...@@ -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_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 */ #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 */ /* BASE-T1 PMA/PMD control register */
#define MDIO_PMA_PMD_BT1_CTRL_CFG_MST 0x4000 /* MASTER-SLAVE config value */ #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