mediatek: switch to pending XFI 10G Ethernet drivers
[openwrt/staging/dangole.git] / target / linux / mediatek / patches-6.1 / 725-03-net-pcs-pcs-mtk-lynxi-add-platform-driver-for-MT7988.patch
1 From 4b1a2716299c0e96a698044aebf3f80513509ae7 Mon Sep 17 00:00:00 2001
2 From: Daniel Golle <daniel@makrotopia.org>
3 Date: Tue, 12 Dec 2023 03:47:18 +0000
4 Subject: [PATCH 3/5] net: pcs: pcs-mtk-lynxi: add platform driver for MT7988
5
6 Introduce a proper platform MFD driver for the LynxI (H)SGMII PCS which
7 is going to initially be used for the MT7988 SoC.
8
9 Signed-off-by: Daniel Golle <daniel@makrotopia.org>
10 ---
11 drivers/net/pcs/pcs-mtk-lynxi.c | 227 ++++++++++++++++++++++++++++--
12 include/linux/pcs/pcs-mtk-lynxi.h | 11 ++
13 2 files changed, 227 insertions(+), 11 deletions(-)
14
15 --- a/drivers/net/pcs/pcs-mtk-lynxi.c
16 +++ b/drivers/net/pcs/pcs-mtk-lynxi.c
17 @@ -1,6 +1,6 @@
18 // SPDX-License-Identifier: GPL-2.0
19 // Copyright (c) 2018-2019 MediaTek Inc.
20 -/* A library for MediaTek SGMII circuit
21 +/* A library and platform driver for the MediaTek LynxI SGMII circuit
22 *
23 * Author: Sean Wang <sean.wang@mediatek.com>
24 * Author: Alexander Couzens <lynxis@fe80.eu>
25 @@ -8,11 +8,17 @@
26 *
27 */
28
29 +#include <linux/clk.h>
30 #include <linux/mdio.h>
31 +#include <linux/mfd/syscon.h>
32 +#include <linux/mutex.h>
33 #include <linux/of.h>
34 +#include <linux/of_platform.h>
35 #include <linux/pcs/pcs-mtk-lynxi.h>
36 #include <linux/phylink.h>
37 +#include <linux/platform_device.h>
38 #include <linux/regmap.h>
39 +#include <linux/reset.h>
40
41 /* SGMII subsystem config registers */
42 /* BMCR (low 16) BMSR (high 16) */
43 @@ -65,6 +71,8 @@
44 #define SGMII_PN_SWAP_MASK GENMASK(1, 0)
45 #define SGMII_PN_SWAP_TX_RX (BIT(0) | BIT(1))
46
47 +#define MTK_NETSYS_V3_AMA_RGC3 0x128
48 +
49 /* struct mtk_pcs_lynxi - This structure holds each sgmii regmap andassociated
50 * data
51 * @regmap: The register map pointing at the range used to setup
52 @@ -74,15 +82,29 @@
53 * @interface: Currently configured interface mode
54 * @pcs: Phylink PCS structure
55 * @flags: Flags indicating hardware properties
56 + * @rstc: Reset controller
57 + * @sgmii_sel: SGMII Register Clock
58 + * @sgmii_rx: SGMII RX Clock
59 + * @sgmii_tx: SGMII TX Clock
60 + * @node: List node
61 */
62 struct mtk_pcs_lynxi {
63 struct regmap *regmap;
64 + struct device *dev;
65 u32 ana_rgc3;
66 phy_interface_t interface;
67 struct phylink_pcs pcs;
68 u32 flags;
69 + struct reset_control *rstc;
70 + struct clk *sgmii_sel;
71 + struct clk *sgmii_rx;
72 + struct clk *sgmii_tx;
73 + struct list_head node;
74 };
75
76 +static LIST_HEAD(mtk_pcs_lynxi_instances);
77 +static DEFINE_MUTEX(instance_mutex);
78 +
79 static struct mtk_pcs_lynxi *pcs_to_mtk_pcs_lynxi(struct phylink_pcs *pcs)
80 {
81 return container_of(pcs, struct mtk_pcs_lynxi, pcs);
82 @@ -111,6 +133,17 @@ static void mtk_pcs_lynxi_get_state(stru
83 phylink_mii_c22_pcs_decode_state(state, bmsr, FIELD_GET(SGMII_LPA, adv));
84 }
85
86 +static void mtk_sgmii_reset(struct mtk_pcs_lynxi *mpcs)
87 +{
88 + if (!mpcs->rstc)
89 + return;
90 +
91 + reset_control_assert(mpcs->rstc);
92 + udelay(100);
93 + reset_control_deassert(mpcs->rstc);
94 + mdelay(1);
95 +}
96 +
97 static int mtk_pcs_lynxi_config(struct phylink_pcs *pcs, unsigned int neg_mode,
98 phy_interface_t interface,
99 const unsigned long *advertising,
100 @@ -158,6 +191,7 @@ static int mtk_pcs_lynxi_config(struct p
101 SGMII_PHYA_PWD);
102
103 /* Reset SGMII PCS state */
104 + mtk_sgmii_reset(mpcs);
105 regmap_set_bits(mpcs->regmap, SGMSYS_RESERVED_0,
106 SGMII_SW_RESET);
107
108 @@ -244,10 +278,29 @@ static void mtk_pcs_lynxi_link_up(struct
109 }
110 }
111
112 +static int mtk_pcs_lynxi_enable(struct phylink_pcs *pcs)
113 +{
114 + struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs);
115 +
116 + if (mpcs->sgmii_tx && mpcs->sgmii_rx) {
117 + clk_prepare_enable(mpcs->sgmii_rx);
118 + clk_prepare_enable(mpcs->sgmii_tx);
119 + }
120 +
121 + return 0;
122 +}
123 +
124 static void mtk_pcs_lynxi_disable(struct phylink_pcs *pcs)
125 {
126 struct mtk_pcs_lynxi *mpcs = pcs_to_mtk_pcs_lynxi(pcs);
127
128 + regmap_set_bits(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, SGMII_PHYA_PWD);
129 +
130 + if (mpcs->sgmii_tx && mpcs->sgmii_rx) {
131 + clk_disable_unprepare(mpcs->sgmii_tx);
132 + clk_disable_unprepare(mpcs->sgmii_rx);
133 + }
134 +
135 mpcs->interface = PHY_INTERFACE_MODE_NA;
136 }
137
138 @@ -257,11 +310,12 @@ static const struct phylink_pcs_ops mtk_
139 .pcs_an_restart = mtk_pcs_lynxi_restart_an,
140 .pcs_link_up = mtk_pcs_lynxi_link_up,
141 .pcs_disable = mtk_pcs_lynxi_disable,
142 + .pcs_enable = mtk_pcs_lynxi_enable,
143 };
144
145 -struct phylink_pcs *mtk_pcs_lynxi_create(struct device *dev,
146 - struct regmap *regmap, u32 ana_rgc3,
147 - u32 flags)
148 +static struct phylink_pcs *mtk_pcs_lynxi_init(struct device *dev, struct regmap *regmap,
149 + u32 ana_rgc3, u32 flags,
150 + struct mtk_pcs_lynxi *prealloc)
151 {
152 struct mtk_pcs_lynxi *mpcs;
153 u32 id, ver;
154 @@ -269,29 +323,33 @@ struct phylink_pcs *mtk_pcs_lynxi_create
155
156 ret = regmap_read(regmap, SGMSYS_PCS_DEVICE_ID, &id);
157 if (ret < 0)
158 - return NULL;
159 + return ERR_PTR(ret);
160
161 if (id != SGMII_LYNXI_DEV_ID) {
162 dev_err(dev, "unknown PCS device id %08x\n", id);
163 - return NULL;
164 + return ERR_PTR(-ENODEV);
165 }
166
167 ret = regmap_read(regmap, SGMSYS_PCS_SCRATCH, &ver);
168 if (ret < 0)
169 - return NULL;
170 + return ERR_PTR(ret);
171
172 ver = FIELD_GET(SGMII_DEV_VERSION, ver);
173 if (ver != 0x1) {
174 dev_err(dev, "unknown PCS device version %04x\n", ver);
175 - return NULL;
176 + return ERR_PTR(-ENODEV);
177 }
178
179 dev_dbg(dev, "MediaTek LynxI SGMII PCS (id 0x%08x, ver 0x%04x)\n", id,
180 ver);
181
182 - mpcs = kzalloc(sizeof(*mpcs), GFP_KERNEL);
183 - if (!mpcs)
184 - return NULL;
185 + if (prealloc) {
186 + mpcs = prealloc;
187 + } else {
188 + mpcs = kzalloc(sizeof(*mpcs), GFP_KERNEL);
189 + if (!mpcs)
190 + return ERR_PTR(-ENOMEM);
191 + };
192
193 mpcs->ana_rgc3 = ana_rgc3;
194 mpcs->regmap = regmap;
195 @@ -302,6 +360,13 @@ struct phylink_pcs *mtk_pcs_lynxi_create
196 mpcs->interface = PHY_INTERFACE_MODE_NA;
197
198 return &mpcs->pcs;
199 +};
200 +
201 +struct phylink_pcs *mtk_pcs_lynxi_create(struct device *dev,
202 + struct regmap *regmap, u32 ana_rgc3,
203 + u32 flags)
204 +{
205 + return mtk_pcs_lynxi_init(dev, regmap, ana_rgc3, flags, NULL);
206 }
207 EXPORT_SYMBOL(mtk_pcs_lynxi_create);
208
209 @@ -314,4 +379,144 @@ void mtk_pcs_lynxi_destroy(struct phylin
210 }
211 EXPORT_SYMBOL(mtk_pcs_lynxi_destroy);
212
213 +static int mtk_pcs_lynxi_probe(struct platform_device *pdev)
214 +{
215 + struct device *dev = &pdev->dev;
216 + struct device_node *np = dev->of_node;
217 + struct mtk_pcs_lynxi *mpcs;
218 + struct phylink_pcs *pcs;
219 + struct regmap *regmap;
220 + u32 flags = 0;
221 +
222 + mpcs = devm_kzalloc(dev, sizeof(*mpcs), GFP_KERNEL);
223 + if (!mpcs)
224 + return -ENOMEM;
225 +
226 + mpcs->dev = dev;
227 + regmap = syscon_node_to_regmap(np->parent);
228 + if (IS_ERR(regmap))
229 + return PTR_ERR(regmap);
230 +
231 + if (of_property_read_bool(np->parent, "mediatek,pnswap"))
232 + flags |= MTK_SGMII_FLAG_PN_SWAP;
233 +
234 + mpcs->rstc = of_reset_control_get_shared(np->parent, NULL);
235 + if (IS_ERR(mpcs->rstc))
236 + return PTR_ERR(mpcs->rstc);
237 +
238 + reset_control_deassert(mpcs->rstc);
239 + mpcs->sgmii_sel = devm_clk_get_enabled(dev, "sgmii_sel");
240 + if (IS_ERR(mpcs->sgmii_sel))
241 + return PTR_ERR(mpcs->sgmii_sel);
242 +
243 + mpcs->sgmii_rx = devm_clk_get(dev, "sgmii_rx");
244 + if (IS_ERR(mpcs->sgmii_rx))
245 + return PTR_ERR(mpcs->sgmii_rx);
246 +
247 + mpcs->sgmii_tx = devm_clk_get(dev, "sgmii_tx");
248 + if (IS_ERR(mpcs->sgmii_tx))
249 + return PTR_ERR(mpcs->sgmii_tx);
250 +
251 + pcs = mtk_pcs_lynxi_init(dev, regmap, (uintptr_t)of_device_get_match_data(dev),
252 + flags, mpcs);
253 + if (IS_ERR(pcs))
254 + return PTR_ERR(pcs);
255 +
256 + regmap_set_bits(mpcs->regmap, SGMSYS_QPHY_PWR_STATE_CTRL, SGMII_PHYA_PWD);
257 +
258 + platform_set_drvdata(pdev, mpcs);
259 +
260 + mutex_lock(&instance_mutex);
261 + list_add_tail(&mpcs->node, &mtk_pcs_lynxi_instances);
262 + mutex_unlock(&instance_mutex);
263 +
264 + return 0;
265 +}
266 +
267 +static int mtk_pcs_lynxi_remove(struct platform_device *pdev)
268 +{
269 + struct device *dev = &pdev->dev;
270 + struct mtk_pcs_lynxi *cur, *tmp;
271 +
272 + mutex_lock(&instance_mutex);
273 + list_for_each_entry_safe(cur, tmp, &mtk_pcs_lynxi_instances, node)
274 + if (cur->dev == dev) {
275 + list_del(&cur->node);
276 + kfree(cur);
277 + break;
278 + }
279 + mutex_unlock(&instance_mutex);
280 +
281 + return 0;
282 +}
283 +
284 +static const struct of_device_id mtk_pcs_lynxi_of_match[] = {
285 + { .compatible = "mediatek,mt7988-sgmii", .data = (void *)MTK_NETSYS_V3_AMA_RGC3 },
286 + { /* sentinel */ },
287 +};
288 +MODULE_DEVICE_TABLE(of, mtk_pcs_lynxi_of_match);
289 +
290 +struct phylink_pcs *mtk_pcs_lynxi_get(struct device *dev, struct device_node *np)
291 +{
292 + struct platform_device *pdev;
293 + struct mtk_pcs_lynxi *mpcs;
294 +
295 + if (!np)
296 + return NULL;
297 +
298 + if (!of_device_is_available(np))
299 + return ERR_PTR(-ENODEV);
300 +
301 + if (!of_match_node(mtk_pcs_lynxi_of_match, np))
302 + return ERR_PTR(-EINVAL);
303 +
304 + pdev = of_find_device_by_node(np);
305 + if (!pdev || !platform_get_drvdata(pdev)) {
306 + if (pdev)
307 + put_device(&pdev->dev);
308 + return ERR_PTR(-EPROBE_DEFER);
309 + }
310 +
311 + mpcs = platform_get_drvdata(pdev);
312 + device_link_add(dev, mpcs->dev, DL_FLAG_AUTOREMOVE_CONSUMER);
313 +
314 + return &mpcs->pcs;
315 +}
316 +EXPORT_SYMBOL(mtk_pcs_lynxi_get);
317 +
318 +void mtk_pcs_lynxi_put(struct phylink_pcs *pcs)
319 +{
320 + struct mtk_pcs_lynxi *cur, *mpcs = NULL;
321 +
322 + if (!pcs)
323 + return;
324 +
325 + mutex_lock(&instance_mutex);
326 + list_for_each_entry(cur, &mtk_pcs_lynxi_instances, node)
327 + if (pcs == &cur->pcs) {
328 + mpcs = cur;
329 + break;
330 + }
331 + mutex_unlock(&instance_mutex);
332 +
333 + if (WARN_ON(!mpcs))
334 + return;
335 +
336 + put_device(mpcs->dev);
337 +}
338 +EXPORT_SYMBOL(mtk_pcs_lynxi_put);
339 +
340 +static struct platform_driver mtk_pcs_lynxi_driver = {
341 + .driver = {
342 + .name = "mtk-pcs-lynxi",
343 + .suppress_bind_attrs = true,
344 + .of_match_table = mtk_pcs_lynxi_of_match,
345 + },
346 + .probe = mtk_pcs_lynxi_probe,
347 + .remove = mtk_pcs_lynxi_remove,
348 +};
349 +module_platform_driver(mtk_pcs_lynxi_driver);
350 +
351 +MODULE_AUTHOR("Daniel Golle <daniel@makrotopia.org>");
352 +MODULE_DESCRIPTION("MediaTek LynxI HSGMII PCS");
353 MODULE_LICENSE("GPL");
354 --- a/include/linux/pcs/pcs-mtk-lynxi.h
355 +++ b/include/linux/pcs/pcs-mtk-lynxi.h
356 @@ -10,4 +10,15 @@ struct phylink_pcs *mtk_pcs_lynxi_create
357 struct regmap *regmap,
358 u32 ana_rgc3, u32 flags);
359 void mtk_pcs_lynxi_destroy(struct phylink_pcs *pcs);
360 +
361 +#if IS_ENABLED(CONFIG_PCS_MTK_LYNXI)
362 +struct phylink_pcs *mtk_pcs_lynxi_get(struct device *dev, struct device_node *np);
363 +void mtk_pcs_lynxi_put(struct phylink_pcs *pcs);
364 +#else
365 +static inline struct phylink_pcs *mtk_pcs_lynxi_get(struct device *dev, struct device_node *np)
366 +{
367 + return NULL;
368 +}
369 +static inline void mtk_pcs_lynxi_put(struct phylink_pcs *pcs) { }
370 +#endif /* IS_ENABLED(CONFIG_PCS_MTK_LYNXI) */
371 #endif