715-24-v6.5-net-phylink-add-PCS-negotiation-mode.patch 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. From 79b07c3e9c4a2272927be8848c26b372516e1958 Mon Sep 17 00:00:00 2001
  2. From: "Russell King (Oracle)" <[email protected]>
  3. Date: Fri, 16 Jun 2023 13:06:22 +0100
  4. Subject: [PATCH 21/21] net: phylink: add PCS negotiation mode
  5. PCS have to work out whether they should enable PCS negotiation by
  6. looking at the "mode" and "interface" arguments, and the Autoneg bit
  7. in the advertising mask.
  8. This leads to some complex logic, so lets pull that out into phylink
  9. and instead pass a "neg_mode" argument to the PCS configuration and
  10. link up methods, instead of the "mode" argument.
  11. In order to transition drivers, add a "neg_mode" flag to the phylink
  12. PCS structure to PCS can indicate whether they want to be passed the
  13. neg_mode or the old mode argument.
  14. Signed-off-by: Russell King (Oracle) <[email protected]>
  15. Link: https://lore.kernel.org/r/[email protected]
  16. Signed-off-by: Jakub Kicinski <[email protected]>
  17. ---
  18. drivers/net/phy/phylink.c | 45 +++++++++++++----
  19. include/linux/phylink.h | 104 +++++++++++++++++++++++++++++++++++---
  20. 2 files changed, 132 insertions(+), 17 deletions(-)
  21. --- a/drivers/net/phy/phylink.c
  22. +++ b/drivers/net/phy/phylink.c
  23. @@ -71,6 +71,7 @@ struct phylink {
  24. struct mutex state_mutex;
  25. struct phylink_link_state phy_state;
  26. struct work_struct resolve;
  27. + unsigned int pcs_neg_mode;
  28. bool mac_link_dropped;
  29. bool using_mac_select_pcs;
  30. @@ -991,23 +992,23 @@ static void phylink_resolve_an_pause(str
  31. }
  32. }
  33. -static int phylink_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
  34. +static int phylink_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
  35. const struct phylink_link_state *state,
  36. bool permit_pause_to_mac)
  37. {
  38. if (!pcs)
  39. return 0;
  40. - return pcs->ops->pcs_config(pcs, mode, state->interface,
  41. + return pcs->ops->pcs_config(pcs, neg_mode, state->interface,
  42. state->advertising, permit_pause_to_mac);
  43. }
  44. -static void phylink_pcs_link_up(struct phylink_pcs *pcs, unsigned int mode,
  45. +static void phylink_pcs_link_up(struct phylink_pcs *pcs, unsigned int neg_mode,
  46. phy_interface_t interface, int speed,
  47. int duplex)
  48. {
  49. if (pcs && pcs->ops->pcs_link_up)
  50. - pcs->ops->pcs_link_up(pcs, mode, interface, speed, duplex);
  51. + pcs->ops->pcs_link_up(pcs, neg_mode, interface, speed, duplex);
  52. }
  53. static void phylink_pcs_poll_stop(struct phylink *pl)
  54. @@ -1057,10 +1058,15 @@ static void phylink_major_config(struct
  55. struct phylink_pcs *pcs = NULL;
  56. bool pcs_changed = false;
  57. unsigned int rate_kbd;
  58. + unsigned int neg_mode;
  59. int err;
  60. phylink_dbg(pl, "major config %s\n", phy_modes(state->interface));
  61. + pl->pcs_neg_mode = phylink_pcs_neg_mode(pl->cur_link_an_mode,
  62. + state->interface,
  63. + state->advertising);
  64. +
  65. if (pl->using_mac_select_pcs) {
  66. pcs = pl->mac_ops->mac_select_pcs(pl->config, state->interface);
  67. if (IS_ERR(pcs)) {
  68. @@ -1093,9 +1099,12 @@ static void phylink_major_config(struct
  69. phylink_mac_config(pl, state);
  70. - err = phylink_pcs_config(pl->pcs, pl->cur_link_an_mode, state,
  71. - !!(pl->link_config.pause &
  72. - MLO_PAUSE_AN));
  73. + neg_mode = pl->cur_link_an_mode;
  74. + if (pl->pcs && pl->pcs->neg_mode)
  75. + neg_mode = pl->pcs_neg_mode;
  76. +
  77. + err = phylink_pcs_config(pl->pcs, neg_mode, state,
  78. + !!(pl->link_config.pause & MLO_PAUSE_AN));
  79. if (err < 0)
  80. phylink_err(pl, "pcs_config failed: %pe\n",
  81. ERR_PTR(err));
  82. @@ -1130,6 +1139,7 @@ static void phylink_major_config(struct
  83. */
  84. static int phylink_change_inband_advert(struct phylink *pl)
  85. {
  86. + unsigned int neg_mode;
  87. int ret;
  88. if (test_bit(PHYLINK_DISABLE_STOPPED, &pl->phylink_disable_state))
  89. @@ -1148,12 +1158,20 @@ static int phylink_change_inband_advert(
  90. __ETHTOOL_LINK_MODE_MASK_NBITS, pl->link_config.advertising,
  91. pl->link_config.pause);
  92. + /* Recompute the PCS neg mode */
  93. + pl->pcs_neg_mode = phylink_pcs_neg_mode(pl->cur_link_an_mode,
  94. + pl->link_config.interface,
  95. + pl->link_config.advertising);
  96. +
  97. + neg_mode = pl->cur_link_an_mode;
  98. + if (pl->pcs->neg_mode)
  99. + neg_mode = pl->pcs_neg_mode;
  100. +
  101. /* Modern PCS-based method; update the advert at the PCS, and
  102. * restart negotiation if the pcs_config() helper indicates that
  103. * the programmed advertisement has changed.
  104. */
  105. - ret = phylink_pcs_config(pl->pcs, pl->cur_link_an_mode,
  106. - &pl->link_config,
  107. + ret = phylink_pcs_config(pl->pcs, neg_mode, &pl->link_config,
  108. !!(pl->link_config.pause & MLO_PAUSE_AN));
  109. if (ret < 0)
  110. return ret;
  111. @@ -1256,6 +1274,7 @@ static void phylink_link_up(struct phyli
  112. struct phylink_link_state link_state)
  113. {
  114. struct net_device *ndev = pl->netdev;
  115. + unsigned int neg_mode;
  116. int speed, duplex;
  117. bool rx_pause;
  118. @@ -1286,8 +1305,12 @@ static void phylink_link_up(struct phyli
  119. pl->cur_interface = link_state.interface;
  120. - phylink_pcs_link_up(pl->pcs, pl->cur_link_an_mode, pl->cur_interface,
  121. - speed, duplex);
  122. + neg_mode = pl->cur_link_an_mode;
  123. + if (pl->pcs && pl->pcs->neg_mode)
  124. + neg_mode = pl->pcs_neg_mode;
  125. +
  126. + phylink_pcs_link_up(pl->pcs, neg_mode, pl->cur_interface, speed,
  127. + duplex);
  128. pl->mac_ops->mac_link_up(pl->config, pl->phydev, pl->cur_link_an_mode,
  129. pl->cur_interface, speed, duplex,
  130. --- a/include/linux/phylink.h
  131. +++ b/include/linux/phylink.h
  132. @@ -21,6 +21,24 @@ enum {
  133. MLO_AN_FIXED, /* Fixed-link mode */
  134. MLO_AN_INBAND, /* In-band protocol */
  135. + /* PCS "negotiation" mode.
  136. + * PHYLINK_PCS_NEG_NONE - protocol has no inband capability
  137. + * PHYLINK_PCS_NEG_OUTBAND - some out of band or fixed link setting
  138. + * PHYLINK_PCS_NEG_INBAND_DISABLED - inband mode disabled, e.g.
  139. + * 1000base-X with autoneg off
  140. + * PHYLINK_PCS_NEG_INBAND_ENABLED - inband mode enabled
  141. + * Additionally, this can be tested using bitmasks:
  142. + * PHYLINK_PCS_NEG_INBAND - inband mode selected
  143. + * PHYLINK_PCS_NEG_ENABLED - negotiation mode enabled
  144. + */
  145. + PHYLINK_PCS_NEG_NONE = 0,
  146. + PHYLINK_PCS_NEG_ENABLED = BIT(4),
  147. + PHYLINK_PCS_NEG_OUTBAND = BIT(5),
  148. + PHYLINK_PCS_NEG_INBAND = BIT(6),
  149. + PHYLINK_PCS_NEG_INBAND_DISABLED = PHYLINK_PCS_NEG_INBAND,
  150. + PHYLINK_PCS_NEG_INBAND_ENABLED = PHYLINK_PCS_NEG_INBAND |
  151. + PHYLINK_PCS_NEG_ENABLED,
  152. +
  153. /* MAC_SYM_PAUSE and MAC_ASYM_PAUSE are used when configuring our
  154. * autonegotiation advertisement. They correspond to the PAUSE and
  155. * ASM_DIR bits defined by 802.3, respectively.
  156. @@ -80,6 +98,70 @@ static inline bool phylink_autoneg_inban
  157. }
  158. /**
  159. + * phylink_pcs_neg_mode() - helper to determine PCS inband mode
  160. + * @mode: one of %MLO_AN_FIXED, %MLO_AN_PHY, %MLO_AN_INBAND.
  161. + * @interface: interface mode to be used
  162. + * @advertising: adertisement ethtool link mode mask
  163. + *
  164. + * Determines the negotiation mode to be used by the PCS, and returns
  165. + * one of:
  166. + * %PHYLINK_PCS_NEG_NONE: interface mode does not support inband
  167. + * %PHYLINK_PCS_NEG_OUTBAND: an out of band mode (e.g. reading the PHY)
  168. + * will be used.
  169. + * %PHYLINK_PCS_NEG_INBAND_DISABLED: inband mode selected but autoneg disabled
  170. + * %PHYLINK_PCS_NEG_INBAND_ENABLED: inband mode selected and autoneg enabled
  171. + *
  172. + * Note: this is for cases where the PCS itself is involved in negotiation
  173. + * (e.g. Clause 37, SGMII and similar) not Clause 73.
  174. + */
  175. +static inline unsigned int phylink_pcs_neg_mode(unsigned int mode,
  176. + phy_interface_t interface,
  177. + const unsigned long *advertising)
  178. +{
  179. + unsigned int neg_mode;
  180. +
  181. + switch (interface) {
  182. + case PHY_INTERFACE_MODE_SGMII:
  183. + case PHY_INTERFACE_MODE_QSGMII:
  184. + case PHY_INTERFACE_MODE_QUSGMII:
  185. + case PHY_INTERFACE_MODE_USXGMII:
  186. + /* These protocols are designed for use with a PHY which
  187. + * communicates its negotiation result back to the MAC via
  188. + * inband communication. Note: there exist PHYs that run
  189. + * with SGMII but do not send the inband data.
  190. + */
  191. + if (!phylink_autoneg_inband(mode))
  192. + neg_mode = PHYLINK_PCS_NEG_OUTBAND;
  193. + else
  194. + neg_mode = PHYLINK_PCS_NEG_INBAND_ENABLED;
  195. + break;
  196. +
  197. + case PHY_INTERFACE_MODE_1000BASEX:
  198. + case PHY_INTERFACE_MODE_2500BASEX:
  199. + /* 1000base-X is designed for use media-side for Fibre
  200. + * connections, and thus the Autoneg bit needs to be
  201. + * taken into account. We also do this for 2500base-X
  202. + * as well, but drivers may not support this, so may
  203. + * need to override this.
  204. + */
  205. + if (!phylink_autoneg_inband(mode))
  206. + neg_mode = PHYLINK_PCS_NEG_OUTBAND;
  207. + else if (linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
  208. + advertising))
  209. + neg_mode = PHYLINK_PCS_NEG_INBAND_ENABLED;
  210. + else
  211. + neg_mode = PHYLINK_PCS_NEG_INBAND_DISABLED;
  212. + break;
  213. +
  214. + default:
  215. + neg_mode = PHYLINK_PCS_NEG_NONE;
  216. + break;
  217. + }
  218. +
  219. + return neg_mode;
  220. +}
  221. +
  222. +/**
  223. * struct phylink_link_state - link state structure
  224. * @advertising: ethtool bitmask containing advertised link modes
  225. * @lp_advertising: ethtool bitmask containing link partner advertised link
  226. @@ -436,6 +518,7 @@ struct phylink_pcs_ops;
  227. /**
  228. * struct phylink_pcs - PHYLINK PCS instance
  229. * @ops: a pointer to the &struct phylink_pcs_ops structure
  230. + * @neg_mode: provide PCS neg mode via "mode" argument
  231. * @poll: poll the PCS for link changes
  232. *
  233. * This structure is designed to be embedded within the PCS private data,
  234. @@ -443,6 +526,7 @@ struct phylink_pcs_ops;
  235. */
  236. struct phylink_pcs {
  237. const struct phylink_pcs_ops *ops;
  238. + bool neg_mode;
  239. bool poll;
  240. };
  241. @@ -460,12 +544,12 @@ struct phylink_pcs_ops {
  242. const struct phylink_link_state *state);
  243. void (*pcs_get_state)(struct phylink_pcs *pcs,
  244. struct phylink_link_state *state);
  245. - int (*pcs_config)(struct phylink_pcs *pcs, unsigned int mode,
  246. + int (*pcs_config)(struct phylink_pcs *pcs, unsigned int neg_mode,
  247. phy_interface_t interface,
  248. const unsigned long *advertising,
  249. bool permit_pause_to_mac);
  250. void (*pcs_an_restart)(struct phylink_pcs *pcs);
  251. - void (*pcs_link_up)(struct phylink_pcs *pcs, unsigned int mode,
  252. + void (*pcs_link_up)(struct phylink_pcs *pcs, unsigned int neg_mode,
  253. phy_interface_t interface, int speed, int duplex);
  254. };
  255. @@ -508,7 +592,7 @@ void pcs_get_state(struct phylink_pcs *p
  256. /**
  257. * pcs_config() - Configure the PCS mode and advertisement
  258. * @pcs: a pointer to a &struct phylink_pcs.
  259. - * @mode: one of %MLO_AN_FIXED, %MLO_AN_PHY, %MLO_AN_INBAND.
  260. + * @neg_mode: link negotiation mode (see below)
  261. * @interface: interface mode to be used
  262. * @advertising: adertisement ethtool link mode mask
  263. * @permit_pause_to_mac: permit forwarding pause resolution to MAC
  264. @@ -526,8 +610,12 @@ void pcs_get_state(struct phylink_pcs *p
  265. * For 1000BASE-X, the advertisement should be programmed into the PCS.
  266. *
  267. * For most 10GBASE-R, there is no advertisement.
  268. + *
  269. + * The %neg_mode argument should be tested via the phylink_mode_*() family of
  270. + * functions, or for PCS that set pcs->neg_mode true, should be tested
  271. + * against the %PHYLINK_PCS_NEG_* definitions.
  272. */
  273. -int pcs_config(struct phylink_pcs *pcs, unsigned int mode,
  274. +int pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
  275. phy_interface_t interface, const unsigned long *advertising,
  276. bool permit_pause_to_mac);
  277. @@ -543,7 +631,7 @@ void pcs_an_restart(struct phylink_pcs *
  278. /**
  279. * pcs_link_up() - program the PCS for the resolved link configuration
  280. * @pcs: a pointer to a &struct phylink_pcs.
  281. - * @mode: link autonegotiation mode
  282. + * @neg_mode: link negotiation mode (see below)
  283. * @interface: link &typedef phy_interface_t mode
  284. * @speed: link speed
  285. * @duplex: link duplex
  286. @@ -552,8 +640,12 @@ void pcs_an_restart(struct phylink_pcs *
  287. * the resolved link parameters. For example, a PCS operating in SGMII
  288. * mode without in-band AN needs to be manually configured for the link
  289. * and duplex setting. Otherwise, this should be a no-op.
  290. + *
  291. + * The %mode argument should be tested via the phylink_mode_*() family of
  292. + * functions, or for PCS that set pcs->neg_mode true, should be tested
  293. + * against the %PHYLINK_PCS_NEG_* definitions.
  294. */
  295. -void pcs_link_up(struct phylink_pcs *pcs, unsigned int mode,
  296. +void pcs_link_up(struct phylink_pcs *pcs, unsigned int neg_mode,
  297. phy_interface_t interface, int speed, int duplex);
  298. #endif