723-v6.3-net-pcs-add-driver-for-MediaTek-SGMII-PCS.patch 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394
  1. From 4765a9722e09765866e131ec31f7b9cf4c1f4854 Mon Sep 17 00:00:00 2001
  2. From: Daniel Golle <[email protected]>
  3. Date: Sun, 19 Mar 2023 12:57:50 +0000
  4. Subject: [PATCH] net: pcs: add driver for MediaTek SGMII PCS
  5. The SGMII core found in several MediaTek SoCs is identical to what can
  6. also be found in MediaTek's MT7531 Ethernet switch IC.
  7. As this has not always been clear, both drivers developed different
  8. implementations to deal with the PCS.
  9. Recently Alexander Couzens pointed out this fact which lead to the
  10. development of this shared driver.
  11. Add a dedicated driver, mostly by copying the code now found in the
  12. Ethernet driver. The now redundant code will be removed by a follow-up
  13. commit.
  14. Suggested-by: Alexander Couzens <[email protected]>
  15. Suggested-by: Russell King (Oracle) <[email protected]>
  16. Signed-off-by: Daniel Golle <[email protected]>
  17. Tested-by: Frank Wunderlich <[email protected]>
  18. Reviewed-by: Russell King (Oracle) <[email protected]>
  19. Signed-off-by: Jakub Kicinski <[email protected]>
  20. ---
  21. MAINTAINERS | 8 +
  22. drivers/net/pcs/Kconfig | 7 +
  23. drivers/net/pcs/Makefile | 1 +
  24. drivers/net/pcs/pcs-mtk-lynxi.c | 305 ++++++++++++++++++++++++++++++
  25. include/linux/pcs/pcs-mtk-lynxi.h | 13 ++
  26. 5 files changed, 334 insertions(+)
  27. create mode 100644 drivers/net/pcs/pcs-mtk-lynxi.c
  28. create mode 100644 include/linux/pcs/pcs-mtk-lynxi.h
  29. --- a/MAINTAINERS
  30. +++ b/MAINTAINERS
  31. @@ -12928,6 +12928,14 @@ L: [email protected]
  32. S: Maintained
  33. F: drivers/net/ethernet/mediatek/
  34. +MEDIATEK ETHERNET PCS DRIVER
  35. +M: Alexander Couzens <[email protected]>
  36. +M: Daniel Golle <[email protected]>
  37. +L: [email protected]
  38. +S: Maintained
  39. +F: drivers/net/pcs/pcs-mtk-lynxi.c
  40. +F: include/linux/pcs/pcs-mtk-lynxi.h
  41. +
  42. MEDIATEK I2C CONTROLLER DRIVER
  43. M: Qii Wang <[email protected]>
  44. L: [email protected]
  45. --- a/drivers/net/pcs/Kconfig
  46. +++ b/drivers/net/pcs/Kconfig
  47. @@ -32,4 +32,11 @@ config PCS_ALTERA_TSE
  48. This module provides helper functions for the Altera Triple Speed
  49. Ethernet SGMII PCS, that can be found on the Intel Socfpga family.
  50. +config PCS_MTK_LYNXI
  51. + tristate
  52. + select REGMAP
  53. + help
  54. + This module provides helpers to phylink for managing the LynxI PCS
  55. + which is part of MediaTek's SoC and Ethernet switch ICs.
  56. +
  57. endmenu
  58. --- a/drivers/net/pcs/Makefile
  59. +++ b/drivers/net/pcs/Makefile
  60. @@ -7,3 +7,4 @@ obj-$(CONFIG_PCS_XPCS) += pcs_xpcs.o
  61. obj-$(CONFIG_PCS_LYNX) += pcs-lynx.o
  62. obj-$(CONFIG_PCS_RZN1_MIIC) += pcs-rzn1-miic.o
  63. obj-$(CONFIG_PCS_ALTERA_TSE) += pcs-altera-tse.o
  64. +obj-$(CONFIG_PCS_MTK_LYNXI) += pcs-mtk-lynxi.o
  65. --- /dev/null
  66. +++ b/drivers/net/pcs/pcs-mtk-lynxi.c
  67. @@ -0,0 +1,305 @@
  68. +// SPDX-License-Identifier: GPL-2.0
  69. +// Copyright (c) 2018-2019 MediaTek Inc.
  70. +/* A library for MediaTek SGMII circuit
  71. + *
  72. + * Author: Sean Wang <[email protected]>
  73. + * Author: Alexander Couzens <[email protected]>
  74. + * Author: Daniel Golle <[email protected]>
  75. + *
  76. + */
  77. +
  78. +#include <linux/mdio.h>
  79. +#include <linux/of.h>
  80. +#include <linux/pcs/pcs-mtk-lynxi.h>
  81. +#include <linux/phylink.h>
  82. +#include <linux/regmap.h>
  83. +
  84. +/* SGMII subsystem config registers */
  85. +/* BMCR (low 16) BMSR (high 16) */
  86. +#define SGMSYS_PCS_CONTROL_1 0x0
  87. +#define SGMII_BMCR GENMASK(15, 0)
  88. +#define SGMII_BMSR GENMASK(31, 16)
  89. +
  90. +#define SGMSYS_PCS_DEVICE_ID 0x4
  91. +#define SGMII_LYNXI_DEV_ID 0x4d544950
  92. +
  93. +#define SGMSYS_PCS_ADVERTISE 0x8
  94. +#define SGMII_ADVERTISE GENMASK(15, 0)
  95. +#define SGMII_LPA GENMASK(31, 16)
  96. +
  97. +#define SGMSYS_PCS_SCRATCH 0x14
  98. +#define SGMII_DEV_VERSION GENMASK(31, 16)
  99. +
  100. +/* Register to programmable link timer, the unit in 2 * 8ns */
  101. +#define SGMSYS_PCS_LINK_TIMER 0x18
  102. +#define SGMII_LINK_TIMER_MASK GENMASK(19, 0)
  103. +#define SGMII_LINK_TIMER_VAL(ns) FIELD_PREP(SGMII_LINK_TIMER_MASK, \
  104. + ((ns) / 2 / 8))
  105. +
  106. +/* Register to control remote fault */
  107. +#define SGMSYS_SGMII_MODE 0x20
  108. +#define SGMII_IF_MODE_SGMII BIT(0)
  109. +#define SGMII_SPEED_DUPLEX_AN BIT(1)
  110. +#define SGMII_SPEED_MASK GENMASK(3, 2)
  111. +#define SGMII_SPEED_10 FIELD_PREP(SGMII_SPEED_MASK, 0)
  112. +#define SGMII_SPEED_100 FIELD_PREP(SGMII_SPEED_MASK, 1)
  113. +#define SGMII_SPEED_1000 FIELD_PREP(SGMII_SPEED_MASK, 2)
  114. +#define SGMII_DUPLEX_HALF BIT(4)
  115. +#define SGMII_REMOTE_FAULT_DIS BIT(8)
  116. +
  117. +/* Register to reset SGMII design */
  118. +#define SGMSYS_RESERVED_0 0x34
  119. +#define SGMII_SW_RESET BIT(0)
  120. +
  121. +/* Register to set SGMII speed, ANA RG_ Control Signals III */
  122. +#define SGMII_PHY_SPEED_MASK GENMASK(3, 2)
  123. +#define SGMII_PHY_SPEED_1_25G FIELD_PREP(SGMII_PHY_SPEED_MASK, 0)
  124. +#define SGMII_PHY_SPEED_3_125G FIELD_PREP(SGMII_PHY_SPEED_MASK, 1)
  125. +
  126. +/* Register to power up QPHY */
  127. +#define SGMSYS_QPHY_PWR_STATE_CTRL 0xe8
  128. +#define SGMII_PHYA_PWD BIT(4)
  129. +
  130. +/* Register to QPHY wrapper control */
  131. +#define SGMSYS_QPHY_WRAP_CTRL 0xec
  132. +#define SGMII_PN_SWAP_MASK GENMASK(1, 0)
  133. +#define SGMII_PN_SWAP_TX_RX (BIT(0) | BIT(1))
  134. +
  135. +/* struct mtk_pcs_lynxi - This structure holds each sgmii regmap andassociated
  136. + * data
  137. + * @regmap: The register map pointing at the range used to setup
  138. + * SGMII modes
  139. + * @dev: Pointer to device owning the PCS
  140. + * @ana_rgc3: The offset of register ANA_RGC3 relative to regmap
  141. + * @interface: Currently configured interface mode
  142. + * @pcs: Phylink PCS structure
  143. + * @flags: Flags indicating hardware properties
  144. + */
  145. +struct mtk_pcs_lynxi {
  146. + struct regmap *regmap;
  147. + u32 ana_rgc3;
  148. + phy_interface_t interface;
  149. + struct phylink_pcs pcs;
  150. + u32 flags;
  151. +};
  152. +
  153. +static struct mtk_pcs_lynxi *pcs_to_mtk_pcs_lynxi(struct phylink_pcs *pcs)
  154. +{
  155. + return container_of(pcs, struct mtk_pcs_lynxi, pcs);
  156. +}
  157. +
  158. +static void mtk_pcs_lynxi_get_state(struct phylink_pcs *pcs,
  159. + struct phylink_link_state *state)
  160. +{
  161. + struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs);
  162. + unsigned int bm, adv;
  163. +
  164. + /* Read the BMSR and LPA */
  165. + regmap_read(mpcs->regmap, SGMSYS_PCS_CONTROL_1, &bm);
  166. + regmap_read(mpcs->regmap, SGMSYS_PCS_ADVERTISE, &adv);
  167. +
  168. + phylink_mii_c22_pcs_decode_state(state, FIELD_GET(SGMII_BMSR, bm),
  169. + FIELD_GET(SGMII_LPA, adv));
  170. +}
  171. +
  172. +static int mtk_pcs_lynxi_config(struct phylink_pcs *pcs, unsigned int mode,
  173. + phy_interface_t interface,
  174. + const unsigned long *advertising,
  175. + bool permit_pause_to_mac)
  176. +{
  177. + struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs);
  178. + bool mode_changed = false, changed, use_an;
  179. + unsigned int rgc3, sgm_mode, bmcr;
  180. + int advertise, link_timer;
  181. +
  182. + advertise = phylink_mii_c22_pcs_encode_advertisement(interface,
  183. + advertising);
  184. + if (advertise < 0)
  185. + return advertise;
  186. +
  187. + /* Clearing IF_MODE_BIT0 switches the PCS to BASE-X mode, and
  188. + * we assume that fixes it's speed at bitrate = line rate (in
  189. + * other words, 1000Mbps or 2500Mbps).
  190. + */
  191. + if (interface == PHY_INTERFACE_MODE_SGMII) {
  192. + sgm_mode = SGMII_IF_MODE_SGMII;
  193. + if (phylink_autoneg_inband(mode)) {
  194. + sgm_mode |= SGMII_REMOTE_FAULT_DIS |
  195. + SGMII_SPEED_DUPLEX_AN;
  196. + use_an = true;
  197. + } else {
  198. + use_an = false;
  199. + }
  200. + } else if (phylink_autoneg_inband(mode)) {
  201. + /* 1000base-X or 2500base-X autoneg */
  202. + sgm_mode = SGMII_REMOTE_FAULT_DIS;
  203. + use_an = linkmode_test_bit(ETHTOOL_LINK_MODE_Autoneg_BIT,
  204. + advertising);
  205. + } else {
  206. + /* 1000base-X or 2500base-X without autoneg */
  207. + sgm_mode = 0;
  208. + use_an = false;
  209. + }
  210. +
  211. + if (use_an)
  212. + bmcr = BMCR_ANENABLE;
  213. + else
  214. + bmcr = 0;
  215. +
  216. + if (mpcs->interface != interface) {
  217. + link_timer = phylink_get_link_timer_ns(interface);
  218. + if (link_timer < 0)
  219. + return link_timer;
  220. +
  221. + /* PHYA power down */
  222. + regmap_set_bits(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL,
  223. + SGMII_PHYA_PWD);
  224. +
  225. + /* Reset SGMII PCS state */
  226. + regmap_set_bits(mpcs->regmap, SGMSYS_RESERVED_0,
  227. + SGMII_SW_RESET);
  228. +
  229. + if (mpcs->flags & MTK_SGMII_FLAG_PN_SWAP)
  230. + regmap_update_bits(mpcs->regmap, SGMSYS_QPHY_WRAP_CTRL,
  231. + SGMII_PN_SWAP_MASK,
  232. + SGMII_PN_SWAP_TX_RX);
  233. +
  234. + if (interface == PHY_INTERFACE_MODE_2500BASEX)
  235. + rgc3 = SGMII_PHY_SPEED_3_125G;
  236. + else
  237. + rgc3 = SGMII_PHY_SPEED_1_25G;
  238. +
  239. + /* Configure the underlying interface speed */
  240. + regmap_update_bits(mpcs->regmap, mpcs->ana_rgc3,
  241. + SGMII_PHY_SPEED_MASK, rgc3);
  242. +
  243. + /* Setup the link timer */
  244. + regmap_write(mpcs->regmap, SGMSYS_PCS_LINK_TIMER,
  245. + SGMII_LINK_TIMER_VAL(link_timer));
  246. +
  247. + mpcs->interface = interface;
  248. + mode_changed = true;
  249. + }
  250. +
  251. + /* Update the advertisement, noting whether it has changed */
  252. + regmap_update_bits_check(mpcs->regmap, SGMSYS_PCS_ADVERTISE,
  253. + SGMII_ADVERTISE, advertise, &changed);
  254. +
  255. + /* Update the sgmsys mode register */
  256. + regmap_update_bits(mpcs->regmap, SGMSYS_SGMII_MODE,
  257. + SGMII_REMOTE_FAULT_DIS | SGMII_SPEED_DUPLEX_AN |
  258. + SGMII_IF_MODE_SGMII, sgm_mode);
  259. +
  260. + /* Update the BMCR */
  261. + regmap_update_bits(mpcs->regmap, SGMSYS_PCS_CONTROL_1,
  262. + BMCR_ANENABLE, bmcr);
  263. +
  264. + /* Release PHYA power down state
  265. + * Only removing bit SGMII_PHYA_PWD isn't enough.
  266. + * There are cases when the SGMII_PHYA_PWD register contains 0x9 which
  267. + * prevents SGMII from working. The SGMII still shows link but no traffic
  268. + * can flow. Writing 0x0 to the PHYA_PWD register fix the issue. 0x0 was
  269. + * taken from a good working state of the SGMII interface.
  270. + * Unknown how much the QPHY needs but it is racy without a sleep.
  271. + * Tested on mt7622 & mt7986.
  272. + */
  273. + usleep_range(50, 100);
  274. + regmap_write(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, 0);
  275. +
  276. + return changed || mode_changed;
  277. +}
  278. +
  279. +static void mtk_pcs_lynxi_restart_an(struct phylink_pcs *pcs)
  280. +{
  281. + struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs);
  282. +
  283. + regmap_set_bits(mpcs->regmap, SGMSYS_PCS_CONTROL_1, BMCR_ANRESTART);
  284. +}
  285. +
  286. +static void mtk_pcs_lynxi_link_up(struct phylink_pcs *pcs, unsigned int mode,
  287. + phy_interface_t interface, int speed,
  288. + int duplex)
  289. +{
  290. + struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs);
  291. + unsigned int sgm_mode;
  292. +
  293. + if (!phylink_autoneg_inband(mode)) {
  294. + /* Force the speed and duplex setting */
  295. + if (speed == SPEED_10)
  296. + sgm_mode = SGMII_SPEED_10;
  297. + else if (speed == SPEED_100)
  298. + sgm_mode = SGMII_SPEED_100;
  299. + else
  300. + sgm_mode = SGMII_SPEED_1000;
  301. +
  302. + if (duplex != DUPLEX_FULL)
  303. + sgm_mode |= SGMII_DUPLEX_HALF;
  304. +
  305. + regmap_update_bits(mpcs->regmap, SGMSYS_SGMII_MODE,
  306. + SGMII_DUPLEX_HALF | SGMII_SPEED_MASK,
  307. + sgm_mode);
  308. + }
  309. +}
  310. +
  311. +static const struct phylink_pcs_ops mtk_pcs_lynxi_ops = {
  312. + .pcs_get_state = mtk_pcs_lynxi_get_state,
  313. + .pcs_config = mtk_pcs_lynxi_config,
  314. + .pcs_an_restart = mtk_pcs_lynxi_restart_an,
  315. + .pcs_link_up = mtk_pcs_lynxi_link_up,
  316. +};
  317. +
  318. +struct phylink_pcs *mtk_pcs_lynxi_create(struct device *dev,
  319. + struct regmap *regmap, u32 ana_rgc3,
  320. + u32 flags)
  321. +{
  322. + struct mtk_pcs_lynxi *mpcs;
  323. + u32 id, ver;
  324. + int ret;
  325. +
  326. + ret = regmap_read(regmap, SGMSYS_PCS_DEVICE_ID, &id);
  327. + if (ret < 0)
  328. + return NULL;
  329. +
  330. + if (id != SGMII_LYNXI_DEV_ID) {
  331. + dev_err(dev, "unknown PCS device id %08x\n", id);
  332. + return NULL;
  333. + }
  334. +
  335. + ret = regmap_read(regmap, SGMSYS_PCS_SCRATCH, &ver);
  336. + if (ret < 0)
  337. + return NULL;
  338. +
  339. + ver = FIELD_GET(SGMII_DEV_VERSION, ver);
  340. + if (ver != 0x1) {
  341. + dev_err(dev, "unknown PCS device version %04x\n", ver);
  342. + return NULL;
  343. + }
  344. +
  345. + dev_dbg(dev, "MediaTek LynxI SGMII PCS (id 0x%08x, ver 0x%04x)\n", id,
  346. + ver);
  347. +
  348. + mpcs = kzalloc(sizeof(*mpcs), GFP_KERNEL);
  349. + if (!mpcs)
  350. + return NULL;
  351. +
  352. + mpcs->ana_rgc3 = ana_rgc3;
  353. + mpcs->regmap = regmap;
  354. + mpcs->flags = flags;
  355. + mpcs->pcs.ops = &mtk_pcs_lynxi_ops;
  356. + mpcs->pcs.poll = true;
  357. + mpcs->interface = PHY_INTERFACE_MODE_NA;
  358. +
  359. + return &mpcs->pcs;
  360. +}
  361. +EXPORT_SYMBOL(mtk_pcs_lynxi_create);
  362. +
  363. +void mtk_pcs_lynxi_destroy(struct phylink_pcs *pcs)
  364. +{
  365. + if (!pcs)
  366. + return;
  367. +
  368. + kfree(pcs_to_mtk_pcs_lynxi(pcs));
  369. +}
  370. +EXPORT_SYMBOL(mtk_pcs_lynxi_destroy);
  371. +
  372. +MODULE_LICENSE("GPL");
  373. --- /dev/null
  374. +++ b/include/linux/pcs/pcs-mtk-lynxi.h
  375. @@ -0,0 +1,13 @@
  376. +/* SPDX-License-Identifier: GPL-2.0 */
  377. +#ifndef __LINUX_PCS_MTK_LYNXI_H
  378. +#define __LINUX_PCS_MTK_LYNXI_H
  379. +
  380. +#include <linux/phylink.h>
  381. +#include <linux/regmap.h>
  382. +
  383. +#define MTK_SGMII_FLAG_PN_SWAP BIT(0)
  384. +struct phylink_pcs *mtk_pcs_lynxi_create(struct device *dev,
  385. + struct regmap *regmap,
  386. + u32 ana_rgc3, u32 flags);
  387. +void mtk_pcs_lynxi_destroy(struct phylink_pcs *pcs);
  388. +#endif