1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | |
3 | #include <linux/err.h> |
4 | #include <linux/module.h> |
5 | #include <linux/of.h> |
6 | #include <linux/of_platform.h> |
7 | #include <linux/phy.h> |
8 | #include <linux/phy/phy.h> |
9 | #include <linux/platform_device.h> |
10 | |
11 | #include <dt-bindings/phy/phy-lan966x-serdes.h> |
12 | #include "lan966x_serdes_regs.h" |
13 | |
14 | #define PLL_CONF_MASK GENMASK(4, 3) |
15 | #define PLL_CONF_25MHZ 0 |
16 | #define PLL_CONF_125MHZ 1 |
17 | #define PLL_CONF_SERDES_125MHZ 2 |
18 | #define PLL_CONF_BYPASS 3 |
19 | |
20 | #define lan_offset_(id, tinst, tcnt, \ |
21 | gbase, ginst, gcnt, gwidth, \ |
22 | raddr, rinst, rcnt, rwidth) \ |
23 | (gbase + ((ginst) * gwidth) + raddr + ((rinst) * rwidth)) |
24 | #define lan_offset(...) lan_offset_(__VA_ARGS__) |
25 | |
26 | #define lan_rmw(val, mask, reg, off) \ |
27 | lan_rmw_(val, mask, reg, lan_offset(off)) |
28 | |
29 | #define SERDES_MUX(_idx, _port, _mode, _submode, _mask, _mux) { \ |
30 | .idx = _idx, \ |
31 | .port = _port, \ |
32 | .mode = _mode, \ |
33 | .submode = _submode, \ |
34 | .mask = _mask, \ |
35 | .mux = _mux, \ |
36 | } |
37 | |
38 | #define SERDES_MUX_GMII(i, p, m, c) \ |
39 | SERDES_MUX(i, p, PHY_MODE_ETHERNET, PHY_INTERFACE_MODE_GMII, m, c) |
40 | #define SERDES_MUX_SGMII(i, p, m, c) \ |
41 | SERDES_MUX(i, p, PHY_MODE_ETHERNET, PHY_INTERFACE_MODE_SGMII, m, c) |
42 | #define SERDES_MUX_QSGMII(i, p, m, c) \ |
43 | SERDES_MUX(i, p, PHY_MODE_ETHERNET, PHY_INTERFACE_MODE_QSGMII, m, c) |
44 | #define SERDES_MUX_RGMII(i, p, m, c) \ |
45 | SERDES_MUX(i, p, PHY_MODE_ETHERNET, PHY_INTERFACE_MODE_RGMII, m, c), \ |
46 | SERDES_MUX(i, p, PHY_MODE_ETHERNET, PHY_INTERFACE_MODE_RGMII_TXID, m, c), \ |
47 | SERDES_MUX(i, p, PHY_MODE_ETHERNET, PHY_INTERFACE_MODE_RGMII_RXID, m, c), \ |
48 | SERDES_MUX(i, p, PHY_MODE_ETHERNET, PHY_INTERFACE_MODE_RGMII_ID, m, c) |
49 | |
50 | static void lan_rmw_(u32 val, u32 mask, void __iomem *mem, u32 offset) |
51 | { |
52 | u32 v; |
53 | |
54 | v = readl(addr: mem + offset); |
55 | v = (v & ~mask) | (val & mask); |
56 | writel(val: v, addr: mem + offset); |
57 | } |
58 | |
59 | struct serdes_mux { |
60 | u8 idx; |
61 | u8 port; |
62 | enum phy_mode mode; |
63 | int submode; |
64 | u32 mask; |
65 | u32 mux; |
66 | }; |
67 | |
68 | static const struct serdes_mux lan966x_serdes_muxes[] = { |
69 | SERDES_MUX_QSGMII(SERDES6G(1), 0, HSIO_HW_CFG_QSGMII_ENA, |
70 | HSIO_HW_CFG_QSGMII_ENA_SET(BIT(0))), |
71 | SERDES_MUX_QSGMII(SERDES6G(1), 1, HSIO_HW_CFG_QSGMII_ENA, |
72 | HSIO_HW_CFG_QSGMII_ENA_SET(BIT(0))), |
73 | SERDES_MUX_QSGMII(SERDES6G(1), 2, HSIO_HW_CFG_QSGMII_ENA, |
74 | HSIO_HW_CFG_QSGMII_ENA_SET(BIT(0))), |
75 | SERDES_MUX_QSGMII(SERDES6G(1), 3, HSIO_HW_CFG_QSGMII_ENA, |
76 | HSIO_HW_CFG_QSGMII_ENA_SET(BIT(0))), |
77 | |
78 | SERDES_MUX_QSGMII(SERDES6G(2), 4, HSIO_HW_CFG_QSGMII_ENA, |
79 | HSIO_HW_CFG_QSGMII_ENA_SET(BIT(1))), |
80 | SERDES_MUX_QSGMII(SERDES6G(2), 5, HSIO_HW_CFG_QSGMII_ENA, |
81 | HSIO_HW_CFG_QSGMII_ENA_SET(BIT(1))), |
82 | SERDES_MUX_QSGMII(SERDES6G(2), 6, HSIO_HW_CFG_QSGMII_ENA, |
83 | HSIO_HW_CFG_QSGMII_ENA_SET(BIT(1))), |
84 | SERDES_MUX_QSGMII(SERDES6G(2), 7, HSIO_HW_CFG_QSGMII_ENA, |
85 | HSIO_HW_CFG_QSGMII_ENA_SET(BIT(1))), |
86 | |
87 | SERDES_MUX_GMII(CU(0), 0, HSIO_HW_CFG_GMII_ENA, |
88 | HSIO_HW_CFG_GMII_ENA_SET(BIT(0))), |
89 | SERDES_MUX_GMII(CU(1), 1, HSIO_HW_CFG_GMII_ENA, |
90 | HSIO_HW_CFG_GMII_ENA_SET(BIT(1))), |
91 | |
92 | SERDES_MUX_SGMII(SERDES6G(0), 0, HSIO_HW_CFG_SD6G_0_CFG, 0), |
93 | SERDES_MUX_SGMII(SERDES6G(1), 1, HSIO_HW_CFG_SD6G_1_CFG, 0), |
94 | SERDES_MUX_SGMII(SERDES6G(0), 2, HSIO_HW_CFG_SD6G_0_CFG, |
95 | HSIO_HW_CFG_SD6G_0_CFG_SET(1)), |
96 | SERDES_MUX_SGMII(SERDES6G(1), 3, HSIO_HW_CFG_SD6G_1_CFG, |
97 | HSIO_HW_CFG_SD6G_1_CFG_SET(1)), |
98 | |
99 | SERDES_MUX_SGMII(SERDES6G(2), 4, 0, 0), |
100 | |
101 | SERDES_MUX_RGMII(RGMII(0), 2, HSIO_HW_CFG_RGMII_0_CFG | |
102 | HSIO_HW_CFG_RGMII_ENA | |
103 | HSIO_HW_CFG_GMII_ENA, |
104 | HSIO_HW_CFG_RGMII_0_CFG_SET(0) | |
105 | HSIO_HW_CFG_RGMII_ENA_SET(BIT(0)) | |
106 | HSIO_HW_CFG_GMII_ENA_SET(BIT(2))), |
107 | SERDES_MUX_RGMII(RGMII(1), 3, HSIO_HW_CFG_RGMII_1_CFG | |
108 | HSIO_HW_CFG_RGMII_ENA | |
109 | HSIO_HW_CFG_GMII_ENA, |
110 | HSIO_HW_CFG_RGMII_1_CFG_SET(0) | |
111 | HSIO_HW_CFG_RGMII_ENA_SET(BIT(1)) | |
112 | HSIO_HW_CFG_GMII_ENA_SET(BIT(3))), |
113 | SERDES_MUX_RGMII(RGMII(0), 5, HSIO_HW_CFG_RGMII_0_CFG | |
114 | HSIO_HW_CFG_RGMII_ENA | |
115 | HSIO_HW_CFG_GMII_ENA, |
116 | HSIO_HW_CFG_RGMII_0_CFG_SET(BIT(0)) | |
117 | HSIO_HW_CFG_RGMII_ENA_SET(BIT(0)) | |
118 | HSIO_HW_CFG_GMII_ENA_SET(BIT(5))), |
119 | SERDES_MUX_RGMII(RGMII(1), 6, HSIO_HW_CFG_RGMII_1_CFG | |
120 | HSIO_HW_CFG_RGMII_ENA | |
121 | HSIO_HW_CFG_GMII_ENA, |
122 | HSIO_HW_CFG_RGMII_1_CFG_SET(BIT(0)) | |
123 | HSIO_HW_CFG_RGMII_ENA_SET(BIT(1)) | |
124 | HSIO_HW_CFG_GMII_ENA_SET(BIT(6))), |
125 | }; |
126 | |
127 | struct serdes_ctrl { |
128 | void __iomem *regs; |
129 | struct device *dev; |
130 | struct phy *phys[SERDES_MAX]; |
131 | int ref125; |
132 | }; |
133 | |
134 | struct serdes_macro { |
135 | u8 idx; |
136 | int port; |
137 | struct serdes_ctrl *ctrl; |
138 | int speed; |
139 | phy_interface_t mode; |
140 | }; |
141 | |
142 | enum lan966x_sd6g40_mode { |
143 | LAN966X_SD6G40_MODE_QSGMII, |
144 | LAN966X_SD6G40_MODE_SGMII, |
145 | }; |
146 | |
147 | enum lan966x_sd6g40_ltx2rx { |
148 | LAN966X_SD6G40_TX2RX_LOOP_NONE, |
149 | LAN966X_SD6G40_LTX2RX |
150 | }; |
151 | |
152 | struct lan966x_sd6g40_setup_args { |
153 | enum lan966x_sd6g40_mode mode; |
154 | enum lan966x_sd6g40_ltx2rx tx2rx_loop; |
155 | bool txinvert; |
156 | bool rxinvert; |
157 | bool refclk125M; |
158 | bool mute; |
159 | }; |
160 | |
161 | struct lan966x_sd6g40_mode_args { |
162 | enum lan966x_sd6g40_mode mode; |
163 | u8 lane_10bit_sel; |
164 | u8 mpll_multiplier; |
165 | u8 ref_clkdiv2; |
166 | u8 tx_rate; |
167 | u8 rx_rate; |
168 | }; |
169 | |
170 | struct lan966x_sd6g40_setup { |
171 | u8 rx_term_en; |
172 | u8 lane_10bit_sel; |
173 | u8 tx_invert; |
174 | u8 rx_invert; |
175 | u8 mpll_multiplier; |
176 | u8 lane_loopbk_en; |
177 | u8 ref_clkdiv2; |
178 | u8 tx_rate; |
179 | u8 rx_rate; |
180 | }; |
181 | |
182 | static int lan966x_sd6g40_reg_cfg(struct serdes_macro *macro, |
183 | struct lan966x_sd6g40_setup *res_struct, |
184 | u32 idx) |
185 | { |
186 | u32 value; |
187 | |
188 | /* Note: SerDes HSIO is configured in 1G_LAN mode */ |
189 | lan_rmw(HSIO_SD_CFG_LANE_10BIT_SEL_SET(res_struct->lane_10bit_sel) | |
190 | HSIO_SD_CFG_RX_RATE_SET(res_struct->rx_rate) | |
191 | HSIO_SD_CFG_TX_RATE_SET(res_struct->tx_rate) | |
192 | HSIO_SD_CFG_TX_INVERT_SET(res_struct->tx_invert) | |
193 | HSIO_SD_CFG_RX_INVERT_SET(res_struct->rx_invert) | |
194 | HSIO_SD_CFG_LANE_LOOPBK_EN_SET(res_struct->lane_loopbk_en) | |
195 | HSIO_SD_CFG_RX_RESET_SET(0) | |
196 | HSIO_SD_CFG_TX_RESET_SET(0), |
197 | HSIO_SD_CFG_LANE_10BIT_SEL | |
198 | HSIO_SD_CFG_RX_RATE | |
199 | HSIO_SD_CFG_TX_RATE | |
200 | HSIO_SD_CFG_TX_INVERT | |
201 | HSIO_SD_CFG_RX_INVERT | |
202 | HSIO_SD_CFG_LANE_LOOPBK_EN | |
203 | HSIO_SD_CFG_RX_RESET | |
204 | HSIO_SD_CFG_TX_RESET, |
205 | macro->ctrl->regs, HSIO_SD_CFG(idx)); |
206 | |
207 | lan_rmw(HSIO_MPLL_CFG_MPLL_MULTIPLIER_SET(res_struct->mpll_multiplier) | |
208 | HSIO_MPLL_CFG_REF_CLKDIV2_SET(res_struct->ref_clkdiv2), |
209 | HSIO_MPLL_CFG_MPLL_MULTIPLIER | |
210 | HSIO_MPLL_CFG_REF_CLKDIV2, |
211 | macro->ctrl->regs, HSIO_MPLL_CFG(idx)); |
212 | |
213 | lan_rmw(HSIO_SD_CFG_RX_TERM_EN_SET(res_struct->rx_term_en), |
214 | HSIO_SD_CFG_RX_TERM_EN, |
215 | macro->ctrl->regs, HSIO_SD_CFG(idx)); |
216 | |
217 | lan_rmw(HSIO_MPLL_CFG_REF_SSP_EN_SET(1), |
218 | HSIO_MPLL_CFG_REF_SSP_EN, |
219 | macro->ctrl->regs, HSIO_MPLL_CFG(idx)); |
220 | |
221 | usleep_range(USEC_PER_MSEC, max: 2 * USEC_PER_MSEC); |
222 | |
223 | lan_rmw(HSIO_SD_CFG_PHY_RESET_SET(0), |
224 | HSIO_SD_CFG_PHY_RESET, |
225 | macro->ctrl->regs, HSIO_SD_CFG(idx)); |
226 | |
227 | usleep_range(USEC_PER_MSEC, max: 2 * USEC_PER_MSEC); |
228 | |
229 | lan_rmw(HSIO_MPLL_CFG_MPLL_EN_SET(1), |
230 | HSIO_MPLL_CFG_MPLL_EN, |
231 | macro->ctrl->regs, HSIO_MPLL_CFG(idx)); |
232 | |
233 | usleep_range(min: 7 * USEC_PER_MSEC, max: 8 * USEC_PER_MSEC); |
234 | |
235 | value = readl(addr: macro->ctrl->regs + lan_offset(HSIO_SD_STAT(idx))); |
236 | value = HSIO_SD_STAT_MPLL_STATE_GET(value); |
237 | if (value != 0x1) { |
238 | dev_err(macro->ctrl->dev, |
239 | "Unexpected sd_sd_stat[%u] mpll_state was 0x1 but is 0x%x\n" , |
240 | idx, value); |
241 | return -EIO; |
242 | } |
243 | |
244 | lan_rmw(HSIO_SD_CFG_TX_CM_EN_SET(1), |
245 | HSIO_SD_CFG_TX_CM_EN, |
246 | macro->ctrl->regs, HSIO_SD_CFG(idx)); |
247 | |
248 | usleep_range(USEC_PER_MSEC, max: 2 * USEC_PER_MSEC); |
249 | |
250 | value = readl(addr: macro->ctrl->regs + lan_offset(HSIO_SD_STAT(idx))); |
251 | value = HSIO_SD_STAT_TX_CM_STATE_GET(value); |
252 | if (value != 0x1) { |
253 | dev_err(macro->ctrl->dev, |
254 | "Unexpected sd_sd_stat[%u] tx_cm_state was 0x1 but is 0x%x\n" , |
255 | idx, value); |
256 | return -EIO; |
257 | } |
258 | |
259 | lan_rmw(HSIO_SD_CFG_RX_PLL_EN_SET(1) | |
260 | HSIO_SD_CFG_TX_EN_SET(1), |
261 | HSIO_SD_CFG_RX_PLL_EN | |
262 | HSIO_SD_CFG_TX_EN, |
263 | macro->ctrl->regs, HSIO_SD_CFG(idx)); |
264 | |
265 | usleep_range(USEC_PER_MSEC, max: 2 * USEC_PER_MSEC); |
266 | |
267 | /* Waiting for serdes 0 rx DPLL to lock... */ |
268 | value = readl(addr: macro->ctrl->regs + lan_offset(HSIO_SD_STAT(idx))); |
269 | value = HSIO_SD_STAT_RX_PLL_STATE_GET(value); |
270 | if (value != 0x1) { |
271 | dev_err(macro->ctrl->dev, |
272 | "Unexpected sd_sd_stat[%u] rx_pll_state was 0x1 but is 0x%x\n" , |
273 | idx, value); |
274 | return -EIO; |
275 | } |
276 | |
277 | /* Waiting for serdes 0 tx operational... */ |
278 | value = readl(addr: macro->ctrl->regs + lan_offset(HSIO_SD_STAT(idx))); |
279 | value = HSIO_SD_STAT_TX_STATE_GET(value); |
280 | if (value != 0x1) { |
281 | dev_err(macro->ctrl->dev, |
282 | "Unexpected sd_sd_stat[%u] tx_state was 0x1 but is 0x%x\n" , |
283 | idx, value); |
284 | return -EIO; |
285 | } |
286 | |
287 | lan_rmw(HSIO_SD_CFG_TX_DATA_EN_SET(1) | |
288 | HSIO_SD_CFG_RX_DATA_EN_SET(1), |
289 | HSIO_SD_CFG_TX_DATA_EN | |
290 | HSIO_SD_CFG_RX_DATA_EN, |
291 | macro->ctrl->regs, HSIO_SD_CFG(idx)); |
292 | |
293 | return 0; |
294 | } |
295 | |
296 | static int lan966x_sd6g40_get_conf_from_mode(struct serdes_macro *macro, |
297 | enum lan966x_sd6g40_mode f_mode, |
298 | bool ref125M, |
299 | struct lan966x_sd6g40_mode_args *ret_val) |
300 | { |
301 | switch (f_mode) { |
302 | case LAN966X_SD6G40_MODE_QSGMII: |
303 | ret_val->lane_10bit_sel = 0; |
304 | if (ref125M) { |
305 | ret_val->mpll_multiplier = 40; |
306 | ret_val->ref_clkdiv2 = 0x1; |
307 | ret_val->tx_rate = 0x0; |
308 | ret_val->rx_rate = 0x0; |
309 | } else { |
310 | ret_val->mpll_multiplier = 100; |
311 | ret_val->ref_clkdiv2 = 0x0; |
312 | ret_val->tx_rate = 0x0; |
313 | ret_val->rx_rate = 0x0; |
314 | } |
315 | break; |
316 | |
317 | case LAN966X_SD6G40_MODE_SGMII: |
318 | ret_val->lane_10bit_sel = 1; |
319 | if (ref125M) { |
320 | ret_val->mpll_multiplier = macro->speed == SPEED_2500 ? 50 : 40; |
321 | ret_val->ref_clkdiv2 = 0x1; |
322 | ret_val->tx_rate = macro->speed == SPEED_2500 ? 0x1 : 0x2; |
323 | ret_val->rx_rate = macro->speed == SPEED_2500 ? 0x1 : 0x2; |
324 | } else { |
325 | ret_val->mpll_multiplier = macro->speed == SPEED_2500 ? 125 : 100; |
326 | ret_val->ref_clkdiv2 = 0x0; |
327 | ret_val->tx_rate = macro->speed == SPEED_2500 ? 0x1 : 0x2; |
328 | ret_val->rx_rate = macro->speed == SPEED_2500 ? 0x1 : 0x2; |
329 | } |
330 | break; |
331 | |
332 | default: |
333 | return -EOPNOTSUPP; |
334 | } |
335 | |
336 | return 0; |
337 | } |
338 | |
339 | static int lan966x_calc_sd6g40_setup_lane(struct serdes_macro *macro, |
340 | struct lan966x_sd6g40_setup_args config, |
341 | struct lan966x_sd6g40_setup *ret_val) |
342 | { |
343 | struct lan966x_sd6g40_mode_args sd6g40_mode; |
344 | struct lan966x_sd6g40_mode_args *mode_args = &sd6g40_mode; |
345 | int ret; |
346 | |
347 | ret = lan966x_sd6g40_get_conf_from_mode(macro, f_mode: config.mode, |
348 | ref125M: config.refclk125M, ret_val: mode_args); |
349 | if (ret) |
350 | return ret; |
351 | |
352 | ret_val->lane_10bit_sel = mode_args->lane_10bit_sel; |
353 | ret_val->rx_rate = mode_args->rx_rate; |
354 | ret_val->tx_rate = mode_args->tx_rate; |
355 | ret_val->mpll_multiplier = mode_args->mpll_multiplier; |
356 | ret_val->ref_clkdiv2 = mode_args->ref_clkdiv2; |
357 | ret_val->rx_term_en = 0; |
358 | |
359 | if (config.tx2rx_loop == LAN966X_SD6G40_LTX2RX) |
360 | ret_val->lane_loopbk_en = 1; |
361 | else |
362 | ret_val->lane_loopbk_en = 0; |
363 | |
364 | ret_val->tx_invert = !!config.txinvert; |
365 | ret_val->rx_invert = !!config.rxinvert; |
366 | |
367 | return 0; |
368 | } |
369 | |
370 | static int lan966x_sd6g40_setup_lane(struct serdes_macro *macro, |
371 | struct lan966x_sd6g40_setup_args config, |
372 | u32 idx) |
373 | { |
374 | struct lan966x_sd6g40_setup calc_results = {}; |
375 | int ret; |
376 | |
377 | ret = lan966x_calc_sd6g40_setup_lane(macro, config, ret_val: &calc_results); |
378 | if (ret) |
379 | return ret; |
380 | |
381 | return lan966x_sd6g40_reg_cfg(macro, res_struct: &calc_results, idx); |
382 | } |
383 | |
384 | static int lan966x_sd6g40_setup(struct serdes_macro *macro, u32 idx, int mode) |
385 | { |
386 | struct lan966x_sd6g40_setup_args conf = {}; |
387 | |
388 | conf.refclk125M = macro->ctrl->ref125; |
389 | |
390 | if (mode == PHY_INTERFACE_MODE_QSGMII) |
391 | conf.mode = LAN966X_SD6G40_MODE_QSGMII; |
392 | else |
393 | conf.mode = LAN966X_SD6G40_MODE_SGMII; |
394 | |
395 | return lan966x_sd6g40_setup_lane(macro, config: conf, idx); |
396 | } |
397 | |
398 | static int lan966x_rgmii_setup(struct serdes_macro *macro, u32 idx, int mode) |
399 | { |
400 | bool tx_delay = false; |
401 | bool rx_delay = false; |
402 | |
403 | /* Configure RGMII */ |
404 | lan_rmw(HSIO_RGMII_CFG_RGMII_RX_RST_SET(0) | |
405 | HSIO_RGMII_CFG_RGMII_TX_RST_SET(0) | |
406 | HSIO_RGMII_CFG_TX_CLK_CFG_SET(macro->speed == SPEED_1000 ? 1 : |
407 | macro->speed == SPEED_100 ? 2 : |
408 | macro->speed == SPEED_10 ? 3 : 0), |
409 | HSIO_RGMII_CFG_RGMII_RX_RST | |
410 | HSIO_RGMII_CFG_RGMII_TX_RST | |
411 | HSIO_RGMII_CFG_TX_CLK_CFG, |
412 | macro->ctrl->regs, HSIO_RGMII_CFG(idx)); |
413 | |
414 | if (mode == PHY_INTERFACE_MODE_RGMII || |
415 | mode == PHY_INTERFACE_MODE_RGMII_TXID) |
416 | rx_delay = true; |
417 | |
418 | if (mode == PHY_INTERFACE_MODE_RGMII || |
419 | mode == PHY_INTERFACE_MODE_RGMII_RXID) |
420 | tx_delay = true; |
421 | |
422 | /* Setup DLL configuration */ |
423 | lan_rmw(HSIO_DLL_CFG_DLL_RST_SET(0) | |
424 | HSIO_DLL_CFG_DLL_ENA_SET(rx_delay), |
425 | HSIO_DLL_CFG_DLL_RST | |
426 | HSIO_DLL_CFG_DLL_ENA, |
427 | macro->ctrl->regs, HSIO_DLL_CFG(idx == 0 ? 0x0 : 0x2)); |
428 | |
429 | lan_rmw(HSIO_DLL_CFG_DELAY_ENA_SET(rx_delay), |
430 | HSIO_DLL_CFG_DELAY_ENA, |
431 | macro->ctrl->regs, HSIO_DLL_CFG(idx == 0 ? 0x0 : 0x2)); |
432 | |
433 | lan_rmw(HSIO_DLL_CFG_DLL_RST_SET(0) | |
434 | HSIO_DLL_CFG_DLL_ENA_SET(tx_delay), |
435 | HSIO_DLL_CFG_DLL_RST | |
436 | HSIO_DLL_CFG_DLL_ENA, |
437 | macro->ctrl->regs, HSIO_DLL_CFG(idx == 0 ? 0x1 : 0x3)); |
438 | |
439 | lan_rmw(HSIO_DLL_CFG_DELAY_ENA_SET(tx_delay), |
440 | HSIO_DLL_CFG_DELAY_ENA, |
441 | macro->ctrl->regs, HSIO_DLL_CFG(idx == 0 ? 0x1 : 0x3)); |
442 | |
443 | return 0; |
444 | } |
445 | |
446 | static int serdes_set_speed(struct phy *phy, int speed) |
447 | { |
448 | struct serdes_macro *macro = phy_get_drvdata(phy); |
449 | |
450 | if (!phy_interface_mode_is_rgmii(mode: macro->mode)) |
451 | return 0; |
452 | |
453 | macro->speed = speed; |
454 | lan966x_rgmii_setup(macro, idx: macro->idx - (SERDES6G_MAX + 1), mode: macro->mode); |
455 | |
456 | return 0; |
457 | } |
458 | |
459 | static int serdes_set_mode(struct phy *phy, enum phy_mode mode, int submode) |
460 | { |
461 | struct serdes_macro *macro = phy_get_drvdata(phy); |
462 | unsigned int i; |
463 | int val; |
464 | |
465 | /* As of now only PHY_MODE_ETHERNET is supported */ |
466 | if (mode != PHY_MODE_ETHERNET) |
467 | return -EOPNOTSUPP; |
468 | |
469 | if (submode == PHY_INTERFACE_MODE_2500BASEX) |
470 | macro->speed = SPEED_2500; |
471 | else |
472 | macro->speed = SPEED_1000; |
473 | |
474 | if (submode == PHY_INTERFACE_MODE_1000BASEX || |
475 | submode == PHY_INTERFACE_MODE_2500BASEX) |
476 | submode = PHY_INTERFACE_MODE_SGMII; |
477 | |
478 | if (submode == PHY_INTERFACE_MODE_QUSGMII) |
479 | submode = PHY_INTERFACE_MODE_QSGMII; |
480 | |
481 | for (i = 0; i < ARRAY_SIZE(lan966x_serdes_muxes); i++) { |
482 | if (macro->idx != lan966x_serdes_muxes[i].idx || |
483 | mode != lan966x_serdes_muxes[i].mode || |
484 | submode != lan966x_serdes_muxes[i].submode || |
485 | macro->port != lan966x_serdes_muxes[i].port) |
486 | continue; |
487 | |
488 | val = readl(addr: macro->ctrl->regs + lan_offset(HSIO_HW_CFG)); |
489 | val |= lan966x_serdes_muxes[i].mux; |
490 | lan_rmw(val, lan966x_serdes_muxes[i].mask, |
491 | macro->ctrl->regs, HSIO_HW_CFG); |
492 | |
493 | macro->mode = lan966x_serdes_muxes[i].submode; |
494 | |
495 | if (macro->idx < CU_MAX) |
496 | return 0; |
497 | |
498 | if (macro->idx < SERDES6G_MAX) |
499 | return lan966x_sd6g40_setup(macro, |
500 | idx: macro->idx - (CU_MAX + 1), |
501 | mode: macro->mode); |
502 | |
503 | if (macro->idx < RGMII_MAX) |
504 | return lan966x_rgmii_setup(macro, |
505 | idx: macro->idx - (SERDES6G_MAX + 1), |
506 | mode: macro->mode); |
507 | |
508 | return -EOPNOTSUPP; |
509 | } |
510 | |
511 | return -EINVAL; |
512 | } |
513 | |
514 | static const struct phy_ops serdes_ops = { |
515 | .set_mode = serdes_set_mode, |
516 | .set_speed = serdes_set_speed, |
517 | .owner = THIS_MODULE, |
518 | }; |
519 | |
520 | static struct phy *serdes_simple_xlate(struct device *dev, |
521 | const struct of_phandle_args *args) |
522 | { |
523 | struct serdes_ctrl *ctrl = dev_get_drvdata(dev); |
524 | unsigned int port, idx, i; |
525 | |
526 | if (args->args_count != 2) |
527 | return ERR_PTR(error: -EINVAL); |
528 | |
529 | port = args->args[0]; |
530 | idx = args->args[1]; |
531 | |
532 | for (i = 0; i < SERDES_MAX; i++) { |
533 | struct serdes_macro *macro = phy_get_drvdata(phy: ctrl->phys[i]); |
534 | |
535 | if (idx != macro->idx) |
536 | continue; |
537 | |
538 | macro->port = port; |
539 | return ctrl->phys[i]; |
540 | } |
541 | |
542 | return ERR_PTR(error: -ENODEV); |
543 | } |
544 | |
545 | static int serdes_phy_create(struct serdes_ctrl *ctrl, u8 idx, struct phy **phy) |
546 | { |
547 | struct serdes_macro *macro; |
548 | |
549 | *phy = devm_phy_create(dev: ctrl->dev, NULL, ops: &serdes_ops); |
550 | if (IS_ERR(ptr: *phy)) |
551 | return PTR_ERR(ptr: *phy); |
552 | |
553 | macro = devm_kzalloc(dev: ctrl->dev, size: sizeof(*macro), GFP_KERNEL); |
554 | if (!macro) |
555 | return -ENOMEM; |
556 | |
557 | macro->idx = idx; |
558 | macro->ctrl = ctrl; |
559 | macro->port = -1; |
560 | |
561 | phy_set_drvdata(phy: *phy, data: macro); |
562 | |
563 | return 0; |
564 | } |
565 | |
566 | static int serdes_probe(struct platform_device *pdev) |
567 | { |
568 | struct phy_provider *provider; |
569 | struct serdes_ctrl *ctrl; |
570 | void __iomem *hw_stat; |
571 | unsigned int i; |
572 | u32 val; |
573 | int ret; |
574 | |
575 | ctrl = devm_kzalloc(dev: &pdev->dev, size: sizeof(*ctrl), GFP_KERNEL); |
576 | if (!ctrl) |
577 | return -ENOMEM; |
578 | |
579 | ctrl->dev = &pdev->dev; |
580 | ctrl->regs = devm_platform_get_and_ioremap_resource(pdev, index: 0, NULL); |
581 | if (IS_ERR(ptr: ctrl->regs)) |
582 | return PTR_ERR(ptr: ctrl->regs); |
583 | |
584 | hw_stat = devm_platform_get_and_ioremap_resource(pdev, index: 1, NULL); |
585 | if (IS_ERR(ptr: hw_stat)) |
586 | return PTR_ERR(ptr: hw_stat); |
587 | |
588 | for (i = 0; i < SERDES_MAX; i++) { |
589 | ret = serdes_phy_create(ctrl, idx: i, phy: &ctrl->phys[i]); |
590 | if (ret) |
591 | return ret; |
592 | } |
593 | |
594 | val = readl(addr: hw_stat); |
595 | val = FIELD_GET(PLL_CONF_MASK, val); |
596 | ctrl->ref125 = (val == PLL_CONF_125MHZ || |
597 | val == PLL_CONF_SERDES_125MHZ); |
598 | |
599 | dev_set_drvdata(dev: &pdev->dev, data: ctrl); |
600 | |
601 | provider = devm_of_phy_provider_register(ctrl->dev, |
602 | serdes_simple_xlate); |
603 | |
604 | return PTR_ERR_OR_ZERO(ptr: provider); |
605 | } |
606 | |
607 | static const struct of_device_id serdes_ids[] = { |
608 | { .compatible = "microchip,lan966x-serdes" , }, |
609 | {}, |
610 | }; |
611 | MODULE_DEVICE_TABLE(of, serdes_ids); |
612 | |
613 | static struct platform_driver mscc_lan966x_serdes = { |
614 | .probe = serdes_probe, |
615 | .driver = { |
616 | .name = "microchip,lan966x-serdes" , |
617 | .of_match_table = of_match_ptr(serdes_ids), |
618 | }, |
619 | }; |
620 | |
621 | module_platform_driver(mscc_lan966x_serdes); |
622 | |
623 | MODULE_DESCRIPTION("Microchip lan966x switch serdes driver" ); |
624 | MODULE_AUTHOR("Horatiu Vultur <horatiu.vultur@microchip.com>" ); |
625 | MODULE_LICENSE("GPL v2" ); |
626 | |