From b81017aeee4eb9159296cdb68889932649317b9b Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Wed, 2 Jun 2021 19:20:11 +0300 Subject: [PATCH 1/9] net: pcs: xpcs: delete shim definition for mdio_xpcs_get_ops() CONFIG_STMMAC_ETH selects CONFIG_PCS_XPCS, so there should be no situation where the shim should be needed. Signed-off-by: Vladimir Oltean Signed-off-by: David S. Miller --- include/linux/pcs/pcs-xpcs.h | 7 ------- 1 file changed, 7 deletions(-) diff --git a/include/linux/pcs/pcs-xpcs.h b/include/linux/pcs/pcs-xpcs.h index 5938ced805f47..c4d0a2c469c7a 100644 --- a/include/linux/pcs/pcs-xpcs.h +++ b/include/linux/pcs/pcs-xpcs.h @@ -36,13 +36,6 @@ struct mdio_xpcs_ops { int enable); }; -#if IS_ENABLED(CONFIG_PCS_XPCS) struct mdio_xpcs_ops *mdio_xpcs_get_ops(void); -#else -static inline struct mdio_xpcs_ops *mdio_xpcs_get_ops(void) -{ - return NULL; -} -#endif #endif /* __LINUX_PCS_XPCS_H */ From a54a8b71f6faca9a794c285e2ecde74a0dbec65a Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Wed, 2 Jun 2021 19:20:12 +0300 Subject: [PATCH 2/9] net: pcs: xpcs: there is only one PHY ID The xpcs driver has an apparently inadequate structure for the actual hardware it drives. These defines and the xpcs_probe() function would suggest that there is one PHY ID per supported PHY interface type, and the driver simply validates whether the mode it should operate in (the argument of xpcs_probe) matches what the hardware is capable of: #define SYNOPSYS_XPCS_USXGMII_ID 0x7996ced0 #define SYNOPSYS_XPCS_10GKR_ID 0x7996ced0 #define SYNOPSYS_XPCS_XLGMII_ID 0x7996ced0 #define SYNOPSYS_XPCS_SGMII_ID 0x7996ced0 #define SYNOPSYS_XPCS_MASK 0xffffffff but that is not the case, because upon closer inspection, all the above 4 PHY ID definitions are in fact equal. So it is the same XPCS that is compatible with all 4 sets of PHY interface types. This change introduces an array of struct xpcs_compat which is populated by the single struct xpcs_id instance. It also eliminates the bogus defines for multiple Synopsys XPCS PHY IDs and replaces them with a single XPCS_ID, which better reflects the way in which the hardware operates. Because we are touching this area of the code anyway, the new array of struct xpcs_compat, as well as the array of xpcs_id, have been moved towards the end of the file, since they are variable declarations not definitions. If whichever of struct xpcs_compat or struct xpcs_id need to gain a function pointer member in the future, it is easier to reference functions (no forward declarations needed) if we have the const variable declarations at the end of the file. Signed-off-by: Vladimir Oltean Signed-off-by: David S. Miller --- drivers/net/pcs/pcs-xpcs.c | 129 ++++++++++++++++++++++--------------- 1 file changed, 78 insertions(+), 51 deletions(-) diff --git a/drivers/net/pcs/pcs-xpcs.c b/drivers/net/pcs/pcs-xpcs.c index aa985a5aae8d6..9f2da9e873c48 100644 --- a/drivers/net/pcs/pcs-xpcs.c +++ b/drivers/net/pcs/pcs-xpcs.c @@ -12,10 +12,7 @@ #include #include -#define SYNOPSYS_XPCS_USXGMII_ID 0x7996ced0 -#define SYNOPSYS_XPCS_10GKR_ID 0x7996ced0 -#define SYNOPSYS_XPCS_XLGMII_ID 0x7996ced0 -#define SYNOPSYS_XPCS_SGMII_ID 0x7996ced0 +#define SYNOPSYS_XPCS_ID 0x7996ced0 #define SYNOPSYS_XPCS_MASK 0xffffffff /* Vendor regs access */ @@ -163,56 +160,39 @@ static const int xpcs_sgmii_features[] = { static const phy_interface_t xpcs_usxgmii_interfaces[] = { PHY_INTERFACE_MODE_USXGMII, - PHY_INTERFACE_MODE_MAX, }; static const phy_interface_t xpcs_10gkr_interfaces[] = { PHY_INTERFACE_MODE_10GKR, - PHY_INTERFACE_MODE_MAX, }; static const phy_interface_t xpcs_xlgmii_interfaces[] = { PHY_INTERFACE_MODE_XLGMII, - PHY_INTERFACE_MODE_MAX, }; static const phy_interface_t xpcs_sgmii_interfaces[] = { PHY_INTERFACE_MODE_SGMII, - PHY_INTERFACE_MODE_MAX, }; -static struct xpcs_id { - u32 id; - u32 mask; +enum { + DW_XPCS_USXGMII, + DW_XPCS_10GKR, + DW_XPCS_XLGMII, + DW_XPCS_SGMII, + DW_XPCS_INTERFACE_MAX, +}; + +struct xpcs_compat { const int *supported; const phy_interface_t *interface; + int num_interfaces; int an_mode; -} xpcs_id_list[] = { - { - .id = SYNOPSYS_XPCS_USXGMII_ID, - .mask = SYNOPSYS_XPCS_MASK, - .supported = xpcs_usxgmii_features, - .interface = xpcs_usxgmii_interfaces, - .an_mode = DW_AN_C73, - }, { - .id = SYNOPSYS_XPCS_10GKR_ID, - .mask = SYNOPSYS_XPCS_MASK, - .supported = xpcs_10gkr_features, - .interface = xpcs_10gkr_interfaces, - .an_mode = DW_AN_C73, - }, { - .id = SYNOPSYS_XPCS_XLGMII_ID, - .mask = SYNOPSYS_XPCS_MASK, - .supported = xpcs_xlgmii_features, - .interface = xpcs_xlgmii_interfaces, - .an_mode = DW_AN_C73, - }, { - .id = SYNOPSYS_XPCS_SGMII_ID, - .mask = SYNOPSYS_XPCS_MASK, - .supported = xpcs_sgmii_features, - .interface = xpcs_sgmii_interfaces, - .an_mode = DW_AN_C37_SGMII, - }, +}; + +struct xpcs_id { + u32 id; + u32 mask; + const struct xpcs_compat *compat; }; static int xpcs_read(struct mdio_xpcs_args *xpcs, int dev, u32 reg) @@ -911,35 +891,82 @@ static u32 xpcs_get_id(struct mdio_xpcs_args *xpcs) } static bool xpcs_check_features(struct mdio_xpcs_args *xpcs, - struct xpcs_id *match, + const struct xpcs_id *match, phy_interface_t interface) { - int i; + int i, j; - for (i = 0; match->interface[i] != PHY_INTERFACE_MODE_MAX; i++) { - if (match->interface[i] == interface) - break; - } + for (i = 0; i < DW_XPCS_INTERFACE_MAX; i++) { + const struct xpcs_compat *compat = &match->compat[i]; + bool supports_interface = false; - if (match->interface[i] == PHY_INTERFACE_MODE_MAX) - return false; + for (j = 0; j < compat->num_interfaces; j++) { + if (compat->interface[j] == interface) { + supports_interface = true; + break; + } + } + + if (!supports_interface) + continue; - for (i = 0; match->supported[i] != __ETHTOOL_LINK_MODE_MASK_NBITS; i++) - set_bit(match->supported[i], xpcs->supported); + /* Populate the supported link modes for this + * PHY interface type + */ + for (j = 0; compat->supported[j] != __ETHTOOL_LINK_MODE_MASK_NBITS; j++) + set_bit(compat->supported[j], xpcs->supported); - xpcs->an_mode = match->an_mode; + xpcs->an_mode = compat->an_mode; - return true; + return true; + } + + return false; } +static const struct xpcs_compat synopsys_xpcs_compat[DW_XPCS_INTERFACE_MAX] = { + [DW_XPCS_USXGMII] = { + .supported = xpcs_usxgmii_features, + .interface = xpcs_usxgmii_interfaces, + .num_interfaces = ARRAY_SIZE(xpcs_usxgmii_interfaces), + .an_mode = DW_AN_C73, + }, + [DW_XPCS_10GKR] = { + .supported = xpcs_10gkr_features, + .interface = xpcs_10gkr_interfaces, + .num_interfaces = ARRAY_SIZE(xpcs_10gkr_interfaces), + .an_mode = DW_AN_C73, + }, + [DW_XPCS_XLGMII] = { + .supported = xpcs_xlgmii_features, + .interface = xpcs_xlgmii_interfaces, + .num_interfaces = ARRAY_SIZE(xpcs_xlgmii_interfaces), + .an_mode = DW_AN_C73, + }, + [DW_XPCS_SGMII] = { + .supported = xpcs_sgmii_features, + .interface = xpcs_sgmii_interfaces, + .num_interfaces = ARRAY_SIZE(xpcs_sgmii_interfaces), + .an_mode = DW_AN_C37_SGMII, + }, +}; + +static const struct xpcs_id xpcs_id_list[] = { + { + .id = SYNOPSYS_XPCS_ID, + .mask = SYNOPSYS_XPCS_MASK, + .compat = synopsys_xpcs_compat, + }, +}; + static int xpcs_probe(struct mdio_xpcs_args *xpcs, phy_interface_t interface) { + const struct xpcs_id *match = NULL; u32 xpcs_id = xpcs_get_id(xpcs); - struct xpcs_id *match = NULL; int i; for (i = 0; i < ARRAY_SIZE(xpcs_id_list); i++) { - struct xpcs_id *entry = &xpcs_id_list[i]; + const struct xpcs_id *entry = &xpcs_id_list[i]; if ((xpcs_id & entry->mask) == entry->id) { match = entry; From 9900074ecccec472c9d89929c3d37c235f45d33a Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Wed, 2 Jun 2021 19:20:13 +0300 Subject: [PATCH 3/9] net: pcs: xpcs: make the checks related to the PHY interface mode stateless The operating mode of the driver is currently to populate its struct mdio_xpcs_args::supported and struct mdio_xpcs_args::an_mode statically in xpcs_probe(), based on the passed phy_interface_t, and work with those. However this is not the operation that phylink expects from a PCS driver, because the port might be attached to an SFP cage that triggers changes of the phy_interface_t dynamically as one SFP module is unpluggged and another is plugged. To migrate towards that model, the struct mdio_xpcs_args should not cache anything related to the phy_interface_t, but just look up the statically defined, const struct xpcs_compat structure corresponding to the detected PCS OUI/model number. So we delete the "supported" and "an_mode" members of struct mdio_xpcs_args, and add the "id" structure there (since the ID is not expected to change at runtime). Since xpcs->supported is used deep in the code in _xpcs_config_aneg_c73(), we need to modify some function headers to pass the xpcs_compat from all callers. In turn, the xpcs_compat is always supplied externally to the xpcs module: - Most of the time by phylink - In xpcs_probe() it is needed because xpcs_soft_reset() writes to MDIO_MMD_PCS or to MDIO_MMD_VEND2 depending on whether an_mode is clause 37 or clause 73. In order to not introduce functional changes related to when the soft reset is issued, we continue to require the initial phy_interface_t argument to be passed to xpcs_probe() so we can pass this on to xpcs_soft_reset(). - stmmac_open() wants to know whether to call stmmac_init_phy() or not, and for that it looks inside xpcs->an_mode, because the clause 73 (backplane) AN modes supposedly do not have a PHY. Because we moved an_mode outside of struct mdio_xpcs_args, this is now no longer directly possible, so we introduce a helper function xpcs_get_an_mode() which protects the data encapsulation of the xpcs module and requires a phy_interface_t to be passed as argument. This function can look up the appropriate compat based on the phy_interface_t. Signed-off-by: Vladimir Oltean Signed-off-by: David S. Miller --- .../net/ethernet/stmicro/stmmac/stmmac_main.c | 4 +- drivers/net/pcs/pcs-xpcs.c | 175 +++++++++++------- include/linux/pcs/pcs-xpcs.h | 6 +- 3 files changed, 120 insertions(+), 65 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index 13720bf6f6ff9..c96a89fa4e3cf 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -3638,6 +3638,7 @@ static int stmmac_request_irq(struct net_device *dev) int stmmac_open(struct net_device *dev) { struct stmmac_priv *priv = netdev_priv(dev); + int mode = priv->plat->phy_interface; int bfsize = 0; u32 chan; int ret; @@ -3650,7 +3651,8 @@ int stmmac_open(struct net_device *dev) if (priv->hw->pcs != STMMAC_PCS_TBI && priv->hw->pcs != STMMAC_PCS_RTBI && - priv->hw->xpcs_args.an_mode != DW_AN_C73) { + (!priv->hw->xpcs || + xpcs_get_an_mode(&priv->hw->xpcs_args, mode) != DW_AN_C73)) { ret = stmmac_init_phy(dev); if (ret) { netdev_err(priv->dev, diff --git a/drivers/net/pcs/pcs-xpcs.c b/drivers/net/pcs/pcs-xpcs.c index 9f2da9e873c48..610073cb55d0a 100644 --- a/drivers/net/pcs/pcs-xpcs.c +++ b/drivers/net/pcs/pcs-xpcs.c @@ -195,6 +195,49 @@ struct xpcs_id { const struct xpcs_compat *compat; }; +static const struct xpcs_compat *xpcs_find_compat(const struct xpcs_id *id, + phy_interface_t interface) +{ + int i, j; + + for (i = 0; i < DW_XPCS_INTERFACE_MAX; i++) { + const struct xpcs_compat *compat = &id->compat[i]; + + for (j = 0; j < compat->num_interfaces; j++) + if (compat->interface[j] == interface) + return compat; + } + + return NULL; +} + +int xpcs_get_an_mode(struct mdio_xpcs_args *xpcs, phy_interface_t interface) +{ + const struct xpcs_compat *compat; + + compat = xpcs_find_compat(xpcs->id, interface); + if (!compat) + return -ENODEV; + + return compat->an_mode; +} +EXPORT_SYMBOL_GPL(xpcs_get_an_mode); + +static bool __xpcs_linkmode_supported(const struct xpcs_compat *compat, + enum ethtool_link_mode_bit_indices linkmode) +{ + int i; + + for (i = 0; compat->supported[i] != __ETHTOOL_LINK_MODE_MASK_NBITS; i++) + if (compat->supported[i] == linkmode) + return true; + + return false; +} + +#define xpcs_linkmode_supported(compat, mode) \ + __xpcs_linkmode_supported(compat, ETHTOOL_LINK_MODE_ ## mode ## _BIT) + static int xpcs_read(struct mdio_xpcs_args *xpcs, int dev, u32 reg) { u32 reg_addr = MII_ADDR_C45 | dev << 16 | reg; @@ -246,11 +289,12 @@ static int xpcs_poll_reset(struct mdio_xpcs_args *xpcs, int dev) return (ret & MDIO_CTRL1_RESET) ? -ETIMEDOUT : 0; } -static int xpcs_soft_reset(struct mdio_xpcs_args *xpcs) +static int xpcs_soft_reset(struct mdio_xpcs_args *xpcs, + const struct xpcs_compat *compat) { int ret, dev; - switch (xpcs->an_mode) { + switch (compat->an_mode) { case DW_AN_C73: dev = MDIO_MMD_PCS; break; @@ -419,7 +463,8 @@ static int xpcs_config_usxgmii(struct mdio_xpcs_args *xpcs, int speed) return xpcs_write_vpcs(xpcs, MDIO_CTRL1, ret | DW_USXGMII_RST); } -static int _xpcs_config_aneg_c73(struct mdio_xpcs_args *xpcs) +static int _xpcs_config_aneg_c73(struct mdio_xpcs_args *xpcs, + const struct xpcs_compat *compat) { int ret, adv; @@ -431,7 +476,7 @@ static int _xpcs_config_aneg_c73(struct mdio_xpcs_args *xpcs) /* SR_AN_ADV3 */ adv = 0; - if (phylink_test(xpcs->supported, 2500baseX_Full)) + if (xpcs_linkmode_supported(compat, 2500baseX_Full)) adv |= DW_C73_2500KX; /* TODO: 5000baseKR */ @@ -442,11 +487,11 @@ static int _xpcs_config_aneg_c73(struct mdio_xpcs_args *xpcs) /* SR_AN_ADV2 */ adv = 0; - if (phylink_test(xpcs->supported, 1000baseKX_Full)) + if (xpcs_linkmode_supported(compat, 1000baseKX_Full)) adv |= DW_C73_1000KX; - if (phylink_test(xpcs->supported, 10000baseKX4_Full)) + if (xpcs_linkmode_supported(compat, 10000baseKX4_Full)) adv |= DW_C73_10000KX4; - if (phylink_test(xpcs->supported, 10000baseKR_Full)) + if (xpcs_linkmode_supported(compat, 10000baseKR_Full)) adv |= DW_C73_10000KR; ret = xpcs_write(xpcs, MDIO_MMD_AN, DW_SR_AN_ADV2, adv); @@ -455,19 +500,20 @@ static int _xpcs_config_aneg_c73(struct mdio_xpcs_args *xpcs) /* SR_AN_ADV1 */ adv = DW_C73_AN_ADV_SF; - if (phylink_test(xpcs->supported, Pause)) + if (xpcs_linkmode_supported(compat, Pause)) adv |= DW_C73_PAUSE; - if (phylink_test(xpcs->supported, Asym_Pause)) + if (xpcs_linkmode_supported(compat, Asym_Pause)) adv |= DW_C73_ASYM_PAUSE; return xpcs_write(xpcs, MDIO_MMD_AN, DW_SR_AN_ADV1, adv); } -static int xpcs_config_aneg_c73(struct mdio_xpcs_args *xpcs) +static int xpcs_config_aneg_c73(struct mdio_xpcs_args *xpcs, + const struct xpcs_compat *compat) { int ret; - ret = _xpcs_config_aneg_c73(xpcs); + ret = _xpcs_config_aneg_c73(xpcs, compat); if (ret < 0) return ret; @@ -481,7 +527,8 @@ static int xpcs_config_aneg_c73(struct mdio_xpcs_args *xpcs) } static int xpcs_aneg_done_c73(struct mdio_xpcs_args *xpcs, - struct phylink_link_state *state) + struct phylink_link_state *state, + const struct xpcs_compat *compat) { int ret; @@ -496,7 +543,7 @@ static int xpcs_aneg_done_c73(struct mdio_xpcs_args *xpcs, /* Check if Aneg outcome is valid */ if (!(ret & DW_C73_AN_ADV_SF)) { - xpcs_config_aneg_c73(xpcs); + xpcs_config_aneg_c73(xpcs, compat); return 0; } @@ -642,8 +689,31 @@ static int xpcs_validate(struct mdio_xpcs_args *xpcs, unsigned long *supported, struct phylink_link_state *state) { - linkmode_and(supported, supported, xpcs->supported); - linkmode_and(state->advertising, state->advertising, xpcs->supported); + __ETHTOOL_DECLARE_LINK_MODE_MASK(xpcs_supported); + const struct xpcs_compat *compat; + int i; + + /* phylink expects us to report all supported modes with + * PHY_INTERFACE_MODE_NA, just don't limit the supported and + * advertising masks and exit. + */ + if (state->interface == PHY_INTERFACE_MODE_NA) + return 0; + + bitmap_zero(xpcs_supported, __ETHTOOL_LINK_MODE_MASK_NBITS); + + compat = xpcs_find_compat(xpcs->id, state->interface); + + /* Populate the supported link modes for this + * PHY interface type + */ + if (compat) + for (i = 0; compat->supported[i] != __ETHTOOL_LINK_MODE_MASK_NBITS; i++) + set_bit(compat->supported[i], xpcs_supported); + + linkmode_and(supported, supported, xpcs_supported); + linkmode_and(state->advertising, state->advertising, xpcs_supported); + return 0; } @@ -724,12 +794,17 @@ static int xpcs_config_aneg_c37_sgmii(struct mdio_xpcs_args *xpcs) static int xpcs_config(struct mdio_xpcs_args *xpcs, const struct phylink_link_state *state) { + const struct xpcs_compat *compat; int ret; - switch (xpcs->an_mode) { + compat = xpcs_find_compat(xpcs->id, state->interface); + if (!compat) + return -ENODEV; + + switch (compat->an_mode) { case DW_AN_C73: if (state->an_enabled) { - ret = xpcs_config_aneg_c73(xpcs); + ret = xpcs_config_aneg_c73(xpcs, compat); if (ret) return ret; } @@ -747,7 +822,8 @@ static int xpcs_config(struct mdio_xpcs_args *xpcs, } static int xpcs_get_state_c73(struct mdio_xpcs_args *xpcs, - struct phylink_link_state *state) + struct phylink_link_state *state, + const struct xpcs_compat *compat) { int ret; @@ -757,7 +833,7 @@ static int xpcs_get_state_c73(struct mdio_xpcs_args *xpcs, /* ... and then we check the faults. */ ret = xpcs_read_fault_c73(xpcs, state); if (ret) { - ret = xpcs_soft_reset(xpcs); + ret = xpcs_soft_reset(xpcs, compat); if (ret) return ret; @@ -766,7 +842,7 @@ static int xpcs_get_state_c73(struct mdio_xpcs_args *xpcs, return xpcs_config(xpcs, state); } - if (state->an_enabled && xpcs_aneg_done_c73(xpcs, state)) { + if (state->an_enabled && xpcs_aneg_done_c73(xpcs, state, compat)) { state->an_complete = true; xpcs_read_lpa_c73(xpcs, state); xpcs_resolve_lpa_c73(xpcs, state); @@ -823,11 +899,16 @@ static int xpcs_get_state_c37_sgmii(struct mdio_xpcs_args *xpcs, static int xpcs_get_state(struct mdio_xpcs_args *xpcs, struct phylink_link_state *state) { + const struct xpcs_compat *compat; int ret; - switch (xpcs->an_mode) { + compat = xpcs_find_compat(xpcs->id, state->interface); + if (!compat) + return -ENODEV; + + switch (compat->an_mode) { case DW_AN_C73: - ret = xpcs_get_state_c73(xpcs, state); + ret = xpcs_get_state_c73(xpcs, state, compat); if (ret) return ret; break; @@ -890,40 +971,6 @@ static u32 xpcs_get_id(struct mdio_xpcs_args *xpcs) return 0xffffffff; } -static bool xpcs_check_features(struct mdio_xpcs_args *xpcs, - const struct xpcs_id *match, - phy_interface_t interface) -{ - int i, j; - - for (i = 0; i < DW_XPCS_INTERFACE_MAX; i++) { - const struct xpcs_compat *compat = &match->compat[i]; - bool supports_interface = false; - - for (j = 0; j < compat->num_interfaces; j++) { - if (compat->interface[j] == interface) { - supports_interface = true; - break; - } - } - - if (!supports_interface) - continue; - - /* Populate the supported link modes for this - * PHY interface type - */ - for (j = 0; compat->supported[j] != __ETHTOOL_LINK_MODE_MASK_NBITS; j++) - set_bit(compat->supported[j], xpcs->supported); - - xpcs->an_mode = compat->an_mode; - - return true; - } - - return false; -} - static const struct xpcs_compat synopsys_xpcs_compat[DW_XPCS_INTERFACE_MAX] = { [DW_XPCS_USXGMII] = { .supported = xpcs_usxgmii_features, @@ -961,19 +1008,23 @@ static const struct xpcs_id xpcs_id_list[] = { static int xpcs_probe(struct mdio_xpcs_args *xpcs, phy_interface_t interface) { - const struct xpcs_id *match = NULL; u32 xpcs_id = xpcs_get_id(xpcs); int i; for (i = 0; i < ARRAY_SIZE(xpcs_id_list); i++) { const struct xpcs_id *entry = &xpcs_id_list[i]; + const struct xpcs_compat *compat; - if ((xpcs_id & entry->mask) == entry->id) { - match = entry; + if ((xpcs_id & entry->mask) != entry->id) + continue; - if (xpcs_check_features(xpcs, match, interface)) - return xpcs_soft_reset(xpcs); - } + xpcs->id = entry; + + compat = xpcs_find_compat(entry, interface); + if (!compat) + return -ENODEV; + + return xpcs_soft_reset(xpcs, compat); } return -ENODEV; diff --git a/include/linux/pcs/pcs-xpcs.h b/include/linux/pcs/pcs-xpcs.h index c4d0a2c469c7a..c2ec440d2c5d2 100644 --- a/include/linux/pcs/pcs-xpcs.h +++ b/include/linux/pcs/pcs-xpcs.h @@ -14,11 +14,12 @@ #define DW_AN_C73 1 #define DW_AN_C37_SGMII 2 +struct xpcs_id; + struct mdio_xpcs_args { - __ETHTOOL_DECLARE_LINK_MODE_MASK(supported); struct mii_bus *bus; + const struct xpcs_id *id; int addr; - int an_mode; }; struct mdio_xpcs_ops { @@ -36,6 +37,7 @@ struct mdio_xpcs_ops { int enable); }; +int xpcs_get_an_mode(struct mdio_xpcs_args *xpcs, phy_interface_t interface); struct mdio_xpcs_ops *mdio_xpcs_get_ops(void); #endif /* __LINUX_PCS_XPCS_H */ From a1a753ed1d4ae46c1c1874fb1af899f6579a7547 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Wed, 2 Jun 2021 19:20:14 +0300 Subject: [PATCH 4/9] net: pcs: xpcs: export xpcs_validate Calling a function pointer with a single implementation through struct mdio_xpcs_ops is clunky, and the stmmac_do_callback system forces this to return int, even though it always returns zero. Simply remove the "validate" function pointer from struct mdio_xpcs_ops and replace it with an exported xpcs_validate symbol which is called directly by stmmac. priv->hw->xpcs is of the type "const struct mdio_xpcs_ops *" and is used as a placeholder/synonym for priv->plat->mdio_bus_data->has_xpcs. It is done that way because the mdio_bus_data pointer might or might not be populated in all stmmac instantiations. Signed-off-by: Vladimir Oltean Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/hwif.h | 2 -- drivers/net/ethernet/stmicro/stmmac/stmmac_main.c | 3 ++- drivers/net/pcs/pcs-xpcs.c | 11 ++++------- include/linux/pcs/pcs-xpcs.h | 5 ++--- 4 files changed, 8 insertions(+), 13 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/hwif.h b/drivers/net/ethernet/stmicro/stmmac/hwif.h index dbafedb242909..441985f9cf49e 100644 --- a/drivers/net/ethernet/stmicro/stmmac/hwif.h +++ b/drivers/net/ethernet/stmicro/stmmac/hwif.h @@ -614,8 +614,6 @@ struct stmmac_mmc_ops { stmmac_do_void_callback(__priv, mmc, read, __args) /* XPCS callbacks */ -#define stmmac_xpcs_validate(__priv, __args...) \ - stmmac_do_callback(__priv, xpcs, validate, __args) #define stmmac_xpcs_config(__priv, __args...) \ stmmac_do_callback(__priv, xpcs, config, __args) #define stmmac_xpcs_get_state(__priv, __args...) \ diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index c96a89fa4e3cf..b7e6ab05ddd98 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -996,7 +996,8 @@ static void stmmac_validate(struct phylink_config *config, linkmode_andnot(state->advertising, state->advertising, mask); /* If PCS is supported, check which modes it supports. */ - stmmac_xpcs_validate(priv, &priv->hw->xpcs_args, supported, state); + if (priv->hw->xpcs) + xpcs_validate(&priv->hw->xpcs_args, supported, state); } static void stmmac_mac_pcs_get_state(struct phylink_config *config, diff --git a/drivers/net/pcs/pcs-xpcs.c b/drivers/net/pcs/pcs-xpcs.c index 610073cb55d0a..2f7791bcf07b4 100644 --- a/drivers/net/pcs/pcs-xpcs.c +++ b/drivers/net/pcs/pcs-xpcs.c @@ -685,9 +685,8 @@ static void xpcs_resolve_pma(struct mdio_xpcs_args *xpcs, } } -static int xpcs_validate(struct mdio_xpcs_args *xpcs, - unsigned long *supported, - struct phylink_link_state *state) +void xpcs_validate(struct mdio_xpcs_args *xpcs, unsigned long *supported, + struct phylink_link_state *state) { __ETHTOOL_DECLARE_LINK_MODE_MASK(xpcs_supported); const struct xpcs_compat *compat; @@ -698,7 +697,7 @@ static int xpcs_validate(struct mdio_xpcs_args *xpcs, * advertising masks and exit. */ if (state->interface == PHY_INTERFACE_MODE_NA) - return 0; + return; bitmap_zero(xpcs_supported, __ETHTOOL_LINK_MODE_MASK_NBITS); @@ -713,9 +712,8 @@ static int xpcs_validate(struct mdio_xpcs_args *xpcs, linkmode_and(supported, supported, xpcs_supported); linkmode_and(state->advertising, state->advertising, xpcs_supported); - - return 0; } +EXPORT_SYMBOL_GPL(xpcs_validate); static int xpcs_config_eee(struct mdio_xpcs_args *xpcs, int mult_fact_100ns, int enable) @@ -1031,7 +1029,6 @@ static int xpcs_probe(struct mdio_xpcs_args *xpcs, phy_interface_t interface) } static struct mdio_xpcs_ops xpcs_ops = { - .validate = xpcs_validate, .config = xpcs_config, .get_state = xpcs_get_state, .link_up = xpcs_link_up, diff --git a/include/linux/pcs/pcs-xpcs.h b/include/linux/pcs/pcs-xpcs.h index c2ec440d2c5d2..5ec9aaca01fe9 100644 --- a/include/linux/pcs/pcs-xpcs.h +++ b/include/linux/pcs/pcs-xpcs.h @@ -23,9 +23,6 @@ struct mdio_xpcs_args { }; struct mdio_xpcs_ops { - int (*validate)(struct mdio_xpcs_args *xpcs, - unsigned long *supported, - struct phylink_link_state *state); int (*config)(struct mdio_xpcs_args *xpcs, const struct phylink_link_state *state); int (*get_state)(struct mdio_xpcs_args *xpcs, @@ -39,5 +36,7 @@ struct mdio_xpcs_ops { int xpcs_get_an_mode(struct mdio_xpcs_args *xpcs, phy_interface_t interface); struct mdio_xpcs_ops *mdio_xpcs_get_ops(void); +void xpcs_validate(struct mdio_xpcs_args *xpcs, unsigned long *supported, + struct phylink_link_state *state); #endif /* __LINUX_PCS_XPCS_H */ From 14b517cb62d6efc8866f176c922de03dfe1564f3 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Wed, 2 Jun 2021 19:20:15 +0300 Subject: [PATCH 5/9] net: pcs: xpcs: export xpcs_config_eee There is no good reason why we need to go through: stmmac_xpcs_config_eee -> stmmac_do_callback -> mdio_xpcs_ops->config_eee -> xpcs_config_eee when we can simply call xpcs_config_eee. priv->hw->xpcs is of the type "const struct mdio_xpcs_ops *" and is used as a placeholder/synonym for priv->plat->mdio_bus_data->has_xpcs. It is done that way because the mdio_bus_data pointer might or might not be populated in all stmmac instantiations. Signed-off-by: Vladimir Oltean Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/hwif.h | 2 -- drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c | 12 +++++++----- drivers/net/pcs/pcs-xpcs.c | 6 +++--- include/linux/pcs/pcs-xpcs.h | 4 ++-- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/hwif.h b/drivers/net/ethernet/stmicro/stmmac/hwif.h index 441985f9cf49e..c10d11dbde617 100644 --- a/drivers/net/ethernet/stmicro/stmmac/hwif.h +++ b/drivers/net/ethernet/stmicro/stmmac/hwif.h @@ -622,8 +622,6 @@ struct stmmac_mmc_ops { stmmac_do_callback(__priv, xpcs, link_up, __args) #define stmmac_xpcs_probe(__priv, __args...) \ stmmac_do_callback(__priv, xpcs, probe, __args) -#define stmmac_xpcs_config_eee(__priv, __args...) \ - stmmac_do_callback(__priv, xpcs, config_eee, __args) struct stmmac_regs_off { u32 ptp_off; diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c index 1f6d749fd9a3d..ba7d0f40723a2 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c @@ -720,11 +720,13 @@ static int stmmac_ethtool_op_set_eee(struct net_device *dev, netdev_warn(priv->dev, "Setting EEE tx-lpi is not supported\n"); - ret = stmmac_xpcs_config_eee(priv, &priv->hw->xpcs_args, - priv->plat->mult_fact_100ns, - edata->eee_enabled); - if (ret) - return ret; + if (priv->hw->xpcs) { + ret = xpcs_config_eee(&priv->hw->xpcs_args, + priv->plat->mult_fact_100ns, + edata->eee_enabled); + if (ret) + return ret; + } if (!edata->eee_enabled) stmmac_disable_eee_mode(priv); diff --git a/drivers/net/pcs/pcs-xpcs.c b/drivers/net/pcs/pcs-xpcs.c index 2f7791bcf07b4..2f2ffab855aa0 100644 --- a/drivers/net/pcs/pcs-xpcs.c +++ b/drivers/net/pcs/pcs-xpcs.c @@ -715,8 +715,8 @@ void xpcs_validate(struct mdio_xpcs_args *xpcs, unsigned long *supported, } EXPORT_SYMBOL_GPL(xpcs_validate); -static int xpcs_config_eee(struct mdio_xpcs_args *xpcs, int mult_fact_100ns, - int enable) +int xpcs_config_eee(struct mdio_xpcs_args *xpcs, int mult_fact_100ns, + int enable) { int ret; @@ -747,6 +747,7 @@ static int xpcs_config_eee(struct mdio_xpcs_args *xpcs, int mult_fact_100ns, ret |= DW_VR_MII_EEE_TRN_LPI; return xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_EEE_MCTRL1, ret); } +EXPORT_SYMBOL_GPL(xpcs_config_eee); static int xpcs_config_aneg_c37_sgmii(struct mdio_xpcs_args *xpcs) { @@ -1033,7 +1034,6 @@ static struct mdio_xpcs_ops xpcs_ops = { .get_state = xpcs_get_state, .link_up = xpcs_link_up, .probe = xpcs_probe, - .config_eee = xpcs_config_eee, }; struct mdio_xpcs_ops *mdio_xpcs_get_ops(void) diff --git a/include/linux/pcs/pcs-xpcs.h b/include/linux/pcs/pcs-xpcs.h index 5ec9aaca01fe9..ae74a336dcb97 100644 --- a/include/linux/pcs/pcs-xpcs.h +++ b/include/linux/pcs/pcs-xpcs.h @@ -30,13 +30,13 @@ struct mdio_xpcs_ops { int (*link_up)(struct mdio_xpcs_args *xpcs, int speed, phy_interface_t interface); int (*probe)(struct mdio_xpcs_args *xpcs, phy_interface_t interface); - int (*config_eee)(struct mdio_xpcs_args *xpcs, int mult_fact_100ns, - int enable); }; int xpcs_get_an_mode(struct mdio_xpcs_args *xpcs, phy_interface_t interface); struct mdio_xpcs_ops *mdio_xpcs_get_ops(void); void xpcs_validate(struct mdio_xpcs_args *xpcs, unsigned long *supported, struct phylink_link_state *state); +int xpcs_config_eee(struct mdio_xpcs_args *xpcs, int mult_fact_100ns, + int enable); #endif /* __LINUX_PCS_XPCS_H */ From 8e2bb9569942f9cb2ef816dbf66fbf3e8d722720 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Wed, 2 Jun 2021 19:20:16 +0300 Subject: [PATCH 6/9] net: pcs: xpcs: export xpcs_probe Similar to the other recently functions, it is not necessary for xpcs_probe to be a function pointer, so export it so that it can be called directly. Signed-off-by: Vladimir Oltean Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/hwif.h | 2 -- drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c | 2 +- drivers/net/pcs/pcs-xpcs.c | 4 ++-- include/linux/pcs/pcs-xpcs.h | 2 +- 4 files changed, 4 insertions(+), 6 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/hwif.h b/drivers/net/ethernet/stmicro/stmmac/hwif.h index c10d11dbde617..3dc291ff9f325 100644 --- a/drivers/net/ethernet/stmicro/stmmac/hwif.h +++ b/drivers/net/ethernet/stmicro/stmmac/hwif.h @@ -620,8 +620,6 @@ struct stmmac_mmc_ops { stmmac_do_callback(__priv, xpcs, get_state, __args) #define stmmac_xpcs_link_up(__priv, __args...) \ stmmac_do_callback(__priv, xpcs, link_up, __args) -#define stmmac_xpcs_probe(__priv, __args...) \ - stmmac_do_callback(__priv, xpcs, probe, __args) struct stmmac_regs_off { u32 ptp_off; diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c index e293bf1ce9f37..56deb92a8430d 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c @@ -521,7 +521,7 @@ int stmmac_mdio_register(struct net_device *ndev) for (addr = 0; addr < max_addr; addr++) { xpcs->addr = addr; - ret = stmmac_xpcs_probe(priv, xpcs, mode); + ret = xpcs_probe(xpcs, mode); if (!ret) { found = 1; break; diff --git a/drivers/net/pcs/pcs-xpcs.c b/drivers/net/pcs/pcs-xpcs.c index 2f2ffab855aa0..7f51eb4bbaa4e 100644 --- a/drivers/net/pcs/pcs-xpcs.c +++ b/drivers/net/pcs/pcs-xpcs.c @@ -1005,7 +1005,7 @@ static const struct xpcs_id xpcs_id_list[] = { }, }; -static int xpcs_probe(struct mdio_xpcs_args *xpcs, phy_interface_t interface) +int xpcs_probe(struct mdio_xpcs_args *xpcs, phy_interface_t interface) { u32 xpcs_id = xpcs_get_id(xpcs); int i; @@ -1028,12 +1028,12 @@ static int xpcs_probe(struct mdio_xpcs_args *xpcs, phy_interface_t interface) return -ENODEV; } +EXPORT_SYMBOL_GPL(xpcs_probe); static struct mdio_xpcs_ops xpcs_ops = { .config = xpcs_config, .get_state = xpcs_get_state, .link_up = xpcs_link_up, - .probe = xpcs_probe, }; struct mdio_xpcs_ops *mdio_xpcs_get_ops(void) diff --git a/include/linux/pcs/pcs-xpcs.h b/include/linux/pcs/pcs-xpcs.h index ae74a336dcb97..1d8581b74d81f 100644 --- a/include/linux/pcs/pcs-xpcs.h +++ b/include/linux/pcs/pcs-xpcs.h @@ -29,7 +29,6 @@ struct mdio_xpcs_ops { struct phylink_link_state *state); int (*link_up)(struct mdio_xpcs_args *xpcs, int speed, phy_interface_t interface); - int (*probe)(struct mdio_xpcs_args *xpcs, phy_interface_t interface); }; int xpcs_get_an_mode(struct mdio_xpcs_args *xpcs, phy_interface_t interface); @@ -38,5 +37,6 @@ void xpcs_validate(struct mdio_xpcs_args *xpcs, unsigned long *supported, struct phylink_link_state *state); int xpcs_config_eee(struct mdio_xpcs_args *xpcs, int mult_fact_100ns, int enable); +int xpcs_probe(struct mdio_xpcs_args *xpcs, phy_interface_t interface); #endif /* __LINUX_PCS_XPCS_H */ From 679e283ec7d6cfdf997cbb911d6857c115029007 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Wed, 2 Jun 2021 19:20:17 +0300 Subject: [PATCH 7/9] net: pcs: xpcs: use mdiobus_c45_addr in xpcs_{read,write} Use the dedicated helper for abstracting away how the clause 45 address is packed in reg_addr. Signed-off-by: Vladimir Oltean Signed-off-by: David S. Miller --- drivers/net/pcs/pcs-xpcs.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/pcs/pcs-xpcs.c b/drivers/net/pcs/pcs-xpcs.c index 7f51eb4bbaa4e..afabb9209c52c 100644 --- a/drivers/net/pcs/pcs-xpcs.c +++ b/drivers/net/pcs/pcs-xpcs.c @@ -240,14 +240,14 @@ static bool __xpcs_linkmode_supported(const struct xpcs_compat *compat, static int xpcs_read(struct mdio_xpcs_args *xpcs, int dev, u32 reg) { - u32 reg_addr = MII_ADDR_C45 | dev << 16 | reg; + u32 reg_addr = mdiobus_c45_addr(dev, reg); return mdiobus_read(xpcs->bus, xpcs->addr, reg_addr); } static int xpcs_write(struct mdio_xpcs_args *xpcs, int dev, u32 reg, u16 val) { - u32 reg_addr = MII_ADDR_C45 | dev << 16 | reg; + u32 reg_addr = mdiobus_c45_addr(dev, reg); return mdiobus_write(xpcs->bus, xpcs->addr, reg_addr, val); } From 2cac15dae2f6e2f86bef1acc2a7f78fc97a0a060 Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Wed, 2 Jun 2021 19:20:18 +0300 Subject: [PATCH 8/9] net: pcs: xpcs: convert to mdio_device Unify the 2 existing PCS drivers (lynx and xpcs) by doing a similar thing on probe, which is to have a *_create function that takes a struct mdio_device * given by the caller, and builds a private PCS structure around that. This changes stmmac to hold only a pointer to the xpcs, as opposed to the full structure. This will be used in the next patch when struct mdio_xpcs_ops is removed. Currently a pointer to struct mdio_xpcs_ops is used as a shorthand to determine whether the port has an XPCS or not. We can do the same now with the mdio_xpcs_args pointer. Signed-off-by: Vladimir Oltean Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/common.h | 2 +- .../ethernet/stmicro/stmmac/stmmac_ethtool.c | 2 +- .../net/ethernet/stmicro/stmmac/stmmac_main.c | 10 ++-- .../net/ethernet/stmicro/stmmac/stmmac_mdio.c | 39 ++++++++------ drivers/net/pcs/pcs-xpcs.c | 53 +++++++++++++++---- include/linux/pcs/pcs-xpcs.h | 7 +-- 6 files changed, 76 insertions(+), 37 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h index 619e3c0760d66..4bcd1d3407667 100644 --- a/drivers/net/ethernet/stmicro/stmmac/common.h +++ b/drivers/net/ethernet/stmicro/stmmac/common.h @@ -504,7 +504,7 @@ struct mac_device_info { const struct stmmac_tc_ops *tc; const struct stmmac_mmc_ops *mmc; const struct mdio_xpcs_ops *xpcs; - struct mdio_xpcs_args xpcs_args; + struct mdio_xpcs_args *xpcs_args; struct mii_regs mii; /* MII register Addresses */ struct mac_link link; void __iomem *pcsr; /* vpointer to device CSRs */ diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c index ba7d0f40723a2..050576ee704df 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c @@ -721,7 +721,7 @@ static int stmmac_ethtool_op_set_eee(struct net_device *dev, "Setting EEE tx-lpi is not supported\n"); if (priv->hw->xpcs) { - ret = xpcs_config_eee(&priv->hw->xpcs_args, + ret = xpcs_config_eee(priv->hw->xpcs_args, priv->plat->mult_fact_100ns, edata->eee_enabled); if (ret) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index b7e6ab05ddd98..e9e5bcb79d483 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -997,7 +997,7 @@ static void stmmac_validate(struct phylink_config *config, /* If PCS is supported, check which modes it supports. */ if (priv->hw->xpcs) - xpcs_validate(&priv->hw->xpcs_args, supported, state); + xpcs_validate(priv->hw->xpcs_args, supported, state); } static void stmmac_mac_pcs_get_state(struct phylink_config *config, @@ -1006,7 +1006,7 @@ static void stmmac_mac_pcs_get_state(struct phylink_config *config, struct stmmac_priv *priv = netdev_priv(to_net_dev(config->dev)); state->link = 0; - stmmac_xpcs_get_state(priv, &priv->hw->xpcs_args, state); + stmmac_xpcs_get_state(priv, priv->hw->xpcs_args, state); } static void stmmac_mac_config(struct phylink_config *config, unsigned int mode, @@ -1014,7 +1014,7 @@ static void stmmac_mac_config(struct phylink_config *config, unsigned int mode, { struct stmmac_priv *priv = netdev_priv(to_net_dev(config->dev)); - stmmac_xpcs_config(priv, &priv->hw->xpcs_args, state); + stmmac_xpcs_config(priv, priv->hw->xpcs_args, state); } static void stmmac_mac_an_restart(struct phylink_config *config) @@ -1061,7 +1061,7 @@ static void stmmac_mac_link_up(struct phylink_config *config, struct stmmac_priv *priv = netdev_priv(to_net_dev(config->dev)); u32 ctrl; - stmmac_xpcs_link_up(priv, &priv->hw->xpcs_args, speed, interface); + stmmac_xpcs_link_up(priv, priv->hw->xpcs_args, speed, interface); ctrl = readl(priv->ioaddr + MAC_CTRL_REG); ctrl &= ~priv->hw->link.speed_mask; @@ -3653,7 +3653,7 @@ int stmmac_open(struct net_device *dev) if (priv->hw->pcs != STMMAC_PCS_TBI && priv->hw->pcs != STMMAC_PCS_RTBI && (!priv->hw->xpcs || - xpcs_get_an_mode(&priv->hw->xpcs_args, mode) != DW_AN_C73)) { + xpcs_get_an_mode(priv->hw->xpcs_args, mode) != DW_AN_C73)) { ret = stmmac_init_phy(dev); if (ret) { netdev_err(priv->dev, diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c index 56deb92a8430d..9b4bf78d2eaa0 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c @@ -510,25 +510,27 @@ int stmmac_mdio_register(struct net_device *ndev) } /* Try to probe the XPCS by scanning all addresses. */ - if (priv->hw->xpcs) { - struct mdio_xpcs_args *xpcs = &priv->hw->xpcs_args; - int ret, mode = priv->plat->phy_interface; - max_addr = PHY_MAX_ADDR; - - xpcs->bus = new_bus; - - found = 0; - for (addr = 0; addr < max_addr; addr++) { - xpcs->addr = addr; - - ret = xpcs_probe(xpcs, mode); - if (!ret) { - found = 1; - break; + if (mdio_bus_data->has_xpcs) { + int mode = priv->plat->phy_interface; + struct mdio_device *mdiodev; + struct mdio_xpcs_args *xpcs; + + for (addr = 0; addr < PHY_MAX_ADDR; addr++) { + mdiodev = mdio_device_create(new_bus, addr); + if (IS_ERR(mdiodev)) + continue; + + xpcs = xpcs_create(mdiodev, mode); + if (IS_ERR_OR_NULL(xpcs)) { + mdio_device_free(mdiodev); + continue; } + + priv->hw->xpcs_args = xpcs; + break; } - if (!found && !mdio_node) { + if (!priv->hw->xpcs_args) { dev_warn(dev, "No XPCS found\n"); err = -ENODEV; goto no_xpcs_found; @@ -560,6 +562,11 @@ int stmmac_mdio_unregister(struct net_device *ndev) if (!priv->mii) return 0; + if (priv->hw->xpcs) { + mdio_device_free(priv->hw->xpcs_args->mdiodev); + xpcs_destroy(priv->hw->xpcs_args); + } + mdiobus_unregister(priv->mii); priv->mii->priv = NULL; mdiobus_free(priv->mii); diff --git a/drivers/net/pcs/pcs-xpcs.c b/drivers/net/pcs/pcs-xpcs.c index afabb9209c52c..e17e72175ebb1 100644 --- a/drivers/net/pcs/pcs-xpcs.c +++ b/drivers/net/pcs/pcs-xpcs.c @@ -241,15 +241,19 @@ static bool __xpcs_linkmode_supported(const struct xpcs_compat *compat, static int xpcs_read(struct mdio_xpcs_args *xpcs, int dev, u32 reg) { u32 reg_addr = mdiobus_c45_addr(dev, reg); + struct mii_bus *bus = xpcs->mdiodev->bus; + int addr = xpcs->mdiodev->addr; - return mdiobus_read(xpcs->bus, xpcs->addr, reg_addr); + return mdiobus_read(bus, addr, reg_addr); } static int xpcs_write(struct mdio_xpcs_args *xpcs, int dev, u32 reg, u16 val) { u32 reg_addr = mdiobus_c45_addr(dev, reg); + struct mii_bus *bus = xpcs->mdiodev->bus; + int addr = xpcs->mdiodev->addr; - return mdiobus_write(xpcs->bus, xpcs->addr, reg_addr, val); + return mdiobus_write(bus, addr, reg_addr, val); } static int xpcs_read_vendor(struct mdio_xpcs_args *xpcs, int dev, u32 reg) @@ -315,7 +319,7 @@ static int xpcs_soft_reset(struct mdio_xpcs_args *xpcs, #define xpcs_warn(__xpcs, __state, __args...) \ ({ \ if ((__state)->link) \ - dev_warn(&(__xpcs)->bus->dev, ##__args); \ + dev_warn(&(__xpcs)->mdiodev->dev, ##__args); \ }) static int xpcs_read_fault_c73(struct mdio_xpcs_args *xpcs, @@ -1005,10 +1009,20 @@ static const struct xpcs_id xpcs_id_list[] = { }, }; -int xpcs_probe(struct mdio_xpcs_args *xpcs, phy_interface_t interface) +struct mdio_xpcs_args *xpcs_create(struct mdio_device *mdiodev, + phy_interface_t interface) { - u32 xpcs_id = xpcs_get_id(xpcs); - int i; + struct mdio_xpcs_args *xpcs; + u32 xpcs_id; + int i, ret; + + xpcs = kzalloc(sizeof(*xpcs), GFP_KERNEL); + if (!xpcs) + return NULL; + + xpcs->mdiodev = mdiodev; + + xpcs_id = xpcs_get_id(xpcs); for (i = 0; i < ARRAY_SIZE(xpcs_id_list); i++) { const struct xpcs_id *entry = &xpcs_id_list[i]; @@ -1020,15 +1034,32 @@ int xpcs_probe(struct mdio_xpcs_args *xpcs, phy_interface_t interface) xpcs->id = entry; compat = xpcs_find_compat(entry, interface); - if (!compat) - return -ENODEV; + if (!compat) { + ret = -ENODEV; + goto out; + } - return xpcs_soft_reset(xpcs, compat); + ret = xpcs_soft_reset(xpcs, compat); + if (ret) + goto out; + + return xpcs; } - return -ENODEV; + ret = -ENODEV; + +out: + kfree(xpcs); + + return ERR_PTR(ret); +} +EXPORT_SYMBOL_GPL(xpcs_create); + +void xpcs_destroy(struct mdio_xpcs_args *xpcs) +{ + kfree(xpcs); } -EXPORT_SYMBOL_GPL(xpcs_probe); +EXPORT_SYMBOL_GPL(xpcs_destroy); static struct mdio_xpcs_ops xpcs_ops = { .config = xpcs_config, diff --git a/include/linux/pcs/pcs-xpcs.h b/include/linux/pcs/pcs-xpcs.h index 1d8581b74d81f..57a199393d63b 100644 --- a/include/linux/pcs/pcs-xpcs.h +++ b/include/linux/pcs/pcs-xpcs.h @@ -17,9 +17,8 @@ struct xpcs_id; struct mdio_xpcs_args { - struct mii_bus *bus; + struct mdio_device *mdiodev; const struct xpcs_id *id; - int addr; }; struct mdio_xpcs_ops { @@ -37,6 +36,8 @@ void xpcs_validate(struct mdio_xpcs_args *xpcs, unsigned long *supported, struct phylink_link_state *state); int xpcs_config_eee(struct mdio_xpcs_args *xpcs, int mult_fact_100ns, int enable); -int xpcs_probe(struct mdio_xpcs_args *xpcs, phy_interface_t interface); +struct mdio_xpcs_args *xpcs_create(struct mdio_device *mdiodev, + phy_interface_t interface); +void xpcs_destroy(struct mdio_xpcs_args *xpcs); #endif /* __LINUX_PCS_XPCS_H */ From 11059740e616f4d83d8d9e3f8a63dafefdc2ae5d Mon Sep 17 00:00:00 2001 From: Vladimir Oltean Date: Wed, 2 Jun 2021 19:20:19 +0300 Subject: [PATCH 9/9] net: pcs: xpcs: convert to phylink_pcs_ops Since all the remaining members of struct mdio_xpcs_ops have direct equivalents in struct phylink_pcs_ops, it is about time we remove it altogether. Since the phylink ops return void, we need to remove the error propagation from the various xpcs methods and simply print an error message where appropriate. Since xpcs_get_state_c73() detects link faults and attempts to reset the link on its own by calling xpcs_config(), but xpcs_config() now has a lot of phylink arguments which are not needed and cannot be simply fabricated by anybody else except phylink, the actual implementation has been moved into a smaller xpcs_do_config(). The const struct mdio_xpcs_ops *priv->hw->xpcs has been removed, so we need to look at the struct mdio_xpcs_args pointer now as an indication whether the port has an XPCS or not. Signed-off-by: Vladimir Oltean Signed-off-by: David S. Miller --- drivers/net/ethernet/stmicro/stmmac/common.h | 3 +- drivers/net/ethernet/stmicro/stmmac/hwif.h | 8 -- .../ethernet/stmicro/stmmac/stmmac_ethtool.c | 2 +- .../net/ethernet/stmicro/stmmac/stmmac_main.c | 36 +++---- .../net/ethernet/stmicro/stmmac/stmmac_mdio.c | 16 +-- drivers/net/pcs/pcs-xpcs.c | 99 +++++++++++-------- include/linux/pcs/pcs-xpcs.h | 11 +-- 7 files changed, 78 insertions(+), 97 deletions(-) diff --git a/drivers/net/ethernet/stmicro/stmmac/common.h b/drivers/net/ethernet/stmicro/stmmac/common.h index 4bcd1d3407667..8a83f9e1e95b6 100644 --- a/drivers/net/ethernet/stmicro/stmmac/common.h +++ b/drivers/net/ethernet/stmicro/stmmac/common.h @@ -503,8 +503,7 @@ struct mac_device_info { const struct stmmac_hwtimestamp *ptp; const struct stmmac_tc_ops *tc; const struct stmmac_mmc_ops *mmc; - const struct mdio_xpcs_ops *xpcs; - struct mdio_xpcs_args *xpcs_args; + struct mdio_xpcs_args *xpcs; struct mii_regs mii; /* MII register Addresses */ struct mac_link link; void __iomem *pcsr; /* vpointer to device CSRs */ diff --git a/drivers/net/ethernet/stmicro/stmmac/hwif.h b/drivers/net/ethernet/stmicro/stmmac/hwif.h index 3dc291ff9f325..6dc1c98ebec82 100644 --- a/drivers/net/ethernet/stmicro/stmmac/hwif.h +++ b/drivers/net/ethernet/stmicro/stmmac/hwif.h @@ -613,14 +613,6 @@ struct stmmac_mmc_ops { #define stmmac_mmc_read(__priv, __args...) \ stmmac_do_void_callback(__priv, mmc, read, __args) -/* XPCS callbacks */ -#define stmmac_xpcs_config(__priv, __args...) \ - stmmac_do_callback(__priv, xpcs, config, __args) -#define stmmac_xpcs_get_state(__priv, __args...) \ - stmmac_do_callback(__priv, xpcs, get_state, __args) -#define stmmac_xpcs_link_up(__priv, __args...) \ - stmmac_do_callback(__priv, xpcs, link_up, __args) - struct stmmac_regs_off { u32 ptp_off; u32 mmc_off; diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c index 050576ee704df..d0ce608b81c38 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_ethtool.c @@ -721,7 +721,7 @@ static int stmmac_ethtool_op_set_eee(struct net_device *dev, "Setting EEE tx-lpi is not supported\n"); if (priv->hw->xpcs) { - ret = xpcs_config_eee(priv->hw->xpcs_args, + ret = xpcs_config_eee(priv->hw->xpcs, priv->plat->mult_fact_100ns, edata->eee_enabled); if (ret) diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c index e9e5bcb79d483..6d41dd6f9f7a6 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_main.c @@ -997,29 +997,13 @@ static void stmmac_validate(struct phylink_config *config, /* If PCS is supported, check which modes it supports. */ if (priv->hw->xpcs) - xpcs_validate(priv->hw->xpcs_args, supported, state); -} - -static void stmmac_mac_pcs_get_state(struct phylink_config *config, - struct phylink_link_state *state) -{ - struct stmmac_priv *priv = netdev_priv(to_net_dev(config->dev)); - - state->link = 0; - stmmac_xpcs_get_state(priv, priv->hw->xpcs_args, state); + xpcs_validate(priv->hw->xpcs, supported, state); } static void stmmac_mac_config(struct phylink_config *config, unsigned int mode, const struct phylink_link_state *state) { - struct stmmac_priv *priv = netdev_priv(to_net_dev(config->dev)); - - stmmac_xpcs_config(priv, priv->hw->xpcs_args, state); -} - -static void stmmac_mac_an_restart(struct phylink_config *config) -{ - /* Not Supported */ + /* Nothing to do, xpcs_config() handles everything */ } static void stmmac_fpe_link_state_handle(struct stmmac_priv *priv, bool is_up) @@ -1061,8 +1045,6 @@ static void stmmac_mac_link_up(struct phylink_config *config, struct stmmac_priv *priv = netdev_priv(to_net_dev(config->dev)); u32 ctrl; - stmmac_xpcs_link_up(priv, priv->hw->xpcs_args, speed, interface); - ctrl = readl(priv->ioaddr + MAC_CTRL_REG); ctrl &= ~priv->hw->link.speed_mask; @@ -1155,9 +1137,7 @@ static void stmmac_mac_link_up(struct phylink_config *config, static const struct phylink_mac_ops stmmac_phylink_mac_ops = { .validate = stmmac_validate, - .mac_pcs_get_state = stmmac_mac_pcs_get_state, .mac_config = stmmac_mac_config, - .mac_an_restart = stmmac_mac_an_restart, .mac_link_down = stmmac_mac_link_down, .mac_link_up = stmmac_mac_link_up, }; @@ -1234,6 +1214,7 @@ static int stmmac_init_phy(struct net_device *dev) static int stmmac_phy_setup(struct stmmac_priv *priv) { + struct stmmac_mdio_bus_data *mdio_bus_data = priv->plat->mdio_bus_data; struct fwnode_handle *fwnode = of_fwnode_handle(priv->plat->phylink_node); int mode = priv->plat->phy_interface; struct phylink *phylink; @@ -1241,8 +1222,7 @@ static int stmmac_phy_setup(struct stmmac_priv *priv) priv->phylink_config.dev = &priv->dev->dev; priv->phylink_config.type = PHYLINK_NETDEV; priv->phylink_config.pcs_poll = true; - priv->phylink_config.ovr_an_inband = - priv->plat->mdio_bus_data->xpcs_an_inband; + priv->phylink_config.ovr_an_inband = mdio_bus_data->xpcs_an_inband; if (!fwnode) fwnode = dev_fwnode(priv->device); @@ -1252,6 +1232,12 @@ static int stmmac_phy_setup(struct stmmac_priv *priv) if (IS_ERR(phylink)) return PTR_ERR(phylink); + if (mdio_bus_data->has_xpcs) { + struct mdio_xpcs_args *xpcs = priv->hw->xpcs; + + phylink_set_pcs(phylink, &xpcs->pcs); + } + priv->phylink = phylink; return 0; } @@ -3653,7 +3639,7 @@ int stmmac_open(struct net_device *dev) if (priv->hw->pcs != STMMAC_PCS_TBI && priv->hw->pcs != STMMAC_PCS_RTBI && (!priv->hw->xpcs || - xpcs_get_an_mode(priv->hw->xpcs_args, mode) != DW_AN_C73)) { + xpcs_get_an_mode(priv->hw->xpcs, mode) != DW_AN_C73)) { ret = stmmac_init_phy(dev); if (ret) { netdev_err(priv->dev, diff --git a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c index 9b4bf78d2eaa0..6312a152c8ad9 100644 --- a/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c +++ b/drivers/net/ethernet/stmicro/stmmac/stmmac_mdio.c @@ -444,14 +444,6 @@ int stmmac_mdio_register(struct net_device *ndev) max_addr = PHY_MAX_ADDR; } - if (mdio_bus_data->has_xpcs) { - priv->hw->xpcs = mdio_xpcs_get_ops(); - if (!priv->hw->xpcs) { - err = -ENODEV; - goto bus_register_fail; - } - } - if (mdio_bus_data->needs_reset) new_bus->reset = &stmmac_mdio_reset; @@ -526,11 +518,11 @@ int stmmac_mdio_register(struct net_device *ndev) continue; } - priv->hw->xpcs_args = xpcs; + priv->hw->xpcs = xpcs; break; } - if (!priv->hw->xpcs_args) { + if (!priv->hw->xpcs) { dev_warn(dev, "No XPCS found\n"); err = -ENODEV; goto no_xpcs_found; @@ -563,8 +555,8 @@ int stmmac_mdio_unregister(struct net_device *ndev) return 0; if (priv->hw->xpcs) { - mdio_device_free(priv->hw->xpcs_args->mdiodev); - xpcs_destroy(priv->hw->xpcs_args); + mdio_device_free(priv->hw->xpcs->mdiodev); + xpcs_destroy(priv->hw->xpcs); } mdiobus_unregister(priv->mii); diff --git a/drivers/net/pcs/pcs-xpcs.c b/drivers/net/pcs/pcs-xpcs.c index e17e72175ebb1..34164437c1359 100644 --- a/drivers/net/pcs/pcs-xpcs.c +++ b/drivers/net/pcs/pcs-xpcs.c @@ -100,6 +100,9 @@ /* VR MII EEE Control 1 defines */ #define DW_VR_MII_EEE_TRN_LPI BIT(0) /* Transparent Mode Enable */ +#define phylink_pcs_to_xpcs(pl_pcs) \ + container_of((pl_pcs), struct mdio_xpcs_args, pcs) + static const int xpcs_usxgmii_features[] = { ETHTOOL_LINK_MODE_Pause_BIT, ETHTOOL_LINK_MODE_Asym_Pause_BIT, @@ -413,7 +416,7 @@ static int xpcs_get_max_usxgmii_speed(const unsigned long *supported) return max; } -static int xpcs_config_usxgmii(struct mdio_xpcs_args *xpcs, int speed) +static void xpcs_config_usxgmii(struct mdio_xpcs_args *xpcs, int speed) { int ret, speed_sel; @@ -438,33 +441,40 @@ static int xpcs_config_usxgmii(struct mdio_xpcs_args *xpcs, int speed) break; default: /* Nothing to do here */ - return -EINVAL; + return; } ret = xpcs_read_vpcs(xpcs, MDIO_CTRL1); if (ret < 0) - return ret; + goto out; ret = xpcs_write_vpcs(xpcs, MDIO_CTRL1, ret | DW_USXGMII_EN); if (ret < 0) - return ret; + goto out; ret = xpcs_read(xpcs, MDIO_MMD_VEND2, MDIO_CTRL1); if (ret < 0) - return ret; + goto out; ret &= ~DW_USXGMII_SS_MASK; ret |= speed_sel | DW_USXGMII_FULL; ret = xpcs_write(xpcs, MDIO_MMD_VEND2, MDIO_CTRL1, ret); if (ret < 0) - return ret; + goto out; ret = xpcs_read_vpcs(xpcs, MDIO_CTRL1); if (ret < 0) - return ret; + goto out; + + ret = xpcs_write_vpcs(xpcs, MDIO_CTRL1, ret | DW_USXGMII_RST); + if (ret < 0) + goto out; + + return; - return xpcs_write_vpcs(xpcs, MDIO_CTRL1, ret | DW_USXGMII_RST); +out: + pr_err("%s: XPCS access returned %pe\n", __func__, ERR_PTR(ret)); } static int _xpcs_config_aneg_c73(struct mdio_xpcs_args *xpcs, @@ -794,19 +804,19 @@ static int xpcs_config_aneg_c37_sgmii(struct mdio_xpcs_args *xpcs) return xpcs_write(xpcs, MDIO_MMD_VEND2, DW_VR_MII_DIG_CTRL1, ret); } -static int xpcs_config(struct mdio_xpcs_args *xpcs, - const struct phylink_link_state *state) +static int xpcs_do_config(struct mdio_xpcs_args *xpcs, + phy_interface_t interface, unsigned int mode) { const struct xpcs_compat *compat; int ret; - compat = xpcs_find_compat(xpcs->id, state->interface); + compat = xpcs_find_compat(xpcs->id, interface); if (!compat) return -ENODEV; switch (compat->an_mode) { case DW_AN_C73: - if (state->an_enabled) { + if (phylink_autoneg_inband(mode)) { ret = xpcs_config_aneg_c73(xpcs, compat); if (ret) return ret; @@ -824,6 +834,16 @@ static int xpcs_config(struct mdio_xpcs_args *xpcs, return 0; } +static int xpcs_config(struct phylink_pcs *pcs, unsigned int mode, + phy_interface_t interface, + const unsigned long *advertising, + bool permit_pause_to_mac) +{ + struct mdio_xpcs_args *xpcs = phylink_pcs_to_xpcs(pcs); + + return xpcs_do_config(xpcs, interface, mode); +} + static int xpcs_get_state_c73(struct mdio_xpcs_args *xpcs, struct phylink_link_state *state, const struct xpcs_compat *compat) @@ -842,7 +862,7 @@ static int xpcs_get_state_c73(struct mdio_xpcs_args *xpcs, state->link = 0; - return xpcs_config(xpcs, state); + return xpcs_do_config(xpcs, state->interface, MLO_AN_INBAND); } if (state->an_enabled && xpcs_aneg_done_c73(xpcs, state, compat)) { @@ -899,41 +919,45 @@ static int xpcs_get_state_c37_sgmii(struct mdio_xpcs_args *xpcs, return 0; } -static int xpcs_get_state(struct mdio_xpcs_args *xpcs, - struct phylink_link_state *state) +static void xpcs_get_state(struct phylink_pcs *pcs, + struct phylink_link_state *state) { + struct mdio_xpcs_args *xpcs = phylink_pcs_to_xpcs(pcs); const struct xpcs_compat *compat; int ret; compat = xpcs_find_compat(xpcs->id, state->interface); if (!compat) - return -ENODEV; + return; switch (compat->an_mode) { case DW_AN_C73: ret = xpcs_get_state_c73(xpcs, state, compat); - if (ret) - return ret; + if (ret) { + pr_err("xpcs_get_state_c73 returned %pe\n", + ERR_PTR(ret)); + return; + } break; case DW_AN_C37_SGMII: ret = xpcs_get_state_c37_sgmii(xpcs, state); - if (ret) - return ret; + if (ret) { + pr_err("xpcs_get_state_c37_sgmii returned %pe\n", + ERR_PTR(ret)); + } break; default: - return -1; + return; } - - return 0; } -static int xpcs_link_up(struct mdio_xpcs_args *xpcs, int speed, - phy_interface_t interface) +static void xpcs_link_up(struct phylink_pcs *pcs, unsigned int mode, + phy_interface_t interface, int speed, int duplex) { + struct mdio_xpcs_args *xpcs = phylink_pcs_to_xpcs(pcs); + if (interface == PHY_INTERFACE_MODE_USXGMII) return xpcs_config_usxgmii(xpcs, speed); - - return 0; } static u32 xpcs_get_id(struct mdio_xpcs_args *xpcs) @@ -1009,6 +1033,12 @@ static const struct xpcs_id xpcs_id_list[] = { }, }; +static const struct phylink_pcs_ops xpcs_phylink_ops = { + .pcs_config = xpcs_config, + .pcs_get_state = xpcs_get_state, + .pcs_link_up = xpcs_link_up, +}; + struct mdio_xpcs_args *xpcs_create(struct mdio_device *mdiodev, phy_interface_t interface) { @@ -1039,6 +1069,9 @@ struct mdio_xpcs_args *xpcs_create(struct mdio_device *mdiodev, goto out; } + xpcs->pcs.ops = &xpcs_phylink_ops; + xpcs->pcs.poll = true; + ret = xpcs_soft_reset(xpcs, compat); if (ret) goto out; @@ -1061,16 +1094,4 @@ void xpcs_destroy(struct mdio_xpcs_args *xpcs) } EXPORT_SYMBOL_GPL(xpcs_destroy); -static struct mdio_xpcs_ops xpcs_ops = { - .config = xpcs_config, - .get_state = xpcs_get_state, - .link_up = xpcs_link_up, -}; - -struct mdio_xpcs_ops *mdio_xpcs_get_ops(void) -{ - return &xpcs_ops; -} -EXPORT_SYMBOL_GPL(mdio_xpcs_get_ops); - MODULE_LICENSE("GPL v2"); diff --git a/include/linux/pcs/pcs-xpcs.h b/include/linux/pcs/pcs-xpcs.h index 57a199393d63b..0860a5b59f10a 100644 --- a/include/linux/pcs/pcs-xpcs.h +++ b/include/linux/pcs/pcs-xpcs.h @@ -19,19 +19,10 @@ struct xpcs_id; struct mdio_xpcs_args { struct mdio_device *mdiodev; const struct xpcs_id *id; -}; - -struct mdio_xpcs_ops { - int (*config)(struct mdio_xpcs_args *xpcs, - const struct phylink_link_state *state); - int (*get_state)(struct mdio_xpcs_args *xpcs, - struct phylink_link_state *state); - int (*link_up)(struct mdio_xpcs_args *xpcs, int speed, - phy_interface_t interface); + struct phylink_pcs pcs; }; int xpcs_get_an_mode(struct mdio_xpcs_args *xpcs, phy_interface_t interface); -struct mdio_xpcs_ops *mdio_xpcs_get_ops(void); void xpcs_validate(struct mdio_xpcs_args *xpcs, unsigned long *supported, struct phylink_link_state *state); int xpcs_config_eee(struct mdio_xpcs_args *xpcs, int mult_fact_100ns,