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