Commit d35bbe84 authored by David S. Miller's avatar David S. Miller

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: default avatarDavid S. Miller <davem@davemloft.net>
parents 150e8f8a 087f5b87
...@@ -783,24 +783,43 @@ int phy_write_paged(struct phy_device *phydev, int page, u32 regnum, u16 val) ...@@ -783,24 +783,43 @@ int phy_write_paged(struct phy_device *phydev, int page, u32 regnum, u16 val)
EXPORT_SYMBOL(phy_write_paged); EXPORT_SYMBOL(phy_write_paged);
/** /**
* phy_modify_paged() - Convenience function for modifying a paged register * phy_modify_paged_changed() - Function for modifying a paged register
* @phydev: a pointer to a &struct phy_device * @phydev: a pointer to a &struct phy_device
* @page: the page for the phy * @page: the page for the phy
* @regnum: register number * @regnum: register number
* @mask: bit mask of bits to clear * @mask: bit mask of bits to clear
* @set: bit mask of bits to set * @set: bit mask of bits to set
* *
* Same rules as for phy_read() and phy_write(). * Returns negative errno, 0 if there was no change, and 1 in case of change
*/ */
int phy_modify_paged(struct phy_device *phydev, int page, u32 regnum, int phy_modify_paged_changed(struct phy_device *phydev, int page, u32 regnum,
u16 mask, u16 set) u16 mask, u16 set)
{ {
int ret = 0, oldpage; int ret = 0, oldpage;
oldpage = phy_select_page(phydev, page); oldpage = phy_select_page(phydev, page);
if (oldpage >= 0) if (oldpage >= 0)
ret = __phy_modify(phydev, regnum, mask, set); ret = __phy_modify_changed(phydev, regnum, mask, set);
return phy_restore_page(phydev, oldpage, ret); 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
* @page: the page for the phy
* @regnum: register number
* @mask: bit mask of bits to clear
* @set: bit mask of bits to set
*
* Same rules as for phy_read() and phy_write().
*/
int phy_modify_paged(struct phy_device *phydev, int page, u32 regnum,
u16 mask, u16 set)
{
int ret = phy_modify_paged_changed(phydev, page, regnum, mask, set);
return ret < 0 ? ret : 0;
}
EXPORT_SYMBOL(phy_modify_paged); EXPORT_SYMBOL(phy_modify_paged);
...@@ -1564,24 +1564,20 @@ EXPORT_SYMBOL(phy_reset_after_clk_enable); ...@@ -1564,24 +1564,20 @@ EXPORT_SYMBOL(phy_reset_after_clk_enable);
*/ */
static int genphy_config_advert(struct phy_device *phydev) static int genphy_config_advert(struct phy_device *phydev)
{ {
u32 advertise; int err, bmsr, changed = 0;
int bmsr, adv; u32 adv;
int err, changed = 0;
/* Only allow advertising what this PHY supports */ /* Only allow advertising what this PHY supports */
linkmode_and(phydev->advertising, phydev->advertising, linkmode_and(phydev->advertising, phydev->advertising,
phydev->supported); phydev->supported);
if (!ethtool_convert_link_mode_to_legacy_u32(&advertise,
phydev->advertising)) adv = linkmode_adv_to_mii_adv_t(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);
/* Setup standard advertisement */ /* Setup standard advertisement */
err = phy_modify_changed(phydev, MII_ADVERTISE, err = phy_modify_changed(phydev, MII_ADVERTISE,
ADVERTISE_ALL | ADVERTISE_100BASE4 | ADVERTISE_ALL | ADVERTISE_100BASE4 |
ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM, ADVERTISE_PAUSE_CAP | ADVERTISE_PAUSE_ASYM,
ethtool_adv_to_mii_adv_t(advertise)); adv);
if (err < 0) if (err < 0)
return err; return err;
if (err > 0) if (err > 0)
...@@ -1598,13 +1594,7 @@ static int genphy_config_advert(struct phy_device *phydev) ...@@ -1598,13 +1594,7 @@ static int genphy_config_advert(struct phy_device *phydev)
if (!(bmsr & BMSR_ESTATEN)) if (!(bmsr & BMSR_ESTATEN))
return changed; return changed;
/* Configure gigabit if it's supported */ adv = linkmode_adv_to_mii_ctrl1000_t(phydev->advertising);
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);
err = phy_modify_changed(phydev, MII_CTRL1000, err = phy_modify_changed(phydev, MII_CTRL1000,
ADVERTISE_1000FULL | ADVERTISE_1000HALF, ADVERTISE_1000FULL | ADVERTISE_1000HALF,
...@@ -1681,18 +1671,20 @@ int genphy_restart_aneg(struct phy_device *phydev) ...@@ -1681,18 +1671,20 @@ int genphy_restart_aneg(struct phy_device *phydev)
EXPORT_SYMBOL(genphy_restart_aneg); 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 * @phydev: target phy_device struct
* @changed: whether autoneg is requested
* *
* Description: If auto-negotiation is enabled, we configure the * Description: If auto-negotiation is enabled, we configure the
* advertising, and then restart auto-negotiation. If it is not * advertising, and then restart auto-negotiation. If it is not
* enabled, then we write the BMCR. * 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) if (AUTONEG_ENABLE != phydev->autoneg)
return genphy_setup_forced(phydev); return genphy_setup_forced(phydev);
...@@ -1700,10 +1692,10 @@ int genphy_config_aneg(struct phy_device *phydev) ...@@ -1700,10 +1692,10 @@ int genphy_config_aneg(struct phy_device *phydev)
err = genphy_config_advert(phydev); err = genphy_config_advert(phydev);
if (err < 0) /* error */ if (err < 0) /* error */
return err; return err;
else if (err)
changed = true;
changed |= err; if (!changed) {
if (changed == 0) {
/* Advertisement hasn't changed, but maybe aneg was never on to /* Advertisement hasn't changed, but maybe aneg was never on to
* begin with? Or maybe phy was isolated? * begin with? Or maybe phy was isolated?
*/ */
...@@ -1713,18 +1705,15 @@ int genphy_config_aneg(struct phy_device *phydev) ...@@ -1713,18 +1705,15 @@ int genphy_config_aneg(struct phy_device *phydev)
return ctl; return ctl;
if (!(ctl & BMCR_ANENABLE) || (ctl & BMCR_ISOLATE)) 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 /* Only restart aneg if we are advertising something different
* than we were before. * than we were before.
*/ */
if (changed > 0) return changed ? genphy_restart_aneg(phydev) : 0;
return genphy_restart_aneg(phydev);
return 0;
} }
EXPORT_SYMBOL(genphy_config_aneg); EXPORT_SYMBOL(__genphy_config_aneg);
/** /**
* genphy_aneg_done - return auto-negotiation status * genphy_aneg_done - return auto-negotiation status
...@@ -1811,8 +1800,6 @@ int genphy_read_status(struct phy_device *phydev) ...@@ -1811,8 +1800,6 @@ int genphy_read_status(struct phy_device *phydev)
phydev->pause = 0; phydev->pause = 0;
phydev->asym_pause = 0; phydev->asym_pause = 0;
linkmode_zero(phydev->lp_advertising);
if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete) { if (phydev->autoneg == AUTONEG_ENABLE && phydev->autoneg_complete) {
if (phydev->is_gigabit_capable) { if (phydev->is_gigabit_capable) {
lpagb = phy_read(phydev, MII_STAT1000); lpagb = phy_read(phydev, MII_STAT1000);
......
...@@ -39,6 +39,11 @@ ...@@ -39,6 +39,11 @@
#define RTL8366RB_POWER_SAVE 0x15 #define RTL8366RB_POWER_SAVE 0x15
#define RTL8366RB_POWER_SAVE_ON BIT(12) #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_DESCRIPTION("Realtek PHY driver");
MODULE_AUTHOR("Johnson Leung"); MODULE_AUTHOR("Johnson Leung");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
...@@ -256,6 +261,53 @@ static int rtl8366rb_config_init(struct phy_device *phydev) ...@@ -256,6 +261,53 @@ static int rtl8366rb_config_init(struct phy_device *phydev)
return ret; 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[] = { static struct phy_driver realtek_drvs[] = {
{ {
PHY_ID_MATCH_EXACT(0x00008201), PHY_ID_MATCH_EXACT(0x00008201),
...@@ -332,6 +384,16 @@ static struct phy_driver realtek_drvs[] = { ...@@ -332,6 +384,16 @@ static struct phy_driver realtek_drvs[] = {
.resume = genphy_resume, .resume = genphy_resume,
.read_page = rtl821x_read_page, .read_page = rtl821x_read_page,
.write_page = rtl821x_write_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), PHY_ID_MATCH_EXACT(0x001cc961),
.name = "RTL8366RB Gigabit Ethernet", .name = "RTL8366RB Gigabit Ethernet",
......
...@@ -984,6 +984,8 @@ int phy_select_page(struct phy_device *phydev, int page); ...@@ -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_restore_page(struct phy_device *phydev, int oldpage, int ret);
int phy_read_paged(struct phy_device *phydev, int page, u32 regnum); 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_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, int phy_modify_paged(struct phy_device *phydev, int page, u32 regnum,
u16 mask, u16 set); u16 mask, u16 set);
...@@ -1069,7 +1071,7 @@ int genphy_read_abilities(struct phy_device *phydev); ...@@ -1069,7 +1071,7 @@ int genphy_read_abilities(struct phy_device *phydev);
int genphy_setup_forced(struct phy_device *phydev); int genphy_setup_forced(struct phy_device *phydev);
int genphy_restart_aneg(struct phy_device *phydev); int genphy_restart_aneg(struct phy_device *phydev);
int genphy_config_eee_advert(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_aneg_done(struct phy_device *phydev);
int genphy_update_link(struct phy_device *phydev); int genphy_update_link(struct phy_device *phydev);
int genphy_read_status(struct phy_device *phydev); int genphy_read_status(struct phy_device *phydev);
...@@ -1077,6 +1079,12 @@ int genphy_suspend(struct phy_device *phydev); ...@@ -1077,6 +1079,12 @@ int genphy_suspend(struct phy_device *phydev);
int genphy_resume(struct phy_device *phydev); int genphy_resume(struct phy_device *phydev);
int genphy_loopback(struct phy_device *phydev, bool enable); int genphy_loopback(struct phy_device *phydev, bool enable);
int genphy_soft_reset(struct phy_device *phydev); 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) static inline int genphy_no_soft_reset(struct phy_device *phydev)
{ {
return 0; return 0;
......
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