Commit 08048ba4 authored by Vladimir Oltean's avatar Vladimir Oltean Committed by Jakub Kicinski
Browse files

net: phy: aquantia: save a local shadow of GLOBAL_CFG register values



Currently, aqr_gen2_fill_interface_modes() reads VEND1_GLOBAL_CFG_*
registers to populate phydev->supported_interfaces. But this is not
the only place which needs to read these registers. There is also
aqr107_read_rate().

Based on the premise that these values are statically set by firmware
and the driver only needs to read them, the proposal is to read them
only once, at config_init() time, and use the cached values also in
aqr107_read_rate().

This patch only refactors the aqr_gen2_fill_interface_modes() code to
save the registers to driver memory, and to populate supported_interfaces
based on that.

Signed-off-by: default avatarVladimir Oltean <vladimir.oltean@nxp.com>
Link: https://patch.msgid.link/20250821152022.1065237-7-vladimir.oltean@nxp.com


Signed-off-by: default avatarJakub Kicinski <kuba@kernel.org>
parent ab1dfcb5
Loading
Loading
Loading
Loading
+27 −0
Original line number Diff line number Diff line
@@ -174,11 +174,38 @@ static const struct aqr107_hw_stat aqr107_hw_stats[] = {

#define AQR107_SGMII_STAT_SZ ARRAY_SIZE(aqr107_hw_stats)

static const struct {
	int speed;
	u16 reg;
} aqr_global_cfg_regs[] = {
	{ SPEED_10,	VEND1_GLOBAL_CFG_10M, },
	{ SPEED_100,	VEND1_GLOBAL_CFG_100M, },
	{ SPEED_1000,	VEND1_GLOBAL_CFG_1G, },
	{ SPEED_2500,	VEND1_GLOBAL_CFG_2_5G, },
	{ SPEED_5000,	VEND1_GLOBAL_CFG_5G, },
	{ SPEED_10000,	VEND1_GLOBAL_CFG_10G, },
};

#define AQR_NUM_GLOBAL_CFG ARRAY_SIZE(aqr_global_cfg_regs)

enum aqr_rate_adaptation {
	AQR_RATE_ADAPT_NONE,
	AQR_RATE_ADAPT_USX,
	AQR_RATE_ADAPT_PAUSE,
};

struct aqr_global_syscfg {
	int speed;
	phy_interface_t interface;
	enum aqr_rate_adaptation rate_adapt;
};

struct aqr107_priv {
	u64 sgmii_stats[AQR107_SGMII_STAT_SZ];
	unsigned long leds_active_low;
	unsigned long leds_active_high;
	bool wait_on_global_cfg;
	struct aqr_global_syscfg global_cfg[AQR_NUM_GLOBAL_CFG];
};

#if IS_REACHABLE(CONFIG_HWMON)
+60 −31
Original line number Diff line number Diff line
@@ -860,44 +860,24 @@ static int aqr_gen1_config_init(struct phy_device *phydev)
	return 0;
}

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 aqr_gen2_fill_interface_modes(struct phy_device *phydev)
/* 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.
 */
static int aqr_gen2_read_global_syscfg(struct phy_device *phydev)
{
	unsigned long *possible = phydev->possible_interfaces;
	struct aqr107_priv *priv = phydev->priv;
	unsigned int serdes_mode, rate_adapt;
	phy_interface_t interface;
	int i, val, ret;
	int i, val;

	/* It's been observed on some models that - when coming out of suspend
	 * - the FW signals that the PHY is ready but the GLOBAL_CFG registers
	 * continue on returning zeroes for some time. Let's poll the 100M
	 * register until it returns a real value as both 113c and 115c support
	 * this mode.
	 */
	if (priv->wait_on_global_cfg) {
		ret = phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1,
						VEND1_GLOBAL_CFG_100M, val,
						val != 0, 1000, 100000, false);
		if (ret)
			return ret;
	}
	for (i = 0; i < AQR_NUM_GLOBAL_CFG; i++) {
		struct aqr_global_syscfg *syscfg = &priv->global_cfg[i];

		syscfg->speed = aqr_global_cfg_regs[i].speed;

	/* 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]);
				   aqr_global_cfg_regs[i].reg);
		if (val < 0)
			return val;

@@ -931,6 +911,55 @@ static int aqr_gen2_fill_interface_modes(struct phy_device *phydev)
			break;
		}

		syscfg->interface = interface;

		switch (rate_adapt) {
		case VEND1_GLOBAL_CFG_RATE_ADAPT_NONE:
			syscfg->rate_adapt = AQR_RATE_ADAPT_NONE;
			break;
		case VEND1_GLOBAL_CFG_RATE_ADAPT_USX:
			syscfg->rate_adapt = AQR_RATE_ADAPT_USX;
			break;
		case VEND1_GLOBAL_CFG_RATE_ADAPT_PAUSE:
			syscfg->rate_adapt = AQR_RATE_ADAPT_PAUSE;
			break;
		default:
			phydev_warn(phydev, "unrecognized rate adapt mode %u\n",
				    rate_adapt);
			break;
		}
	}

	return 0;
}

static int aqr_gen2_fill_interface_modes(struct phy_device *phydev)
{
	unsigned long *possible = phydev->possible_interfaces;
	struct aqr107_priv *priv = phydev->priv;
	phy_interface_t interface;
	int i, val, ret;

	/* It's been observed on some models that - when coming out of suspend
	 * - the FW signals that the PHY is ready but the GLOBAL_CFG registers
	 * continue on returning zeroes for some time. Let's poll the 100M
	 * register until it returns a real value as both 113c and 115c support
	 * this mode.
	 */
	if (priv->wait_on_global_cfg) {
		ret = phy_read_mmd_poll_timeout(phydev, MDIO_MMD_VEND1,
						VEND1_GLOBAL_CFG_100M, val,
						val != 0, 1000, 100000, false);
		if (ret)
			return ret;
	}

	ret = aqr_gen2_read_global_syscfg(phydev);
	if (ret)
		return ret;

	for (i = 0; i < AQR_NUM_GLOBAL_CFG; i++) {
		interface = priv->global_cfg[i].interface;
		if (interface != PHY_INTERFACE_MODE_NA)
			__set_bit(interface, possible);
	}