Commit fd1de45a authored by Paolo Abeni's avatar Paolo Abeni
Browse files

Merge branch 'net-phy-realtek-various-improvements-for-2-5ge-phys'

Daniel Golle says:

====================
net: phy: realtek: various improvements for 2.5GE PHYs

This series improves the RealTek PHY driver, mostly for 2.5GE PHYs.
It implements configuring SGMII and 2500Base-X in-band auto-negotiation
and improves using the PHYs in Clause-22-only mode.

Note that the rtl822x_serdes_write() function introduced by this series
is going to be reused to configure polarities of SerDes RX and TX lanes
once series "PHY polarity inversion via generic device tree properties"
has been applied.

Access to other registers on MDIO_MMD_VEND2 is important for more than
just configuring autonegotiation, it is also used to setup ALDPS or to
disable the PHY responding to the MDIO broadcast address 0. Both will be
implemented by follow-up patches.

The address translation function for registers on MDIO_MMD_VEND2 into
paged registers can potentially also be used to describe other paged
access in a more consistent way, but that mostly makes sense on PHYs
which also support Clause-45, so this series doesn't convert all the
existing paged access on RealTek's 1GE PHYs which do not support
Clause-45.
====================

Link: https://patch.msgid.link/cover.1767630451.git.daniel@makrotopia.org


Signed-off-by: default avatarPaolo Abeni <pabeni@redhat.com>
parents 957346a6 d8489935
Loading
Loading
Loading
Loading
+0 −6
Original line number Diff line number Diff line
@@ -7,7 +7,6 @@
#define __PHYLIB_INTERNAL_H

struct phy_device;
struct mii_bus;

/*
 * phy_supported_speeds - return all speeds currently supported by a PHY device
@@ -21,11 +20,6 @@ void of_set_phy_timing_role(struct phy_device *phydev);
int phy_speed_down_core(struct phy_device *phydev);
void phy_check_downshift(struct phy_device *phydev);

int mmd_phy_read(struct mii_bus *bus, int phy_addr, bool is_c45,
		 int devad, u32 regnum);
int mmd_phy_write(struct mii_bus *bus, int phy_addr, bool is_c45,
		  int devad, u32 regnum, u16 val);

int genphy_c45_read_eee_adv(struct phy_device *phydev, unsigned long *adv);

#endif /* __PHYLIB_INTERNAL_H */
+5 −0
Original line number Diff line number Diff line
@@ -8,6 +8,7 @@

struct device_node;
struct phy_device;
struct mii_bus;

struct device_node *phy_package_get_node(struct phy_device *phydev);
void *phy_package_get_priv(struct phy_device *phydev);
@@ -30,5 +31,9 @@ int devm_phy_package_join(struct device *dev, struct phy_device *phydev,
			  int base_addr, size_t priv_size);
int devm_of_phy_package_join(struct device *dev, struct phy_device *phydev,
			     size_t priv_size);
int mmd_phy_read(struct mii_bus *bus, int phy_addr, bool is_c45,
		 int devad, u32 regnum);
int mmd_phy_write(struct mii_bus *bus, int phy_addr, bool is_c45,
		  int devad, u32 regnum, u16 val);

#endif /* __PHYLIB_H */
+230 −77
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@
#include <linux/clk.h>
#include <linux/string_choices.h>

#include "../phylib.h"
#include "realtek.h"

#define RTL8201F_IER				0x13
@@ -131,9 +132,17 @@
#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_VND1_SERDES_CMD			0x7587
#define  RTL822X_VND1_SERDES_CMD_WRITE		BIT(1)
#define  RTL822X_VND1_SERDES_CMD_BUSY		BIT(0)
#define RTL822X_VND1_SERDES_ADDR		0x7588
#define  RTL822X_VND1_SERDES_ADDR_AUTONEG	0x2
#define   RTL822X_VND1_SERDES_INBAND_DISABLE	0x71d0
#define   RTL822X_VND1_SERDES_INBAND_ENABLE	0x70d0
#define RTL822X_VND1_SERDES_DATA		0x7589

#define RTL822X_VND2_TO_PAGE(reg)		((reg) >> 4)
#define RTL822X_VND2_TO_PAGE_REG(reg)		(16 + (((reg) & GENMASK(3, 0)) >> 1))
#define RTL822X_VND2_C22_REG(reg)		(0xa400 + 2 * (reg))

#define RTL8221B_VND2_INER			0xa4d2
@@ -1145,7 +1154,8 @@ static int rtlgen_read_status(struct phy_device *phydev)
	if (!phydev->link)
		return 0;

	val = phy_read_paged(phydev, 0xa43, 0x12);
	val = phy_read_paged(phydev, RTL822X_VND2_TO_PAGE(RTL_VND2_PHYSR),
			     RTL822X_VND2_TO_PAGE_REG(RTL_VND2_PHYSR));
	if (val < 0)
		return val;

