739-05-net-pcs-add-driver-for-MediaTek-USXGMII-PCS.patch 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547
  1. From dde0e95fff92e9f5009f3bea75278e0e34a48822 Mon Sep 17 00:00:00 2001
  2. From: Daniel Golle <[email protected]>
  3. Date: Tue, 12 Dec 2023 03:47:47 +0000
  4. Subject: [PATCH 5/5] net: pcs: add driver for MediaTek USXGMII PCS
  5. Add driver for USXGMII PCS found in the MediaTek MT7988 SoC and supporting
  6. USXGMII, 10GBase-R and 5GBase-R interface modes.
  7. Signed-off-by: Daniel Golle <[email protected]>
  8. ---
  9. MAINTAINERS | 2 +
  10. drivers/net/pcs/Kconfig | 11 +
  11. drivers/net/pcs/Makefile | 1 +
  12. drivers/net/pcs/pcs-mtk-usxgmii.c | 456 ++++++++++++++++++++++++++++
  13. include/linux/pcs/pcs-mtk-usxgmii.h | 27 ++
  14. 5 files changed, 497 insertions(+)
  15. create mode 100644 drivers/net/pcs/pcs-mtk-usxgmii.c
  16. create mode 100644 include/linux/pcs/pcs-mtk-usxgmii.h
  17. --- a/MAINTAINERS
  18. +++ b/MAINTAINERS
  19. @@ -13348,7 +13348,9 @@ M: Daniel Golle <[email protected]>
  20. L: [email protected]
  21. S: Maintained
  22. F: drivers/net/pcs/pcs-mtk-lynxi.c
  23. +F: drivers/net/pcs/pcs-mtk-usxgmii.c
  24. F: include/linux/pcs/pcs-mtk-lynxi.h
  25. +F: include/linux/pcs/pcs-mtk-usxgmii.h
  26. MEDIATEK ETHERNET PHY DRIVERS
  27. M: Daniel Golle <[email protected]>
  28. --- a/drivers/net/pcs/Kconfig
  29. +++ b/drivers/net/pcs/Kconfig
  30. @@ -25,6 +25,17 @@ config PCS_MTK_LYNXI
  31. This module provides helpers to phylink for managing the LynxI PCS
  32. which is part of MediaTek's SoC and Ethernet switch ICs.
  33. +config PCS_MTK_USXGMII
  34. + tristate "MediaTek USXGMII PCS"
  35. + select PCS_MTK_LYNXI
  36. + select PHY_MTK_PEXTP
  37. + select PHYLINK
  38. + help
  39. + This module provides a driver for MediaTek's USXGMII PCS supporting
  40. + 10GBase-R, 5GBase-R and USXGMII interface modes.
  41. + 1000Base-X, 2500Base-X and Cisco SGMII are supported on the same
  42. + differential pairs via an embedded LynxI PHY.
  43. +
  44. config PCS_RZN1_MIIC
  45. tristate "Renesas RZ/N1 MII converter"
  46. depends on OF && (ARCH_RZN1 || COMPILE_TEST)
  47. --- a/drivers/net/pcs/Makefile
  48. +++ b/drivers/net/pcs/Makefile
  49. @@ -7,3 +7,4 @@ obj-$(CONFIG_PCS_XPCS) += pcs_xpcs.o
  50. obj-$(CONFIG_PCS_LYNX) += pcs-lynx.o
  51. obj-$(CONFIG_PCS_MTK_LYNXI) += pcs-mtk-lynxi.o
  52. obj-$(CONFIG_PCS_RZN1_MIIC) += pcs-rzn1-miic.o
  53. +obj-$(CONFIG_PCS_MTK_USXGMII) += pcs-mtk-usxgmii.o
  54. --- /dev/null
  55. +++ b/drivers/net/pcs/pcs-mtk-usxgmii.c
  56. @@ -0,0 +1,456 @@
  57. +// SPDX-License-Identifier: GPL-2.0
  58. +/*
  59. + * Copyright (c) 2023 MediaTek Inc.
  60. + * Author: Henry Yen <[email protected]>
  61. + * Daniel Golle <[email protected]>
  62. + */
  63. +
  64. +#include <linux/clk.h>
  65. +#include <linux/io.h>
  66. +#include <linux/mfd/syscon.h>
  67. +#include <linux/mdio.h>
  68. +#include <linux/mutex.h>
  69. +#include <linux/of.h>
  70. +#include <linux/of_platform.h>
  71. +#include <linux/reset.h>
  72. +#include <linux/pcs/pcs-mtk-usxgmii.h>
  73. +#include <linux/platform_device.h>
  74. +
  75. +/* USXGMII subsystem config registers */
  76. +/* Register to control speed */
  77. +#define RG_PHY_TOP_SPEED_CTRL1 0x80c
  78. +#define USXGMII_RATE_UPDATE_MODE BIT(31)
  79. +#define USXGMII_MAC_CK_GATED BIT(29)
  80. +#define USXGMII_IF_FORCE_EN BIT(28)
  81. +#define USXGMII_RATE_ADAPT_MODE GENMASK(10, 8)
  82. +#define USXGMII_RATE_ADAPT_MODE_X1 0
  83. +#define USXGMII_RATE_ADAPT_MODE_X2 1
  84. +#define USXGMII_RATE_ADAPT_MODE_X4 2
  85. +#define USXGMII_RATE_ADAPT_MODE_X10 3
  86. +#define USXGMII_RATE_ADAPT_MODE_X100 4
  87. +#define USXGMII_RATE_ADAPT_MODE_X5 5
  88. +#define USXGMII_RATE_ADAPT_MODE_X50 6
  89. +#define USXGMII_XFI_RX_MODE GENMASK(6, 4)
  90. +#define USXGMII_XFI_TX_MODE GENMASK(2, 0)
  91. +#define USXGMII_XFI_MODE_10G 0
  92. +#define USXGMII_XFI_MODE_5G 1
  93. +#define USXGMII_XFI_MODE_2P5G 3
  94. +
  95. +/* Register to control PCS AN */
  96. +#define RG_PCS_AN_CTRL0 0x810
  97. +#define USXGMII_AN_RESTART BIT(31)
  98. +#define USXGMII_AN_SYNC_CNT GENMASK(30, 11)
  99. +#define USXGMII_AN_ENABLE BIT(0)
  100. +
  101. +#define RG_PCS_AN_CTRL2 0x818
  102. +#define USXGMII_LINK_TIMER_IDLE_DETECT GENMASK(29, 20)
  103. +#define USXGMII_LINK_TIMER_COMP_ACK_DETECT GENMASK(19, 10)
  104. +#define USXGMII_LINK_TIMER_AN_RESTART GENMASK(9, 0)
  105. +
  106. +/* Register to read PCS AN status */
  107. +#define RG_PCS_AN_STS0 0x81c
  108. +#define USXGMII_LPA GENMASK(15, 0)
  109. +#define USXGMII_LPA_LATCH BIT(31)
  110. +
  111. +/* Register to read PCS link status */
  112. +#define RG_PCS_RX_STATUS0 0x904
  113. +#define RG_PCS_RX_STATUS_UPDATE BIT(16)
  114. +#define RG_PCS_RX_LINK_STATUS BIT(2)
  115. +
  116. +/* struct mtk_usxgmii_pcs - This structure holds each usxgmii PCS
  117. + * @pcs: Phylink PCS structure
  118. + * @dev: Pointer to device structure
  119. + * @base: IO memory to access PCS hardware
  120. + * @clk: Pointer to USXGMII clk
  121. + * @reset: Pointer to USXGMII reset control
  122. + * @interface: Currently selected interface mode
  123. + * @neg_mode: Currently used phylink neg_mode
  124. + * @node: List node
  125. + */
  126. +struct mtk_usxgmii_pcs {
  127. + struct phylink_pcs pcs;
  128. + struct device *dev;
  129. + void __iomem *base;
  130. + struct clk *clk;
  131. + struct reset_control *reset;
  132. + phy_interface_t interface;
  133. + unsigned int neg_mode;
  134. + struct list_head node;
  135. +};
  136. +
  137. +static LIST_HEAD(mtk_usxgmii_pcs_instances);
  138. +static DEFINE_MUTEX(instance_mutex);
  139. +
  140. +static u32 mtk_r32(struct mtk_usxgmii_pcs *mpcs, unsigned int reg)
  141. +{
  142. + return ioread32(mpcs->base + reg);
  143. +}
  144. +
  145. +static void mtk_m32(struct mtk_usxgmii_pcs *mpcs, unsigned int reg, u32 mask, u32 set)
  146. +{
  147. + u32 val;
  148. +
  149. + val = ioread32(mpcs->base + reg);
  150. + val &= ~mask;
  151. + val |= set;
  152. + iowrite32(val, mpcs->base + reg);
  153. +}
  154. +
  155. +static struct mtk_usxgmii_pcs *pcs_to_mtk_usxgmii_pcs(struct phylink_pcs *pcs)
  156. +{
  157. + return container_of(pcs, struct mtk_usxgmii_pcs, pcs);
  158. +}
  159. +
  160. +static void mtk_usxgmii_reset(struct mtk_usxgmii_pcs *mpcs)
  161. +{
  162. + reset_control_assert(mpcs->reset);
  163. + udelay(100);
  164. + reset_control_deassert(mpcs->reset);
  165. +
  166. + mdelay(10);
  167. +}
  168. +
  169. +static int mtk_usxgmii_pcs_config(struct phylink_pcs *pcs, unsigned int neg_mode,
  170. + phy_interface_t interface,
  171. + const unsigned long *advertising,
  172. + bool permit_pause_to_mac)
  173. +{
  174. + struct mtk_usxgmii_pcs *mpcs = pcs_to_mtk_usxgmii_pcs(pcs);
  175. + unsigned int an_ctrl = 0, link_timer = 0, xfi_mode = 0, adapt_mode = 0;
  176. + bool mode_changed = false;
  177. +
  178. + if (interface == PHY_INTERFACE_MODE_USXGMII) {
  179. + an_ctrl = FIELD_PREP(USXGMII_AN_SYNC_CNT, 0x1FF) | USXGMII_AN_ENABLE;
  180. + link_timer = FIELD_PREP(USXGMII_LINK_TIMER_IDLE_DETECT, 0x7B) |
  181. + FIELD_PREP(USXGMII_LINK_TIMER_COMP_ACK_DETECT, 0x7B) |
  182. + FIELD_PREP(USXGMII_LINK_TIMER_AN_RESTART, 0x7B);
  183. + xfi_mode = FIELD_PREP(USXGMII_XFI_RX_MODE, USXGMII_XFI_MODE_10G) |
  184. + FIELD_PREP(USXGMII_XFI_TX_MODE, USXGMII_XFI_MODE_10G);
  185. + } else if (interface == PHY_INTERFACE_MODE_10GBASER) {
  186. + an_ctrl = FIELD_PREP(USXGMII_AN_SYNC_CNT, 0x1FF);
  187. + link_timer = FIELD_PREP(USXGMII_LINK_TIMER_IDLE_DETECT, 0x7B) |
  188. + FIELD_PREP(USXGMII_LINK_TIMER_COMP_ACK_DETECT, 0x7B) |
  189. + FIELD_PREP(USXGMII_LINK_TIMER_AN_RESTART, 0x7B);
  190. + xfi_mode = FIELD_PREP(USXGMII_XFI_RX_MODE, USXGMII_XFI_MODE_10G) |
  191. + FIELD_PREP(USXGMII_XFI_TX_MODE, USXGMII_XFI_MODE_10G);
  192. + adapt_mode = USXGMII_RATE_UPDATE_MODE;
  193. + } else if (interface == PHY_INTERFACE_MODE_5GBASER) {
  194. + an_ctrl = FIELD_PREP(USXGMII_AN_SYNC_CNT, 0xFF);
  195. + link_timer = FIELD_PREP(USXGMII_LINK_TIMER_IDLE_DETECT, 0x3D) |
  196. + FIELD_PREP(USXGMII_LINK_TIMER_COMP_ACK_DETECT, 0x3D) |
  197. + FIELD_PREP(USXGMII_LINK_TIMER_AN_RESTART, 0x3D);
  198. + xfi_mode = FIELD_PREP(USXGMII_XFI_RX_MODE, USXGMII_XFI_MODE_5G) |
  199. + FIELD_PREP(USXGMII_XFI_TX_MODE, USXGMII_XFI_MODE_5G);
  200. + adapt_mode = USXGMII_RATE_UPDATE_MODE;
  201. + } else {
  202. + return -EINVAL;
  203. + }
  204. +
  205. + adapt_mode |= FIELD_PREP(USXGMII_RATE_ADAPT_MODE, USXGMII_RATE_ADAPT_MODE_X1);
  206. +
  207. + if (mpcs->interface != interface) {
  208. + mpcs->interface = interface;
  209. + mode_changed = true;
  210. + }
  211. +
  212. + mtk_usxgmii_reset(mpcs);
  213. +
  214. + /* Setup USXGMII AN ctrl */
  215. + mtk_m32(mpcs, RG_PCS_AN_CTRL0,
  216. + USXGMII_AN_SYNC_CNT | USXGMII_AN_ENABLE,
  217. + an_ctrl);
  218. +
  219. + mtk_m32(mpcs, RG_PCS_AN_CTRL2,
  220. + USXGMII_LINK_TIMER_IDLE_DETECT |
  221. + USXGMII_LINK_TIMER_COMP_ACK_DETECT |
  222. + USXGMII_LINK_TIMER_AN_RESTART,
  223. + link_timer);
  224. +
  225. + mpcs->neg_mode = neg_mode;
  226. +
  227. + /* Gated MAC CK */
  228. + mtk_m32(mpcs, RG_PHY_TOP_SPEED_CTRL1,
  229. + USXGMII_MAC_CK_GATED, USXGMII_MAC_CK_GATED);
  230. +
  231. + /* Enable interface force mode */
  232. + mtk_m32(mpcs, RG_PHY_TOP_SPEED_CTRL1,
  233. + USXGMII_IF_FORCE_EN, USXGMII_IF_FORCE_EN);
  234. +
  235. + /* Setup USXGMII adapt mode */
  236. + mtk_m32(mpcs, RG_PHY_TOP_SPEED_CTRL1,
  237. + USXGMII_RATE_UPDATE_MODE | USXGMII_RATE_ADAPT_MODE,
  238. + adapt_mode);
  239. +
  240. + /* Setup USXGMII speed */
  241. + mtk_m32(mpcs, RG_PHY_TOP_SPEED_CTRL1,
  242. + USXGMII_XFI_RX_MODE | USXGMII_XFI_TX_MODE,
  243. + xfi_mode);
  244. +
  245. + usleep_range(1, 10);
  246. +
  247. + /* Un-gated MAC CK */
  248. + mtk_m32(mpcs, RG_PHY_TOP_SPEED_CTRL1, USXGMII_MAC_CK_GATED, 0);
  249. +
  250. + usleep_range(1, 10);
  251. +
  252. + /* Disable interface force mode for the AN mode */
  253. + if (an_ctrl & USXGMII_AN_ENABLE)
  254. + mtk_m32(mpcs, RG_PHY_TOP_SPEED_CTRL1, USXGMII_IF_FORCE_EN, 0);
  255. +
  256. + return mode_changed;
  257. +}
  258. +
  259. +static void mtk_usxgmii_pcs_get_fixed_speed(struct mtk_usxgmii_pcs *mpcs,
  260. + struct phylink_link_state *state)
  261. +{
  262. + u32 val = mtk_r32(mpcs, RG_PHY_TOP_SPEED_CTRL1);
  263. + int speed;
  264. +
  265. + /* Calculate speed from interface speed and rate adapt mode */
  266. + switch (FIELD_GET(USXGMII_XFI_RX_MODE, val)) {
  267. + case USXGMII_XFI_MODE_10G:
  268. + speed = 10000;
  269. + break;
  270. + case USXGMII_XFI_MODE_5G:
  271. + speed = 5000;
  272. + break;
  273. + case USXGMII_XFI_MODE_2P5G:
  274. + speed = 2500;
  275. + break;
  276. + default:
  277. + state->speed = SPEED_UNKNOWN;
  278. + return;
  279. + }
  280. +
  281. + switch (FIELD_GET(USXGMII_RATE_ADAPT_MODE, val)) {
  282. + case USXGMII_RATE_ADAPT_MODE_X100:
  283. + speed /= 100;
  284. + break;
  285. + case USXGMII_RATE_ADAPT_MODE_X50:
  286. + speed /= 50;
  287. + break;
  288. + case USXGMII_RATE_ADAPT_MODE_X10:
  289. + speed /= 10;
  290. + break;
  291. + case USXGMII_RATE_ADAPT_MODE_X5:
  292. + speed /= 5;
  293. + break;
  294. + case USXGMII_RATE_ADAPT_MODE_X4:
  295. + speed /= 4;
  296. + break;
  297. + case USXGMII_RATE_ADAPT_MODE_X2:
  298. + speed /= 2;
  299. + break;
  300. + case USXGMII_RATE_ADAPT_MODE_X1:
  301. + break;
  302. + default:
  303. + state->speed = SPEED_UNKNOWN;
  304. + return;
  305. + }
  306. +
  307. + state->speed = speed;
  308. + state->duplex = DUPLEX_FULL;
  309. +}
  310. +
  311. +static void mtk_usxgmii_pcs_get_an_state(struct mtk_usxgmii_pcs *mpcs,
  312. + struct phylink_link_state *state)
  313. +{
  314. + u16 lpa;
  315. +
  316. + /* Refresh LPA by toggling LPA_LATCH */
  317. + mtk_m32(mpcs, RG_PCS_AN_STS0, USXGMII_LPA_LATCH, USXGMII_LPA_LATCH);
  318. + ndelay(1020);
  319. + mtk_m32(mpcs, RG_PCS_AN_STS0, USXGMII_LPA_LATCH, 0);
  320. + ndelay(1020);
  321. + lpa = FIELD_GET(USXGMII_LPA, mtk_r32(mpcs, RG_PCS_AN_STS0));
  322. +
  323. + phylink_decode_usxgmii_word(state, lpa);
  324. +}
  325. +
  326. +static void mtk_usxgmii_pcs_get_state(struct phylink_pcs *pcs,
  327. + struct phylink_link_state *state)
  328. +{
  329. + struct mtk_usxgmii_pcs *mpcs = pcs_to_mtk_usxgmii_pcs(pcs);
  330. +
  331. + /* Refresh USXGMII link status by toggling RG_PCS_AN_STATUS_UPDATE */
  332. + mtk_m32(mpcs, RG_PCS_RX_STATUS0, RG_PCS_RX_STATUS_UPDATE,
  333. + RG_PCS_RX_STATUS_UPDATE);
  334. + ndelay(1020);
  335. + mtk_m32(mpcs, RG_PCS_RX_STATUS0, RG_PCS_RX_STATUS_UPDATE, 0);
  336. + ndelay(1020);
  337. +
  338. + /* Read USXGMII link status */
  339. + state->link = FIELD_GET(RG_PCS_RX_LINK_STATUS,
  340. + mtk_r32(mpcs, RG_PCS_RX_STATUS0));
  341. +
  342. + /* Continuously repeat re-configuration sequence until link comes up */
  343. + if (!state->link) {
  344. + mtk_usxgmii_pcs_config(pcs, mpcs->neg_mode,
  345. + state->interface, NULL, false);
  346. + return;
  347. + }
  348. +
  349. + if (FIELD_GET(USXGMII_AN_ENABLE, mtk_r32(mpcs, RG_PCS_AN_CTRL0)))
  350. + mtk_usxgmii_pcs_get_an_state(mpcs, state);
  351. + else
  352. + mtk_usxgmii_pcs_get_fixed_speed(mpcs, state);
  353. +}
  354. +
  355. +static void mtk_usxgmii_pcs_restart_an(struct phylink_pcs *pcs)
  356. +{
  357. + struct mtk_usxgmii_pcs *mpcs = pcs_to_mtk_usxgmii_pcs(pcs);
  358. +
  359. + mtk_m32(mpcs, RG_PCS_AN_CTRL0, USXGMII_AN_RESTART, USXGMII_AN_RESTART);
  360. +}
  361. +
  362. +static void mtk_usxgmii_pcs_link_up(struct phylink_pcs *pcs, unsigned int neg_mode,
  363. + phy_interface_t interface,
  364. + int speed, int duplex)
  365. +{
  366. + /* Reconfiguring USXGMII to ensure the quality of the RX signal
  367. + * after the line side link up.
  368. + */
  369. + mtk_usxgmii_pcs_config(pcs, neg_mode, interface, NULL, false);
  370. +}
  371. +
  372. +static void mtk_usxgmii_pcs_disable(struct phylink_pcs *pcs)
  373. +{
  374. + struct mtk_usxgmii_pcs *mpcs = pcs_to_mtk_usxgmii_pcs(pcs);
  375. +
  376. + mpcs->interface = PHY_INTERFACE_MODE_NA;
  377. + mpcs->neg_mode = -1;
  378. +}
  379. +
  380. +static const struct phylink_pcs_ops mtk_usxgmii_pcs_ops = {
  381. + .pcs_config = mtk_usxgmii_pcs_config,
  382. + .pcs_get_state = mtk_usxgmii_pcs_get_state,
  383. + .pcs_an_restart = mtk_usxgmii_pcs_restart_an,
  384. + .pcs_link_up = mtk_usxgmii_pcs_link_up,
  385. + .pcs_disable = mtk_usxgmii_pcs_disable,
  386. +};
  387. +
  388. +static int mtk_usxgmii_probe(struct platform_device *pdev)
  389. +{
  390. + struct device *dev = &pdev->dev;
  391. + struct mtk_usxgmii_pcs *mpcs;
  392. +
  393. + mpcs = devm_kzalloc(dev, sizeof(*mpcs), GFP_KERNEL);
  394. + if (!mpcs)
  395. + return -ENOMEM;
  396. +
  397. + mpcs->base = devm_platform_ioremap_resource(pdev, 0);
  398. + if (IS_ERR(mpcs->base))
  399. + return PTR_ERR(mpcs->base);
  400. +
  401. + mpcs->dev = dev;
  402. + mpcs->pcs.ops = &mtk_usxgmii_pcs_ops;
  403. + mpcs->pcs.poll = true;
  404. + mpcs->pcs.neg_mode = true;
  405. + mpcs->interface = PHY_INTERFACE_MODE_NA;
  406. + mpcs->neg_mode = -1;
  407. +
  408. + mpcs->clk = devm_clk_get_enabled(mpcs->dev, NULL);
  409. + if (IS_ERR(mpcs->clk))
  410. + return PTR_ERR(mpcs->clk);
  411. +
  412. + mpcs->reset = devm_reset_control_get_shared(dev, NULL);
  413. + if (IS_ERR(mpcs->reset))
  414. + return PTR_ERR(mpcs->reset);
  415. +
  416. + reset_control_deassert(mpcs->reset);
  417. +
  418. + platform_set_drvdata(pdev, mpcs);
  419. +
  420. + mutex_lock(&instance_mutex);
  421. + list_add_tail(&mpcs->node, &mtk_usxgmii_pcs_instances);
  422. + mutex_unlock(&instance_mutex);
  423. +
  424. + return 0;
  425. +}
  426. +
  427. +static int mtk_usxgmii_remove(struct platform_device *pdev)
  428. +{
  429. + struct device *dev = &pdev->dev;
  430. + struct mtk_usxgmii_pcs *cur, *tmp;
  431. +
  432. + mutex_lock(&instance_mutex);
  433. + list_for_each_entry_safe(cur, tmp, &mtk_usxgmii_pcs_instances, node)
  434. + if (cur->dev == dev) {
  435. + list_del(&cur->node);
  436. + break;
  437. + }
  438. + mutex_unlock(&instance_mutex);
  439. +
  440. + return 0;
  441. +}
  442. +
  443. +static const struct of_device_id mtk_usxgmii_of_mtable[] = {
  444. + { .compatible = "mediatek,mt7988-usxgmiisys" },
  445. + { /* sentinel */ },
  446. +};
  447. +MODULE_DEVICE_TABLE(of, mtk_usxgmii_of_mtable);
  448. +
  449. +struct phylink_pcs *mtk_usxgmii_pcs_get(struct device *dev, struct device_node *np)
  450. +{
  451. + struct platform_device *pdev;
  452. + struct mtk_usxgmii_pcs *mpcs;
  453. +
  454. + if (!np)
  455. + return NULL;
  456. +
  457. + if (!of_device_is_available(np))
  458. + return ERR_PTR(-ENODEV);
  459. +
  460. + if (!of_match_node(mtk_usxgmii_of_mtable, np))
  461. + return ERR_PTR(-EINVAL);
  462. +
  463. + pdev = of_find_device_by_node(np);
  464. + if (!pdev || !platform_get_drvdata(pdev)) {
  465. + if (pdev)
  466. + put_device(&pdev->dev);
  467. + return ERR_PTR(-EPROBE_DEFER);
  468. + }
  469. +
  470. + mpcs = platform_get_drvdata(pdev);
  471. + device_link_add(dev, mpcs->dev, DL_FLAG_AUTOREMOVE_CONSUMER);
  472. +
  473. + return &mpcs->pcs;
  474. +}
  475. +EXPORT_SYMBOL(mtk_usxgmii_pcs_get);
  476. +
  477. +void mtk_usxgmii_pcs_put(struct phylink_pcs *pcs)
  478. +{
  479. + struct mtk_usxgmii_pcs *cur, *mpcs = NULL;
  480. +
  481. + if (!pcs)
  482. + return;
  483. +
  484. + mutex_lock(&instance_mutex);
  485. + list_for_each_entry(cur, &mtk_usxgmii_pcs_instances, node)
  486. + if (pcs == &cur->pcs) {
  487. + mpcs = cur;
  488. + break;
  489. + }
  490. + mutex_unlock(&instance_mutex);
  491. +
  492. + if (WARN_ON(!mpcs))
  493. + return;
  494. +
  495. + put_device(mpcs->dev);
  496. +}
  497. +EXPORT_SYMBOL(mtk_usxgmii_pcs_put);
  498. +
  499. +static struct platform_driver mtk_usxgmii_driver = {
  500. + .driver = {
  501. + .name = "mtk_usxgmii",
  502. + .suppress_bind_attrs = true,
  503. + .of_match_table = mtk_usxgmii_of_mtable,
  504. + },
  505. + .probe = mtk_usxgmii_probe,
  506. + .remove = mtk_usxgmii_remove,
  507. +};
  508. +module_platform_driver(mtk_usxgmii_driver);
  509. +
  510. +MODULE_LICENSE("GPL");
  511. +MODULE_DESCRIPTION("MediaTek USXGMII PCS driver");
  512. +MODULE_AUTHOR("Daniel Golle <[email protected]>");
  513. --- /dev/null
  514. +++ b/include/linux/pcs/pcs-mtk-usxgmii.h
  515. @@ -0,0 +1,27 @@
  516. +/* SPDX-License-Identifier: GPL-2.0 */
  517. +#ifndef __LINUX_PCS_MTK_USXGMII_H
  518. +#define __LINUX_PCS_MTK_USXGMII_H
  519. +
  520. +#include <linux/phylink.h>
  521. +
  522. +/**
  523. + * mtk_usxgmii_select_pcs() - Get MediaTek PCS instance
  524. + * @np: Pointer to device node indentifying a MediaTek USXGMII PCS
  525. + * @mode: Ethernet PHY interface mode
  526. + *
  527. + * Return PCS identified by a device node and the PHY interface mode in use
  528. + *
  529. + * Return: Pointer to phylink PCS instance of NULL
  530. + */
  531. +#if IS_ENABLED(CONFIG_PCS_MTK_USXGMII)
  532. +struct phylink_pcs *mtk_usxgmii_pcs_get(struct device *dev, struct device_node *np);
  533. +void mtk_usxgmii_pcs_put(struct phylink_pcs *pcs);
  534. +#else
  535. +static inline struct phylink_pcs *mtk_usxgmii_pcs_get(struct device *dev, struct device_node *np)
  536. +{
  537. + return NULL;
  538. +}
  539. +static inline void mtk_usxgmii_pcs_put(struct phylink_pcs *pcs) { }
  540. +#endif /* IS_ENABLED(CONFIG_PCS_MTK_USXGMII) */
  541. +
  542. +#endif /* __LINUX_PCS_MTK_USXGMII_H */