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

Merge branch 'rtl8226b-serdes-switching'

Eric Woudstra says:

====================
rtl8226b/8221b add C45 instances and SerDes switching

Based on the comments in [PATCH net-next]
"Realtek RTL822x PHY rework to c45 and SerDes interface switching"

Adds SerDes switching interface between 2500base-x and sgmii for
rtl8221b and rtl8226b.

Add get_rate_matching() for rtl8226b and rtl8221b, reading the serdes
mode from phy.

Driver instances are added for rtl8226b and rtl8221b for Clause 45
access only. The existing code is not touched, they use newly added
functions. They also use the same rtl822xb_config_init() and
rtl822xb_get_rate_matching() as these functions also can be used for
direct Clause 45 access. Also Adds definition of MMC 31 registers,
which cannot be used through C45-over-C22, only when phydev->is_c45
is set.

Change rtlgen_get_speed() so the register value is passed as argument.
Using Clause 45 access, this value is retrieved differently.
Rename it to rtlgen_decode_speed() and add a call to it in
rtl822x_c45_read_status().

Add rtl822x_c45_get_features() to set supported port for rtl8221b.

Then 1 quirk is added for sfp modules known to have a rtl8221b
behind RollBall, Clause 45 only, protocol.

Changed in PATCH v4:
* Changed switch to if statement in rtl822xb_get_rate_matching()
* Removed setting ETHTOOL_LINK_MODE_MII_BIT in rtl822x_c45_get_features()

Changed in PATCH v3:
* Only apply to rtl8221b and rtl8226b phy's
* Set phydev->rate_matching in .config_init()
* Removed OEM SFP fixup for now, as there are modules with the same
  vendor name/PN, but with different PHY's. We found rtl8221b, but
  also the ty8821, which is not yet supported.

Changed in PATCH v2:
* Set author to Marek for the commit of the new C45 instances
* Separate commit for setting supported ports
* Renamed rtlgen_get_speed to rtlgen_decode_speed
* Always fill in possible interfaces
* Renamed sfp_fixup_oem_2_5g to sfp_fixup_oem_2_5gbaset
* Only update phydev->interface when link is up

Alexander Couzens (1):
  net: phy: realtek: configure SerDes mode for rtl822xb PHYs

Eric Woudstra (3):
  net: phy: realtek: add get_rate_matching() for rtl822xb PHYs
  net: phy: realtek: Change rtlgen_get_speed() to rtlgen_decode_speed()
  net: phy: realtek: add rtl822x_c45_get_features() to set supported
    port