@@ -1238,6 +1248,79 @@ static int rtl822x_probe(struct phy_device *phydev)
	return 0;
}

/* RTL822x cannot access MDIO_MMD_VEND2 via MII_MMD_CTRL/MII_MMD_DATA.
 * A mapping to use paged access needs to be used instead.
 * All other MMD devices can be accessed as usual.
 */
static int rtl822xb_read_mmd(struct phy_device *phydev, int devnum, u16 reg)
{
	int oldpage, ret, read_ret;
	u16 page;

	/* Use default method for all MMDs except MDIO_MMD_VEND2 or in case
	 * Clause-45 access is available
	 */
	if (devnum != MDIO_MMD_VEND2 || phydev->is_c45)
		return mmd_phy_read(phydev->mdio.bus, phydev->mdio.addr,
				    phydev->is_c45, devnum, reg);

	/* Use paged access for MDIO_MMD_VEND2 over Clause-22 */
	page = RTL822X_VND2_TO_PAGE(reg);
	oldpage = __phy_read(phydev, RTL821x_PAGE_SELECT);
	if (oldpage < 0)
		return oldpage;

	if (oldpage != page) {
		ret = __phy_write(phydev, RTL821x_PAGE_SELECT, page);
		if (ret < 0)
			return ret;
	}

	read_ret = __phy_read(phydev, RTL822X_VND2_TO_PAGE_REG(reg));
	if (oldpage != page) {
		ret = __phy_write(phydev, RTL821x_PAGE_SELECT, oldpage);
		if (ret < 0)
			return ret;
	}

	return read_ret;
}

static int rtl822xb_write_mmd(struct phy_device *phydev, int devnum, u16 reg,
			      u16 val)
{
	int oldpage, ret, write_ret;
	u16 page;

	/* Use default method for all MMDs except MDIO_MMD_VEND2 or in case
	 * Clause-45 access is available
	 */
	if (devnum != MDIO_MMD_VEND2 || phydev->is_c45)
		return mmd_phy_write(phydev->mdio.bus, phydev->mdio.addr,
				     phydev->is_c45, devnum, reg, val);

	/* Use paged access for MDIO_MMD_VEND2 over Clause-22 */
	page = RTL822X_VND2_TO_PAGE(reg);
	oldpage = __phy_read(phydev, RTL821x_PAGE_SELECT);
	if (oldpage < 0)
		return oldpage;

	if (oldpage != page) {
		ret = __phy_write(phydev, RTL821x_PAGE_SELECT, page);
		if (ret < 0)
			return ret;
	}

	write_ret = __phy_write(phydev,  RTL822X_VND2_TO_PAGE_REG(reg), val);
	if (oldpage != page) {
		ret = __phy_write(phydev, RTL821x_PAGE_SELECT, oldpage);
		if (ret < 0)
			return ret;
	}

	return write_ret;
}

static int rtl822x_set_serdes_option_mode(struct phy_device *phydev, bool gen1)
{
	bool has_2500, has_sgmii;
@@ -1308,6 +1391,50 @@ static int rtl822xb_config_init(struct phy_device *phydev)
	return rtl822x_set_serdes_option_mode(phydev, false);
}

static int rtl822x_serdes_write(struct phy_device *phydev, u16 reg, u16 val)
{
	int ret, poll;

	ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, RTL822X_VND1_SERDES_ADDR, reg);
	if (ret < 0)
		return ret;

	ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, RTL822X_VND1_SERDES_DATA, val);
	if (ret < 0)
		return ret;

	ret = phy_write_mmd(phydev, MDIO_MMD_VEND1, RTL822X_VND1_SERDES_CMD,
			    RTL822X_VND1_SERDES_CMD_WRITE |
			    RTL822X_VND1_SERDES_CMD_BUSY);
	if (ret < 0)
		return ret;

	return phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1,
					 RTL822X_VND1_SERDES_CMD, poll,
					 !(poll & RTL822X_VND1_SERDES_CMD_BUSY),
					 500, 100000, false);
}

static int rtl822x_config_inband(struct phy_device *phydev, unsigned int modes)
{
	return rtl822x_serdes_write(phydev, RTL822X_VND1_SERDES_ADDR_AUTONEG,
				    (modes != LINK_INBAND_DISABLE) ?
				    RTL822X_VND1_SERDES_INBAND_ENABLE :
				    RTL822X_VND1_SERDES_INBAND_DISABLE);
}

static unsigned int rtl822x_inband_caps(struct phy_device *phydev,
					phy_interface_t interface)
{
	switch (interface) {
	case PHY_INTERFACE_MODE_2500BASEX:
	case PHY_INTERFACE_MODE_SGMII:
		return LINK_INBAND_DISABLE | LINK_INBAND_ENABLE;
	default:
		return 0;
	}
}

