Commit cae0de45 authored by Jakub Kicinski's avatar Jakub Kicinski
Browse files

Merge branch 'net-phylink-improve-phy-validation'

Russell King says:

====================
net: phylink: improve PHY validation

One of the issues which has concerned me about the rate matching
implenentation that we have is that phy_get_rate_matching() returns
whether rate matching will be used for a particular interface, and we
enquire only for one interface.

Aquantia PHYs can be programmed with the rate matching and interface
mode settings on a per-media speed basis using the per-speed vendor 1
global configuration registers.

Thus, it is possible for the PHY to be configured to use rate matching
for 10G, 5G, 2.5G with 10GBASE-R, and then SGMII for the remaining
speeds. Therefore, it clearly doesn't make sense to enquire about rate
matching for just one interface mode.

Also, PHYs that change their interfaces are handled sub-optimally, in
that we validate all the interface modes that the host supports, rather
than the interface modes that the PHY will use.

This patch series changes the way we validate PHYs, but in order to do
so, we need to know exactly which interface modes will be used by the
PHY. So that phylib can convey this information, we add
"possible_interfaces" to struct phy_device.

possible_interfaces is to be filled in by a phylib driver once the PHY
is configured (in other words in the PHYs .config_init method) with the
interface modes that it will switch between. This then allows users of
phylib to know which interface modes will be used by the PHY.

This allows us to solve both these issues: where possible_interfaces is
provided, we can validate which ethtool link modes can be supported by
looking at which interface modes that both the PHY and host support,
and request rate matching information for each mode.

This should improve the accuracy of the validation.

Sending this out again without RFC as Jie Luo will need it for the
QCA8084 changes. No changes except to add the attributations already
received. Thanks!
====================

Link: https://lore.kernel.org/r/ZWCWn+uNkVLPaQhn@shell.armlinux.org.uk


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parents e1df5202 7a1f9a17
Loading
Loading
Loading
Loading
+5 −0
Original line number Diff line number Diff line
@@ -47,6 +47,11 @@
#define VEND1_GLOBAL_CFG_5G			0x031e
#define VEND1_GLOBAL_CFG_10G			0x031f
/* ...and now the fields */
#define VEND1_GLOBAL_CFG_SERDES_MODE		GENMASK(2, 0)
#define VEND1_GLOBAL_CFG_SERDES_MODE_XFI	0
#define VEND1_GLOBAL_CFG_SERDES_MODE_SGMII	3
#define VEND1_GLOBAL_CFG_SERDES_MODE_OCSGMII	4
#define VEND1_GLOBAL_CFG_SERDES_MODE_XFI5G	6
#define VEND1_GLOBAL_CFG_RATE_ADAPT		GENMASK(8, 7)
#define VEND1_GLOBAL_CFG_RATE_ADAPT_NONE	0
#define VEND1_GLOBAL_CFG_RATE_ADAPT_USX		1
+75 −1
Original line number Diff line number Diff line
@@ -656,6 +656,80 @@ static int aqr107_resume(struct phy_device *phydev)
	return aqr107_wait_processor_intensive_op(phydev);
}

static const u16 aqr_global_cfg_regs[] = {
	VEND1_GLOBAL_CFG_10M,
	VEND1_GLOBAL_CFG_100M,
	VEND1_GLOBAL_CFG_1G,
	VEND1_GLOBAL_CFG_2_5G,
	VEND1_GLOBAL_CFG_5G,
	VEND1_GLOBAL_CFG_10G
};