====================
Signed-off-by: default avatarDavid S. Miller <davem@davemloft.net>
parents af74be9f 1c77c721
......@@ -54,6 +54,25 @@
RTL8201F_ISR_LINK)
#define RTL8201F_IER 0x13
#define RTL822X_VND1_SERDES_OPTION 0x697a
#define RTL822X_VND1_SERDES_OPTION_MODE_MASK GENMASK(5, 0)
#define RTL822X_VND1_SERDES_OPTION_MODE_2500BASEX_SGMII 0
#define RTL822X_VND1_SERDES_OPTION_MODE_2500BASEX 2
#define RTL822X_VND1_SERDES_CTRL3 0x7580
#define RTL822X_VND1_SERDES_CTRL3_MODE_MASK GENMASK(5, 0)
#define RTL822X_VND1_SERDES_CTRL3_MODE_SGMII 0x02
#define RTL822X_VND1_SERDES_CTRL3_MODE_2500BASEX 0x16
/* RTL822X_VND2_XXXXX registers are only accessible when phydev->is_c45
* is set, they cannot be accessed by C45-over-C22.
*/
#define RTL822X_VND2_GBCR 0xa412
#define RTL822X_VND2_GANLPAR 0xa414
#define RTL822X_VND2_PHYSR 0xa434
#define RTL8366RB_POWER_SAVE 0x15
#define RTL8366RB_POWER_SAVE_ON BIT(12)
......@@ -64,6 +83,9 @@
#define RTL_GENERIC_PHYID 0x001cc800
#define RTL_8211FVD_PHYID 0x001cc878
#define RTL_8221B_VB_CG 0x001cc849
#define RTL_8221B_VN_CG 0x001cc84a
#define RTL_8251B 0x001cc862
MODULE_DESCRIPTION("Realtek PHY driver");
MODULE_AUTHOR("Johnson Leung");
......@@ -531,17 +553,8 @@ static int rtl8366rb_config_init(struct phy_device *phydev)
}
/* get actual speed to cover the downshift case */
static int rtlgen_get_speed(struct phy_device *phydev)
static void rtlgen_decode_speed(struct phy_device *phydev, int val)
{
int val;
if (!phydev->link)
return 0;
val = phy_read_paged(phydev, 0xa43, 0x12);
if (val < 0)
return val;
switch (val & RTLGEN_SPEED_MASK) {
case 0x0000:
phydev->speed = SPEED_10;
......@@ -564,19 +577,26 @@ static int rtlgen_get_speed(struct phy_device *phydev)
default:
break;
}
return 0;
}
static int rtlgen_read_status(struct phy_device *phydev)
{
int ret;
int ret, val;
ret = genphy_read_status(phydev);
if (ret < 0)
return ret;
return rtlgen_get_speed(phydev);
if (!phydev->link)
return 0;
val = phy_read_paged(phydev, 0xa43, 0x12);
if (val < 0)
return val;
rtlgen_decode_speed(phydev, val);
return 0;
}
static int rtlgen_read_mmd(struct phy_device *phydev, int devnum, u16 regnum)
......@@ -659,6 +679,84 @@ static int rtl822x_write_mmd(struct phy_device *phydev, int devnum, u16 regnum,
return ret;
}
static int rtl822xb_config_init(struct phy_device *phydev)
{
bool has_2500, has_sgmii;
u16 mode;
int ret;
has_2500 = test_bit(PHY_INTERFACE_MODE_2500BASEX,
phydev->host_interfaces) ||
phydev->interface == PHY_INTERFACE_MODE_2500BASEX;
has_sgmii = test_bit(PHY_INTERFACE_MODE_SGMII,
phydev->host_interfaces) ||
phydev->interface == PHY_INTERFACE_MODE_SGMII;
/* fill in possible interfaces */
__assign_bit(PHY_INTERFACE_MODE_2500BASEX, phydev->possible_interfaces,
has_2500);
__assign_bit(PHY_INTERFACE_MODE_SGMII, phydev->possible_interfaces,
has_sgmii);
if (!has_2500 && !has_sgmii)
return 0;
/* determine SerDes option mode */
if (has_2500 && !has_sgmii) {
mode = RTL822X_VND1_SERDES_OPTION_MODE_2500BASEX;
phydev->rate_matching = RATE_MATCH_PAUSE;
} else {
mode = RTL822X_VND1_SERDES_OPTION_MODE_2500BASEX_SGMII;
phydev->rate_matching = RATE_MATCH_NONE;
}
/* the following sequence with magic numbers sets up the SerDes
* option mode
*/
ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x75f3, 0);
if (ret < 0)
return ret;
ret = phy_modify_mmd_changed(phydev, MDIO_MMD_VEND1,
RTL822X_VND1_SERDES_OPTION,
RTL822X_VND1_SERDES_OPTION_MODE_MASK,
mode);
if (ret < 0)
return ret;
ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x6a04, 0x0503);
if (ret < 0)
return ret;
ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x6f10, 0xd455);
if (ret < 0)
return ret;
return phy_write_mmd(phydev, MDIO_MMD_VEND1, 0x6f11, 0x8020);
}
static int rtl822xb_get_rate_matching(struct phy_device *phydev,
phy_interface_t iface)
{
int val;
/* Only rate matching at 2500base-x */
if (iface != PHY_INTERFACE_MODE_2500BASEX)
return RATE_MATCH_NONE;
val = phy_read_mmd(phydev, MDIO_MMD_VEND1, RTL822X_VND1_SERDES_OPTION);
if (val < 0)
return val;
if ((val & RTL822X_VND1_SERDES_OPTION_MODE_MASK) ==
RTL822X_VND1_SERDES_OPTION_MODE_2500BASEX)
return RATE_MATCH_PAUSE;
/* RTL822X_VND1_SERDES_OPTION_MODE_2500BASEX_SGMII */
return RATE_MATCH_NONE;
}
static int rtl822x_get_features(struct phy_device *phydev)
{
int val;
......@@ -695,10 +793,30 @@ static int rtl822x_config_aneg(struct phy_device *phydev)
return __genphy_config_aneg(phydev, ret);
}
static int rtl822x_read_status(struct phy_device *phydev)
static void rtl822xb_update_interface(struct phy_device *phydev)
{
int ret;
int val;
if (!phydev->link)
return;
/* Change interface according to serdes mode */
val = phy_read_mmd(phydev, MDIO_MMD_VEND1, RTL822X_VND1_SERDES_CTRL3);
if (val < 0)
return;
switch (val & RTL822X_VND1_SERDES_CTRL3_MODE_MASK) {
case RTL822X_VND1_SERDES_CTRL3_MODE_2500BASEX:
phydev->interface = PHY_INTERFACE_MODE_2500BASEX;
break;
case RTL822X_VND1_SERDES_CTRL3_MODE_SGMII:
phydev->interface = PHY_INTERFACE_MODE_SGMII;
break;
}
}
static int rtl822x_read_status(struct phy_device *phydev)
{
if (phydev->autoneg == AUTONEG_ENABLE) {
int lpadv = phy_read_paged(phydev, 0xa5d, 0x13);
......@@ -709,11 +827,99 @@ static int rtl822x_read_status(struct phy_device *phydev)
lpadv);
}
ret = genphy_read_status(phydev);
return rtlgen_read_status(phydev);
}
static int rtl822xb_read_status(struct phy_device *phydev)
{
int ret;
ret = rtl822x_read_status(phydev);
if (ret < 0)
return ret;
rtl822xb_update_interface(phydev);
return 0;
}
static int rtl822x_c45_get_features(struct phy_device *phydev)
{
linkmode_set_bit(ETHTOOL_LINK_MODE_TP_BIT,
phydev->supported);
return genphy_c45_pma_read_abilities(phydev);
}
static int rtl822x_c45_config_aneg(struct phy_device *phydev)
{
bool changed = false;
int ret, val;
if (phydev->autoneg == AUTONEG_DISABLE)
return genphy_c45_pma_setup_forced(phydev);
ret = genphy_c45_an_config_aneg(phydev);
if (ret < 0)
return ret;
if (ret > 0)
changed = true;
val = linkmode_adv_to_mii_ctrl1000_t(phydev->advertising);
/* Vendor register as C45 has no standardized support for 1000BaseT */
ret = phy_modify_mmd_changed(phydev, MDIO_MMD_VEND2, RTL822X_VND2_GBCR,
ADVERTISE_1000FULL, val);
if (ret < 0)
return ret;
if (ret > 0)
changed = true;
return genphy_c45_check_and_restart_aneg(phydev, changed);
}
static int rtl822x_c45_read_status(struct phy_device *phydev)
{
int ret, val;
ret = genphy_c45_read_status(phydev);
if (ret < 0)
return ret;
/* Vendor register as C45 has no standardized support for 1000BaseT */
if (phydev->autoneg == AUTONEG_ENABLE) {
val = phy_read_mmd(phydev, MDIO_MMD_VEND2,
RTL822X_VND2_GANLPAR);
if (val < 0)
return val;
mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising, val);
}
if (!phydev->link)
return 0;
/* Read actual speed from vendor register. */
val = phy_read_mmd(phydev, MDIO_MMD_VEND2, RTL822X_VND2_PHYSR);
if (val < 0)
return val;
rtlgen_decode_speed(phydev, val);
return 0;
}
static int rtl822xb_c45_read_status(struct phy_device *phydev)
{
int ret;
ret = rtl822x_c45_read_status(phydev);
if (ret < 0)
return ret;
return rtlgen_get_speed(phydev);
rtl822xb_update_interface(phydev);
return 0;
}
static bool rtlgen_supports_2_5gbps(struct phy_device *phydev)
......@@ -739,6 +945,35 @@ static int rtl8226_match_phy_device(struct phy_device *phydev)
rtlgen_supports_2_5gbps(phydev);
}
static int rtlgen_is_c45_match(struct phy_device *phydev, unsigned int id,
bool is_c45)
{
if (phydev->is_c45)
return is_c45 && (id == phydev->c45_ids.device_ids[1]);
else
return !is_c45 && (id == phydev->phy_id);
}
static int rtl8221b_vb_cg_c22_match_phy_device(struct phy_device *phydev)
{
return rtlgen_is_c45_match(phydev, RTL_8221B_VB_CG, false);
}
static int rtl8221b_vb_cg_c45_match_phy_device(struct phy_device *phydev)
{
return rtlgen_is_c45_match(phydev, RTL_8221B_VB_CG, true);
}
static int rtl8221b_vn_cg_c22_match_phy_device(struct phy_device *phydev)
{
return rtlgen_is_c45_match(phydev, RTL_8221B_VN_CG, false);
}
static int rtl8221b_vn_cg_c45_match_phy_device(struct phy_device *phydev)
{
return rtlgen_is_c45_match(phydev, RTL_8221B_VN_CG, true);
}
static int rtlgen_resume(struct phy_device *phydev)
{
int ret = genphy_resume(phydev);
......@@ -749,6 +984,15 @@ static int rtlgen_resume(struct phy_device *phydev)
return ret;
}
static int rtlgen_c45_resume(struct phy_device *phydev)
{
int ret = genphy_c45_pma_resume(phydev);
msleep(20);
return ret;
}
static int rtl9000a_config_init(struct phy_device *phydev)
{
phydev->autoneg = AUTONEG_DISABLE;
......@@ -988,7 +1232,9 @@ static struct phy_driver realtek_drvs[] = {
.name = "RTL8226B_RTL8221B 2.5Gbps PHY",
.get_features = rtl822x_get_features,
.config_aneg = rtl822x_config_aneg,
.read_status = rtl822x_read_status,
.config_init = rtl822xb_config_init,
.get_rate_matching = rtl822xb_get_rate_matching,
.read_status = rtl822xb_read_status,
.suspend = genphy_suspend,
.resume = rtlgen_resume,
.read_page = rtl821x_read_page,
......@@ -1010,31 +1256,57 @@ static struct phy_driver realtek_drvs[] = {
.name = "RTL8226B-CG_RTL8221B-CG 2.5Gbps PHY",
.get_features = rtl822x_get_features,
.config_aneg = rtl822x_config_aneg,
.read_status = rtl822x_read_status,
.config_init = rtl822xb_config_init,
.get_rate_matching = rtl822xb_get_rate_matching,
.read_status = rtl822xb_read_status,
.suspend = genphy_suspend,
.resume = rtlgen_resume,
.read_page = rtl821x_read_page,
.write_page = rtl821x_write_page,
}, {
PHY_ID_MATCH_EXACT(0x001cc849),
.name = "RTL8221B-VB-CG 2.5Gbps PHY",
.match_phy_device = rtl8221b_vb_cg_c22_match_phy_device,
.name = "RTL8221B-VB-CG 2.5Gbps PHY (C22)",
.get_features = rtl822x_get_features,
.config_aneg = rtl822x_config_aneg,
.read_status = rtl822x_read_status,
.config_init = rtl822xb_config_init,
.get_rate_matching = rtl822xb_get_rate_matching,
.read_status = rtl822xb_read_status,
.suspend = genphy_suspend,
.resume = rtlgen_resume,
.read_page = rtl821x_read_page,
.write_page = rtl821x_write_page,
}, {
PHY_ID_MATCH_EXACT(0x001cc84a),
.name = "RTL8221B-VM-CG 2.5Gbps PHY",
.match_phy_device = rtl8221b_vb_cg_c45_match_phy_device,
.name = "RTL8221B-VB-CG 2.5Gbps PHY (C45)",
.config_init = rtl822xb_config_init,
.get_rate_matching = rtl822xb_get_rate_matching,
.get_features = rtl822x_c45_get_features,
.config_aneg = rtl822x_c45_config_aneg,
.read_status = rtl822xb_c45_read_status,
.suspend = genphy_c45_pma_suspend,
.resume = rtlgen_c45_resume,
}, {
.match_phy_device = rtl8221b_vn_cg_c22_match_phy_device,
.name = "RTL8221B-VM-CG 2.5Gbps PHY (C22)",
.get_features = rtl822x_get_features,
.config_aneg = rtl822x_config_aneg,
.read_status = rtl822x_read_status,
.config_init = rtl822xb_config_init,
.get_rate_matching = rtl822xb_get_rate_matching,
.read_status = rtl822xb_read_status,
.suspend = genphy_suspend,
.resume = rtlgen_resume,
.read_page = rtl821x_read_page,
.write_page = rtl821x_write_page,
}, {
.match_phy_device = rtl8221b_vn_cg_c45_match_phy_device,
.name = "RTL8221B-VN-CG 2.5Gbps PHY (C45)",
.config_init = rtl822xb_config_init,
.get_rate_matching = rtl822xb_get_rate_matching,
.get_features = rtl822x_c45_get_features,
.config_aneg = rtl822x_c45_config_aneg,
.read_status = rtl822xb_c45_read_status,
.suspend = genphy_c45_pma_suspend,
.resume = rtlgen_c45_resume,
}, {
PHY_ID_MATCH_EXACT(0x001cc862),
.name = "RTL8251B 5Gbps PHY",
......
......@@ -506,6 +506,7 @@ static const struct sfp_quirk sfp_quirks[] = {
SFP_QUIRK_M("OEM", "SFP-2.5G-T", sfp_quirk_oem_2_5g),
SFP_QUIRK_F("OEM", "RTSFP-10", sfp_fixup_rollball_cc),
SFP_QUIRK_F("OEM", "RTSFP-10G", sfp_fixup_rollball_cc),
SFP_QUIRK_F("Turris", "RTSFP-2.5G", sfp_fixup_rollball),
SFP_QUIRK_F("Turris", "RTSFP-10", sfp_fixup_rollball),
SFP_QUIRK_F("Turris", "RTSFP-10G", sfp_fixup_rollball),
};
......
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