* [PATCH net-next v3 1/3] net: phy: Move/rename phylink_interface_max_speed
2022-12-02 18:17 [PATCH net-next v3 0/3] phy: aquantia: Determine rate adaptation support from registers Sean Anderson
@ 2022-12-02 18:17 ` Sean Anderson
2022-12-02 19:00 ` Russell King (Oracle)
2022-12-02 18:17 ` [PATCH net-next v3 2/3] net: mdio: Update speed register bits Sean Anderson
2022-12-02 18:17 ` [PATCH net-next v3 3/3] phy: aquantia: Determine rate adaptation support from registers Sean Anderson
2 siblings, 1 reply; 8+ messages in thread
From: Sean Anderson @ 2022-12-02 18:17 UTC (permalink / raw)
To: Andrew Lunn, Heiner Kallweit, Russell King, netdev
Cc: David S . Miller, Tim Harvey, Vladimir Oltean, Eric Dumazet,
Paolo Abeni, linux-kernel, Jakub Kicinski, Sean Anderson
This is really a core phy function like phy_interface_num_ports. Move it
to drivers/net/phy/phy-core.c and rename it accordingly.
Signed-off-by: Sean Anderson <sean.anderson@seco.com>
---
(no changes since v2)
Changes in v2:
- New
drivers/net/phy/phy-core.c | 70 +++++++++++++++++++++++++++++++++++
drivers/net/phy/phylink.c | 75 ++------------------------------------
include/linux/phy.h | 1 +
3 files changed, 74 insertions(+), 72 deletions(-)
diff --git a/drivers/net/phy/phy-core.c b/drivers/net/phy/phy-core.c
index 5d08c627a516..5a515434a228 100644
--- a/drivers/net/phy/phy-core.c
+++ b/drivers/net/phy/phy-core.c
@@ -150,6 +150,76 @@ int phy_interface_num_ports(phy_interface_t interface)
}
EXPORT_SYMBOL_GPL(phy_interface_num_ports);
+/**
+ * phy_interface_max_speed() - get the maximum speed of a phy interface
+ * @interface: phy interface mode defined by &typedef phy_interface_t
+ *
+ * Determine the maximum speed of a phy interface. This is intended to help
+ * determine the correct speed to pass to the MAC when the phy is performing
+ * rate matching.
+ *
+ * Return: The maximum speed of @interface
+ */
+int phy_interface_max_speed(phy_interface_t interface)
+{
+ switch (interface) {
+ case PHY_INTERFACE_MODE_100BASEX:
+ case PHY_INTERFACE_MODE_REVRMII:
+ case PHY_INTERFACE_MODE_RMII:
+ case PHY_INTERFACE_MODE_SMII:
+ case PHY_INTERFACE_MODE_REVMII:
+ case PHY_INTERFACE_MODE_MII:
+ return SPEED_100;
+
+ case PHY_INTERFACE_MODE_TBI:
+ case PHY_INTERFACE_MODE_MOCA:
+ case PHY_INTERFACE_MODE_RTBI:
+ case PHY_INTERFACE_MODE_1000BASEX:
+ case PHY_INTERFACE_MODE_1000BASEKX:
+ case PHY_INTERFACE_MODE_TRGMII:
+ case PHY_INTERFACE_MODE_RGMII_TXID:
+ case PHY_INTERFACE_MODE_RGMII_RXID:
+ case PHY_INTERFACE_MODE_RGMII_ID:
+ case PHY_INTERFACE_MODE_RGMII:
+ case PHY_INTERFACE_MODE_QSGMII:
+ case PHY_INTERFACE_MODE_SGMII:
+ case PHY_INTERFACE_MODE_GMII:
+ return SPEED_1000;
+
+ case PHY_INTERFACE_MODE_2500BASEX:
+ return SPEED_2500;
+
+ case PHY_INTERFACE_MODE_5GBASER:
+ return SPEED_5000;
+
+ case PHY_INTERFACE_MODE_XGMII:
+ case PHY_INTERFACE_MODE_RXAUI:
+ case PHY_INTERFACE_MODE_XAUI:
+ case PHY_INTERFACE_MODE_10GBASER:
+ case PHY_INTERFACE_MODE_10GKR:
+ case PHY_INTERFACE_MODE_USXGMII:
+ case PHY_INTERFACE_MODE_QUSGMII:
+ return SPEED_10000;
+
+ case PHY_INTERFACE_MODE_25GBASER:
+ return SPEED_25000;
+
+ case PHY_INTERFACE_MODE_XLGMII:
+ return SPEED_40000;
+
+ case PHY_INTERFACE_MODE_INTERNAL:
+ case PHY_INTERFACE_MODE_NA:
+ case PHY_INTERFACE_MODE_MAX:
+ /* No idea! Garbage in, unknown out */
+ return SPEED_UNKNOWN;
+ }
+
+ /* If we get here, someone forgot to add an interface mode above */
+ WARN_ON_ONCE(1);
+ return SPEED_UNKNOWN;
+}
+EXPORT_SYMBOL_GPL(phy_interface_max_speed);
+
/* A mapping of all SUPPORTED settings to speed/duplex. This table
* must be grouped by speed and sorted in descending match priority
* - iow, descending speed.
diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c
index 09cc65c0da93..f8cba09f9d87 100644
--- a/drivers/net/phy/phylink.c
+++ b/drivers/net/phy/phylink.c
@@ -156,75 +156,6 @@ static const char *phylink_an_mode_str(unsigned int mode)
return mode < ARRAY_SIZE(modestr) ? modestr[mode] : "unknown";
}
-/**
- * phylink_interface_max_speed() - get the maximum speed of a phy interface
- * @interface: phy interface mode defined by &typedef phy_interface_t
- *
- * Determine the maximum speed of a phy interface. This is intended to help
- * determine the correct speed to pass to the MAC when the phy is performing
- * rate matching.
- *
- * Return: The maximum speed of @interface
- */
-static int phylink_interface_max_speed(phy_interface_t interface)
-{
- switch (interface) {
- case PHY_INTERFACE_MODE_100BASEX:
- case PHY_INTERFACE_MODE_REVRMII:
- case PHY_INTERFACE_MODE_RMII:
- case PHY_INTERFACE_MODE_SMII:
- case PHY_INTERFACE_MODE_REVMII:
- case PHY_INTERFACE_MODE_MII:
- return SPEED_100;
-
- case PHY_INTERFACE_MODE_TBI:
- case PHY_INTERFACE_MODE_MOCA:
- case PHY_INTERFACE_MODE_RTBI:
- case PHY_INTERFACE_MODE_1000BASEX:
- case PHY_INTERFACE_MODE_1000BASEKX:
- case PHY_INTERFACE_MODE_TRGMII:
- case PHY_INTERFACE_MODE_RGMII_TXID:
- case PHY_INTERFACE_MODE_RGMII_RXID:
- case PHY_INTERFACE_MODE_RGMII_ID:
- case PHY_INTERFACE_MODE_RGMII:
- case PHY_INTERFACE_MODE_QSGMII:
- case PHY_INTERFACE_MODE_SGMII:
- case PHY_INTERFACE_MODE_GMII:
- return SPEED_1000;
-
- case PHY_INTERFACE_MODE_2500BASEX:
- return SPEED_2500;
-
- case PHY_INTERFACE_MODE_5GBASER:
- return SPEED_5000;
-
- case PHY_INTERFACE_MODE_XGMII:
- case PHY_INTERFACE_MODE_RXAUI:
- case PHY_INTERFACE_MODE_XAUI:
- case PHY_INTERFACE_MODE_10GBASER:
- case PHY_INTERFACE_MODE_10GKR:
- case PHY_INTERFACE_MODE_USXGMII:
- case PHY_INTERFACE_MODE_QUSGMII:
- return SPEED_10000;
-
- case PHY_INTERFACE_MODE_25GBASER:
- return SPEED_25000;
-
- case PHY_INTERFACE_MODE_XLGMII:
- return SPEED_40000;
-
- case PHY_INTERFACE_MODE_INTERNAL:
- case PHY_INTERFACE_MODE_NA:
- case PHY_INTERFACE_MODE_MAX:
- /* No idea! Garbage in, unknown out */
- return SPEED_UNKNOWN;
- }
-
- /* If we get here, someone forgot to add an interface mode above */
- WARN_ON_ONCE(1);
- return SPEED_UNKNOWN;
-}
-
/**
* phylink_caps_to_linkmodes() - Convert capabilities to ethtool link modes
* @linkmodes: ethtool linkmode mask (must be already initialised)
@@ -435,7 +366,7 @@ unsigned long phylink_get_capabilities(phy_interface_t interface,
unsigned long mac_capabilities,
int rate_matching)
{
- int max_speed = phylink_interface_max_speed(interface);
+ int max_speed = phy_interface_max_speed(interface);
unsigned long caps = MAC_SYM_PAUSE | MAC_ASYM_PAUSE;
unsigned long matched_caps = 0;
@@ -1221,7 +1152,7 @@ static void phylink_link_up(struct phylink *pl,
* the link_state) to the interface speed, and will send
* pause frames to the MAC to limit its transmission speed.
*/
- speed = phylink_interface_max_speed(link_state.interface);
+ speed = phy_interface_max_speed(link_state.interface);
duplex = DUPLEX_FULL;
rx_pause = true;
break;
@@ -1231,7 +1162,7 @@ static void phylink_link_up(struct phylink *pl,
* the link_state) to the interface speed, and will cause
* collisions to the MAC to limit its transmission speed.
*/
- speed = phylink_interface_max_speed(link_state.interface);
+ speed = phy_interface_max_speed(link_state.interface);
duplex = DUPLEX_HALF;
break;
}
diff --git a/include/linux/phy.h b/include/linux/phy.h
index 71eeb4e3b1fd..65d21a79bab3 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -1004,6 +1004,7 @@ const char *phy_duplex_to_str(unsigned int duplex);
const char *phy_rate_matching_to_str(int rate_matching);
int phy_interface_num_ports(phy_interface_t interface);
+int phy_interface_max_speed(phy_interface_t interface);
/* A structure for mapping a particular speed and duplex
* combination to a particular SUPPORTED and ADVERTISED value
--
2.35.1.1320.gc452695387.dirty
^ permalink raw reply related [flat|nested] 8+ messages in thread* [PATCH net-next v3 3/3] phy: aquantia: Determine rate adaptation support from registers
2022-12-02 18:17 [PATCH net-next v3 0/3] phy: aquantia: Determine rate adaptation support from registers Sean Anderson
2022-12-02 18:17 ` [PATCH net-next v3 1/3] net: phy: Move/rename phylink_interface_max_speed Sean Anderson
2022-12-02 18:17 ` [PATCH net-next v3 2/3] net: mdio: Update speed register bits Sean Anderson
@ 2022-12-02 18:17 ` Sean Anderson
2022-12-03 5:26 ` Jakub Kicinski
2 siblings, 1 reply; 8+ messages in thread
From: Sean Anderson @ 2022-12-02 18:17 UTC (permalink / raw)
To: Andrew Lunn, Heiner Kallweit, Russell King, netdev
Cc: David S . Miller, Tim Harvey, Vladimir Oltean, Eric Dumazet,
Paolo Abeni, linux-kernel, Jakub Kicinski, Sean Anderson
When autonegotiation completes, the phy interface will be set based on
the global config register for that speed. If the SERDES mode is set to
something which the MAC does not support, then the link will not come
up. To avoid this, validate each combination of interface speed and link
speed which might be configured. This way, we ensure that we only
consider rate adaptation in our advertisement when we can actually use
it.
The API for get_rate_matching requires that PHY_INTERFACE_MODE_NA be
handled properly. To do this, we adopt a structure similar to
phylink_validate. At the top-level, we either validate a particular
interface speed or all of them. Below that, we validate each combination
of serdes speed and link speed.
For some firmwares, not all speeds are supported. In this case, the
global config register for that speed will be initialized to zero
(indicating that rate adaptation is not supported). We can detect this
by reading the PMA/PMD speed register to determine which speeds are
supported. This register is read once in probe and cached for later.
Signed-off-by: Sean Anderson <sean.anderson@seco.com>
---
This commit fixes 3c42563b3041 ("net: phy: aquantia: Add support for
rate matching"). In an effort to avoid backporting of this commit until
it has soaked in master for a while, the fixes tag has been left off.
Changes in v3:
- Fix incorrect bits for PMA/PMD speed
Changes in v2:
- Rework to just validate things instead of modifying registers
drivers/net/phy/aquantia_main.c | 160 ++++++++++++++++++++++++++++++--
1 file changed, 154 insertions(+), 6 deletions(-)
diff --git a/drivers/net/phy/aquantia_main.c b/drivers/net/phy/aquantia_main.c
index 334a6904ca5a..e157e000d95b 100644
--- a/drivers/net/phy/aquantia_main.c
+++ b/drivers/net/phy/aquantia_main.c
@@ -111,6 +111,12 @@
#define VEND1_GLOBAL_CFG_RATE_ADAPT_NONE 0
#define VEND1_GLOBAL_CFG_RATE_ADAPT_USX 1
#define VEND1_GLOBAL_CFG_RATE_ADAPT_PAUSE 2
+#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_SERDES_MODE_XFI20G 7
#define VEND1_GLOBAL_RSVD_STAT1 0xc885
#define VEND1_GLOBAL_RSVD_STAT1_FW_BUILD_ID GENMASK(7, 4)
@@ -175,6 +181,7 @@ static const struct aqr107_hw_stat aqr107_hw_stats[] = {
struct aqr107_priv {
u64 sgmii_stats[AQR107_SGMII_STAT_SZ];
+ int pmapmd_speeds;
};
static int aqr107_get_sset_count(struct phy_device *phydev)
@@ -677,13 +684,146 @@ static int aqr107_wait_processor_intensive_op(struct phy_device *phydev)
return 0;
}
+/**
+ * struct aqr107_link_speed_cfg - Common configuration for link speeds
+ * @speed - The speed of this config
+ * @reg - The global system configuration register for this speed
+ * @speed_bit - The bit in the PMA/PMD speed ability register which determines
+ * whether this link speed is supported
+ */
+struct aqr107_link_speed_cfg {
+ int speed;
+ u16 reg, speed_bit;
+};
+
+/**
+ * aqr107_rate_adapt_ok_one() - Validate rate adaptation for one configuration
+ * @phydev: The phy to act on
+ * @serdes_speed: The speed of the serdes (aka the phy interface)
+ * @link_cfg: The config for the link speed
+ *
+ * This function validates whether rate adaptation will work for a particular
+ * combination of @serdes_speed and @link_cfg.
+ *
+ * Return: %true if the @link_cfg.reg is configured for rate adaptation, %true
+ * if @link_cfg.speed will not be advertised, %false otherwise.
+ */
+static bool aqr107_rate_adapt_ok_one(struct phy_device *phydev, int serdes_speed,
+ const struct aqr107_link_speed_cfg *link_cfg)
+{
+ struct aqr107_priv *priv = phydev->priv;
+ int val;
+
+ phydev_dbg(phydev, "validating link_speed=%d serdes_speed=%d\n",
+ link_cfg->speed, serdes_speed);
+
+ /* Vacuously OK, since we won't advertise it anyway */
+ if (!(priv->pmapmd_speeds & link_cfg->speed_bit))
+ return true;
+
+ val = phy_read_mmd(phydev, MDIO_MMD_VEND1, link_cfg->reg);
+ if (val < 0) {
+ phydev_warn(phydev, "could not read register %x:%.04x (err = %d)\n",
+ MDIO_MMD_VEND1, link_cfg->reg, val);
+ return false;
+ }
+
+ phydev_dbg(phydev, "%x:%.04x = %.04x\n", MDIO_MMD_VEND1, link_cfg->reg, val);
+ if (FIELD_GET(VEND1_GLOBAL_CFG_RATE_ADAPT, val) !=
+ VEND1_GLOBAL_CFG_RATE_ADAPT_PAUSE)
+ return false;
+
+ switch (FIELD_GET(VEND1_GLOBAL_CFG_SERDES_MODE, val)) {
+ case VEND1_GLOBAL_CFG_SERDES_MODE_XFI20G:
+ return serdes_speed == SPEED_20000;
+ case VEND1_GLOBAL_CFG_SERDES_MODE_XFI:
+ return serdes_speed == SPEED_10000;
+ case VEND1_GLOBAL_CFG_SERDES_MODE_XFI5G:
+ return serdes_speed == SPEED_5000;
+ case VEND1_GLOBAL_CFG_SERDES_MODE_OCSGMII:
+ return serdes_speed == SPEED_2500;
+ case VEND1_GLOBAL_CFG_SERDES_MODE_SGMII:
+ return serdes_speed == SPEED_1000;
+ default:
+ return false;
+ }
+}
+
+/**
+ * aqr107_rate_adapt_ok() - Validate rate adaptation for an interface speed
+ * @phydev: The phy device
+ * @speed: The serdes (phy interface) speed
+ *
+ * This validates whether rate adaptation will work for a particular @speed.
+ * All link speeds less than or equal to @speed are validate to ensure they are
+ * configured properly.
+ *
+ * Return: %true if rate adaptation is supported for @speed, %false otherwise.
+ */
+static bool aqr107_rate_adapt_ok(struct phy_device *phydev, int speed)
+{
+ static const struct aqr107_link_speed_cfg speed_table[] = {
+ {
+ .speed = SPEED_10,
+ .reg = VEND1_GLOBAL_CFG_10M,
+ .speed_bit = MDIO_PMA_SPEED_10,
+ },
+ {
+ .speed = SPEED_100,
+ .reg = VEND1_GLOBAL_CFG_100M,
+ .speed_bit = MDIO_PMA_SPEED_100,
+ },
+ {
+ .speed = SPEED_1000,
+ .reg = VEND1_GLOBAL_CFG_1G,
+ .speed_bit = MDIO_PMA_SPEED_1000,
+ },
+ {
+ .speed = SPEED_2500,
+ .reg = VEND1_GLOBAL_CFG_2_5G,
+ .speed_bit = MDIO_PMA_SPEED_2_5G,
+ },
+ {
+ .speed = SPEED_5000,
+ .reg = VEND1_GLOBAL_CFG_5G,
+ .speed_bit = MDIO_PMA_SPEED_5G,
+ },
+ {
+ .speed = SPEED_10000,
+ .reg = VEND1_GLOBAL_CFG_10G,
+ .speed_bit = MDIO_PMA_SPEED_10G,
+ },
+ };
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(speed_table) &&
+ speed_table[i].speed <= speed; i++)
+ if (!aqr107_rate_adapt_ok_one(phydev, speed, &speed_table[i]))
+ return false;
+
+ /* Must match at least one speed */
+ if (i == ARRAY_SIZE(speed_table) && speed != speed_table[i].speed)
+ return false;
+
+ return true;
+}
+
static int aqr107_get_rate_matching(struct phy_device *phydev,
phy_interface_t iface)
{
- if (iface == PHY_INTERFACE_MODE_10GBASER ||
- iface == PHY_INTERFACE_MODE_2500BASEX ||
- iface == PHY_INTERFACE_MODE_NA)
+ if (iface != PHY_INTERFACE_MODE_NA) {
+ if (aqr107_rate_adapt_ok(phydev,
+ phy_interface_max_speed(iface)))
+ return RATE_MATCH_PAUSE;
+ else
+ return RATE_MATCH_NONE;
+ }
+
+ if (aqr107_rate_adapt_ok(phydev, SPEED_10000) ||
+ aqr107_rate_adapt_ok(phydev, SPEED_2500) ||
+ aqr107_rate_adapt_ok(phydev, SPEED_1000))
return RATE_MATCH_PAUSE;
+
return RATE_MATCH_NONE;
}
@@ -713,10 +853,18 @@ static int aqr107_resume(struct phy_device *phydev)
static int aqr107_probe(struct phy_device *phydev)
{
- phydev->priv = devm_kzalloc(&phydev->mdio.dev,
- sizeof(struct aqr107_priv), GFP_KERNEL);
- if (!phydev->priv)
+ struct aqr107_priv *priv;
+
+ priv = devm_kzalloc(&phydev->mdio.dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
return -ENOMEM;
+ phydev->priv = priv;
+
+ priv->pmapmd_speeds = phy_read_mmd(phydev, MDIO_MMD_PMAPMD, MDIO_SPEED);
+ if (priv->pmapmd_speeds < 0) {
+ phydev_err(phydev, "could not read PMA/PMD speeds\n");
+ return priv->pmapmd_speeds;
+ };
return aqr_hwmon_probe(phydev);
}
--
2.35.1.1320.gc452695387.dirty
^ permalink raw reply related [flat|nested] 8+ messages in thread