xref: /linux/drivers/phy/mscc/phy-ocelot-serdes.c (revision da1d9caf95def6f0320819cf941c9fd1069ba9e1)
1 // SPDX-License-Identifier: (GPL-2.0 OR MIT)
2 /*
3  * SerDes PHY driver for Microsemi Ocelot
4  *
5  * Copyright (c) 2018 Microsemi
6  *
7  */
8 
9 #include <linux/err.h>
10 #include <linux/mfd/syscon.h>
11 #include <linux/module.h>
12 #include <linux/of.h>
13 #include <linux/of_platform.h>
14 #include <linux/phy.h>
15 #include <linux/phy/phy.h>
16 #include <linux/platform_device.h>
17 #include <linux/regmap.h>
18 #include <soc/mscc/ocelot_hsio.h>
19 #include <dt-bindings/phy/phy-ocelot-serdes.h>
20 
21 struct serdes_ctrl {
22 	struct regmap		*regs;
23 	struct device		*dev;
24 	struct phy		*phys[SERDES_MAX];
25 };
26 
27 struct serdes_macro {
28 	u8			idx;
29 	/* Not used when in QSGMII or PCIe mode */
30 	int			port;
31 	struct serdes_ctrl	*ctrl;
32 };
33 
34 #define MCB_S6G_CFG_TIMEOUT     50
35 
36 static int __serdes_write_mcb_s6g(struct regmap *regmap, u8 macro, u32 op)
37 {
38 	unsigned int regval = 0;
39 
40 	regmap_write(regmap, HSIO_MCB_S6G_ADDR_CFG, op |
41 		     HSIO_MCB_S6G_ADDR_CFG_SERDES6G_ADDR(BIT(macro)));
42 
43 	return regmap_read_poll_timeout(regmap, HSIO_MCB_S6G_ADDR_CFG, regval,
44 					(regval & op) != op, 100,
45 					MCB_S6G_CFG_TIMEOUT * 1000);
46 }
47 
48 static int serdes_commit_mcb_s6g(struct regmap *regmap, u8 macro)
49 {
50 	return __serdes_write_mcb_s6g(regmap, macro,
51 		HSIO_MCB_S6G_ADDR_CFG_SERDES6G_WR_ONE_SHOT);
52 }
53 
54 static int serdes_update_mcb_s6g(struct regmap *regmap, u8 macro)
55 {
56 	return __serdes_write_mcb_s6g(regmap, macro,
57 		HSIO_MCB_S6G_ADDR_CFG_SERDES6G_RD_ONE_SHOT);
58 }
59 
60 static int serdes_init_s6g(struct regmap *regmap, u8 serdes, int mode)
61 {
62 	u32 pll_fsm_ctrl_data;
63 	u32 ob_ena1v_mode;
64 	u32 des_bw_ana;
65 	u32 ob_ena_cas;
66 	u32 if_mode;
67 	u32 ob_lev;
68 	u32 qrate;
69 	int ret;
70 
71 	if (mode == PHY_INTERFACE_MODE_QSGMII) {
72 		pll_fsm_ctrl_data = 120;
73 		ob_ena1v_mode = 0;
74 		ob_ena_cas = 0;
75 		des_bw_ana = 5;
76 		ob_lev = 24;
77 		if_mode = 3;
78 		qrate = 0;
79 	} else {
80 		pll_fsm_ctrl_data = 60;
81 		ob_ena1v_mode = 1;
82 		ob_ena_cas = 2;
83 		des_bw_ana = 3;
84 		ob_lev = 48;
85 		if_mode = 1;
86 		qrate = 1;
87 	}
88 
89 	ret = serdes_update_mcb_s6g(regmap, serdes);
90 	if (ret)
91 		return ret;
92 
93 	/* Test pattern */
94 
95 	regmap_update_bits(regmap, HSIO_S6G_COMMON_CFG,
96 			   HSIO_S6G_COMMON_CFG_SYS_RST, 0);
97 
98 	regmap_update_bits(regmap, HSIO_S6G_PLL_CFG,
99 			   HSIO_S6G_PLL_CFG_PLL_FSM_ENA, 0);
100 
101 	regmap_update_bits(regmap, HSIO_S6G_IB_CFG,
102 			   HSIO_S6G_IB_CFG_IB_SIG_DET_ENA |
103 			   HSIO_S6G_IB_CFG_IB_REG_ENA |
104 			   HSIO_S6G_IB_CFG_IB_SAM_ENA |
105 			   HSIO_S6G_IB_CFG_IB_EQZ_ENA |
106 			   HSIO_S6G_IB_CFG_IB_CONCUR |
107 			   HSIO_S6G_IB_CFG_IB_CAL_ENA,
108 			   HSIO_S6G_IB_CFG_IB_SIG_DET_ENA |
109 			   HSIO_S6G_IB_CFG_IB_REG_ENA |
110 			   HSIO_S6G_IB_CFG_IB_SAM_ENA |
111 			   HSIO_S6G_IB_CFG_IB_EQZ_ENA |
112 			   HSIO_S6G_IB_CFG_IB_CONCUR);
113 
114 	regmap_update_bits(regmap, HSIO_S6G_IB_CFG1,
115 			   HSIO_S6G_IB_CFG1_IB_FRC_OFFSET |
116 			   HSIO_S6G_IB_CFG1_IB_FRC_LP |
117 			   HSIO_S6G_IB_CFG1_IB_FRC_MID |
118 			   HSIO_S6G_IB_CFG1_IB_FRC_HP |
119 			   HSIO_S6G_IB_CFG1_IB_FILT_OFFSET |
120 			   HSIO_S6G_IB_CFG1_IB_FILT_LP |
121 			   HSIO_S6G_IB_CFG1_IB_FILT_MID |
122 			   HSIO_S6G_IB_CFG1_IB_FILT_HP,
123 			   HSIO_S6G_IB_CFG1_IB_FILT_OFFSET |
124 			   HSIO_S6G_IB_CFG1_IB_FILT_HP |
125 			   HSIO_S6G_IB_CFG1_IB_FILT_LP |
126 			   HSIO_S6G_IB_CFG1_IB_FILT_MID);
127 
128 	regmap_update_bits(regmap, HSIO_S6G_IB_CFG2,
129 			   HSIO_S6G_IB_CFG2_IB_UREG_M,
130 			   HSIO_S6G_IB_CFG2_IB_UREG(4));
131 
132 	regmap_update_bits(regmap, HSIO_S6G_IB_CFG3,
133 			   HSIO_S6G_IB_CFG3_IB_INI_OFFSET_M |
134 			   HSIO_S6G_IB_CFG3_IB_INI_LP_M |
135 			   HSIO_S6G_IB_CFG3_IB_INI_MID_M |
136 			   HSIO_S6G_IB_CFG3_IB_INI_HP_M,
137 			   HSIO_S6G_IB_CFG3_IB_INI_OFFSET(31) |
138 			   HSIO_S6G_IB_CFG3_IB_INI_LP(1) |
139 			   HSIO_S6G_IB_CFG3_IB_INI_MID(31) |
140 			   HSIO_S6G_IB_CFG3_IB_INI_HP(0));
141 
142 	regmap_update_bits(regmap, HSIO_S6G_MISC_CFG,
143 			   HSIO_S6G_MISC_CFG_LANE_RST,
144 			   HSIO_S6G_MISC_CFG_LANE_RST);
145 
146 	ret = serdes_commit_mcb_s6g(regmap, serdes);
147 	if (ret)
148 		return ret;
149 
150 	/* OB + DES + IB + SER CFG */
151 	regmap_update_bits(regmap, HSIO_S6G_OB_CFG,
152 			   HSIO_S6G_OB_CFG_OB_IDLE |
153 			   HSIO_S6G_OB_CFG_OB_ENA1V_MODE |
154 			   HSIO_S6G_OB_CFG_OB_POST0_M |
155 			   HSIO_S6G_OB_CFG_OB_PREC_M,
156 			   (ob_ena1v_mode ? HSIO_S6G_OB_CFG_OB_ENA1V_MODE : 0) |
157 			   HSIO_S6G_OB_CFG_OB_POST0(0) |
158 			   HSIO_S6G_OB_CFG_OB_PREC(0));
159 
160 	regmap_update_bits(regmap, HSIO_S6G_OB_CFG1,
161 			   HSIO_S6G_OB_CFG1_OB_ENA_CAS_M |
162 			   HSIO_S6G_OB_CFG1_OB_LEV_M,
163 			   HSIO_S6G_OB_CFG1_OB_LEV(ob_lev) |
164 			   HSIO_S6G_OB_CFG1_OB_ENA_CAS(ob_ena_cas));
165 
166 	regmap_update_bits(regmap, HSIO_S6G_DES_CFG,
167 			   HSIO_S6G_DES_CFG_DES_PHS_CTRL_M |
168 			   HSIO_S6G_DES_CFG_DES_CPMD_SEL_M |
169 			   HSIO_S6G_DES_CFG_DES_BW_ANA_M,
170 			   HSIO_S6G_DES_CFG_DES_PHS_CTRL(2) |
171 			   HSIO_S6G_DES_CFG_DES_CPMD_SEL(0) |
172 			   HSIO_S6G_DES_CFG_DES_BW_ANA(des_bw_ana));
173 
174 	regmap_update_bits(regmap, HSIO_S6G_IB_CFG,
175 			   HSIO_S6G_IB_CFG_IB_SIG_DET_CLK_SEL_M |
176 			   HSIO_S6G_IB_CFG_IB_REG_PAT_SEL_OFFSET_M,
177 			   HSIO_S6G_IB_CFG_IB_REG_PAT_SEL_OFFSET(0) |
178 			   HSIO_S6G_IB_CFG_IB_SIG_DET_CLK_SEL(0));
179 
180 	regmap_update_bits(regmap, HSIO_S6G_IB_CFG1,
181 			   HSIO_S6G_IB_CFG1_IB_TSDET_M,
182 			   HSIO_S6G_IB_CFG1_IB_TSDET(16));
183 
184 	regmap_update_bits(regmap, HSIO_S6G_SER_CFG,
185 			   HSIO_S6G_SER_CFG_SER_ALISEL_M |
186 			   HSIO_S6G_SER_CFG_SER_ENALI,
187 			   HSIO_S6G_SER_CFG_SER_ALISEL(0));
188 
189 	regmap_update_bits(regmap, HSIO_S6G_PLL_CFG,
190 			   HSIO_S6G_PLL_CFG_PLL_DIV4 |
191 			   HSIO_S6G_PLL_CFG_PLL_ENA_ROT |
192 			   HSIO_S6G_PLL_CFG_PLL_FSM_CTRL_DATA_M |
193 			   HSIO_S6G_PLL_CFG_PLL_ROT_DIR |
194 			   HSIO_S6G_PLL_CFG_PLL_ROT_FRQ,
195 			   HSIO_S6G_PLL_CFG_PLL_FSM_CTRL_DATA
196 			   (pll_fsm_ctrl_data));
197 
198 	regmap_update_bits(regmap, HSIO_S6G_COMMON_CFG,
199 			   HSIO_S6G_COMMON_CFG_SYS_RST |
200 			   HSIO_S6G_COMMON_CFG_ENA_LANE |
201 			   HSIO_S6G_COMMON_CFG_PWD_RX |
202 			   HSIO_S6G_COMMON_CFG_PWD_TX |
203 			   HSIO_S6G_COMMON_CFG_HRATE |
204 			   HSIO_S6G_COMMON_CFG_QRATE |
205 			   HSIO_S6G_COMMON_CFG_ENA_ELOOP |
206 			   HSIO_S6G_COMMON_CFG_ENA_FLOOP |
207 			   HSIO_S6G_COMMON_CFG_IF_MODE_M,
208 			   HSIO_S6G_COMMON_CFG_SYS_RST |
209 			   HSIO_S6G_COMMON_CFG_ENA_LANE |
210 			   (qrate ? HSIO_S6G_COMMON_CFG_QRATE : 0) |
211 			   HSIO_S6G_COMMON_CFG_IF_MODE(if_mode));
212 
213 	regmap_update_bits(regmap, HSIO_S6G_MISC_CFG,
214 			   HSIO_S6G_MISC_CFG_LANE_RST |
215 			   HSIO_S6G_MISC_CFG_DES_100FX_CPMD_ENA |
216 			   HSIO_S6G_MISC_CFG_RX_LPI_MODE_ENA |
217 			   HSIO_S6G_MISC_CFG_TX_LPI_MODE_ENA,
218 			   HSIO_S6G_MISC_CFG_LANE_RST |
219 			   HSIO_S6G_MISC_CFG_RX_LPI_MODE_ENA);
220 
221 
222 	ret = serdes_commit_mcb_s6g(regmap, serdes);
223 	if (ret)
224 		return ret;
225 
226 	regmap_update_bits(regmap, HSIO_S6G_PLL_CFG,
227 			   HSIO_S6G_PLL_CFG_PLL_FSM_ENA,
228 			   HSIO_S6G_PLL_CFG_PLL_FSM_ENA);
229 
230 	ret = serdes_commit_mcb_s6g(regmap, serdes);
231 	if (ret)
232 		return ret;
233 
234 	/* Wait for PLL bringup */
235 	msleep(20);
236 
237 	regmap_update_bits(regmap, HSIO_S6G_IB_CFG,
238 			   HSIO_S6G_IB_CFG_IB_CAL_ENA,
239 			   HSIO_S6G_IB_CFG_IB_CAL_ENA);
240 
241 	regmap_update_bits(regmap, HSIO_S6G_MISC_CFG,
242 			   HSIO_S6G_MISC_CFG_LANE_RST, 0);
243 
244 	ret = serdes_commit_mcb_s6g(regmap, serdes);
245 	if (ret)
246 		return ret;
247 
248 	/* Wait for calibration */
249 	msleep(60);
250 
251 	regmap_update_bits(regmap, HSIO_S6G_IB_CFG,
252 			   HSIO_S6G_IB_CFG_IB_REG_PAT_SEL_OFFSET_M |
253 			   HSIO_S6G_IB_CFG_IB_SIG_DET_CLK_SEL_M,
254 			   HSIO_S6G_IB_CFG_IB_REG_PAT_SEL_OFFSET(0) |
255 			   HSIO_S6G_IB_CFG_IB_SIG_DET_CLK_SEL(7));
256 
257 	regmap_update_bits(regmap, HSIO_S6G_IB_CFG1,
258 			   HSIO_S6G_IB_CFG1_IB_TSDET_M,
259 			   HSIO_S6G_IB_CFG1_IB_TSDET(3));
260 
261 	/* IB CFG */
262 
263 	return 0;
264 }
265 
266 #define MCB_S1G_CFG_TIMEOUT     50
267 
268 static int __serdes_write_mcb_s1g(struct regmap *regmap, u8 macro, u32 op)
269 {
270 	unsigned int regval;
271 
272 	regmap_write(regmap, HSIO_MCB_S1G_ADDR_CFG, op |
273 		     HSIO_MCB_S1G_ADDR_CFG_SERDES1G_ADDR(BIT(macro)));
274 
275 	return regmap_read_poll_timeout(regmap, HSIO_MCB_S1G_ADDR_CFG, regval,
276 					(regval & op) != op, 100,
277 					MCB_S1G_CFG_TIMEOUT * 1000);
278 }
279 
280 static int serdes_commit_mcb_s1g(struct regmap *regmap, u8 macro)
281 {
282 	return __serdes_write_mcb_s1g(regmap, macro,
283 		HSIO_MCB_S1G_ADDR_CFG_SERDES1G_WR_ONE_SHOT);
284 }
285 
286 static int serdes_update_mcb_s1g(struct regmap *regmap, u8 macro)
287 {
288 	return __serdes_write_mcb_s1g(regmap, macro,
289 		HSIO_MCB_S1G_ADDR_CFG_SERDES1G_RD_ONE_SHOT);
290 }
291 
292 static int serdes_init_s1g(struct regmap *regmap, u8 serdes)
293 {
294 	int ret;
295 
296 	ret = serdes_update_mcb_s1g(regmap, serdes);
297 	if (ret)
298 		return ret;
299 
300 	regmap_update_bits(regmap, HSIO_S1G_COMMON_CFG,
301 			   HSIO_S1G_COMMON_CFG_SYS_RST |
302 			   HSIO_S1G_COMMON_CFG_ENA_LANE |
303 			   HSIO_S1G_COMMON_CFG_ENA_ELOOP |
304 			   HSIO_S1G_COMMON_CFG_ENA_FLOOP,
305 			   HSIO_S1G_COMMON_CFG_ENA_LANE);
306 
307 	regmap_update_bits(regmap, HSIO_S1G_PLL_CFG,
308 			   HSIO_S1G_PLL_CFG_PLL_FSM_ENA |
309 			   HSIO_S1G_PLL_CFG_PLL_FSM_CTRL_DATA_M,
310 			   HSIO_S1G_PLL_CFG_PLL_FSM_CTRL_DATA(200) |
311 			   HSIO_S1G_PLL_CFG_PLL_FSM_ENA);
312 
313 	regmap_update_bits(regmap, HSIO_S1G_MISC_CFG,
314 			   HSIO_S1G_MISC_CFG_DES_100FX_CPMD_ENA |
315 			   HSIO_S1G_MISC_CFG_LANE_RST,
316 			   HSIO_S1G_MISC_CFG_LANE_RST);
317 
318 	ret = serdes_commit_mcb_s1g(regmap, serdes);
319 	if (ret)
320 		return ret;
321 
322 	regmap_update_bits(regmap, HSIO_S1G_COMMON_CFG,
323 			   HSIO_S1G_COMMON_CFG_SYS_RST,
324 			   HSIO_S1G_COMMON_CFG_SYS_RST);
325 
326 	regmap_update_bits(regmap, HSIO_S1G_MISC_CFG,
327 			   HSIO_S1G_MISC_CFG_LANE_RST, 0);
328 
329 	ret = serdes_commit_mcb_s1g(regmap, serdes);
330 	if (ret)
331 		return ret;
332 
333 	return 0;
334 }
335 
336 struct serdes_mux {
337 	u8			idx;
338 	u8			port;
339 	enum phy_mode		mode;
340 	int			submode;
341 	u32			mask;
342 	u32			mux;
343 };
344 
345 #define SERDES_MUX(_idx, _port, _mode, _submode, _mask, _mux) { \
346 	.idx = _idx,						\
347 	.port = _port,						\
348 	.mode = _mode,						\
349 	.submode = _submode,					\
350 	.mask = _mask,						\
351 	.mux = _mux,						\
352 }
353 
354 #define SERDES_MUX_SGMII(i, p, m, c) \
355 	SERDES_MUX(i, p, PHY_MODE_ETHERNET, PHY_INTERFACE_MODE_SGMII, m, c)
356 #define SERDES_MUX_QSGMII(i, p, m, c) \
357 	SERDES_MUX(i, p, PHY_MODE_ETHERNET, PHY_INTERFACE_MODE_QSGMII, m, c)
358 
359 static const struct serdes_mux ocelot_serdes_muxes[] = {
360 	SERDES_MUX_SGMII(SERDES1G(0), 0, 0, 0),
361 	SERDES_MUX_SGMII(SERDES1G(1), 1, HSIO_HW_CFG_DEV1G_5_MODE, 0),
362 	SERDES_MUX_SGMII(SERDES1G(1), 5, HSIO_HW_CFG_QSGMII_ENA |
363 			 HSIO_HW_CFG_DEV1G_5_MODE, HSIO_HW_CFG_DEV1G_5_MODE),
364 	SERDES_MUX_SGMII(SERDES1G(2), 2, HSIO_HW_CFG_DEV1G_4_MODE, 0),
365 	SERDES_MUX_SGMII(SERDES1G(2), 4, HSIO_HW_CFG_QSGMII_ENA |
366 			 HSIO_HW_CFG_DEV1G_4_MODE, HSIO_HW_CFG_DEV1G_4_MODE),
367 	SERDES_MUX_SGMII(SERDES1G(3), 3, HSIO_HW_CFG_DEV1G_6_MODE, 0),
368 	SERDES_MUX_SGMII(SERDES1G(3), 6, HSIO_HW_CFG_QSGMII_ENA |
369 			 HSIO_HW_CFG_DEV1G_6_MODE, HSIO_HW_CFG_DEV1G_6_MODE),
370 	SERDES_MUX_SGMII(SERDES1G(4), 4, HSIO_HW_CFG_QSGMII_ENA |
371 			 HSIO_HW_CFG_DEV1G_4_MODE | HSIO_HW_CFG_DEV1G_9_MODE,
372 			 0),
373 	SERDES_MUX_SGMII(SERDES1G(4), 9, HSIO_HW_CFG_DEV1G_4_MODE |
374 			 HSIO_HW_CFG_DEV1G_9_MODE, HSIO_HW_CFG_DEV1G_4_MODE |
375 			 HSIO_HW_CFG_DEV1G_9_MODE),
376 	SERDES_MUX_SGMII(SERDES1G(5), 5, HSIO_HW_CFG_QSGMII_ENA |
377 			 HSIO_HW_CFG_DEV1G_5_MODE | HSIO_HW_CFG_DEV2G5_10_MODE,
378 			 0),
379 	SERDES_MUX_SGMII(SERDES1G(5), 10, HSIO_HW_CFG_PCIE_ENA |
380 			 HSIO_HW_CFG_DEV1G_5_MODE | HSIO_HW_CFG_DEV2G5_10_MODE,
381 			 HSIO_HW_CFG_DEV1G_5_MODE | HSIO_HW_CFG_DEV2G5_10_MODE),
382 	SERDES_MUX_QSGMII(SERDES6G(0), 4, HSIO_HW_CFG_QSGMII_ENA,
383 			  HSIO_HW_CFG_QSGMII_ENA),
384 	SERDES_MUX_QSGMII(SERDES6G(0), 5, HSIO_HW_CFG_QSGMII_ENA,
385 			  HSIO_HW_CFG_QSGMII_ENA),
386 	SERDES_MUX_QSGMII(SERDES6G(0), 6, HSIO_HW_CFG_QSGMII_ENA,
387 			  HSIO_HW_CFG_QSGMII_ENA),
388 	SERDES_MUX_SGMII(SERDES6G(0), 7, HSIO_HW_CFG_QSGMII_ENA, 0),
389 	SERDES_MUX_QSGMII(SERDES6G(0), 7, HSIO_HW_CFG_QSGMII_ENA,
390 			  HSIO_HW_CFG_QSGMII_ENA),
391 	SERDES_MUX_SGMII(SERDES6G(1), 8, 0, 0),
392 	SERDES_MUX_SGMII(SERDES6G(2), 10, HSIO_HW_CFG_PCIE_ENA |
393 			 HSIO_HW_CFG_DEV2G5_10_MODE, 0),
394 	SERDES_MUX(SERDES6G(2), 10, PHY_MODE_PCIE, 0, HSIO_HW_CFG_PCIE_ENA,
395 		   HSIO_HW_CFG_PCIE_ENA),
396 };
397 
398 static int serdes_set_mode(struct phy *phy, enum phy_mode mode, int submode)
399 {
400 	struct serdes_macro *macro = phy_get_drvdata(phy);
401 	unsigned int i;
402 	int ret;
403 
404 	/* As of now only PHY_MODE_ETHERNET is supported */
405 	if (mode != PHY_MODE_ETHERNET)
406 		return -EOPNOTSUPP;
407 
408 	for (i = 0; i < ARRAY_SIZE(ocelot_serdes_muxes); i++) {
409 		if (macro->idx != ocelot_serdes_muxes[i].idx ||
410 		    mode != ocelot_serdes_muxes[i].mode ||
411 		    submode != ocelot_serdes_muxes[i].submode)
412 			continue;
413 
414 		if (submode != PHY_INTERFACE_MODE_QSGMII &&
415 		    macro->port != ocelot_serdes_muxes[i].port)
416 			continue;
417 
418 		ret = regmap_update_bits(macro->ctrl->regs, HSIO_HW_CFG,
419 					 ocelot_serdes_muxes[i].mask,
420 					 ocelot_serdes_muxes[i].mux);
421 		if (ret)
422 			return ret;
423 
424 		if (macro->idx <= SERDES1G_MAX)
425 			return serdes_init_s1g(macro->ctrl->regs, macro->idx);
426 		else if (macro->idx <= SERDES6G_MAX)
427 			return serdes_init_s6g(macro->ctrl->regs,
428 					       macro->idx - (SERDES1G_MAX + 1),
429 					       ocelot_serdes_muxes[i].submode);
430 
431 		/* PCIe not supported yet */
432 		return -EOPNOTSUPP;
433 	}
434 
435 	return -EINVAL;
436 }
437 
438 static const struct phy_ops serdes_ops = {
439 	.set_mode	= serdes_set_mode,
440 	.owner		= THIS_MODULE,
441 };
442 
443 static struct phy *serdes_simple_xlate(struct device *dev,
444 				       struct of_phandle_args *args)
445 {
446 	struct serdes_ctrl *ctrl = dev_get_drvdata(dev);
447 	unsigned int port, idx, i;
448 
449 	if (args->args_count != 2)
450 		return ERR_PTR(-EINVAL);
451 
452 	port = args->args[0];
453 	idx = args->args[1];
454 
455 	for (i = 0; i < SERDES_MAX; i++) {
456 		struct serdes_macro *macro = phy_get_drvdata(ctrl->phys[i]);
457 
458 		if (idx != macro->idx)
459 			continue;
460 
461 		/* SERDES6G(0) is the only SerDes capable of QSGMII */
462 		if (idx != SERDES6G(0) && macro->port >= 0)
463 			return ERR_PTR(-EBUSY);
464 
465 		macro->port = port;
466 		return ctrl->phys[i];
467 	}
468 
469 	return ERR_PTR(-ENODEV);
470 }
471 
472 static int serdes_phy_create(struct serdes_ctrl *ctrl, u8 idx, struct phy **phy)
473 {
474 	struct serdes_macro *macro;
475 
476 	*phy = devm_phy_create(ctrl->dev, NULL, &serdes_ops);
477 	if (IS_ERR(*phy))
478 		return PTR_ERR(*phy);
479 
480 	macro = devm_kzalloc(ctrl->dev, sizeof(*macro), GFP_KERNEL);
481 	if (!macro)
482 		return -ENOMEM;
483 
484 	macro->idx = idx;
485 	macro->ctrl = ctrl;
486 	macro->port = -1;
487 
488 	phy_set_drvdata(*phy, macro);
489 
490 	return 0;
491 }
492 
493 static int serdes_probe(struct platform_device *pdev)
494 {
495 	struct phy_provider *provider;
496 	struct serdes_ctrl *ctrl;
497 	unsigned int i;
498 	int ret;
499 
500 	ctrl = devm_kzalloc(&pdev->dev, sizeof(*ctrl), GFP_KERNEL);
501 	if (!ctrl)
502 		return -ENOMEM;
503 
504 	ctrl->dev = &pdev->dev;
505 	ctrl->regs = syscon_node_to_regmap(pdev->dev.parent->of_node);
506 	if (IS_ERR(ctrl->regs))
507 		return PTR_ERR(ctrl->regs);
508 
509 	for (i = 0; i < SERDES_MAX; i++) {
510 		ret = serdes_phy_create(ctrl, i, &ctrl->phys[i]);
511 		if (ret)
512 			return ret;
513 	}
514 
515 	dev_set_drvdata(&pdev->dev, ctrl);
516 
517 	provider = devm_of_phy_provider_register(ctrl->dev,
518 						 serdes_simple_xlate);
519 
520 	return PTR_ERR_OR_ZERO(provider);
521 }
522 
523 static const struct of_device_id serdes_ids[] = {
524 	{ .compatible = "mscc,vsc7514-serdes", },
525 	{},
526 };
527 MODULE_DEVICE_TABLE(of, serdes_ids);
528 
529 static struct platform_driver mscc_ocelot_serdes = {
530 	.probe		= serdes_probe,
531 	.driver		= {
532 		.name	= "mscc,ocelot-serdes",
533 		.of_match_table = of_match_ptr(serdes_ids),
534 	},
535 };
536 
537 module_platform_driver(mscc_ocelot_serdes);
538 
539 MODULE_AUTHOR("Quentin Schulz <quentin.schulz@bootlin.com>");
540 MODULE_DESCRIPTION("SerDes driver for Microsemi Ocelot");
541 MODULE_LICENSE("Dual MIT/GPL");
542