Skip to content

Commit 022c3f8

Browse files
oleremdavem330
authored andcommitted
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: Oleksij Rempel <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent cf9f607 commit 022c3f8

File tree

4 files changed

+311
-0
lines changed

4 files changed

+311
-0
lines changed

drivers/net/phy/phy-c45.c

Lines changed: 238 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -661,6 +661,129 @@ int genphy_c45_read_mdix(struct phy_device *phydev)
661661
}
662662
EXPORT_SYMBOL_GPL(genphy_c45_read_mdix);
663663

664+
/**
665+
* genphy_c45_write_eee_adv - write advertised EEE link modes
666+
* @phydev: target phy_device struct
667+
* @adv: the linkmode advertisement settings
668+
*/
669+
int genphy_c45_write_eee_adv(struct phy_device *phydev, unsigned long *adv)
670+
{
671+
int val, changed;
672+
673+
if (linkmode_intersects(phydev->supported, PHY_EEE_CAP1_FEATURES)) {
674+
val = linkmode_to_mii_eee_cap1_t(adv);
675+
676+
/* In eee_broken_modes are stored MDIO_AN_EEE_ADV specific raw
677+
* register values.
678+
*/
679+
val &= ~phydev->eee_broken_modes;
680+
681+
/* IEEE 802.3-2018 45.2.7.13 EEE advertisement 1
682+
* (Register 7.60)
683+
*/
684+
val = phy_modify_mmd_changed(phydev, MDIO_MMD_AN,
685+
MDIO_AN_EEE_ADV,
686+
MDIO_EEE_100TX | MDIO_EEE_1000T |
687+
MDIO_EEE_10GT | MDIO_EEE_1000KX |
688+
MDIO_EEE_10GKX4 | MDIO_EEE_10GKR,
689+
val);
690+
if (val < 0)
691+
return val;
692+
if (val > 0)
693+
changed = 1;
694+
}
695+
696+
if (linkmode_test_bit(ETHTOOL_LINK_MODE_10baseT1L_Full_BIT,
697+
phydev->supported_eee)) {
698+
val = linkmode_adv_to_mii_10base_t1_t(adv);
699+
/* IEEE 802.3cg-2019 45.2.7.25 10BASE-T1 AN control register
700+
* (Register 7.526)
701+
*/
702+
val = phy_modify_mmd_changed(phydev, MDIO_MMD_AN,
703+
MDIO_AN_10BT1_AN_CTRL,
704+
MDIO_AN_10BT1_AN_CTRL_ADV_EEE_T1L,
705+
val);
706+
if (val < 0)
707+
return val;
708+
if (val > 0)
709+
changed = 1;
710+
}
711+
712+
return changed;
713+
}
714+
715+
/**
716+
* genphy_c45_read_eee_adv - read advertised EEE link modes
717+
* @phydev: target phy_device struct
718+
* @adv: the linkmode advertisement status
719+
*/
720+
static int genphy_c45_read_eee_adv(struct phy_device *phydev,
721+
unsigned long *adv)
722+
{
723+
int val;
724+
725+
if (linkmode_intersects(phydev->supported, PHY_EEE_CAP1_FEATURES)) {
726+
/* IEEE 802.3-2018 45.2.7.13 EEE advertisement 1
727+
* (Register 7.60)
728+
*/
729+
val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_ADV);
730+
if (val < 0)
731+
return val;
732+
733+
mii_eee_cap1_mod_linkmode_t(adv, val);
734+
}
735+
736+
if (linkmode_test_bit(ETHTOOL_LINK_MODE_10baseT1L_Full_BIT,
737+
phydev->supported_eee)) {
738+
/* IEEE 802.3cg-2019 45.2.7.25 10BASE-T1 AN control register
739+
* (Register 7.526)
740+
*/
741+
val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_10BT1_AN_CTRL);
742+
if (val < 0)
743+
return val;
744+
745+
mii_10base_t1_adv_mod_linkmode_t(adv, val);
746+
}
747+
748+
return 0;
749+
}
750+
751+
/**
752+
* genphy_c45_read_eee_lpa - read advertised LP EEE link modes
753+
* @phydev: target phy_device struct
754+
* @lpa: the linkmode LP advertisement status
755+
*/
756+
static int genphy_c45_read_eee_lpa(struct phy_device *phydev,
757+
unsigned long *lpa)
758+
{
759+
int val;
760+
761+
if (linkmode_intersects(phydev->supported, PHY_EEE_CAP1_FEATURES)) {
762+
/* IEEE 802.3-2018 45.2.7.14 EEE link partner ability 1
763+
* (Register 7.61)
764+
*/
765+
val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_EEE_LPABLE);
766+
if (val < 0)
767+
return val;
768+
769+
mii_eee_cap1_mod_linkmode_t(lpa, val);
770+
}
771+
772+
if (linkmode_test_bit(ETHTOOL_LINK_MODE_10baseT1L_Full_BIT,
773+
phydev->supported_eee)) {
774+
/* IEEE 802.3cg-2019 45.2.7.26 10BASE-T1 AN status register
775+
* (Register 7.527)
776+
*/
777+
val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_10BT1_AN_STAT);
778+
if (val < 0)
779+
return val;
780+
781+
mii_10base_t1_adv_mod_linkmode_t(lpa, val);
782+
}
783+
784+
return 0;
785+
}
786+
664787
/**
665788
* genphy_c45_read_eee_cap1 - read supported EEE link modes from register 3.20
666789
* @phydev: target phy_device struct
@@ -1194,6 +1317,121 @@ int genphy_c45_plca_get_status(struct phy_device *phydev,
11941317
}
11951318
EXPORT_SYMBOL_GPL(genphy_c45_plca_get_status);
11961319

1320+
/**
1321+
* genphy_c45_eee_is_active - get EEE status
1322+
* @phydev: target phy_device struct
1323+
* @adv: variable to store advertised linkmodes
1324+
* @lp: variable to store LP advertised linkmodes
1325+
* @is_enabled: variable to store EEE enabled/disabled configuration value
1326+
*
1327+
* Description: this function will read local and link partner PHY
1328+
* advertisements. Compare them return current EEE state.
1329+
*/
1330+
int genphy_c45_eee_is_active(struct phy_device *phydev, unsigned long *adv,
1331+
unsigned long *lp, bool *is_enabled)
1332+
{
1333+
__ETHTOOL_DECLARE_LINK_MODE_MASK(tmp_adv) = {};
1334+
__ETHTOOL_DECLARE_LINK_MODE_MASK(tmp_lp) = {};
1335+
__ETHTOOL_DECLARE_LINK_MODE_MASK(common);
1336+
bool eee_enabled, eee_active;
1337+
int ret;
1338+
1339+
ret = genphy_c45_read_eee_adv(phydev, tmp_adv);
1340+
if (ret)
1341+
return ret;
1342+
1343+
ret = genphy_c45_read_eee_lpa(phydev, tmp_lp);
1344+
if (ret)
1345+
return ret;
1346+
1347+
eee_enabled = !linkmode_empty(tmp_adv);
1348+
linkmode_and(common, tmp_adv, tmp_lp);
1349+
if (eee_enabled && !linkmode_empty(common))
1350+
eee_active = phy_check_valid(phydev->speed, phydev->duplex,
1351+
common);
1352+
else
1353+
eee_active = false;
1354+
1355+
if (adv)
1356+
linkmode_copy(adv, tmp_adv);
1357+
if (lp)
1358+
linkmode_copy(lp, tmp_lp);
1359+
if (is_enabled)
1360+
*is_enabled = eee_enabled;
1361+
1362+
return eee_active;
1363+
}
1364+
EXPORT_SYMBOL(genphy_c45_eee_is_active);
1365+
1366+
/**
1367+
* genphy_c45_ethtool_get_eee - get EEE supported and status
1368+
* @phydev: target phy_device struct
1369+
* @data: ethtool_eee data
1370+
*
1371+
* Description: it reports the Supported/Advertisement/LP Advertisement
1372+
* capabilities.
1373+
*/
1374+
int genphy_c45_ethtool_get_eee(struct phy_device *phydev,
1375+
struct ethtool_eee *data)
1376+
{
1377+
__ETHTOOL_DECLARE_LINK_MODE_MASK(adv) = {};
1378+
__ETHTOOL_DECLARE_LINK_MODE_MASK(lp) = {};
1379+
bool overflow = false, is_enabled;
1380+
int ret;
1381+
1382+
ret = genphy_c45_eee_is_active(phydev, adv, lp, &is_enabled);
1383+
if (ret < 0)
1384+
return ret;
1385+
1386+
data->eee_enabled = is_enabled;
1387+
data->eee_active = ret;
1388+
1389+
if (!ethtool_convert_link_mode_to_legacy_u32(&data->supported,
1390+
phydev->supported_eee))
1391+
overflow = true;
1392+
if (!ethtool_convert_link_mode_to_legacy_u32(&data->advertised, adv))
1393+
overflow = true;
1394+
if (!ethtool_convert_link_mode_to_legacy_u32(&data->lp_advertised, lp))
1395+
overflow = true;
1396+
1397+
if (overflow)
1398+
phydev_warn(phydev, "Not all supported or advertised EEE link modes were passed to the user space\n");
1399+
1400+
return 0;
1401+
}
1402+
EXPORT_SYMBOL(genphy_c45_ethtool_get_eee);
1403+
1404+
/**
1405+
* genphy_c45_ethtool_set_eee - get EEE supported and status
1406+
* @phydev: target phy_device struct
1407+
* @data: ethtool_eee data
1408+
*
1409+
* Description: it reportes the Supported/Advertisement/LP Advertisement
1410+
* capabilities.
1411+
*/
1412+
int genphy_c45_ethtool_set_eee(struct phy_device *phydev,
1413+
struct ethtool_eee *data)
1414+
{
1415+
__ETHTOOL_DECLARE_LINK_MODE_MASK(adv) = {};
1416+
int ret;
1417+
1418+
if (data->eee_enabled) {
1419+
if (data->advertised)
1420+
adv[0] = data->advertised;
1421+
else
1422+
linkmode_copy(adv, phydev->supported_eee);
1423+
}
1424+
1425+
ret = genphy_c45_write_eee_adv(phydev, adv);
1426+
if (ret < 0)
1427+
return ret;
1428+
if (ret > 0)
1429+
return phy_restart_aneg(phydev);
1430+
1431+
return 0;
1432+
}
1433+
EXPORT_SYMBOL(genphy_c45_ethtool_set_eee);
1434+
11971435
struct phy_driver genphy_c45_driver = {
11981436
.phy_id = 0xffffffff,
11991437
.phy_id_mask = 0xffffffff,

include/linux/mdio.h

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -428,6 +428,64 @@ static inline void mii_eee_cap1_mod_linkmode_t(unsigned long *adv, u32 val)
428428
adv, val & MDIO_EEE_10GKR);
429429
}
430430

