Linux Audio

Check our new training course

Embedded Linux training

Mar 31-Apr 8, 2025
Register
Loading...
Note: File does not exist in v3.5.6.
  1// SPDX-License-Identifier: GPL-2.0+
  2/* Copyright (c) 2021-2022 NXP. */
  3
  4#include <linux/module.h>
  5#include <linux/phy.h>
  6#include <linux/phy/phy.h>
  7#include <linux/platform_device.h>
  8#include <linux/workqueue.h>
  9
 10#define LYNX_28G_NUM_LANE			8
 11#define LYNX_28G_NUM_PLL			2
 12
 13/* General registers per SerDes block */
 14#define LYNX_28G_PCC8				0x10a0
 15#define LYNX_28G_PCC8_SGMII			0x1
 16#define LYNX_28G_PCC8_SGMII_DIS			0x0
 17
 18#define LYNX_28G_PCCC				0x10b0
 19#define LYNX_28G_PCCC_10GBASER			0x9
 20#define LYNX_28G_PCCC_USXGMII			0x1
 21#define LYNX_28G_PCCC_SXGMII_DIS		0x0
 22
 23#define LYNX_28G_LNa_PCC_OFFSET(lane)		(4 * (LYNX_28G_NUM_LANE - (lane->id) - 1))
 24
 25/* Per PLL registers */
 26#define LYNX_28G_PLLnRSTCTL(pll)		(0x400 + (pll) * 0x100 + 0x0)
 27#define LYNX_28G_PLLnRSTCTL_DIS(rstctl)		(((rstctl) & BIT(24)) >> 24)
 28#define LYNX_28G_PLLnRSTCTL_LOCK(rstctl)	(((rstctl) & BIT(23)) >> 23)
 29
 30#define LYNX_28G_PLLnCR0(pll)			(0x400 + (pll) * 0x100 + 0x4)
 31#define LYNX_28G_PLLnCR0_REFCLK_SEL(cr0)	(((cr0) & GENMASK(20, 16)))
 32#define LYNX_28G_PLLnCR0_REFCLK_SEL_100MHZ	0x0
 33#define LYNX_28G_PLLnCR0_REFCLK_SEL_125MHZ	0x10000
 34#define LYNX_28G_PLLnCR0_REFCLK_SEL_156MHZ	0x20000
 35#define LYNX_28G_PLLnCR0_REFCLK_SEL_150MHZ	0x30000
 36#define LYNX_28G_PLLnCR0_REFCLK_SEL_161MHZ	0x40000
 37
 38#define LYNX_28G_PLLnCR1(pll)			(0x400 + (pll) * 0x100 + 0x8)
 39#define LYNX_28G_PLLnCR1_FRATE_SEL(cr1)		(((cr1) & GENMASK(28, 24)))
 40#define LYNX_28G_PLLnCR1_FRATE_5G_10GVCO	0x0
 41#define LYNX_28G_PLLnCR1_FRATE_5G_25GVCO	0x10000000
 42#define LYNX_28G_PLLnCR1_FRATE_10G_20GVCO	0x6000000
 43
 44/* Per SerDes lane registers */
 45/* Lane a General Control Register */
 46#define LYNX_28G_LNaGCR0(lane)			(0x800 + (lane) * 0x100 + 0x0)
 47#define LYNX_28G_LNaGCR0_PROTO_SEL_MSK		GENMASK(7, 3)
 48#define LYNX_28G_LNaGCR0_PROTO_SEL_SGMII	0x8
 49#define LYNX_28G_LNaGCR0_PROTO_SEL_XFI		0x50
 50#define LYNX_28G_LNaGCR0_IF_WIDTH_MSK		GENMASK(2, 0)
 51#define LYNX_28G_LNaGCR0_IF_WIDTH_10_BIT	0x0
 52#define LYNX_28G_LNaGCR0_IF_WIDTH_20_BIT	0x2
 53
 54/* Lane a Tx Reset Control Register */
 55#define LYNX_28G_LNaTRSTCTL(lane)		(0x800 + (lane) * 0x100 + 0x20)
 56#define LYNX_28G_LNaTRSTCTL_HLT_REQ		BIT(27)
 57#define LYNX_28G_LNaTRSTCTL_RST_DONE		BIT(30)
 58#define LYNX_28G_LNaTRSTCTL_RST_REQ		BIT(31)
 59
 60/* Lane a Tx General Control Register */
 61#define LYNX_28G_LNaTGCR0(lane)			(0x800 + (lane) * 0x100 + 0x24)
 62#define LYNX_28G_LNaTGCR0_USE_PLLF		0x0
 63#define LYNX_28G_LNaTGCR0_USE_PLLS		BIT(28)
 64#define LYNX_28G_LNaTGCR0_USE_PLL_MSK		BIT(28)
 65#define LYNX_28G_LNaTGCR0_N_RATE_FULL		0x0
 66#define LYNX_28G_LNaTGCR0_N_RATE_HALF		0x1000000
 67#define LYNX_28G_LNaTGCR0_N_RATE_QUARTER	0x2000000
 68#define LYNX_28G_LNaTGCR0_N_RATE_MSK		GENMASK(26, 24)
 69
 70#define LYNX_28G_LNaTECR0(lane)			(0x800 + (lane) * 0x100 + 0x30)
 71
 72/* Lane a Rx Reset Control Register */
 73#define LYNX_28G_LNaRRSTCTL(lane)		(0x800 + (lane) * 0x100 + 0x40)
 74#define LYNX_28G_LNaRRSTCTL_HLT_REQ		BIT(27)
 75#define LYNX_28G_LNaRRSTCTL_RST_DONE		BIT(30)
 76#define LYNX_28G_LNaRRSTCTL_RST_REQ		BIT(31)
 77#define LYNX_28G_LNaRRSTCTL_CDR_LOCK		BIT(12)
 78
 79/* Lane a Rx General Control Register */
 80#define LYNX_28G_LNaRGCR0(lane)			(0x800 + (lane) * 0x100 + 0x44)
 81#define LYNX_28G_LNaRGCR0_USE_PLLF		0x0
 82#define LYNX_28G_LNaRGCR0_USE_PLLS		BIT(28)
 83#define LYNX_28G_LNaRGCR0_USE_PLL_MSK		BIT(28)
 84#define LYNX_28G_LNaRGCR0_N_RATE_MSK		GENMASK(26, 24)
 85#define LYNX_28G_LNaRGCR0_N_RATE_FULL		0x0
 86#define LYNX_28G_LNaRGCR0_N_RATE_HALF		0x1000000
 87#define LYNX_28G_LNaRGCR0_N_RATE_QUARTER	0x2000000
 88#define LYNX_28G_LNaRGCR0_N_RATE_MSK		GENMASK(26, 24)
 89
 90#define LYNX_28G_LNaRGCR1(lane)			(0x800 + (lane) * 0x100 + 0x48)
 91
 92#define LYNX_28G_LNaRECR0(lane)			(0x800 + (lane) * 0x100 + 0x50)
 93#define LYNX_28G_LNaRECR1(lane)			(0x800 + (lane) * 0x100 + 0x54)
 94#define LYNX_28G_LNaRECR2(lane)			(0x800 + (lane) * 0x100 + 0x58)
 95
 96#define LYNX_28G_LNaRSCCR0(lane)		(0x800 + (lane) * 0x100 + 0x74)
 97
 98#define LYNX_28G_LNaPSS(lane)			(0x1000 + (lane) * 0x4)
 99#define LYNX_28G_LNaPSS_TYPE(pss)		(((pss) & GENMASK(30, 24)) >> 24)