static int rtl822xb_get_rate_matching(struct phy_device *phydev,
				      phy_interface_t iface)
{
@@ -2097,18 +2224,24 @@ static struct phy_driver realtek_drvs[] = {
		.resume		= rtlgen_resume,
		.read_page	= rtl821x_read_page,
		.write_page	= rtl821x_write_page,
		.read_mmd	= rtl822xb_read_mmd,
		.write_mmd	= rtl822xb_write_mmd,
	}, {
		.match_phy_device = rtl8221b_match_phy_device,
		.name		= "RTL8226B_RTL8221B 2.5Gbps PHY",
		.get_features	= rtl822x_get_features,
		.config_aneg	= rtl822x_config_aneg,
		.config_init	= rtl822xb_config_init,
		.inband_caps	= rtl822x_inband_caps,
		.config_inband	= rtl822x_config_inband,
		.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,
		.read_mmd	= rtl822xb_read_mmd,
		.write_mmd	= rtl822xb_write_mmd,
	}, {
		PHY_ID_MATCH_EXACT(0x001cc838),
		.name		= "RTL8226-CG 2.5Gbps PHY",
@@ -2116,21 +2249,29 @@ static struct phy_driver realtek_drvs[] = {
		.get_features	= rtl822x_c45_get_features,
		.config_aneg	= rtl822x_c45_config_aneg,
		.config_init	= rtl822x_config_init,
		.inband_caps	= rtl822x_inband_caps,
		.config_inband	= rtl822x_config_inband,
		.read_status	= rtl822xb_c45_read_status,
		.suspend	= genphy_c45_pma_suspend,
		.resume		= rtlgen_c45_resume,
		.read_mmd	= rtl822xb_read_mmd,
		.write_mmd	= rtl822xb_write_mmd,
	}, {
		PHY_ID_MATCH_EXACT(0x001cc848),
		.name		= "RTL8226B-CG_RTL8221B-CG 2.5Gbps PHY",
		.get_features	= rtl822x_get_features,
		.config_aneg	= rtl822x_config_aneg,
		.config_init	= rtl822xb_config_init,
		.inband_caps	= rtl822x_inband_caps,
		.config_inband	= rtl822x_config_inband,
		.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,
		.read_mmd	= rtl822xb_read_mmd,
		.write_mmd	= rtl822xb_write_mmd,
	}, {
		.match_phy_device = rtl8221b_vb_cg_c22_match_phy_device,
		.name		= "RTL8221B-VB-CG 2.5Gbps PHY (C22)",
@@ -2138,12 +2279,16 @@ static struct phy_driver realtek_drvs[] = {
		.get_features	= rtl822x_get_features,
		.config_aneg	= rtl822x_config_aneg,
		.config_init	= rtl822xb_config_init,
		.inband_caps	= rtl822x_inband_caps,
		.config_inband	= rtl822x_config_inband,
		.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,
		.read_mmd	= rtl822xb_read_mmd,
		.write_mmd	= rtl822xb_write_mmd,
	}, {
		.match_phy_device = rtl8221b_vb_cg_c45_match_phy_device,
		.name		= "RTL8221B-VB-CG 2.5Gbps PHY (C45)",
@@ -2151,6 +2296,8 @@ static struct phy_driver realtek_drvs[] = {
		.handle_interrupt = rtl8221b_handle_interrupt,
		.probe		= rtl822x_probe,
		.config_init	= rtl822xb_config_init,
		.inband_caps	= rtl822x_inband_caps,
		.config_inband	= rtl822x_config_inband,
		.get_rate_matching = rtl822xb_get_rate_matching,
		.get_features	= rtl822x_c45_get_features,
		.config_aneg	= rtl822x_c45_config_aneg,
@@ -2164,12 +2311,16 @@ static struct phy_driver realtek_drvs[] = {
		.get_features	= rtl822x_get_features,
		.config_aneg	= rtl822x_config_aneg,
		.config_init	= rtl822xb_config_init,
		.inband_caps	= rtl822x_inband_caps,
		.config_inband	= rtl822x_config_inband,
		.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,
		.read_mmd	= rtl822xb_read_mmd,
		.write_mmd	= rtl822xb_write_mmd,
	}, {
		.match_phy_device = rtl8221b_vm_cg_c45_match_phy_device,
		.name		= "RTL8221B-VM-CG 2.5Gbps PHY (C45)",
@@ -2177,6 +2328,8 @@ static struct phy_driver realtek_drvs[] = {
		.handle_interrupt = rtl8221b_handle_interrupt,
		.probe		= rtl822x_probe,
		.config_init	= rtl822xb_config_init,
		.inband_caps	= rtl822x_inband_caps,
		.config_inband	= rtl822x_config_inband,
		.get_rate_matching = rtl822xb_get_rate_matching,
		.get_features	= rtl822x_c45_get_features,
		.config_aneg	= rtl822x_c45_config_aneg,