431+
/**
432+
* linkmode_to_mii_eee_cap1_t()
433+
* @adv: the linkmode advertisement settings
434+
*
435+
* A function that translates linkmode to value for IEEE 802.3-2018 45.2.7.13
436+
* "EEE advertisement 1" register (7.60)
437+
*/
438+
static inline u32 linkmode_to_mii_eee_cap1_t(unsigned long *adv)
439+
{
440+
u32 result = 0;
441+
442+
if (linkmode_test_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, adv))
443+
result |= MDIO_EEE_100TX;
444+
if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, adv))
445+
result |= MDIO_EEE_1000T;
446+
if (linkmode_test_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT, adv))
447+
result |= MDIO_EEE_10GT;
448+
if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseKX_Full_BIT, adv))
449+
result |= MDIO_EEE_1000KX;
450+
if (linkmode_test_bit(ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT, adv))
451+
result |= MDIO_EEE_10GKX4;
452+
if (linkmode_test_bit(ETHTOOL_LINK_MODE_10000baseKR_Full_BIT, adv))
453+
result |= MDIO_EEE_10GKR;
454+
455+
return result;
456+
}
457+
458+
/**
459+
* mii_10base_t1_adv_mod_linkmode_t()
460+
* @adv: linkmode advertisement settings
461+
* @val: register value
462+
*
463+
* A function that translates IEEE 802.3cg-2019 45.2.7.26 "10BASE-T1 AN status"
464+
* register (7.527) value to the linkmode.
465+
*/
466+
static inline void mii_10base_t1_adv_mod_linkmode_t(unsigned long *adv, u16 val)
467+
{
468+
linkmode_mod_bit(ETHTOOL_LINK_MODE_10baseT1L_Full_BIT,
469+
adv, val & MDIO_AN_10BT1_AN_CTRL_ADV_EEE_T1L);
470+
}
471+
472+
/**
473+
* linkmode_adv_to_mii_10base_t1_t()
474+
* @adv: linkmode advertisement settings
475+
*
476+
* A function that translates the linkmode to IEEE 802.3cg-2019 45.2.7.25
477+
* "10BASE-T1 AN control" register (7.526) value.
478+
*/
479+
static inline u32 linkmode_adv_to_mii_10base_t1_t(unsigned long *adv)
480+
{
481+
u32 result = 0;
482+
483+
if (linkmode_test_bit(ETHTOOL_LINK_MODE_10baseT1L_Full_BIT, adv))
484+
result |= MDIO_AN_10BT1_AN_CTRL_ADV_EEE_T1L;
485+
486+
return result;
487+
}
488+
431489
int __mdiobus_read(struct mii_bus *bus, int addr, u32 regnum);
432490
int __mdiobus_write(struct mii_bus *bus, int addr, u32 regnum, u16 val);
433491
int __mdiobus_modify_changed(struct mii_bus *bus, int addr, u32 regnum,

include/linux/phy.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1758,6 +1758,13 @@ int genphy_c45_plca_set_cfg(struct phy_device *phydev,
17581758
const struct phy_plca_cfg *plca_cfg);
17591759
int genphy_c45_plca_get_status(struct phy_device *phydev,
17601760
struct phy_plca_status *plca_st);
1761+
int genphy_c45_eee_is_active(struct phy_device *phydev, unsigned long *adv,
1762+
unsigned long *lp, bool *is_enabled);
1763+
int genphy_c45_ethtool_get_eee(struct phy_device *phydev,
1764+
struct ethtool_eee *data);
1765+
int genphy_c45_ethtool_set_eee(struct phy_device *phydev,
1766+
struct ethtool_eee *data);
1767+
int genphy_c45_write_eee_adv(struct phy_device *phydev, unsigned long *adv);
17611768

17621769
/* Generic C45 PHY driver */
17631770
extern struct phy_driver genphy_c45_driver;

include/uapi/linux/mdio.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@
7979
#define MDIO_AN_T1_LP_L 517 /* BASE-T1 AN LP Base Page ability register [15:0] */
8080
#define MDIO_AN_T1_LP_M 518 /* BASE-T1 AN LP Base Page ability register [31:16] */
8181
#define MDIO_AN_T1_LP_H 519 /* BASE-T1 AN LP Base Page ability register [47:32] */
82+
#define MDIO_AN_10BT1_AN_CTRL 526 /* 10BASE-T1 AN control register */
83+
#define MDIO_AN_10BT1_AN_STAT 527 /* 10BASE-T1 AN status register */
8284
#define MDIO_PMA_PMD_BT1_CTRL 2100 /* BASE-T1 PMA/PMD control register */
8385

8486
/* LASI (Link Alarm Status Interrupt) registers, defined by XENPAK MSA. */
@@ -340,6 +342,12 @@
340342
#define MDIO_AN_T1_LP_H_10L_TX_HI_REQ 0x1000 /* 10BASE-T1L High Level LP Transmit Request */
341343
#define MDIO_AN_T1_LP_H_10L_TX_HI 0x2000 /* 10BASE-T1L High Level LP Transmit Ability */
342344

345+
/* 10BASE-T1 AN control register */
346+
#define MDIO_AN_10BT1_AN_CTRL_ADV_EEE_T1L 0x4000 /* 10BASE-T1L EEE ability advertisement */
347+
348+
/* 10BASE-T1 AN status register */
349+
#define MDIO_AN_10BT1_AN_STAT_LPA_EEE_T1L 0x4000 /* 10BASE-T1L LP EEE ability advertisement */
350+
343351
/* BASE-T1 PMA/PMD control register */
344352
#define MDIO_PMA_PMD_BT1_CTRL_CFG_MST 0x4000 /* MASTER-SLAVE config value */
345353

0 commit comments

Comments
 (0)