static int aqr107_fill_interface_modes(struct phy_device *phydev)
{
	unsigned long *possible = phydev->possible_interfaces;
	unsigned int serdes_mode, rate_adapt;
	phy_interface_t interface;
	int i, val;

	/* Walk the media-speed configuration registers to determine which
	 * host-side serdes modes may be used by the PHY depending on the
	 * negotiated media speed.
	 */
	for (i = 0; i < ARRAY_SIZE(aqr_global_cfg_regs); i++) {
		val = phy_read_mmd(phydev, MDIO_MMD_VEND1,
				   aqr_global_cfg_regs[i]);
		if (val < 0)
			return val;

		serdes_mode = FIELD_GET(VEND1_GLOBAL_CFG_SERDES_MODE, val);
		rate_adapt = FIELD_GET(VEND1_GLOBAL_CFG_RATE_ADAPT, val);

		switch (serdes_mode) {
		case VEND1_GLOBAL_CFG_SERDES_MODE_XFI:
			if (rate_adapt == VEND1_GLOBAL_CFG_RATE_ADAPT_USX)
				interface = PHY_INTERFACE_MODE_USXGMII;
			else
				interface = PHY_INTERFACE_MODE_10GBASER;
			break;

		case VEND1_GLOBAL_CFG_SERDES_MODE_XFI5G:
			interface = PHY_INTERFACE_MODE_5GBASER;
			break;

		case VEND1_GLOBAL_CFG_SERDES_MODE_OCSGMII:
			interface = PHY_INTERFACE_MODE_2500BASEX;
			break;

		case VEND1_GLOBAL_CFG_SERDES_MODE_SGMII:
			interface = PHY_INTERFACE_MODE_SGMII;
			break;

		default:
			phydev_warn(phydev, "unrecognised serdes mode %u\n",
				    serdes_mode);
			interface = PHY_INTERFACE_MODE_NA;
			break;
		}

		if (interface != PHY_INTERFACE_MODE_NA)
			__set_bit(interface, possible);
	}

	return 0;
}

static int aqr113c_config_init(struct phy_device *phydev)
{
	int ret;

	ret = aqr107_config_init(phydev);
	if (ret < 0)
		return ret;

	return aqr107_fill_interface_modes(phydev);
}

static int aqr107_probe(struct phy_device *phydev)
{
	int ret;
@@ -794,7 +868,7 @@ static struct phy_driver aqr_driver[] = {
	.name           = "Aquantia AQR113C",
	.probe          = aqr107_probe,
	.get_rate_matching = aqr107_get_rate_matching,
	.config_init    = aqr107_config_init,
	.config_init    = aqr113c_config_init,
	.config_aneg    = aqr_config_aneg,
	.config_intr    = aqr_config_intr,
	.handle_interrupt       = aqr_handle_interrupt,
+12 −0
Original line number Diff line number Diff line
@@ -29,8 +29,19 @@ static int bcm84881_wait_init(struct phy_device *phydev)
					 100000, 2000000, false);
}

static void bcm84881_fill_possible_interfaces(struct phy_device *phydev)
{
	unsigned long *possible = phydev->possible_interfaces;

	__set_bit(PHY_INTERFACE_MODE_SGMII, possible);
	__set_bit(PHY_INTERFACE_MODE_2500BASEX, possible);
	__set_bit(PHY_INTERFACE_MODE_10GBASER, possible);
}

static int bcm84881_config_init(struct phy_device *phydev)
{
	bcm84881_fill_possible_interfaces(phydev);

	switch (phydev->interface) {
	case PHY_INTERFACE_MODE_SGMII:
	case PHY_INTERFACE_MODE_2500BASEX:
@@ -39,6 +50,7 @@ static int bcm84881_config_init(struct phy_device *phydev)
	default:
		return -ENODEV;
	}

	return 0;
}

+132 −71
Original line number Diff line number Diff line
@@ -141,13 +141,21 @@ enum {
	MV_V2_TEMP_UNKNOWN	= 0x9600, /* unknown function */
};

struct mv3310_mactype {
	bool valid;
	bool fixed_interface;
	phy_interface_t interface_10g;
};

struct mv3310_chip {
	bool (*has_downshift)(struct phy_device *phydev);
	void (*init_supported_interfaces)(unsigned long *mask);
	int (*get_mactype)(struct phy_device *phydev);
	int (*set_mactype)(struct phy_device *phydev, int mactype);
	int (*select_mactype)(unsigned long *interfaces);
	int (*init_interface)(struct phy_device *phydev, int mactype);

