mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-05-10 09:09:55 -04:00
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: Vladimir Oltean <vladimir.oltean@nxp.com> Link: https://patch.msgid.link/20250821152022.1065237-7-vladimir.oltean@nxp.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
This commit is contained in:
committed by
Jakub Kicinski
parent
ab1dfcb5bc
commit
08048ba428
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user