pcs-lynx.c 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346
  1. // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
  2. /* Copyright 2020 NXP
  3. * Lynx PCS MDIO helpers
  4. */
  5. #include <linux/mdio.h>
  6. #include <linux/phylink.h>
  7. #include <linux/pcs-lynx.h>
  8. #define SGMII_CLOCK_PERIOD_NS 8 /* PCS is clocked at 125 MHz */
  9. #define LINK_TIMER_VAL(ns) ((u32)((ns) / SGMII_CLOCK_PERIOD_NS))
  10. #define SGMII_AN_LINK_TIMER_NS 1600000 /* defined by SGMII spec */
  11. #define IEEE8023_LINK_TIMER_NS 10000000
  12. #define LINK_TIMER_LO 0x12
  13. #define LINK_TIMER_HI 0x13
  14. #define IF_MODE 0x14
  15. #define IF_MODE_SGMII_EN BIT(0)
  16. #define IF_MODE_USE_SGMII_AN BIT(1)
  17. #define IF_MODE_SPEED(x) (((x) << 2) & GENMASK(3, 2))
  18. #define IF_MODE_SPEED_MSK GENMASK(3, 2)
  19. #define IF_MODE_HALF_DUPLEX BIT(4)
  20. struct lynx_pcs {
  21. struct phylink_pcs pcs;
  22. struct mdio_device *mdio;
  23. };
  24. enum sgmii_speed {
  25. SGMII_SPEED_10 = 0,
  26. SGMII_SPEED_100 = 1,
  27. SGMII_SPEED_1000 = 2,
  28. SGMII_SPEED_2500 = 2,
  29. };
  30. #define phylink_pcs_to_lynx(pl_pcs) container_of((pl_pcs), struct lynx_pcs, pcs)
  31. #define lynx_to_phylink_pcs(lynx) (&(lynx)->pcs)
  32. struct mdio_device *lynx_get_mdio_device(struct phylink_pcs *pcs)
  33. {
  34. struct lynx_pcs *lynx = phylink_pcs_to_lynx(pcs);
  35. return lynx->mdio;
  36. }
  37. EXPORT_SYMBOL(lynx_get_mdio_device);
  38. static void lynx_pcs_get_state_usxgmii(struct mdio_device *pcs,
  39. struct phylink_link_state *state)
  40. {
  41. struct mii_bus *bus = pcs->bus;
  42. int addr = pcs->addr;
  43. int status, lpa;
  44. status = mdiobus_c45_read(bus, addr, MDIO_MMD_VEND2, MII_BMSR);
  45. if (status < 0)
  46. return;
  47. state->link = !!(status & MDIO_STAT1_LSTATUS);
  48. state->an_complete = !!(status & MDIO_AN_STAT1_COMPLETE);
  49. if (!state->link || !state->an_complete)
  50. return;
  51. lpa = mdiobus_c45_read(bus, addr, MDIO_MMD_VEND2, MII_LPA);
  52. if (lpa < 0)
  53. return;
  54. phylink_decode_usxgmii_word(state, lpa);
  55. }
  56. static void lynx_pcs_get_state_2500basex(struct mdio_device *pcs,
  57. struct phylink_link_state *state)
  58. {
  59. int bmsr, lpa;
  60. bmsr = mdiodev_read(pcs, MII_BMSR);
  61. lpa = mdiodev_read(pcs, MII_LPA);
  62. if (bmsr < 0 || lpa < 0) {
  63. state->link = false;
  64. return;
  65. }
  66. state->link = !!(bmsr & BMSR_LSTATUS);
  67. state->an_complete = !!(bmsr & BMSR_ANEGCOMPLETE);
  68. if (!state->link)
  69. return;
  70. state->speed = SPEED_2500;
  71. state->pause |= MLO_PAUSE_TX | MLO_PAUSE_RX;
  72. state->duplex = DUPLEX_FULL;
  73. }
  74. static void lynx_pcs_get_state(struct phylink_pcs *pcs,
  75. struct phylink_link_state *state)
  76. {
  77. struct lynx_pcs *lynx = phylink_pcs_to_lynx(pcs);
  78. switch (state->interface) {
  79. case PHY_INTERFACE_MODE_1000BASEX:
  80. case PHY_INTERFACE_MODE_SGMII:
  81. case PHY_INTERFACE_MODE_QSGMII:
  82. phylink_mii_c22_pcs_get_state(lynx->mdio, state);
  83. break;
  84. case PHY_INTERFACE_MODE_2500BASEX:
  85. lynx_pcs_get_state_2500basex(lynx->mdio, state);
  86. break;
  87. case PHY_INTERFACE_MODE_USXGMII:
  88. lynx_pcs_get_state_usxgmii(lynx->mdio, state);
  89. break;
  90. case PHY_INTERFACE_MODE_10GBASER:
  91. phylink_mii_c45_pcs_get_state(lynx->mdio, state);
  92. break;
  93. default:
  94. break;
  95. }
  96. dev_dbg(&lynx->mdio->dev,
  97. "mode=%s/%s/%s link=%u an_enabled=%u an_complete=%u\n",
  98. phy_modes(state->interface),
  99. phy_speed_to_str(state->speed),
  100. phy_duplex_to_str(state->duplex),
  101. state->link, state->an_enabled, state->an_complete);
  102. }
  103. static int lynx_pcs_config_giga(struct mdio_device *pcs, unsigned int mode,
  104. phy_interface_t interface,
  105. const unsigned long *advertising)
  106. {
  107. u32 link_timer;
  108. u16 if_mode;
  109. int err;
  110. if (interface == PHY_INTERFACE_MODE_1000BASEX) {
  111. link_timer = LINK_TIMER_VAL(IEEE8023_LINK_TIMER_NS);
  112. mdiodev_write(pcs, LINK_TIMER_LO, link_timer & 0xffff);
  113. mdiodev_write(pcs, LINK_TIMER_HI, link_timer >> 16);
  114. if_mode = 0;
  115. } else {
  116. if_mode = IF_MODE_SGMII_EN;
  117. if (mode == MLO_AN_INBAND) {
  118. if_mode |= IF_MODE_USE_SGMII_AN;
  119. /* Adjust link timer for SGMII */
  120. link_timer = LINK_TIMER_VAL(SGMII_AN_LINK_TIMER_NS);
  121. mdiodev_write(pcs, LINK_TIMER_LO, link_timer & 0xffff);
  122. mdiodev_write(pcs, LINK_TIMER_HI, link_timer >> 16);
  123. }
  124. }
  125. err = mdiodev_modify(pcs, IF_MODE,
  126. IF_MODE_SGMII_EN | IF_MODE_USE_SGMII_AN,
  127. if_mode);
  128. if (err)
  129. return err;
  130. return phylink_mii_c22_pcs_config(pcs, mode, interface, advertising);
  131. }
  132. static int lynx_pcs_config_usxgmii(struct mdio_device *pcs, unsigned int mode,
  133. const unsigned long *advertising)
  134. {
  135. struct mii_bus *bus = pcs->bus;
  136. int addr = pcs->addr;
  137. if (!phylink_autoneg_inband(mode)) {
  138. dev_err(&pcs->dev, "USXGMII only supports in-band AN for now\n");
  139. return -EOPNOTSUPP;
  140. }
  141. /* Configure device ability for the USXGMII Replicator */
  142. return mdiobus_c45_write(bus, addr, MDIO_MMD_VEND2, MII_ADVERTISE,
  143. MDIO_USXGMII_10G | MDIO_USXGMII_LINK |
  144. MDIO_USXGMII_FULL_DUPLEX |
  145. ADVERTISE_SGMII | ADVERTISE_LPACK);
  146. }
  147. static int lynx_pcs_config(struct phylink_pcs *pcs, unsigned int mode,
  148. phy_interface_t ifmode,
  149. const unsigned long *advertising,
  150. bool permit)
  151. {
  152. struct lynx_pcs *lynx = phylink_pcs_to_lynx(pcs);
  153. switch (ifmode) {
  154. case PHY_INTERFACE_MODE_1000BASEX:
  155. case PHY_INTERFACE_MODE_SGMII:
  156. case PHY_INTERFACE_MODE_QSGMII:
  157. return lynx_pcs_config_giga(lynx->mdio, mode, ifmode,
  158. advertising);
  159. case PHY_INTERFACE_MODE_2500BASEX:
  160. if (phylink_autoneg_inband(mode)) {
  161. dev_err(&lynx->mdio->dev,
  162. "AN not supported on 3.125GHz SerDes lane\n");
  163. return -EOPNOTSUPP;
  164. }
  165. break;
  166. case PHY_INTERFACE_MODE_USXGMII:
  167. return lynx_pcs_config_usxgmii(lynx->mdio, mode, advertising);
  168. case PHY_INTERFACE_MODE_10GBASER:
  169. /* Nothing to do here for 10GBASER */
  170. break;
  171. default:
  172. return -EOPNOTSUPP;
  173. }
  174. return 0;
  175. }
  176. static void lynx_pcs_an_restart(struct phylink_pcs *pcs)
  177. {
  178. struct lynx_pcs *lynx = phylink_pcs_to_lynx(pcs);
  179. phylink_mii_c22_pcs_an_restart(lynx->mdio);
  180. }
  181. static void lynx_pcs_link_up_sgmii(struct mdio_device *pcs, unsigned int mode,
  182. int speed, int duplex)
  183. {
  184. u16 if_mode = 0, sgmii_speed;
  185. /* The PCS needs to be configured manually only
  186. * when not operating on in-band mode
  187. */
  188. if (mode == MLO_AN_INBAND)
  189. return;
  190. if (duplex == DUPLEX_HALF)
  191. if_mode |= IF_MODE_HALF_DUPLEX;
  192. switch (speed) {
  193. case SPEED_1000:
  194. sgmii_speed = SGMII_SPEED_1000;
  195. break;
  196. case SPEED_100:
  197. sgmii_speed = SGMII_SPEED_100;
  198. break;
  199. case SPEED_10:
  200. sgmii_speed = SGMII_SPEED_10;
  201. break;
  202. case SPEED_UNKNOWN:
  203. /* Silently don't do anything */
  204. return;
  205. default:
  206. dev_err(&pcs->dev, "Invalid PCS speed %d\n", speed);
  207. return;
  208. }
  209. if_mode |= IF_MODE_SPEED(sgmii_speed);
  210. mdiodev_modify(pcs, IF_MODE,
  211. IF_MODE_HALF_DUPLEX | IF_MODE_SPEED_MSK,
  212. if_mode);
  213. }
  214. /* 2500Base-X is SerDes protocol 7 on Felix and 6 on ENETC. It is a SerDes lane
  215. * clocked at 3.125 GHz which encodes symbols with 8b/10b and does not have
  216. * auto-negotiation of any link parameters. Electrically it is compatible with
  217. * a single lane of XAUI.
  218. * The hardware reference manual wants to call this mode SGMII, but it isn't
  219. * really, since the fundamental features of SGMII:
  220. * - Downgrading the link speed by duplicating symbols
  221. * - Auto-negotiation
  222. * are not there.
  223. * The speed is configured at 1000 in the IF_MODE because the clock frequency
  224. * is actually given by a PLL configured in the Reset Configuration Word (RCW).
  225. * Since there is no difference between fixed speed SGMII w/o AN and 802.3z w/o
  226. * AN, we call this PHY interface type 2500Base-X. In case a PHY negotiates a
  227. * lower link speed on line side, the system-side interface remains fixed at
  228. * 2500 Mbps and we do rate adaptation through pause frames.
  229. */
  230. static void lynx_pcs_link_up_2500basex(struct mdio_device *pcs,
  231. unsigned int mode,
  232. int speed, int duplex)
  233. {
  234. u16 if_mode = 0;
  235. if (mode == MLO_AN_INBAND) {
  236. dev_err(&pcs->dev, "AN not supported for 2500BaseX\n");
  237. return;
  238. }
  239. if (duplex == DUPLEX_HALF)
  240. if_mode |= IF_MODE_HALF_DUPLEX;
  241. if_mode |= IF_MODE_SPEED(SGMII_SPEED_2500);
  242. mdiodev_modify(pcs, IF_MODE,
  243. IF_MODE_HALF_DUPLEX | IF_MODE_SPEED_MSK,
  244. if_mode);
  245. }
  246. static void lynx_pcs_link_up(struct phylink_pcs *pcs, unsigned int mode,
  247. phy_interface_t interface,
  248. int speed, int duplex)
  249. {
  250. struct lynx_pcs *lynx = phylink_pcs_to_lynx(pcs);
  251. switch (interface) {
  252. case PHY_INTERFACE_MODE_SGMII:
  253. case PHY_INTERFACE_MODE_QSGMII:
  254. lynx_pcs_link_up_sgmii(lynx->mdio, mode, speed, duplex);
  255. break;
  256. case PHY_INTERFACE_MODE_2500BASEX:
  257. lynx_pcs_link_up_2500basex(lynx->mdio, mode, speed, duplex);
  258. break;
  259. case PHY_INTERFACE_MODE_USXGMII:
  260. /* At the moment, only in-band AN is supported for USXGMII
  261. * so nothing to do in link_up
  262. */
  263. break;
  264. default:
  265. break;
  266. }
  267. }
  268. static const struct phylink_pcs_ops lynx_pcs_phylink_ops = {
  269. .pcs_get_state = lynx_pcs_get_state,
  270. .pcs_config = lynx_pcs_config,
  271. .pcs_an_restart = lynx_pcs_an_restart,
  272. .pcs_link_up = lynx_pcs_link_up,
  273. };
  274. struct phylink_pcs *lynx_pcs_create(struct mdio_device *mdio)
  275. {
  276. struct lynx_pcs *lynx;
  277. lynx = kzalloc(sizeof(*lynx), GFP_KERNEL);
  278. if (!lynx)
  279. return NULL;
  280. lynx->mdio = mdio;
  281. lynx->pcs.ops = &lynx_pcs_phylink_ops;
  282. lynx->pcs.poll = true;
  283. return lynx_to_phylink_pcs(lynx);
  284. }
  285. EXPORT_SYMBOL(lynx_pcs_create);
  286. void lynx_pcs_destroy(struct phylink_pcs *pcs)
  287. {
  288. struct lynx_pcs *lynx = phylink_pcs_to_lynx(pcs);
  289. kfree(lynx);
  290. }
  291. EXPORT_SYMBOL(lynx_pcs_destroy);
  292. MODULE_LICENSE("Dual BSD/GPL");