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(map: regmap, HSIO_MCB_S6G_ADDR_CFG, val: 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, macro: serdes); |
90 | if (ret) |
91 | return ret; |
92 | |
93 | /* Test pattern */ |
94 | |
95 | regmap_update_bits(map: regmap, HSIO_S6G_COMMON_CFG, |
96 | HSIO_S6G_COMMON_CFG_SYS_RST, val: 0); |
97 | |
98 | regmap_update_bits(map: regmap, HSIO_S6G_PLL_CFG, |
99 | HSIO_S6G_PLL_CFG_PLL_FSM_ENA, val: 0); |
100 | |
101 | regmap_update_bits(map: 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(map: 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(map: 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(map: 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(map: 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, macro: serdes); |
147 | if (ret) |
148 | return ret; |
149 | |
150 | /* OB + DES + IB + SER CFG */ |
151 | regmap_update_bits(map: 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 | val: (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(map: 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(map: 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(map: 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(map: 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(map: 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(map: 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(map: 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(map: 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, macro: serdes); |
223 | if (ret) |
224 | return ret; |
225 | |
226 | regmap_update_bits(map: 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, macro: serdes); |
231 | if (ret) |
232 | return ret; |
233 | |
234 | /* Wait for PLL bringup */ |
235 | msleep(msecs: 20); |
236 | |
237 | regmap_update_bits(map: 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(map: regmap, HSIO_S6G_MISC_CFG, |
242 | HSIO_S6G_MISC_CFG_LANE_RST, val: 0); |
243 | |
244 | ret = serdes_commit_mcb_s6g(regmap, macro: serdes); |
245 | if (ret) |
246 | return ret; |
247 | |
248 | /* Wait for calibration */ |
249 | msleep(msecs: 60); |
250 | |
251 | regmap_update_bits(map: 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(map: 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(map: regmap, HSIO_MCB_S1G_ADDR_CFG, val: 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, macro: serdes); |
297 | if (ret) |
298 | return ret; |
299 | |
300 | regmap_update_bits(map: 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(map: 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(map: 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, macro: serdes); |
319 | if (ret) |
320 | return ret; |
321 | |
322 | regmap_update_bits(map: regmap, HSIO_S1G_COMMON_CFG, |
323 | HSIO_S1G_COMMON_CFG_SYS_RST, |
324 | HSIO_S1G_COMMON_CFG_SYS_RST); |
325 | |
326 | regmap_update_bits(map: regmap, HSIO_S1G_MISC_CFG, |
327 | HSIO_S1G_MISC_CFG_LANE_RST, val: 0); |
328 | |
329 | ret = serdes_commit_mcb_s1g(regmap, macro: 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(map: macro->ctrl->regs, HSIO_HW_CFG, |
419 | mask: ocelot_serdes_muxes[i].mask, |
420 | val: ocelot_serdes_muxes[i].mux); |
421 | if (ret) |
422 | return ret; |
423 | |
424 | if (macro->idx <= SERDES1G_MAX) |
425 | return serdes_init_s1g(regmap: macro->ctrl->regs, serdes: macro->idx); |
426 | else if (macro->idx <= SERDES6G_MAX) |
427 | return serdes_init_s6g(regmap: macro->ctrl->regs, |
428 | serdes: macro->idx - (SERDES1G_MAX + 1), |
429 | mode: 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 | const 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(error: -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(phy: 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(error: -EBUSY); |
464 | |
465 | macro->port = port; |
466 | return ctrl->phys[i]; |
467 | } |
468 | |
469 | return ERR_PTR(error: -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(dev: ctrl->dev, NULL, ops: &serdes_ops); |
477 | if (IS_ERR(ptr: *phy)) |
478 | return PTR_ERR(ptr: *phy); |
479 | |
480 | macro = devm_kzalloc(dev: ctrl->dev, size: 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: *phy, data: 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 | struct resource *res; |
498 | unsigned int i; |
499 | int ret; |
500 | |
501 | ctrl = devm_kzalloc(dev: &pdev->dev, size: sizeof(*ctrl), GFP_KERNEL); |
502 | if (!ctrl) |
503 | return -ENOMEM; |
504 | |
505 | ctrl->dev = &pdev->dev; |
506 | ctrl->regs = syscon_node_to_regmap(np: pdev->dev.parent->of_node); |
507 | if (IS_ERR(ptr: ctrl->regs)) { |
508 | /* Fall back to using IORESOURCE_REG, if possible */ |
509 | res = platform_get_resource(pdev, IORESOURCE_REG, 0); |
510 | if (res) |
511 | ctrl->regs = dev_get_regmap(dev: ctrl->dev->parent, |
512 | name: res->name); |
513 | } |
514 | |
515 | if (IS_ERR(ptr: ctrl->regs)) |
516 | return PTR_ERR(ptr: ctrl->regs); |
517 | |
518 | for (i = 0; i < SERDES_MAX; i++) { |
519 | ret = serdes_phy_create(ctrl, idx: i, phy: &ctrl->phys[i]); |
520 | if (ret) |
521 | return ret; |
522 | } |
523 | |
524 | dev_set_drvdata(dev: &pdev->dev, data: ctrl); |
525 | |
526 | provider = devm_of_phy_provider_register(ctrl->dev, |
527 | serdes_simple_xlate); |
528 | |
529 | return PTR_ERR_OR_ZERO(ptr: provider); |
530 | } |
531 | |
532 | static const struct of_device_id serdes_ids[] = { |
533 | { .compatible = "mscc,vsc7514-serdes" , }, |
534 | {}, |
535 | }; |
536 | MODULE_DEVICE_TABLE(of, serdes_ids); |
537 | |
538 | static struct platform_driver mscc_ocelot_serdes = { |
539 | .probe = serdes_probe, |
540 | .driver = { |
541 | .name = "mscc,ocelot-serdes" , |
542 | .of_match_table = of_match_ptr(serdes_ids), |
543 | }, |
544 | }; |
545 | |
546 | module_platform_driver(mscc_ocelot_serdes); |
547 | |
548 | MODULE_AUTHOR("Quentin Schulz <quentin.schulz@bootlin.com>" ); |
549 | MODULE_DESCRIPTION("SerDes driver for Microsemi Ocelot" ); |
550 | MODULE_LICENSE("Dual MIT/GPL" ); |
551 | |