xref: /linux/drivers/phy/freescale/phy-fsl-lynx-core.c (revision 62cf248de32f061d99cf7cd1675419d739031c5e)
1 // SPDX-License-Identifier: GPL-2.0+
2 /* Copyright 2025-2026 NXP */
3 
4 #include <linux/module.h>
5 #include <linux/platform_device.h>
6 
7 #include "phy-fsl-lynx-core.h"
8 
9 const char *lynx_lane_mode_str(enum lynx_lane_mode lane_mode)
10 {
11 	switch (lane_mode) {
12 	case LANE_MODE_1000BASEX_SGMII:
13 		return "1000Base-X/SGMII";
14 	case LANE_MODE_2500BASEX:
15 		return "2500Base-X";
16 	case LANE_MODE_QSGMII:
17 		return "QSGMII";
18 	case LANE_MODE_10G_QXGMII:
19 		return "10G-QXGMII";
20 	case LANE_MODE_10GBASER:
21 		return "10GBase-R";
22 	case LANE_MODE_USXGMII:
23 		return "USXGMII";
24 	case LANE_MODE_25GBASER:
25 		return "25GBase-R";
26 	default:
27 		return "unknown";
28 	}
29 }
30 EXPORT_SYMBOL_NS_GPL(lynx_lane_mode_str, "PHY_FSL_LYNX");
31 
32 enum lynx_lane_mode phy_interface_to_lane_mode(phy_interface_t intf)
33 {
34 	switch (intf) {
35 	case PHY_INTERFACE_MODE_SGMII:
36 	case PHY_INTERFACE_MODE_1000BASEX:
37 		return LANE_MODE_1000BASEX_SGMII;
38 	case PHY_INTERFACE_MODE_2500BASEX:
39 		return LANE_MODE_2500BASEX;
40 	case PHY_INTERFACE_MODE_QSGMII:
41 		return LANE_MODE_QSGMII;
42 	case PHY_INTERFACE_MODE_10G_QXGMII:
43 		return LANE_MODE_10G_QXGMII;
44 	case PHY_INTERFACE_MODE_10GBASER:
45 		return LANE_MODE_10GBASER;
46 	case PHY_INTERFACE_MODE_USXGMII:
47 		return LANE_MODE_USXGMII;
48 	case PHY_INTERFACE_MODE_25GBASER:
49 		return LANE_MODE_25GBASER;
50 	default:
51 		return LANE_MODE_UNKNOWN;
52 	}
53 }
54 EXPORT_SYMBOL_NS_GPL(phy_interface_to_lane_mode, "PHY_FSL_LYNX");
55 
56 /* By default, assume that if we know how to get the PCCR register and
57  * protocol converter for a lane, that protocol is supported.
58  */
59 static bool lynx_lane_supports_mode_default(struct lynx_lane *lane,
60 					    enum lynx_lane_mode mode)
61 {
62 	struct lynx_priv *priv = lane->priv;
63 	struct lynx_pccr pccr;
64 
65 	if (!priv->info->get_pccr || !priv->info->get_pcvt_offset)
66 		return false;
67 
68 	if (priv->info->get_pccr(mode, lane->id, &pccr) < 0)
69 		return false;
70 
71 	if (priv->info->get_pcvt_offset(lane->id, mode) < 0)
72 		return false;
73 
74 	return true;
75 }
76 
77 /* A lane mode is supported if we have a PLL that can provide its required
78  * clock net, and if there is a protocol converter for that mode on that lane.
79  */
80 bool lynx_lane_supports_mode(struct lynx_lane *lane, enum lynx_lane_mode mode)
81 {
82 	struct lynx_priv *priv = lane->priv;
83 	int i;
84 
85 	if (priv->info->lane_supports_mode) {
86 		if (!priv->info->lane_supports_mode(lane->id, mode))
87 			return false;
88 	} else if (!lynx_lane_supports_mode_default(lane, mode)) {
89 		return false;
90 	}
91 
92 	for (i = 0; i < LYNX_NUM_PLL; i++) {
93 		if (!priv->pll[i].enabled)
94 			continue;
95 
96 		if (test_bit(mode, priv->pll[i].supported))
97 			return true;
98 	}
99 
100 	return false;
101 }
102 EXPORT_SYMBOL_NS_GPL(lynx_lane_supports_mode, "PHY_FSL_LYNX");
103 
104 /* The quad protocols are fixed because the lane has multiple consumers, and
105  * one phy_set_mode_ext() affects the other consumers as well. We have no use
106  * case for dynamic protocol changing here, so disallow it.
107  */
108 static enum lynx_lane_mode lynx_fixed_protocols[] = {
109 	LANE_MODE_QSGMII,
110 	LANE_MODE_10G_QXGMII,
111 };
112 
113 static bool lynx_lane_restrict_fixed_mode_change(struct lynx_lane *lane,
114 						 enum lynx_lane_mode new)
115 {
116 	enum lynx_lane_mode curr = lane->mode;
117 
118 	for (int i = 0; i < ARRAY_SIZE(lynx_fixed_protocols); i++)
119 		if ((curr == lynx_fixed_protocols[i] ||
120 		     new == lynx_fixed_protocols[i]) &&
121 		     curr != new)
122 			return true;
123 
124 	return false;
125 }
126 
127 /* Translate the mode/submode from phy_validate() and phy_set_mode_ext() to a
128  * lane_mode and return 0 if it is supported and we can transition to it from
129  * the current lane mode, or return negative error otherwise.
130  */
131 int lynx_phy_mode_to_lane_mode(struct phy *phy, enum phy_mode mode,
132 			       int submode, enum lynx_lane_mode *lane_mode)
133 {
134 	struct lynx_lane *lane = phy_get_drvdata(phy);
135 	enum lynx_lane_mode tmp_lane_mode;
136 
137 	/* The protocol configuration tables are incomplete for full lane
138 	 * reconfiguration from an arbitrary protocol.
139 	 */
140 	if (lane->mode == LANE_MODE_UNKNOWN)
141 		return -EINVAL;
142 
143 	if (mode != PHY_MODE_ETHERNET)
144 		return -EINVAL;
145 
146 	tmp_lane_mode = phy_interface_to_lane_mode(submode);
147 	if (!lynx_lane_supports_mode(lane, tmp_lane_mode))
148 		return -EINVAL;
149 
150 	if (lynx_lane_restrict_fixed_mode_change(lane, tmp_lane_mode))
151 		return -EINVAL;
152 
153 	if (lane_mode)
154 		*lane_mode = tmp_lane_mode;
155 
156 	return 0;
157 }
158 EXPORT_SYMBOL_NS_GPL(lynx_phy_mode_to_lane_mode, "PHY_FSL_LYNX");
159 
160 struct lynx_pll *lynx_pll_get(struct lynx_priv *priv, enum lynx_lane_mode mode)
161 {
162 	struct lynx_pll *pll;
163 	int i;
164 
165 	for (i = 0; i < LYNX_NUM_PLL; i++) {
166 		pll = &priv->pll[i];
167 
168 		if (!pll->enabled)
169 			continue;
170 
171 		if (test_bit(mode, pll->supported))
172 			return pll;
173 	}
174 
175 	/* no pll supports requested mode, either caller forgot to check
176 	 * lynx_lane_supports_mode(), or this is a bug.
177 	 */
178 	dev_WARN_ONCE(priv->dev, 1, "no pll for lane mode %s\n",
179 		      lynx_lane_mode_str(mode));
180 	return NULL;
181 }
182 EXPORT_SYMBOL_NS_GPL(lynx_pll_get, "PHY_FSL_LYNX");
183 
184 int lynx_pccr_read(struct lynx_lane *lane, enum lynx_lane_mode mode, u32 *val)
185 {
186 	struct lynx_priv *priv = lane->priv;
187 	struct lynx_pccr pccr;
188 	u32 tmp;
189 	int err;
190 
191 	err = priv->info->get_pccr(mode, lane->id, &pccr);
192 	if (err)
193 		return err;
194 
195 	tmp = lynx_read(priv, pccr.offset);
196 	*val = (tmp >> pccr.shift) & GENMASK(pccr.width - 1, 0);
197 
198 	return 0;
199 }
200 EXPORT_SYMBOL_NS_GPL(lynx_pccr_read, "PHY_FSL_LYNX");
201 
202 int lynx_pccr_write(struct lynx_lane *lane, enum lynx_lane_mode mode, u32 val)
203 {
204 	struct lynx_priv *priv = lane->priv;
205 	struct lynx_pccr pccr;
206 	u32 old, tmp, mask;
207 	int err;
208 
209 	err = priv->info->get_pccr(mode, lane->id, &pccr);
210 	if (err)
211 		return err;
212 
213 	old = lynx_read(priv, pccr.offset);
214 	mask = GENMASK(pccr.width - 1, 0) << pccr.shift;
215 	tmp = (old & ~mask) | (val << pccr.shift);
216 	lynx_write(priv, pccr.offset, tmp);
217 
218 	dev_dbg(&lane->phy->dev, "PCCR@0x%x: 0x%x -> 0x%x\n",
219 		pccr.offset, old, tmp);
220 
221 	return 0;
222 }
223 EXPORT_SYMBOL_NS_GPL(lynx_pccr_write, "PHY_FSL_LYNX");
224 
225 int lynx_pcvt_read(struct lynx_lane *lane, enum lynx_lane_mode mode, int cr,
226 		   u32 *val)
227 {
228 	struct lynx_priv *priv = lane->priv;
229 	int offset;
230 
231 	offset = priv->info->get_pcvt_offset(lane->id, mode);
232 	if (offset < 0)
233 		return offset;
234 
235 	*val = lynx_read(priv, offset + cr);
236 
237 	return 0;
238 }
239 EXPORT_SYMBOL_NS_GPL(lynx_pcvt_read, "PHY_FSL_LYNX");
240 
241 int lynx_pcvt_write(struct lynx_lane *lane, enum lynx_lane_mode mode, int cr,
242 		    u32 val)
243 {
244 	struct lynx_priv *priv = lane->priv;
245 	int offset;
246 
247 	offset = priv->info->get_pcvt_offset(lane->id, mode);
248 	if (offset < 0)
249 		return offset;
250 
251 	lynx_write(priv, offset + cr, val);
252 
253 	return 0;
254 }
255 EXPORT_SYMBOL_NS_GPL(lynx_pcvt_write, "PHY_FSL_LYNX");
256 
257 int lynx_pcvt_rmw(struct lynx_lane *lane, enum lynx_lane_mode mode, int cr,
258 		  u32 val, u32 mask)
259 {
260 	int err;
261 	u32 tmp;
262 
263 	err = lynx_pcvt_read(lane, mode, cr, &tmp);
264 	if (err)
265 		return err;
266 
267 	tmp &= ~mask;
268 	tmp |= val;
269 
270 	return lynx_pcvt_write(lane, mode, cr, tmp);
271 }
272 EXPORT_SYMBOL_NS_GPL(lynx_pcvt_rmw, "PHY_FSL_LYNX");
273 
274 #define work_to_lynx(w) container_of((w), struct lynx_priv, cdr_check.work)
275 
276 static void lynx_cdr_lock_check(struct work_struct *work)
277 {
278 	struct lynx_priv *priv = work_to_lynx(work);
279 	struct lynx_lane *lane;
280 
281 	for (int i = priv->info->first_lane; i < priv->info->num_lanes; i++) {
282 		lane = &priv->lane[i];
283 		if (!lane->phy)
284 			continue;
285 
286 		mutex_lock(&lane->phy->mutex);
287 
288 		if (!lane->init || !lane->powered_up) {
289 			mutex_unlock(&lane->phy->mutex);
290 			continue;
291 		}
292 
293 		priv->info->cdr_lock_check(lane);
294 
295 		mutex_unlock(&lane->phy->mutex);
296 	}
297 
298 	queue_delayed_work(system_power_efficient_wq, &priv->cdr_check,
299 			   msecs_to_jiffies(1000));
300 }
301 
302 static struct phy *lynx_xlate(struct device *dev,
303 			      const struct of_phandle_args *args)
304 {
305 	struct lynx_priv *priv = dev_get_drvdata(dev);
306 	int idx;
307 
308 	if (args->args_count == 0)
309 		return of_phy_simple_xlate(dev, args);
310 	else if (args->args_count != 1)
311 		return ERR_PTR(-ENODEV);
312 
313 	idx = args->args[0];
314 
315 	if (WARN_ON(idx >= priv->info->num_lanes ||
316 		    idx < priv->info->first_lane))
317 		return ERR_PTR(-EINVAL);
318 
319 	return priv->lane[idx].phy ?: ERR_PTR(-ENODEV);
320 }
321 
322 static int lynx_probe_lane(struct lynx_priv *priv, int id,
323 			   struct device_node *dn,
324 			   const struct phy_ops *phy_ops)
325 {
326 	struct lynx_lane *lane = &priv->lane[id];
327 	struct phy *phy;
328 
329 	phy = devm_phy_create(priv->dev, dn, phy_ops);
330 	if (IS_ERR(phy))
331 		return PTR_ERR(phy);
332 
333 	lane->priv = priv;
334 	lane->phy = phy;
335 	lane->id = id;
336 	phy_set_drvdata(phy, lane);
337 	priv->info->lane_read_configuration(lane);
338 
339 	return 0;
340 }
341 
342 int lynx_probe(struct platform_device *pdev, const struct lynx_info *info,
343 	       const struct phy_ops *phy_ops)
344 {
345 	struct device *dev = &pdev->dev;
346 	struct phy_provider *provider;
347 	struct device_node *dn;
348 	struct lynx_priv *priv;
349 	int err;
350 
351 	dn = dev_of_node(dev);
352 	if (!dn) {
353 		dev_err(dev, "Device requires an OF node\n");
354 		return -EINVAL;
355 	}
356 
357 	if (!info)
358 		return -ENODEV;
359 
360 	priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
361 	if (!priv)
362 		return -ENOMEM;
363 
364 	priv->dev = dev;
365 	priv->info = info;
366 	priv->big_endian = device_property_read_bool(dev, "big-endian");
367 	dev_set_drvdata(dev, priv);
368 	spin_lock_init(&priv->pcc_lock);
369 	INIT_DELAYED_WORK(&priv->cdr_check, lynx_cdr_lock_check);
370 
371 	priv->lane = devm_kcalloc(dev, priv->info->num_lanes,
372 				  sizeof(*priv->lane), GFP_KERNEL);
373 	if (!priv->lane)
374 		return -ENOMEM;
375 
376 	priv->base = devm_platform_ioremap_resource(pdev, 0);
377 	if (IS_ERR(priv->base))
378 		return PTR_ERR(priv->base);
379 
380 	for (int i = 0; i < LYNX_NUM_PLL; i++) {
381 		struct lynx_pll *pll = &priv->pll[i];
382 
383 		pll->priv = priv;
384 		pll->id = i;
385 		priv->info->pll_read_configuration(pll);
386 	}
387 
388 	if (of_get_child_count(dn)) {
389 		struct device_node *child;
390 
391 		for_each_available_child_of_node(dn, child) {
392 			u32 reg;
393 
394 			/* PHY subnode name must be 'phy'. */
395 			if (!(of_node_name_eq(child, "phy")))
396 				continue;
397 
398 			if (of_property_read_u32(child, "reg", &reg)) {
399 				dev_err(dev, "No \"reg\" property for %pOF\n", child);
400 				of_node_put(child);
401 				return -EINVAL;
402 			}
403 
404 			if (reg < priv->info->first_lane || reg >= priv->info->num_lanes) {
405 				dev_err(dev, "\"reg\" property out of range for %pOF\n", child);
406 				of_node_put(child);
407 				return -EINVAL;
408 			}
409 
410 			err = lynx_probe_lane(priv, reg, child, phy_ops);
411 			if (err) {
412 				of_node_put(child);
413 				return err;
414 			}
415 		}
416 	} else {
417 		for (int i = priv->info->first_lane; i < priv->info->num_lanes; i++) {
418 			err = lynx_probe_lane(priv, i, NULL, phy_ops);
419 			if (err)
420 				return err;
421 		}
422 	}
423 
424 	provider = devm_of_phy_provider_register(dev, lynx_xlate);
425 	if (IS_ERR(provider))
426 		return PTR_ERR(provider);
427 
428 	queue_delayed_work(system_power_efficient_wq, &priv->cdr_check,
429 			   msecs_to_jiffies(1000));
430 
431 	return 0;
432 }
433 EXPORT_SYMBOL_NS_GPL(lynx_probe, "PHY_FSL_LYNX");
434 
435 void lynx_remove(struct platform_device *pdev)
436 {
437 	struct device *dev = &pdev->dev;
438 	struct lynx_priv *priv = dev_get_drvdata(dev);
439 
440 	cancel_delayed_work_sync(&priv->cdr_check);
441 }
442 EXPORT_SYMBOL_NS_GPL(lynx_remove, "PHY_FSL_LYNX");
443 
444 MODULE_LICENSE("GPL");
445 MODULE_DESCRIPTION("Freescale Lynx SerDes core functionality");
446