Merge branch 'realtek-phy-next'

Heiner Kallweit says:

====================
net: phy: realtek: add support for integrated 2.5Gbps PHY in RTL8125

This series adds support for the integrated 2.5Gbps PHY in RTL8125.
First three patches add necessary functionality to phylib.

Changes in v2:
- added patch 1
- changed patch 4 to use a fake PHY ID that is injected by the
  network driver. This allows to use a dedicated PHY driver.
====================

Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
David S. Miller 2019-08-11 21:24:32 -07:00
commit d35bbe84c1
4 changed files with 114 additions and 38 deletions

View File

@ -782,6 +782,29 @@ int phy_write_paged(struct phy_device *phydev, int page, u32 regnum, u16 val)
}
EXPORT_SYMBOL(phy_write_paged);
/**
* phy_modify_paged_changed() - Function for modifying a paged register
* @phydev: a pointer to a &struct phy_device
* @page: the page for the phy
* @regnum: register number
* @mask: bit mask of bits to clear
* @set: bit mask of bits to set
*
* Returns negative errno, 0 if there was no change, and 1 in case of change
*/
int phy_modify_paged_changed(struct phy_device *phydev, int page, u32 regnum,
u16 mask, u16 set)
{
int ret = 0, oldpage;
oldpage = phy_select_page(phydev, page);
if (oldpage >= 0)
ret = __phy_modify_changed(phydev, regnum, mask, set);
return phy_restore_page(phydev, oldpage, ret);
}
EXPORT_SYMBOL(phy_modify_paged_changed);
/**
* phy_modify_paged() - Convenience function for modifying a paged register
* @phydev: a pointer to a &struct phy_device
@ -795,12 +818,8 @@ EXPORT_SYMBOL(phy_write_paged);
int phy_modify_paged(struct phy_device *phydev, int page, u32 regnum,
u16 mask, u16 set)
{
int ret = 0, oldpage;
int ret = phy_modify_paged_changed(phydev, page, regnum, mask, set);
oldpage = phy_select_page(phydev, page);
if (oldpage >= 0)
ret = __phy_modify(phydev, regnum, mask, set);
return phy_restore_page(phydev, oldpage, ret);
return ret < 0 ? ret : 0;
}
EXPORT_SYMBOL(phy_modify_paged);

View File

@ -1564,24 +1564,20 @@ EXPORT_SYMBOL(phy_reset_after_clk_enable);
*/
static int genphy_config_advert(struct phy_device *phydev)
{
u32 advertise;
int bmsr, adv;
int err, changed = 0;
int err, bmsr, changed = 0;
u32 adv;
/* Only allow advertising what this PHY supports */
linkmode_and(phydev->advertising, phydev->advertising,
phydev->supported);
if (!ethtool_convert_link_mode_to_legacy_u32(&advertise,
phydev->advertising))
phydev_warn(phydev, "PHY advertising (%*pb) more modes than genphy supports, some modes not advertised.\n",
__ETHTOOL_LINK_MODE_MASK_NBITS,
phydev->advertising);
adv = linkmode_adv_to_mii_adv_t(phydev->advertising);
/* Setup standard advertisement */
err = phy_modify_changed(phydev, MII_ADVERTISE,
ADVERTISE_ALL | ADVERTISE_100BASE4 |
ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM,
ethtool_adv_to_mii_adv_t(advertise));
adv);
if (err < 0)
return err;
if (err > 0)
@ -1598,13 +1594,7 @@ static int genphy_config_advert(struct phy_device *phydev)
if (!(bmsr & BMSR_ESTATEN))
return changed;
/* Configure gigabit if it's supported */
adv = 0;
if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
phydev->supported) ||
linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
phydev->supported))
adv = ethtool_adv_to_mii_ctrl1000_t(advertise);
adv = linkmode_adv_to_mii_ctrl1000_t(phydev->advertising);
err = phy_modify_changed(phydev, MII_CTRL1000,
ADVERTISE_1000FULL | ADVERTISE_1000HALF,
@ -1681,18 +1671,20 @@ int genphy_restart_aneg(struct phy_device *phydev)
EXPORT_SYMBOL(genphy_restart_aneg);
/**
* genphy_config_aneg - restart auto-negotiation or write BMCR
* __genphy_config_aneg - restart auto-negotiation or write BMCR
* @phydev: target phy_device struct
* @changed: whether autoneg is requested
*
* Description: If auto-negotiation is enabled, we configure the
* advertising, and then restart auto-negotiation. If it is not
* enabled, then we write the BMCR.
*/
int genphy_config_aneg(struct phy_device *phydev)
int __genphy_config_aneg(struct phy_device *phydev, bool changed)
{
int err, changed;
int err;
changed = genphy_config_eee_advert(phydev);
if (genphy_config_eee_advert(phydev))
changed = true;
if (AUTONEG_ENABLE != phydev->autoneg)
return genphy_setup_forced(phydev);
@ -1700,10 +1692,10 @@ int genphy_config_aneg(struct phy_device *phydev)
err = genphy_config_advert(phydev);
if (err < 0) /* error */
return err;
else if (err)
changed = true;
changed |= err;
if (changed == 0) {
if (!changed) {
/* Advertisement hasn't changed, but maybe aneg was never on to
* begin with? Or maybe phy was isolated?
*/
@ -1713,18 +1705,15 @@ int genphy_config_aneg(struct phy_device *phydev)
return ctl;
if (!(ctl & BMCR_ANENABLE) || (ctl & BMCR_ISOLATE))
changed = 1; /* do restart aneg */
changed = true; /* do restart aneg */
}
/* Only restart aneg if we are advertising something different
* than we were before.
*/
if (changed > 0)
return genphy_restart_aneg(phydev);
return 0;
return changed ? genphy_restart_aneg(phydev) : 0;
}
EXPORT_SYMBOL(genphy_config_aneg);
EXPORT_SYMBOL(__genphy_config_aneg);
/**
* genphy_aneg_done - return auto-negotiation status
@ -1811,8 +1800,6 @@ int genphy_read_status(struct phy_device *phydev)
phydev->pause = 0;
phydev->asym_pause = 0;
linkmode_zero(phydev->lp_advertising);
if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete) {
if (phydev->is_gigabit_capable) {
lpagb = phy_read(phydev, MII_STAT1000);

View File

@ -39,6 +39,11 @@
#define RTL8366RB_POWER_SAVE 0x15
#define RTL8366RB_POWER_SAVE_ON BIT(12)
#define RTL_ADV_2500FULL BIT(7)
#define RTL_LPADV_10000FULL BIT(11)
#define RTL_LPADV_5000FULL BIT(6)
#define RTL_LPADV_2500FULL BIT(5)
MODULE_DESCRIPTION("Realtek PHY driver");
MODULE_AUTHOR("Johnson Leung");
MODULE_LICENSE("GPL");
@ -256,6 +261,53 @@ static int rtl8366rb_config_init(struct phy_device *phydev)
return ret;
}
static int rtl8125_get_features(struct phy_device *phydev)
{
linkmode_set_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT,
phydev->supported);
return genphy_read_abilities(phydev);
}
static int rtl8125_config_aneg(struct phy_device *phydev)
{
int ret = 0;
if (phydev->autoneg == AUTONEG_ENABLE) {
u16 adv2500 = 0;
if (linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT,
phydev->advertising))
adv2500 = RTL_ADV_2500FULL;
ret = phy_modify_paged_changed(phydev, 0xa5d, 0x12,
RTL_ADV_2500FULL, adv2500);
if (ret < 0)
return ret;
}
return __genphy_config_aneg(phydev, ret);
}
static int rtl8125_read_status(struct phy_device *phydev)
{
if (phydev->autoneg == AUTONEG_ENABLE) {
int lpadv = phy_read_paged(phydev, 0xa5d, 0x13);
if (lpadv < 0)
return lpadv;
linkmode_mod_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT,
phydev->lp_advertising, lpadv & RTL_LPADV_10000FULL);
linkmode_mod_bit(ETHTOOL_LINK_MODE_5000baseT_Full_BIT,
phydev->lp_advertising, lpadv & RTL_LPADV_5000FULL);
linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT,
phydev->lp_advertising, lpadv & RTL_LPADV_2500FULL);
}
return genphy_read_status(phydev);
}
static struct phy_driver realtek_drvs[] = {
{
PHY_ID_MATCH_EXACT(0x00008201),
@ -332,6 +384,16 @@ static struct phy_driver realtek_drvs[] = {
.resume = genphy_resume,
.read_page = rtl821x_read_page,
.write_page = rtl821x_write_page,
}, {
PHY_ID_MATCH_EXACT(0x001cca50),
.name = "RTL8125 2.5Gbps internal",
.get_features = rtl8125_get_features,
.config_aneg = rtl8125_config_aneg,
.read_status = rtl8125_read_status,
.suspend = genphy_suspend,
.resume = genphy_resume,
.read_page = rtl821x_read_page,
.write_page = rtl821x_write_page,
}, {
PHY_ID_MATCH_EXACT(0x001cc961),
.name = "RTL8366RB Gigabit Ethernet",

View File

@ -984,6 +984,8 @@ int phy_select_page(struct phy_device *phydev, int page);
int phy_restore_page(struct phy_device *phydev, int oldpage, int ret);
int phy_read_paged(struct phy_device *phydev, int page, u32 regnum);
int phy_write_paged(struct phy_device *phydev, int page, u32 regnum, u16 val);
int phy_modify_paged_changed(struct phy_device *phydev, int page, u32 regnum,
u16 mask, u16 set);
int phy_modify_paged(struct phy_device *phydev, int page, u32 regnum,
u16 mask, u16 set);
@ -1069,7 +1071,7 @@ int genphy_read_abilities(struct phy_device *phydev);
int genphy_setup_forced(struct phy_device *phydev);
int genphy_restart_aneg(struct phy_device *phydev);
int genphy_config_eee_advert(struct phy_device *phydev);
int genphy_config_aneg(struct phy_device *phydev);
int __genphy_config_aneg(struct phy_device *phydev, bool changed);
int genphy_aneg_done(struct phy_device *phydev);
int genphy_update_link(struct phy_device *phydev);
int genphy_read_status(struct phy_device *phydev);
@ -1077,6 +1079,12 @@ int genphy_suspend(struct phy_device *phydev);
int genphy_resume(struct phy_device *phydev);
int genphy_loopback(struct phy_device *phydev, bool enable);
int genphy_soft_reset(struct phy_device *phydev);
static inline int genphy_config_aneg(struct phy_device *phydev)
{
return __genphy_config_aneg(phydev, false);
}
static inline int genphy_no_soft_reset(struct phy_device *phydev)
{
return 0;