	const struct mv3310_mactype *mactypes;
	size_t n_mactypes;

#ifdef CONFIG_HWMON
	int (*hwmon_read_temp_reg)(struct phy_device *phydev);
@@ -156,11 +164,10 @@ struct mv3310_chip {

struct mv3310_priv {
	DECLARE_BITMAP(supported_interfaces, PHY_INTERFACE_MODE_MAX);
	const struct mv3310_mactype *mactype;

	u32 firmware_ver;
	bool has_downshift;
	bool rate_match;
	phy_interface_t const_interface;

	struct device *hwmon_dev;
	char *hwmon_name;
@@ -702,70 +709,114 @@ static int mv3310_select_mactype(unsigned long *interfaces)
		return -1;
}

static int mv2110_init_interface(struct phy_device *phydev, int mactype)
{
	struct mv3310_priv *priv = dev_get_drvdata(&phydev->mdio.dev);

	priv->rate_match = false;

	if (mactype == MV_PMA_21X0_PORT_CTRL_MACTYPE_10GBASER_RATE_MATCH)
		priv->rate_match = true;
static const struct mv3310_mactype mv2110_mactypes[] = {
	[MV_PMA_21X0_PORT_CTRL_MACTYPE_USXGMII] = {
		.valid = true,
		.fixed_interface = true,
		.interface_10g = PHY_INTERFACE_MODE_USXGMII,
	},
	[MV_PMA_21X0_PORT_CTRL_MACTYPE_5GBASER] = {
		.valid = true,
		.interface_10g = PHY_INTERFACE_MODE_NA,
	},
	[MV_PMA_21X0_PORT_CTRL_MACTYPE_5GBASER_NO_SGMII_AN] = {
		.valid = true,
		.interface_10g = PHY_INTERFACE_MODE_NA,
	},
	[MV_PMA_21X0_PORT_CTRL_MACTYPE_10GBASER_RATE_MATCH] = {
		.valid = true,
		.fixed_interface = true,
		.interface_10g = PHY_INTERFACE_MODE_10GBASER,
	},
};

	if (mactype == MV_PMA_21X0_PORT_CTRL_MACTYPE_USXGMII)
		priv->const_interface = PHY_INTERFACE_MODE_USXGMII;
	else if (mactype == MV_PMA_21X0_PORT_CTRL_MACTYPE_10GBASER_RATE_MATCH)
		priv->const_interface = PHY_INTERFACE_MODE_10GBASER;
	else if (mactype == MV_PMA_21X0_PORT_CTRL_MACTYPE_5GBASER ||
		 mactype == MV_PMA_21X0_PORT_CTRL_MACTYPE_5GBASER_NO_SGMII_AN)
		priv->const_interface = PHY_INTERFACE_MODE_NA;
	else
		return -EINVAL;
static const struct mv3310_mactype mv3310_mactypes[] = {
	[MV_V2_33X0_PORT_CTRL_MACTYPE_RXAUI] = {
		.valid = true,
		.interface_10g = PHY_INTERFACE_MODE_RXAUI,
	},
	[MV_V2_3310_PORT_CTRL_MACTYPE_XAUI_RATE_MATCH] = {
		.valid = true,
		.fixed_interface = true,
		.interface_10g = PHY_INTERFACE_MODE_XAUI,
	},
	[MV_V2_33X0_PORT_CTRL_MACTYPE_RXAUI_RATE_MATCH] = {
		.valid = true,
		.fixed_interface = true,
		.interface_10g = PHY_INTERFACE_MODE_RXAUI,
	},
	[MV_V2_3310_PORT_CTRL_MACTYPE_XAUI] = {
		.valid = true,
		.interface_10g = PHY_INTERFACE_MODE_XAUI,
	},
	[MV_V2_33X0_PORT_CTRL_MACTYPE_10GBASER] = {
		.valid = true,
		.interface_10g = PHY_INTERFACE_MODE_10GBASER,
	},
	[MV_V2_33X0_PORT_CTRL_MACTYPE_10GBASER_NO_SGMII_AN] = {
		.valid = true,
		.interface_10g = PHY_INTERFACE_MODE_10GBASER,
	},
	[MV_V2_33X0_PORT_CTRL_MACTYPE_10GBASER_RATE_MATCH] = {
		.valid = true,
		.fixed_interface = true,
		.interface_10g = PHY_INTERFACE_MODE_10GBASER,
	},
	[MV_V2_33X0_PORT_CTRL_MACTYPE_USXGMII] = {
		.valid = true,
		.fixed_interface = true,
		.interface_10g = PHY_INTERFACE_MODE_USXGMII,
	},
};

	return 0;
}
static const struct mv3310_mactype mv3340_mactypes[] = {
	[MV_V2_33X0_PORT_CTRL_MACTYPE_RXAUI] = {
		.valid = true,
		.interface_10g = PHY_INTERFACE_MODE_RXAUI,
	},
	[MV_V2_3340_PORT_CTRL_MACTYPE_RXAUI_NO_SGMII_AN] = {
		.valid = true,
		.interface_10g = PHY_INTERFACE_MODE_RXAUI,
	},
	[MV_V2_33X0_PORT_CTRL_MACTYPE_RXAUI_RATE_MATCH] = {
		.valid = true,
		.fixed_interface = true,
		.interface_10g = PHY_INTERFACE_MODE_RXAUI,
	},
	[MV_V2_33X0_PORT_CTRL_MACTYPE_10GBASER] = {
		.valid = true,
		.interface_10g = PHY_INTERFACE_MODE_10GBASER,
	},
	[MV_V2_33X0_PORT_CTRL_MACTYPE_10GBASER_NO_SGMII_AN] = {
		.valid = true,
		.interface_10g = PHY_INTERFACE_MODE_10GBASER,
	},
	[MV_V2_33X0_PORT_CTRL_MACTYPE_10GBASER_RATE_MATCH] = {
		.valid = true,
		.fixed_interface = true,
		.interface_10g = PHY_INTERFACE_MODE_10GBASER,
	},
	[MV_V2_33X0_PORT_CTRL_MACTYPE_USXGMII] = {
		.valid = true,
		.fixed_interface = true,
		.interface_10g = PHY_INTERFACE_MODE_USXGMII,
	},
};

static int mv3310_init_interface(struct phy_device *phydev, int mactype)
static void mv3310_fill_possible_interfaces(struct phy_device *phydev)
{
	struct mv3310_priv *priv = dev_get_drvdata(&phydev->mdio.dev);
	unsigned long *possible = phydev->possible_interfaces;
	const struct mv3310_mactype *mactype = priv->mactype;

	priv->rate_match = false;

	if (mactype == MV_V2_33X0_PORT_CTRL_MACTYPE_10GBASER_RATE_MATCH ||
	    mactype == MV_V2_33X0_PORT_CTRL_MACTYPE_RXAUI_RATE_MATCH ||
	    mactype == MV_V2_3310_PORT_CTRL_MACTYPE_XAUI_RATE_MATCH)
		priv->rate_match = true;

	if (mactype == MV_V2_33X0_PORT_CTRL_MACTYPE_USXGMII)
		priv->const_interface = PHY_INTERFACE_MODE_USXGMII;
	else if (mactype == MV_V2_33X0_PORT_CTRL_MACTYPE_10GBASER_RATE_MATCH ||
		 mactype == MV_V2_33X0_PORT_CTRL_MACTYPE_10GBASER_NO_SGMII_AN ||
		 mactype == MV_V2_33X0_PORT_CTRL_MACTYPE_10GBASER)
		priv->const_interface = PHY_INTERFACE_MODE_10GBASER;
	else if (mactype == MV_V2_33X0_PORT_CTRL_MACTYPE_RXAUI_RATE_MATCH ||
		 mactype == MV_V2_33X0_PORT_CTRL_MACTYPE_RXAUI)
		priv->const_interface = PHY_INTERFACE_MODE_RXAUI;
	else if (mactype == MV_V2_3310_PORT_CTRL_MACTYPE_XAUI_RATE_MATCH ||
		 mactype == MV_V2_3310_PORT_CTRL_MACTYPE_XAUI)
		priv->const_interface = PHY_INTERFACE_MODE_XAUI;
	else
		return -EINVAL;
	if (mactype->interface_10g != PHY_INTERFACE_MODE_NA)
		__set_bit(priv->mactype->interface_10g, possible);

	return 0;
	if (!mactype->fixed_interface) {
		__set_bit(PHY_INTERFACE_MODE_5GBASER, possible);
		__set_bit(PHY_INTERFACE_MODE_2500BASEX, possible);
		__set_bit(PHY_INTERFACE_MODE_SGMII, possible);
	}

static int mv3340_init_interface(struct phy_device *phydev, int mactype)
{
	struct mv3310_priv *priv = dev_get_drvdata(&phydev->mdio.dev);
	int err = 0;

	priv->rate_match = false;

	if (mactype == MV_V2_3340_PORT_CTRL_MACTYPE_RXAUI_NO_SGMII_AN)
		priv->const_interface = PHY_INTERFACE_MODE_RXAUI;
	else
		err = mv3310_init_interface(phydev, mactype);

	return err;
}

static int mv3310_config_init(struct phy_device *phydev)
@@ -803,12 +854,15 @@ static int mv3310_config_init(struct phy_device *phydev)
	if (mactype < 0)
		return mactype;

	err = chip->init_interface(phydev, mactype);
	if (err) {
	if (mactype >= chip->n_mactypes || !chip->mactypes[mactype].valid) {
		phydev_err(phydev, "MACTYPE configuration invalid\n");
		return err;
		return -EINVAL;
	}

	priv->mactype = &chip->mactypes[mactype];

	mv3310_fill_possible_interfaces(phydev);

	/* Enable EDPD mode - saving 600mW */
	err = mv3310_set_edpd(phydev, ETHTOOL_PHY_EDPD_DFLT_TX_MSECS);
	if (err)
@@ -935,9 +989,8 @@ static void mv3310_update_interface(struct phy_device *phydev)
	 *
	 * In USXGMII mode the PHY interface mode is also fixed.
	 */
	if (priv->rate_match ||
	    priv->const_interface == PHY_INTERFACE_MODE_USXGMII) {
		phydev->interface = priv->const_interface;
	if (priv->mactype->fixed_interface) {
		phydev->interface = priv->mactype->interface_10g;
		return;
	}

@@ -949,7 +1002,7 @@ static void mv3310_update_interface(struct phy_device *phydev)
	 */
	switch (phydev->speed) {
	case SPEED_10000:
		phydev->interface = priv->const_interface;
		phydev->interface = priv->mactype->interface_10g;
		break;
	case SPEED_5000:
		phydev->interface = PHY_INTERFACE_MODE_5GBASER;
@@ -1163,7 +1216,9 @@ static const struct mv3310_chip mv3310_type = {
	.get_mactype = mv3310_get_mactype,
	.set_mactype = mv3310_set_mactype,
	.select_mactype = mv3310_select_mactype,
	.init_interface = mv3310_init_interface,

	.mactypes = mv3310_mactypes,
	.n_mactypes = ARRAY_SIZE(mv3310_mactypes),

#ifdef CONFIG_HWMON
	.hwmon_read_temp_reg = mv3310_hwmon_read_temp_reg,
@@ -1176,7 +1231,9 @@ static const struct mv3310_chip mv3340_type = {
	.get_mactype = mv3310_get_mactype,
	.set_mactype = mv3310_set_mactype,
	.select_mactype = mv3310_select_mactype,
	.init_interface = mv3340_init_interface,

	.mactypes = mv3340_mactypes,
	.n_mactypes = ARRAY_SIZE(mv3340_mactypes),

#ifdef CONFIG_HWMON
	.hwmon_read_temp_reg = mv3310_hwmon_read_temp_reg,
@@ -1188,7 +1245,9 @@ static const struct mv3310_chip mv2110_type = {
	.get_mactype = mv2110_get_mactype,
	.set_mactype = mv2110_set_mactype,
	.select_mactype = mv2110_select_mactype,
	.init_interface = mv2110_init_interface,

	.mactypes = mv2110_mactypes,
	.n_mactypes = ARRAY_SIZE(mv2110_mactypes),

#ifdef CONFIG_HWMON
	.hwmon_read_temp_reg = mv2110_hwmon_read_temp_reg,
@@ -1200,7 +1259,9 @@ static const struct mv3310_chip mv2111_type = {
	.get_mactype = mv2110_get_mactype,
	.set_mactype = mv2110_set_mactype,
	.select_mactype = mv2110_select_mactype,
	.init_interface = mv2110_init_interface,

	.mactypes = mv2110_mactypes,
	.n_mactypes = ARRAY_SIZE(mv2110_mactypes),

#ifdef CONFIG_HWMON
	.hwmon_read_temp_reg = mv2110_hwmon_read_temp_reg,
+2 −0
Original line number Diff line number Diff line
@@ -1246,6 +1246,8 @@ int phy_init_hw(struct phy_device *phydev)
	if (ret < 0)
		return ret;

	phy_interface_zero(phydev->possible_interfaces);

	if (phydev->drv->config_init) {
		ret = phydev->drv->config_init(phydev);
		if (ret < 0)
Loading