100#define LYNX_28G_LNaPSS_TYPE_SGMII		0x4
101#define LYNX_28G_LNaPSS_TYPE_XFI		0x28
102
103#define LYNX_28G_SGMIIaCR1(lane)		(0x1804 + (lane) * 0x10)
104#define LYNX_28G_SGMIIaCR1_SGPCS_EN		BIT(11)
105#define LYNX_28G_SGMIIaCR1_SGPCS_DIS		0x0
106#define LYNX_28G_SGMIIaCR1_SGPCS_MSK		BIT(11)
107
108struct lynx_28g_priv;
109
110struct lynx_28g_pll {
111	struct lynx_28g_priv *priv;
112	u32 rstctl, cr0, cr1;
113	int id;
114	DECLARE_PHY_INTERFACE_MASK(supported);
115};
116
117struct lynx_28g_lane {
118	struct lynx_28g_priv *priv;
119	struct phy *phy;
120	bool powered_up;
121	bool init;
122	unsigned int id;
123	phy_interface_t interface;
124};
125
126struct lynx_28g_priv {
127	void __iomem *base;
128	struct device *dev;
129	struct lynx_28g_pll pll[LYNX_28G_NUM_PLL];
130	struct lynx_28g_lane lane[LYNX_28G_NUM_LANE];
131
132	struct delayed_work cdr_check;
133};
134
135static void lynx_28g_rmw(struct lynx_28g_priv *priv, unsigned long off,
136			 u32 val, u32 mask)
137{
138	void __iomem *reg = priv->base + off;
139	u32 orig, tmp;
140
141	orig = ioread32(reg);
142	tmp = orig & ~mask;
143	tmp |= val;
144	iowrite32(tmp, reg);
145}
146
147#define lynx_28g_lane_rmw(lane, reg, val, mask)	\
148	lynx_28g_rmw((lane)->priv, LYNX_28G_##reg(lane->id), \
149		     LYNX_28G_##reg##_##val, LYNX_28G_##reg##_##mask)
150#define lynx_28g_lane_read(lane, reg)			\
151	ioread32((lane)->priv->base + LYNX_28G_##reg((lane)->id))
152#define lynx_28g_pll_read(pll, reg)			\
153	ioread32((pll)->priv->base + LYNX_28G_##reg((pll)->id))
154
155static bool lynx_28g_supports_interface(struct lynx_28g_priv *priv, int intf)
156{
157	int i;
158
159	for (i = 0; i < LYNX_28G_NUM_PLL; i++) {
160		if (LYNX_28G_PLLnRSTCTL_DIS(priv->pll[i].rstctl))
161			continue;
162
163		if (test_bit(intf, priv->pll[i].supported))
164			return true;
165	}
166
167	return false;
168}
169
170static struct lynx_28g_pll *lynx_28g_pll_get(struct lynx_28g_priv *priv,
171					     phy_interface_t intf)
172{
173	struct lynx_28g_pll *pll;
174	int i;
175
176	for (i = 0; i < LYNX_28G_NUM_PLL; i++) {
177		pll = &priv->pll[i];
178
179		if (LYNX_28G_PLLnRSTCTL_DIS(pll->rstctl))
180			continue;
181
182		if (test_bit(intf, pll->supported))
183			return pll;
184	}
185
186	return NULL;
187}
188
189static void lynx_28g_lane_set_nrate(struct lynx_28g_lane *lane,
190				    struct lynx_28g_pll *pll,
191				    phy_interface_t intf)
192{
193	switch (LYNX_28G_PLLnCR1_FRATE_SEL(pll->cr1)) {
194	case LYNX_28G_PLLnCR1_FRATE_5G_10GVCO:
195	case LYNX_28G_PLLnCR1_FRATE_5G_25GVCO:
196		switch (intf) {
197		case PHY_INTERFACE_MODE_SGMII:
198		case PHY_INTERFACE_MODE_1000BASEX:
199			lynx_28g_lane_rmw(lane, LNaTGCR0, N_RATE_QUARTER, N_RATE_MSK);
200			lynx_28g_lane_rmw(lane, LNaRGCR0, N_RATE_QUARTER, N_RATE_MSK);
201			break;
202		default:
203			break;
204		}
205		break;
206	case LYNX_28G_PLLnCR1_FRATE_10G_20GVCO:
207		switch (intf) {
208		case PHY_INTERFACE_MODE_10GBASER:
209		case PHY_INTERFACE_MODE_USXGMII:
210			lynx_28g_lane_rmw(lane, LNaTGCR0, N_RATE_FULL, N_RATE_MSK);
211			lynx_28g_lane_rmw(lane, LNaRGCR0, N_RATE_FULL, N_RATE_MSK);
212			break;
213		default:
214			break;
215		}
216		break;
217	default:
218		break;
219	}
220}
221
222static void lynx_28g_lane_set_pll(struct lynx_28g_lane *lane,
223				  struct lynx_28g_pll *pll)
224{
225	if (pll->id == 0) {
226		lynx_28g_lane_rmw(lane, LNaTGCR0, USE_PLLF, USE_PLL_MSK);
227		lynx_28g_lane_rmw(lane, LNaRGCR0, USE_PLLF, USE_PLL_MSK);
228	} else {
229		lynx_28g_lane_rmw(lane, LNaTGCR0, USE_PLLS, USE_PLL_MSK);
230		lynx_28g_lane_rmw(lane, LNaRGCR0, USE_PLLS, USE_PLL_MSK);
231	}
232}
233
234static void lynx_28g_cleanup_lane(struct lynx_28g_lane *lane)
235{
236	u32 lane_offset = LYNX_28G_LNa_PCC_OFFSET(lane);
237	struct lynx_28g_priv *priv = lane->priv;
238
239	/* Cleanup the protocol configuration registers of the current protocol */
240	switch (lane->interface) {
241	case PHY_INTERFACE_MODE_10GBASER:
242		lynx_28g_rmw(priv, LYNX_28G_PCCC,
243			     LYNX_28G_PCCC_SXGMII_DIS << lane_offset,
244			     GENMASK(3, 0) << lane_offset);
245		break;
246	case PHY_INTERFACE_MODE_SGMII:
247	case PHY_INTERFACE_MODE_1000BASEX:
248		lynx_28g_rmw(priv, LYNX_28G_PCC8,
249			     LYNX_28G_PCC8_SGMII_DIS << lane_offset,
250			     GENMASK(3, 0) << lane_offset);
251		break;
252	default:
253		break;
254	}
255}
256
257static void lynx_28g_lane_set_sgmii(struct lynx_28g_lane *lane)
258{
259	u32 lane_offset = LYNX_28G_LNa_PCC_OFFSET(lane);
260	struct lynx_28g_priv *priv = lane->priv;
261	struct lynx_28g_pll *pll;
262
263	lynx_28g_cleanup_lane(lane);
264
265	/* Setup the lane to run in SGMII */
266	lynx_28g_rmw(priv, LYNX_28G_PCC8,
267		     LYNX_28G_PCC8_SGMII << lane_offset,
268		     GENMASK(3, 0) << lane_offset);
269
270	/* Setup the protocol select and SerDes parallel interface width */
271	lynx_28g_lane_rmw(lane, LNaGCR0, PROTO_SEL_SGMII, PROTO_SEL_MSK);
272	lynx_28g_lane_rmw(lane, LNaGCR0, IF_WIDTH_10_BIT, IF_WIDTH_MSK);
273
274	/* Switch to the PLL that works with this interface type */
275	pll = lynx_28g_pll_get(priv, PHY_INTERFACE_MODE_SGMII);
276	lynx_28g_lane_set_pll(lane, pll);
277
278	/* Choose the portion of clock net to be used on this lane */
279	lynx_28g_lane_set_nrate(lane, pll, PHY_INTERFACE_MODE_SGMII);
280
281	/* Enable the SGMII PCS */
282	lynx_28g_lane_rmw(lane, SGMIIaCR1, SGPCS_EN, SGPCS_MSK);
283
284	/* Configure the appropriate equalization parameters for the protocol */
285	iowrite32(0x00808006, priv->base + LYNX_28G_LNaTECR0(lane->id));
286	iowrite32(0x04310000, priv->base + LYNX_28G_LNaRGCR1(lane->id));
287	iowrite32(0x9f800000, priv->base + LYNX_28G_LNaRECR0(lane->id));
288	iowrite32(0x001f0000, priv->base + LYNX_28G_LNaRECR1(lane->id));
289	iowrite32(0x00000000, priv->base + LYNX_28G_LNaRECR2(lane->id));
290	iowrite32(0x00000000, priv->base + LYNX_28G_LNaRSCCR0(lane->id));
291}
292
293static void lynx_28g_lane_set_10gbaser(struct lynx_28g_lane *lane)
294{
295	u32 lane_offset = LYNX_28G_LNa_PCC_OFFSET(lane);
296	struct lynx_28g_priv *priv = lane->priv;
297	struct lynx_28g_pll *pll;
298
299	lynx_28g_cleanup_lane(lane);
300
301	/* Enable the SXGMII lane */
302	lynx_28g_rmw(priv, LYNX_28G_PCCC,
303		     LYNX_28G_PCCC_10GBASER << lane_offset,
304		     GENMASK(3, 0) << lane_offset);
305
306	/* Setup the protocol select and SerDes parallel interface width */
307	lynx_28g_lane_rmw(lane, LNaGCR0, PROTO_SEL_XFI, PROTO_SEL_MSK);
308	lynx_28g_lane_rmw(lane, LNaGCR0, IF_WIDTH_20_BIT, IF_WIDTH_MSK);
309
310	/* Switch to the PLL that works with this interface type */
311	pll = lynx_28g_pll_get(priv, PHY_INTERFACE_MODE_10GBASER);
312	lynx_28g_lane_set_pll(lane, pll);
313
314	/* Choose the portion of clock net to be used on this lane */
315	lynx_28g_lane_set_nrate(lane, pll, PHY_INTERFACE_MODE_10GBASER);
316
317	/* Disable the SGMII PCS */
318	lynx_28g_lane_rmw(lane, SGMIIaCR1, SGPCS_DIS, SGPCS_MSK);
319
320	/* Configure the appropriate equalization parameters for the protocol */
321	iowrite32(0x10808307, priv->base + LYNX_28G_LNaTECR0(lane->id));
322	iowrite32(0x10000000, priv->base + LYNX_28G_LNaRGCR1(lane->id));
323	iowrite32(0x00000000, priv->base + LYNX_28G_LNaRECR0(lane->id));
324	iowrite32(0x001f0000, priv->base + LYNX_28G_LNaRECR1(lane->id));
325	iowrite32(0x81000020, priv->base + LYNX_28G_LNaRECR2(lane->id));
326	iowrite32(0x00002000, priv->base + LYNX_28G_LNaRSCCR0(lane->id));
327}
328
329static int lynx_28g_power_off(struct phy *phy)
330{
331	struct lynx_28g_lane *lane = phy_get_drvdata(phy);
332	u32 trstctl, rrstctl;
333
334	if (!lane->powered_up)
335		return 0;
336
337	/* Issue a halt request */
338	lynx_28g_lane_rmw(lane, LNaTRSTCTL, HLT_REQ, HLT_REQ);
339	lynx_28g_lane_rmw(lane, LNaRRSTCTL, HLT_REQ, HLT_REQ);
340
341	/* Wait until the halting process is complete */
342	do {
343		trstctl = lynx_28g_lane_read(lane, LNaTRSTCTL);
344		rrstctl = lynx_28g_lane_read(lane, LNaRRSTCTL);
345	} while ((trstctl & LYNX_28G_LNaTRSTCTL_HLT_REQ) ||
346		 (rrstctl & LYNX_28G_LNaRRSTCTL_HLT_REQ));
347
348	lane->powered_up = false;
349
350	return 0;
351}
352
353static int lynx_28g_power_on(struct phy *phy)
354{
355	struct lynx_28g_lane *lane = phy_get_drvdata(phy);
356	u32 trstctl, rrstctl;
357
358	if (lane->powered_up)
359		return 0;
360
361	/* Issue a reset request on the lane */
362	lynx_28g_lane_rmw(lane, LNaTRSTCTL, RST_REQ, RST_REQ);
363	lynx_28g_lane_rmw(lane, LNaRRSTCTL, RST_REQ, RST_REQ);
364
365	/* Wait until the reset sequence is completed */
366	do {
367		trstctl = lynx_28g_lane_read(lane, LNaTRSTCTL);
368		rrstctl = lynx_28g_lane_read(lane, LNaRRSTCTL);
369	} while (!(trstctl & LYNX_28G_LNaTRSTCTL_RST_DONE) ||
370		 !(rrstctl & LYNX_28G_LNaRRSTCTL_RST_DONE));
371
372	lane->powered_up = true;
373
374	return 0;
375}
376
377static int lynx_28g_set_mode(struct phy *phy, enum phy_mode mode, int submode)
378{
379	struct lynx_28g_lane *lane = phy_get_drvdata(phy);
380	struct lynx_28g_priv *priv = lane->priv;
381	int powered_up = lane->powered_up;
382	int err = 0;
383
384	if (mode != PHY_MODE_ETHERNET)
385		return -EOPNOTSUPP;
386
387	if (lane->interface == PHY_INTERFACE_MODE_NA)
388		return -EOPNOTSUPP;
389
390	if (!lynx_28g_supports_interface(priv, submode))
391		return -EOPNOTSUPP;
392
393	/* If the lane is powered up, put the lane into the halt state while
394	 * the reconfiguration is being done.
395	 */
396	if (powered_up)
397		lynx_28g_power_off(phy);
398
399	switch (submode) {
400	case PHY_INTERFACE_MODE_SGMII:
401	case PHY_INTERFACE_MODE_1000BASEX:
402		lynx_28g_lane_set_sgmii(lane);
403		break;
404	case PHY_INTERFACE_MODE_10GBASER:
405		lynx_28g_lane_set_10gbaser(lane);
406		break;
407	default:
408		err = -EOPNOTSUPP;
409		goto out;
410	}
411
412	lane->interface = submode;
413
414out:
415	/* Power up the lane if necessary */
416	if (powered_up)
417		lynx_28g_power_on(phy);
418
419	return err;
420}
421
422static int lynx_28g_validate(struct phy *phy, enum phy_mode mode, int submode,
423			     union phy_configure_opts *opts __always_unused)
424{
425	struct lynx_28g_lane *lane = phy_get_drvdata(phy);
426	struct lynx_28g_priv *priv = lane->priv;
427
428	if (mode != PHY_MODE_ETHERNET)
429		return -EOPNOTSUPP;
430
431	if (!lynx_28g_supports_interface(priv, submode))
432		return -EOPNOTSUPP;
433
434	return 0;
435}
436
437static int lynx_28g_init(struct phy *phy)
438{
439	struct lynx_28g_lane *lane = phy_get_drvdata(phy);
440
441	/* Mark the fact that the lane was init */
442	lane->init = true;
443
444	/* SerDes lanes are powered on at boot time.  Any lane that is managed
445	 * by this driver will get powered down at init time aka at dpaa2-eth
446	 * probe time.
447	 */
448	lane->powered_up = true;
449	lynx_28g_power_off(phy);
450
451	return 0;
452}
453
454static const struct phy_ops lynx_28g_ops = {
455	.init		= lynx_28g_init,
456	.power_on	= lynx_28g_power_on,
457	.power_off	= lynx_28g_power_off,
458	.set_mode	= lynx_28g_set_mode,
459	.validate	= lynx_28g_validate,
460	.owner		= THIS_MODULE,
461};
462
463static void lynx_28g_pll_read_configuration(struct lynx_28g_priv *priv)
464{
465	struct lynx_28g_pll *pll;
466	int i;
467
468	for (i = 0; i < LYNX_28G_NUM_PLL; i++) {
469		pll = &priv->pll[i];
470		pll->priv = priv;
471		pll->id = i;
472
473		pll->rstctl = lynx_28g_pll_read(pll, PLLnRSTCTL);
474		pll->cr0 = lynx_28g_pll_read(pll, PLLnCR0);
475		pll->cr1 = lynx_28g_pll_read(pll, PLLnCR1);
476
477		if (LYNX_28G_PLLnRSTCTL_DIS(pll->rstctl))
478			continue;
479
480		switch (LYNX_28G_PLLnCR1_FRATE_SEL(pll->cr1)) {
481		case LYNX_28G_PLLnCR1_FRATE_5G_10GVCO:
482		case LYNX_28G_PLLnCR1_FRATE_5G_25GVCO:
483			/* 5GHz clock net */
484			__set_bit(PHY_INTERFACE_MODE_1000BASEX, pll->supported);
485			__set_bit(PHY_INTERFACE_MODE_SGMII, pll->supported);
486			break;
487		case LYNX_28G_PLLnCR1_FRATE_10G_20GVCO:
488			/* 10.3125GHz clock net */
489			__set_bit(PHY_INTERFACE_MODE_10GBASER, pll->supported);
490			break;
491		default:
492			/* 6GHz, 12.890625GHz, 8GHz */
493			break;
494		}
495	}
496}
497
498#define work_to_lynx(w) container_of((w), struct lynx_28g_priv, cdr_check.work)
499
500static void lynx_28g_cdr_lock_check(struct work_struct *work)
501{
502	struct lynx_28g_priv *priv = work_to_lynx(work);
503	struct lynx_28g_lane *lane;
504	u32 rrstctl;
505	int i;
506
507	for (i = 0; i < LYNX_28G_NUM_LANE; i++) {
508		lane = &priv->lane[i];
509
510		if (!lane->init)
511			continue;
512
513		if (!lane->powered_up)
514			continue;
515
516		rrstctl = lynx_28g_lane_read(lane, LNaRRSTCTL);
517		if (!(rrstctl & LYNX_28G_LNaRRSTCTL_CDR_LOCK)) {
518			lynx_28g_lane_rmw(lane, LNaRRSTCTL, RST_REQ, RST_REQ);
519			do {
520				rrstctl = lynx_28g_lane_read(lane, LNaRRSTCTL);
521			} while (!(rrstctl & LYNX_28G_LNaRRSTCTL_RST_DONE));
522		}
523	}
524	queue_delayed_work(system_power_efficient_wq, &priv->cdr_check,
525			   msecs_to_jiffies(1000));
526}
527
528static void lynx_28g_lane_read_configuration(struct lynx_28g_lane *lane)
529{
530	u32 pss, protocol;
531
532	pss = lynx_28g_lane_read(lane, LNaPSS);
533	protocol = LYNX_28G_LNaPSS_TYPE(pss);
534	switch (protocol) {
535	case LYNX_28G_LNaPSS_TYPE_SGMII:
536		lane->interface = PHY_INTERFACE_MODE_SGMII;
537		break;
538	case LYNX_28G_LNaPSS_TYPE_XFI:
539		lane->interface = PHY_INTERFACE_MODE_10GBASER;
540		break;
541	default:
542		lane->interface = PHY_INTERFACE_MODE_NA;
543	}
544}
545
546static struct phy *lynx_28g_xlate(struct device *dev,
547				  struct of_phandle_args *args)
548{
549	struct lynx_28g_priv *priv = dev_get_drvdata(dev);
550	int idx = args->args[0];
551
552	if (WARN_ON(idx >= LYNX_28G_NUM_LANE))
553		return ERR_PTR(-EINVAL);
554
555	return priv->lane[idx].phy;
556}
557
558static int lynx_28g_probe(struct platform_device *pdev)
559{
560	struct device *dev = &pdev->dev;
561	struct phy_provider *provider;
562	struct lynx_28g_priv *priv;
563	int i;
564
565	priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
566	if (!priv)
567		return -ENOMEM;
568	priv->dev = &pdev->dev;
569
570	priv->base = devm_platform_ioremap_resource(pdev, 0);
571	if (IS_ERR(priv->base))
572		return PTR_ERR(priv->base);
573
574	lynx_28g_pll_read_configuration(priv);
575
576	for (i = 0; i < LYNX_28G_NUM_LANE; i++) {
577		struct lynx_28g_lane *lane = &priv->lane[i];
578		struct phy *phy;
579
580		memset(lane, 0, sizeof(*lane));
581
582		phy = devm_phy_create(&pdev->dev, NULL, &lynx_28g_ops);
583		if (IS_ERR(phy))
584			return PTR_ERR(phy);
585
586		lane->priv = priv;
587		lane->phy = phy;
588		lane->id = i;
589		phy_set_drvdata(phy, lane);
590		lynx_28g_lane_read_configuration(lane);
591	}
592
593	dev_set_drvdata(dev, priv);
594
595	INIT_DELAYED_WORK(&priv->cdr_check, lynx_28g_cdr_lock_check);
596
597	queue_delayed_work(system_power_efficient_wq, &priv->cdr_check,
598			   msecs_to_jiffies(1000));
599
600	dev_set_drvdata(&pdev->dev, priv);
601	provider = devm_of_phy_provider_register(&pdev->dev, lynx_28g_xlate);
602
603	return PTR_ERR_OR_ZERO(provider);
604}
605
606static const struct of_device_id lynx_28g_of_match_table[] = {
607	{ .compatible = "fsl,lynx-28g" },
608	{ },
609};
610MODULE_DEVICE_TABLE(of, lynx_28g_of_match_table);
611
612static struct platform_driver lynx_28g_driver = {
613	.probe	= lynx_28g_probe,
614	.driver	= {
615		.name = "lynx-28g",
616		.of_match_table = lynx_28g_of_match_table,
617	},
618};
619module_platform_driver(lynx_28g_driver);
620
621MODULE_AUTHOR("Ioana Ciornei <ioana.ciornei@nxp.com>");
622MODULE_DESCRIPTION("Lynx 28G SerDes PHY driver for Layerscape SoCs");
623MODULE_LICENSE("GPL v2");