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: David S. Miller <davem@davemloft.net>
This commit is contained in:
commit
9b0bf4f771
@ -2673,6 +2673,70 @@ static int ksz_max_mtu(struct dsa_switch *ds, int port)
|
||||
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,
|
||||
phy_interface_t interface)
|
||||
{
|
||||
@ -3130,6 +3194,8 @@ static const struct dsa_switch_ops ksz_switch_ops = {
|
||||
.port_txtstamp = ksz_port_txtstamp,
|
||||
.port_rxtstamp = ksz_port_rxtstamp,
|
||||
.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)
|
||||
|
@ -1392,6 +1392,26 @@ static int ksz9131_config_aneg(struct phy_device *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_DUPLEX BIT(6)
|
||||
#define KSZ8873MLL_GLOBAL_CONTROL_4_SPEED BIT(4)
|
||||
@ -4172,6 +4192,7 @@ static struct phy_driver ksphy_driver[] = {
|
||||
.handle_interrupt = kszphy_handle_interrupt,
|
||||
.suspend = genphy_suspend,
|
||||
.resume = genphy_resume,
|
||||
.get_features = ksz9477_get_features,
|
||||
} };
|
||||
|
||||
module_phy_driver(ksphy_driver);
|
||||
|
@ -262,7 +262,11 @@ int genphy_c45_an_config_aneg(struct phy_device *phydev)
|
||||
linkmode_and(phydev->advertising, phydev->advertising,
|
||||
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))
|
||||
return genphy_c45_baset1_an_config_aneg(phydev);
|
||||
@ -661,6 +665,199 @@ 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
|
||||
*/
|
||||
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
|
||||
* @phydev: target phy_device struct
|
||||
@ -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;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(genphy_c45_pma_read_abilities);
|
||||
@ -1124,6 +1326,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,
|
||||
|
@ -242,11 +242,11 @@ unsigned int phy_supported_speeds(struct phy_device *phy,
|
||||
*
|
||||
* Description: Returns true if there is a valid setting, false otherwise.
|
||||
*/
|
||||
static inline bool phy_check_valid(int speed, int duplex,
|
||||
unsigned long *features)
|
||||
bool phy_check_valid(int speed, int duplex, unsigned long *features)
|
||||
{
|
||||
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
|
||||
@ -1457,30 +1457,6 @@ void phy_mac_interrupt(struct phy_device *phydev)
|
||||
}
|
||||
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
|
||||
* @phydev: target phy_device struct
|
||||
@ -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 ret;
|
||||
|
||||
if (!phydev->drv)
|
||||
return -EIO;
|
||||
|
||||
/* According to 802.3az,the EEE is supported only in full duplex-mode.
|
||||
*/
|
||||
if (phydev->duplex == DUPLEX_FULL) {
|
||||
__ETHTOOL_DECLARE_LINK_MODE_MASK(common);
|
||||
__ETHTOOL_DECLARE_LINK_MODE_MASK(lp);
|
||||
__ETHTOOL_DECLARE_LINK_MODE_MASK(adv);
|
||||
int eee_lp, eee_cap, eee_adv;
|
||||
int status;
|
||||
u32 cap;
|
||||
ret = genphy_c45_eee_is_active(phydev, NULL, NULL, NULL);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (!ret)
|
||||
return -EPROTONOSUPPORT;
|
||||
|
||||
/* 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.
|
||||
if (clk_stop_enable)
|
||||
/* Configure the PHY to stop receiving xMII
|
||||
* clock while it is signaling LPI.
|
||||
*/
|
||||
eee_lp = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_LPABLE);
|
||||
if (eee_lp <= 0)
|
||||
goto eee_exit_err;
|
||||
ret = phy_set_bits_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1,
|
||||
MDIO_PCS_CTRL1_CLKSTOP_EN);
|
||||
|
||||
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)
|
||||
/* Configure the PHY to stop receiving xMII
|
||||
* clock while it is signaling LPI.
|
||||
*/
|
||||
phy_set_bits_mmd(phydev, MDIO_MMD_PCS, MDIO_CTRL1,
|
||||
MDIO_PCS_CTRL1_CLKSTOP_EN);
|
||||
|
||||
return 0; /* EEE supported */
|
||||
}
|
||||
eee_exit_err:
|
||||
return -EPROTONOSUPPORT;
|
||||
return ret < 0 ? ret : 0;
|
||||
}
|
||||
EXPORT_SYMBOL(phy_init_eee);
|
||||
|
||||
@ -1578,33 +1517,10 @@ EXPORT_SYMBOL(phy_get_eee_err);
|
||||
*/
|
||||
int phy_ethtool_get_eee(struct phy_device *phydev, struct ethtool_eee *data)
|
||||
{
|
||||
int val;
|
||||
|
||||
if (!phydev->drv)
|
||||
return -EIO;
|
||||
|
||||
/* Get Supported EEE */
|
||||
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;
|
||||
return genphy_c45_ethtool_get_eee(phydev, data);
|
||||
}
|
||||
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 cap, old_adv, adv = 0, ret;
|
||||
|
||||
if (!phydev->drv)
|
||||
return -EIO;
|
||||
|
||||
/* Get Supported EEE */
|
||||
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;
|
||||
return genphy_c45_ethtool_set_eee(phydev, data);
|
||||
}
|
||||
EXPORT_SYMBOL(phy_ethtool_set_eee);
|
||||
|
||||
|
@ -132,6 +132,18 @@ static const int phy_10gbit_full_features_array[] = {
|
||||
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)
|
||||
{
|
||||
/* 10/100 half/full*/
|
||||
@ -213,6 +225,10 @@ static void features_init(void)
|
||||
linkmode_set_bit_array(phy_10gbit_fec_features_array,
|
||||
ARRAY_SIZE(phy_10gbit_fec_features_array),
|
||||
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)
|
||||
@ -2215,7 +2231,10 @@ int __genphy_config_aneg(struct phy_device *phydev, bool changed)
|
||||
{
|
||||
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;
|
||||
|
||||
err = genphy_setup_master_slave(phydev);
|
||||
@ -2637,6 +2656,11 @@ int genphy_read_abilities(struct phy_device *phydev)
|
||||
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;
|
||||
}
|
||||
EXPORT_SYMBOL(genphy_read_abilities);
|
||||
|
@ -402,6 +402,90 @@ static inline u32 linkmode_adv_to_mii_t1_adv_m_t(unsigned long *advertising)
|
||||
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_write(struct mii_bus *bus, int addr, u32 regnum, u16 val);
|
||||
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_
|
||||
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_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_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
|
||||
#define PHY_10GBIT_FEATURES ((unsigned long *)&phy_10gbit_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_EEE_CAP1_FEATURES ((unsigned long *)&phy_eee_cap1_features)
|
||||
|
||||
extern const int phy_basic_ports_array[3];
|
||||
extern const int phy_fibre_port_array[1];
|
||||
@ -572,6 +574,7 @@ struct macsec_ops;
|
||||
* @supported: Combined MAC/PHY supported linkmodes
|
||||
* @advertising: Currently advertised linkmodes
|
||||
* @adv_old: Saved advertised while power saving for WoL
|
||||
* @supported_eee: supported PHY EEE linkmodes
|
||||
* @lp_advertising: Current link partner advertised linkmodes
|
||||
* @host_interfaces: PHY interface modes supported by host
|
||||
* @eee_broken_modes: Energy efficient ethernet modes which should be prohibited
|
||||
@ -676,6 +679,8 @@ struct phy_device {
|
||||
__ETHTOOL_DECLARE_LINK_MODE_MASK(lp_advertising);
|
||||
/* used with phy_speed_down */
|
||||
__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. */
|
||||
DECLARE_PHY_INTERFACE_MASK(host_interfaces);
|
||||
@ -1614,6 +1619,7 @@ int phy_start_aneg(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_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_reset_after_clk_enable(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_read_mdix(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_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,
|
||||
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 */
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user