mirror of
https://github.com/torvalds/linux.git
synced 2026-09-23 22:14:03 +02:00
net: phy: realtek: add support for RTL8261C_CG
This patch adds support for Realtek phy chip RTL8261C_CG. Its PHY ID is 0x001cc898. This patch introduces a distinct family of handlers (probe, get_features, config_aneg, read_status, config_intr, handle_interrupt). Reviewed-by: Andrew Lunn <andrew@lunn.ch> Reviewed-by: Nicolai Buchwitz <nb@tipi-net.de> Signed-off-by: Javen Xu <javen_xu@realsil.com.cn> Link: https://patch.msgid.link/20260728073106.1515-4-javen_xu@realsil.com.cn Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
parent
d7722c0308
commit
b772b5ae45
|
|
@ -141,6 +141,10 @@
|
|||
#define RTL8211F_PHYSICAL_ADDR_WORD1 17
|
||||
#define RTL8211F_PHYSICAL_ADDR_WORD2 18
|
||||
|
||||
#define RTL8261X_EXT_ADDR_REG 0xa436
|
||||
#define RTL8261X_EXT_DATA_REG 0xa438
|
||||
#define RTL_8261X_SUB_PHY_ID_ADDR 0x801d
|
||||
|
||||
#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
|
||||
|
|
@ -251,6 +255,32 @@
|
|||
#define RTL_8221B_VM_CG 0x001cc84a
|
||||
#define RTL_8251B 0x001cc862
|
||||
#define RTL_8261C 0x001cc890
|
||||
#define RTL_8261C_CG 0x001cc898
|
||||
|
||||
#define RTL8261C_CE_MODEL 0x00
|
||||
#define RTL8261X_INT_AUTONEG_ERROR BIT(0)
|
||||
#define RTL8261X_INT_PAGE_RECV BIT(2)
|
||||
#define RTL8261X_INT_AUTONEG_DONE BIT(3)
|
||||
#define RTL8261X_INT_LINK_CHG BIT(4)
|
||||
#define RTL8261X_INT_PHY_REG_ACCESS BIT(5)
|
||||
#define RTL8261X_INT_PME BIT(7)
|
||||
#define RTL8261X_INT_ALDPS_CHG BIT(9)
|
||||
#define RTL8261X_INT_JABBER BIT(10)
|
||||
|
||||
#define RTL8261X_INT_MASK_DEFAULT (RTL8261X_INT_AUTONEG_DONE | \
|
||||
RTL8261X_INT_LINK_CHG | \
|
||||
RTL8261X_INT_AUTONEG_ERROR | \
|
||||
RTL8261X_INT_JABBER)
|
||||
|
||||
#define RTL8261X_INT_MASK_ALL (RTL8261X_INT_AUTONEG_ERROR | \
|
||||
RTL8261X_INT_PAGE_RECV | \
|
||||
RTL8261X_INT_AUTONEG_DONE | \
|
||||
RTL8261X_INT_LINK_CHG | \
|
||||
RTL8261X_INT_PHY_REG_ACCESS | \
|
||||
RTL8261X_INT_PME | \
|
||||
RTL8261X_INT_ALDPS_CHG | \
|
||||
RTL8261X_INT_JABBER)
|
||||
|
||||
|
||||
/* RTL8211E and RTL8211F support up to three LEDs */
|
||||
#define RTL8211x_LED_COUNT 3
|
||||
|
|
@ -310,6 +340,156 @@ static int rtl821x_modify_ext_page(struct phy_device *phydev, u16 ext_page,
|
|||
return phy_restore_page(phydev, oldpage, ret);
|
||||
}
|
||||
|
||||
static int rtl8261x_probe(struct phy_device *phydev)
|
||||
{
|
||||
int sub_phy_id, ret;
|
||||
|
||||
ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, RTL8261X_EXT_ADDR_REG,
|
||||
RTL_8261X_SUB_PHY_ID_ADDR);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, RTL8261X_EXT_DATA_REG);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
sub_phy_id = (ret >> 8) & 0xff;
|
||||
|
||||
switch (sub_phy_id) {
|
||||
case RTL8261C_CE_MODEL:
|
||||
phydev_info(phydev, "RTL8261C detected (sub_id 0x%02x)\n", sub_phy_id);
|
||||
break;
|
||||
|
||||
default:
|
||||
phydev_warn(phydev, "Unknown sub_id 0x%02x, default behavior\n", sub_phy_id);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rtl8261x_get_features(struct phy_device *phydev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = genphy_c45_pma_read_abilities(phydev);
|
||||
if (ret)
|
||||
return ret;
|
||||
/*
|
||||
* Supplement Multi-Gig speeds that may not be automatically detected
|
||||
* RTL8261X supports 2.5G/5G in addition to standard 10G
|
||||
*/
|
||||
linkmode_set_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT,
|
||||
phydev->supported);
|
||||
linkmode_set_bit(ETHTOOL_LINK_MODE_5000baseT_Full_BIT,
|
||||
phydev->supported);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rtl8261x_read_status(struct phy_device *phydev)
|
||||
{
|
||||
int ret, val = 0;
|
||||
|
||||
if (phydev->autoneg == AUTONEG_ENABLE) {
|
||||
ret = genphy_c45_aneg_done(phydev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (ret) {
|
||||
val = phy_read_mmd(phydev, MDIO_MMD_VEND2,
|
||||
RTL822X_VND2_C22_REG(MII_STAT1000));
|
||||
if (val < 0)
|
||||
return val;
|
||||
}
|
||||
}
|
||||
|
||||
mii_stat1000_mod_linkmode_lpa_t(phydev->lp_advertising, val);
|
||||
|
||||
ret = genphy_c45_read_status(phydev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rtl8261x_config_intr(struct phy_device *phydev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
|
||||
ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, RTL8221B_VND2_INSR);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, RTL8221B_VND2_INER,
|
||||
RTL8261X_INT_MASK_DEFAULT);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
} else {
|
||||
ret = phy_write_mmd(phydev, MDIO_MMD_VEND2, RTL8221B_VND2_INER, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = phy_read_mmd(phydev, MDIO_MMD_VEND2, RTL8221B_VND2_INSR);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static irqreturn_t rtl8261x_handle_interrupt(struct phy_device *phydev)
|
||||
{
|
||||
int irq_status;
|
||||
|
||||
irq_status = phy_read_mmd(phydev, MDIO_MMD_VEND2, RTL8221B_VND2_INSR);
|
||||
if (irq_status < 0) {
|
||||
phy_error(phydev);
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
if (!(irq_status & RTL8261X_INT_MASK_ALL))
|
||||
return IRQ_NONE;
|
||||
|
||||
if (irq_status & (RTL8261X_INT_LINK_CHG | RTL8261X_INT_AUTONEG_DONE |
|
||||
RTL8261X_INT_AUTONEG_ERROR | RTL8261X_INT_JABBER))
|
||||
phy_trigger_machine(phydev);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int rtl8261x_config_aneg(struct phy_device *phydev)
|
||||
{
|
||||
u16 adv_1g = 0;
|
||||
int ret;
|
||||
|
||||
ret = genphy_c45_config_aneg(phydev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (phydev->autoneg == AUTONEG_DISABLE)
|
||||
return 0;
|
||||
|
||||
if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
|
||||
phydev->advertising))
|
||||
adv_1g = ADVERTISE_1000FULL;
|
||||
if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT,
|
||||
phydev->advertising))
|
||||
adv_1g |= ADVERTISE_1000HALF;
|
||||
|
||||
ret = phy_modify_mmd_changed(phydev, MDIO_MMD_VEND2,
|
||||
RTL822X_VND2_C22_REG(MII_CTRL1000),
|
||||
ADVERTISE_1000FULL | ADVERTISE_1000HALF,
|
||||
adv_1g);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
if (ret > 0)
|
||||
return genphy_c45_restart_aneg(phydev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rtl821x_probe(struct phy_device *phydev)
|
||||
{
|
||||
struct device *dev = &phydev->mdio.dev;
|
||||
|
|
@ -3002,6 +3182,18 @@ static struct phy_driver realtek_drvs[] = {
|
|||
.resume = genphy_resume,
|
||||
.read_mmd = genphy_read_mmd_unsupported,
|
||||
.write_mmd = genphy_write_mmd_unsupported,
|
||||
}, {
|
||||
PHY_ID_MATCH_EXACT(RTL_8261C_CG),
|
||||
.name = "Realtek RTL8261C 10Gbps PHY",
|
||||
.probe = rtl8261x_probe,
|
||||
.get_features = rtl8261x_get_features,
|
||||
.config_aneg = rtl8261x_config_aneg,
|
||||
.read_status = rtl8261x_read_status,
|
||||
.config_intr = rtl8261x_config_intr,
|
||||
.handle_interrupt = rtl8261x_handle_interrupt,
|
||||
.soft_reset = genphy_c45_pma_soft_reset,
|
||||
.suspend = genphy_c45_pma_suspend,
|
||||
.resume = genphy_c45_pma_resume,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user