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