1 | // SPDX-License-Identifier: (GPL-2.0 OR MIT) |
2 | /* |
3 | * Driver for Microsemi VSC85xx PHYs |
4 | * |
5 | * Author: Nagaraju Lakkaraju |
6 | * License: Dual MIT/GPL |
7 | * Copyright (c) 2016 Microsemi Corporation |
8 | */ |
9 | |
10 | #include <linux/firmware.h> |
11 | #include <linux/jiffies.h> |
12 | #include <linux/kernel.h> |
13 | #include <linux/module.h> |
14 | #include <linux/mdio.h> |
15 | #include <linux/mii.h> |
16 | #include <linux/phy.h> |
17 | #include <linux/of.h> |
18 | #include <linux/netdevice.h> |
19 | #include <dt-bindings/net/mscc-phy-vsc8531.h> |
20 | #include "mscc_serdes.h" |
21 | #include "mscc.h" |
22 | |
23 | static const struct vsc85xx_hw_stat vsc85xx_hw_stats[] = { |
24 | { |
25 | .string = "phy_receive_errors" , |
26 | .reg = MSCC_PHY_ERR_RX_CNT, |
27 | .page = MSCC_PHY_PAGE_STANDARD, |
28 | .mask = ERR_CNT_MASK, |
29 | }, { |
30 | .string = "phy_false_carrier" , |
31 | .reg = MSCC_PHY_ERR_FALSE_CARRIER_CNT, |
32 | .page = MSCC_PHY_PAGE_STANDARD, |
33 | .mask = ERR_CNT_MASK, |
34 | }, { |
35 | .string = "phy_cu_media_link_disconnect" , |
36 | .reg = MSCC_PHY_ERR_LINK_DISCONNECT_CNT, |
37 | .page = MSCC_PHY_PAGE_STANDARD, |
38 | .mask = ERR_CNT_MASK, |
39 | }, { |
40 | .string = "phy_cu_media_crc_good_count" , |
41 | .reg = MSCC_PHY_CU_MEDIA_CRC_VALID_CNT, |
42 | .page = MSCC_PHY_PAGE_EXTENDED, |
43 | .mask = VALID_CRC_CNT_CRC_MASK, |
44 | }, { |
45 | .string = "phy_cu_media_crc_error_count" , |
46 | .reg = MSCC_PHY_EXT_PHY_CNTL_4, |
47 | .page = MSCC_PHY_PAGE_EXTENDED, |
48 | .mask = ERR_CNT_MASK, |
49 | }, |
50 | }; |
51 | |
52 | static const struct vsc85xx_hw_stat vsc8584_hw_stats[] = { |
53 | { |
54 | .string = "phy_receive_errors" , |
55 | .reg = MSCC_PHY_ERR_RX_CNT, |
56 | .page = MSCC_PHY_PAGE_STANDARD, |
57 | .mask = ERR_CNT_MASK, |
58 | }, { |
59 | .string = "phy_false_carrier" , |
60 | .reg = MSCC_PHY_ERR_FALSE_CARRIER_CNT, |
61 | .page = MSCC_PHY_PAGE_STANDARD, |
62 | .mask = ERR_CNT_MASK, |
63 | }, { |
64 | .string = "phy_cu_media_link_disconnect" , |
65 | .reg = MSCC_PHY_ERR_LINK_DISCONNECT_CNT, |
66 | .page = MSCC_PHY_PAGE_STANDARD, |
67 | .mask = ERR_CNT_MASK, |
68 | }, { |
69 | .string = "phy_cu_media_crc_good_count" , |
70 | .reg = MSCC_PHY_CU_MEDIA_CRC_VALID_CNT, |
71 | .page = MSCC_PHY_PAGE_EXTENDED, |
72 | .mask = VALID_CRC_CNT_CRC_MASK, |
73 | }, { |
74 | .string = "phy_cu_media_crc_error_count" , |
75 | .reg = MSCC_PHY_EXT_PHY_CNTL_4, |
76 | .page = MSCC_PHY_PAGE_EXTENDED, |
77 | .mask = ERR_CNT_MASK, |
78 | }, { |
79 | .string = "phy_serdes_tx_good_pkt_count" , |
80 | .reg = MSCC_PHY_SERDES_TX_VALID_CNT, |
81 | .page = MSCC_PHY_PAGE_EXTENDED_3, |
82 | .mask = VALID_CRC_CNT_CRC_MASK, |
83 | }, { |
84 | .string = "phy_serdes_tx_bad_crc_count" , |
85 | .reg = MSCC_PHY_SERDES_TX_CRC_ERR_CNT, |
86 | .page = MSCC_PHY_PAGE_EXTENDED_3, |
87 | .mask = ERR_CNT_MASK, |
88 | }, { |
89 | .string = "phy_serdes_rx_good_pkt_count" , |
90 | .reg = MSCC_PHY_SERDES_RX_VALID_CNT, |
91 | .page = MSCC_PHY_PAGE_EXTENDED_3, |
92 | .mask = VALID_CRC_CNT_CRC_MASK, |
93 | }, { |
94 | .string = "phy_serdes_rx_bad_crc_count" , |
95 | .reg = MSCC_PHY_SERDES_RX_CRC_ERR_CNT, |
96 | .page = MSCC_PHY_PAGE_EXTENDED_3, |
97 | .mask = ERR_CNT_MASK, |
98 | }, |
99 | }; |
100 | |
101 | #if IS_ENABLED(CONFIG_OF_MDIO) |
102 | static const struct vsc8531_edge_rate_table edge_table[] = { |
103 | {MSCC_VDDMAC_3300, { 0, 2, 4, 7, 10, 17, 29, 53} }, |
104 | {MSCC_VDDMAC_2500, { 0, 3, 6, 10, 14, 23, 37, 63} }, |
105 | {MSCC_VDDMAC_1800, { 0, 5, 9, 16, 23, 35, 52, 76} }, |
106 | {MSCC_VDDMAC_1500, { 0, 6, 14, 21, 29, 42, 58, 77} }, |
107 | }; |
108 | #endif |
109 | |
110 | static const int vsc85xx_internal_delay[] = {200, 800, 1100, 1700, 2000, 2300, |
111 | 2600, 3400}; |
112 | |
113 | static int vsc85xx_phy_read_page(struct phy_device *phydev) |
114 | { |
115 | return __phy_read(phydev, MSCC_EXT_PAGE_ACCESS); |
116 | } |
117 | |
118 | static int vsc85xx_phy_write_page(struct phy_device *phydev, int page) |
119 | { |
120 | return __phy_write(phydev, MSCC_EXT_PAGE_ACCESS, val: page); |
121 | } |
122 | |
123 | static int vsc85xx_get_sset_count(struct phy_device *phydev) |
124 | { |
125 | struct vsc8531_private *priv = phydev->priv; |
126 | |
127 | if (!priv) |
128 | return 0; |
129 | |
130 | return priv->nstats; |
131 | } |
132 | |
133 | static void vsc85xx_get_strings(struct phy_device *phydev, u8 *data) |
134 | { |
135 | struct vsc8531_private *priv = phydev->priv; |
136 | int i; |
137 | |
138 | if (!priv) |
139 | return; |
140 | |
141 | for (i = 0; i < priv->nstats; i++) |
142 | strscpy(data + i * ETH_GSTRING_LEN, priv->hw_stats[i].string, |
143 | ETH_GSTRING_LEN); |
144 | } |
145 | |
146 | static u64 vsc85xx_get_stat(struct phy_device *phydev, int i) |
147 | { |
148 | struct vsc8531_private *priv = phydev->priv; |
149 | int val; |
150 | |
151 | val = phy_read_paged(phydev, page: priv->hw_stats[i].page, |
152 | regnum: priv->hw_stats[i].reg); |
153 | if (val < 0) |
154 | return U64_MAX; |
155 | |
156 | val = val & priv->hw_stats[i].mask; |
157 | priv->stats[i] += val; |
158 | |
159 | return priv->stats[i]; |
160 | } |
161 | |
162 | static void vsc85xx_get_stats(struct phy_device *phydev, |
163 | struct ethtool_stats *stats, u64 *data) |
164 | { |
165 | struct vsc8531_private *priv = phydev->priv; |
166 | int i; |
167 | |
168 | if (!priv) |
169 | return; |
170 | |
171 | for (i = 0; i < priv->nstats; i++) |
172 | data[i] = vsc85xx_get_stat(phydev, i); |
173 | } |
174 | |
175 | static int vsc85xx_led_cntl_set(struct phy_device *phydev, |
176 | u8 led_num, |
177 | u8 mode) |
178 | { |
179 | int rc; |
180 | u16 reg_val; |
181 | |
182 | mutex_lock(&phydev->lock); |
183 | reg_val = phy_read(phydev, MSCC_PHY_LED_MODE_SEL); |
184 | reg_val &= ~LED_MODE_SEL_MASK(led_num); |
185 | reg_val |= LED_MODE_SEL(led_num, (u16)mode); |
186 | rc = phy_write(phydev, MSCC_PHY_LED_MODE_SEL, val: reg_val); |
187 | mutex_unlock(lock: &phydev->lock); |
188 | |
189 | return rc; |
190 | } |
191 | |
192 | static int vsc85xx_mdix_get(struct phy_device *phydev, u8 *mdix) |
193 | { |
194 | u16 reg_val; |
195 | |
196 | reg_val = phy_read(phydev, MSCC_PHY_DEV_AUX_CNTL); |
197 | if (reg_val & HP_AUTO_MDIX_X_OVER_IND_MASK) |
198 | *mdix = ETH_TP_MDI_X; |
199 | else |
200 | *mdix = ETH_TP_MDI; |
201 | |
202 | return 0; |
203 | } |
204 | |
205 | static int vsc85xx_mdix_set(struct phy_device *phydev, u8 mdix) |
206 | { |
207 | int rc; |
208 | u16 reg_val; |
209 | |
210 | reg_val = phy_read(phydev, MSCC_PHY_BYPASS_CONTROL); |
211 | if (mdix == ETH_TP_MDI || mdix == ETH_TP_MDI_X) { |
212 | reg_val |= (DISABLE_PAIR_SWAP_CORR_MASK | |
213 | DISABLE_POLARITY_CORR_MASK | |
214 | DISABLE_HP_AUTO_MDIX_MASK); |
215 | } else { |
216 | reg_val &= ~(DISABLE_PAIR_SWAP_CORR_MASK | |
217 | DISABLE_POLARITY_CORR_MASK | |
218 | DISABLE_HP_AUTO_MDIX_MASK); |
219 | } |
220 | rc = phy_write(phydev, MSCC_PHY_BYPASS_CONTROL, val: reg_val); |
221 | if (rc) |
222 | return rc; |
223 | |
224 | reg_val = 0; |
225 | |
226 | if (mdix == ETH_TP_MDI) |
227 | reg_val = FORCE_MDI_CROSSOVER_MDI; |
228 | else if (mdix == ETH_TP_MDI_X) |
229 | reg_val = FORCE_MDI_CROSSOVER_MDIX; |
230 | |
231 | rc = phy_modify_paged(phydev, MSCC_PHY_PAGE_EXTENDED, |
232 | MSCC_PHY_EXT_MODE_CNTL, FORCE_MDI_CROSSOVER_MASK, |
233 | set: reg_val); |
234 | if (rc < 0) |
235 | return rc; |
236 | |
237 | return genphy_restart_aneg(phydev); |
238 | } |
239 | |
240 | static int vsc85xx_downshift_get(struct phy_device *phydev, u8 *count) |
241 | { |
242 | int reg_val; |
243 | |
244 | reg_val = phy_read_paged(phydev, MSCC_PHY_PAGE_EXTENDED, |
245 | MSCC_PHY_ACTIPHY_CNTL); |
246 | if (reg_val < 0) |
247 | return reg_val; |
248 | |
249 | reg_val &= DOWNSHIFT_CNTL_MASK; |
250 | if (!(reg_val & DOWNSHIFT_EN)) |
251 | *count = DOWNSHIFT_DEV_DISABLE; |
252 | else |
253 | *count = ((reg_val & ~DOWNSHIFT_EN) >> DOWNSHIFT_CNTL_POS) + 2; |
254 | |
255 | return 0; |
256 | } |
257 | |
258 | static int vsc85xx_downshift_set(struct phy_device *phydev, u8 count) |
259 | { |
260 | if (count == DOWNSHIFT_DEV_DEFAULT_COUNT) { |
261 | /* Default downshift count 3 (i.e. Bit3:2 = 0b01) */ |
262 | count = ((1 << DOWNSHIFT_CNTL_POS) | DOWNSHIFT_EN); |
263 | } else if (count > DOWNSHIFT_COUNT_MAX || count == 1) { |
264 | phydev_err(phydev, "Downshift count should be 2,3,4 or 5\n" ); |
265 | return -ERANGE; |
266 | } else if (count) { |
267 | /* Downshift count is either 2,3,4 or 5 */ |
268 | count = (((count - 2) << DOWNSHIFT_CNTL_POS) | DOWNSHIFT_EN); |
269 | } |
270 | |
271 | return phy_modify_paged(phydev, MSCC_PHY_PAGE_EXTENDED, |
272 | MSCC_PHY_ACTIPHY_CNTL, DOWNSHIFT_CNTL_MASK, |
273 | set: count); |
274 | } |
275 | |
276 | static int vsc85xx_wol_set(struct phy_device *phydev, |
277 | struct ethtool_wolinfo *wol) |
278 | { |
279 | const u8 *mac_addr = phydev->attached_dev->dev_addr; |
280 | int rc; |
281 | u16 reg_val; |
282 | u8 i; |
283 | u16 pwd[3] = {0, 0, 0}; |
284 | struct ethtool_wolinfo *wol_conf = wol; |
285 | |
286 | rc = phy_select_page(phydev, MSCC_PHY_PAGE_EXTENDED_2); |
287 | if (rc < 0) |
288 | return phy_restore_page(phydev, oldpage: rc, ret: rc); |
289 | |
290 | if (wol->wolopts & WAKE_MAGIC) { |
291 | /* Store the device address for the magic packet */ |
292 | for (i = 0; i < ARRAY_SIZE(pwd); i++) |
293 | pwd[i] = mac_addr[5 - (i * 2 + 1)] << 8 | |
294 | mac_addr[5 - i * 2]; |
295 | __phy_write(phydev, MSCC_PHY_WOL_LOWER_MAC_ADDR, val: pwd[0]); |
296 | __phy_write(phydev, MSCC_PHY_WOL_MID_MAC_ADDR, val: pwd[1]); |
297 | __phy_write(phydev, MSCC_PHY_WOL_UPPER_MAC_ADDR, val: pwd[2]); |
298 | } else { |
299 | __phy_write(phydev, MSCC_PHY_WOL_LOWER_MAC_ADDR, val: 0); |
300 | __phy_write(phydev, MSCC_PHY_WOL_MID_MAC_ADDR, val: 0); |
301 | __phy_write(phydev, MSCC_PHY_WOL_UPPER_MAC_ADDR, val: 0); |
302 | } |
303 | |
304 | if (wol_conf->wolopts & WAKE_MAGICSECURE) { |
305 | for (i = 0; i < ARRAY_SIZE(pwd); i++) |
306 | pwd[i] = wol_conf->sopass[5 - (i * 2 + 1)] << 8 | |
307 | wol_conf->sopass[5 - i * 2]; |
308 | __phy_write(phydev, MSCC_PHY_WOL_LOWER_PASSWD, val: pwd[0]); |
309 | __phy_write(phydev, MSCC_PHY_WOL_MID_PASSWD, val: pwd[1]); |
310 | __phy_write(phydev, MSCC_PHY_WOL_UPPER_PASSWD, val: pwd[2]); |
311 | } else { |
312 | __phy_write(phydev, MSCC_PHY_WOL_LOWER_PASSWD, val: 0); |
313 | __phy_write(phydev, MSCC_PHY_WOL_MID_PASSWD, val: 0); |
314 | __phy_write(phydev, MSCC_PHY_WOL_UPPER_PASSWD, val: 0); |
315 | } |
316 | |
317 | reg_val = __phy_read(phydev, MSCC_PHY_WOL_MAC_CONTROL); |
318 | if (wol_conf->wolopts & WAKE_MAGICSECURE) |
319 | reg_val |= SECURE_ON_ENABLE; |
320 | else |
321 | reg_val &= ~SECURE_ON_ENABLE; |
322 | __phy_write(phydev, MSCC_PHY_WOL_MAC_CONTROL, val: reg_val); |
323 | |
324 | rc = phy_restore_page(phydev, oldpage: rc, ret: rc > 0 ? 0 : rc); |
325 | if (rc < 0) |
326 | return rc; |
327 | |
328 | if (wol->wolopts & WAKE_MAGIC) { |
329 | /* Enable the WOL interrupt */ |
330 | reg_val = phy_read(phydev, MII_VSC85XX_INT_MASK); |
331 | reg_val |= MII_VSC85XX_INT_MASK_WOL; |
332 | rc = phy_write(phydev, MII_VSC85XX_INT_MASK, val: reg_val); |
333 | if (rc) |
334 | return rc; |
335 | } else { |
336 | /* Disable the WOL interrupt */ |
337 | reg_val = phy_read(phydev, MII_VSC85XX_INT_MASK); |
338 | reg_val &= (~MII_VSC85XX_INT_MASK_WOL); |
339 | rc = phy_write(phydev, MII_VSC85XX_INT_MASK, val: reg_val); |
340 | if (rc) |
341 | return rc; |
342 | } |
343 | /* Clear WOL iterrupt status */ |
344 | reg_val = phy_read(phydev, MII_VSC85XX_INT_STATUS); |
345 | |
346 | return 0; |
347 | } |
348 | |
349 | static void vsc85xx_wol_get(struct phy_device *phydev, |
350 | struct ethtool_wolinfo *wol) |
351 | { |
352 | int rc; |
353 | u16 reg_val; |
354 | u8 i; |
355 | u16 pwd[3] = {0, 0, 0}; |
356 | struct ethtool_wolinfo *wol_conf = wol; |
357 | |
358 | rc = phy_select_page(phydev, MSCC_PHY_PAGE_EXTENDED_2); |
359 | if (rc < 0) |
360 | goto out_restore_page; |
361 | |
362 | reg_val = __phy_read(phydev, MSCC_PHY_WOL_MAC_CONTROL); |
363 | if (reg_val & SECURE_ON_ENABLE) |
364 | wol_conf->wolopts |= WAKE_MAGICSECURE; |
365 | if (wol_conf->wolopts & WAKE_MAGICSECURE) { |
366 | pwd[0] = __phy_read(phydev, MSCC_PHY_WOL_LOWER_PASSWD); |
367 | pwd[1] = __phy_read(phydev, MSCC_PHY_WOL_MID_PASSWD); |
368 | pwd[2] = __phy_read(phydev, MSCC_PHY_WOL_UPPER_PASSWD); |
369 | for (i = 0; i < ARRAY_SIZE(pwd); i++) { |
370 | wol_conf->sopass[5 - i * 2] = pwd[i] & 0x00ff; |
371 | wol_conf->sopass[5 - (i * 2 + 1)] = (pwd[i] & 0xff00) |
372 | >> 8; |
373 | } |
374 | } |
375 | |
376 | out_restore_page: |
377 | phy_restore_page(phydev, oldpage: rc, ret: rc > 0 ? 0 : rc); |
378 | } |
379 | |
380 | #if IS_ENABLED(CONFIG_OF_MDIO) |
381 | static int vsc85xx_edge_rate_magic_get(struct phy_device *phydev) |
382 | { |
383 | u32 vdd, sd; |
384 | int i, j; |
385 | struct device *dev = &phydev->mdio.dev; |
386 | struct device_node *of_node = dev->of_node; |
387 | u8 sd_array_size = ARRAY_SIZE(edge_table[0].slowdown); |
388 | |
389 | if (!of_node) |
390 | return -ENODEV; |
391 | |
392 | if (of_property_read_u32(np: of_node, propname: "vsc8531,vddmac" , out_value: &vdd)) |
393 | vdd = MSCC_VDDMAC_3300; |
394 | |
395 | if (of_property_read_u32(np: of_node, propname: "vsc8531,edge-slowdown" , out_value: &sd)) |
396 | sd = 0; |
397 | |
398 | for (i = 0; i < ARRAY_SIZE(edge_table); i++) |
399 | if (edge_table[i].vddmac == vdd) |
400 | for (j = 0; j < sd_array_size; j++) |
401 | if (edge_table[i].slowdown[j] == sd) |
402 | return (sd_array_size - j - 1); |
403 | |
404 | return -EINVAL; |
405 | } |
406 | |
407 | static int vsc85xx_dt_led_mode_get(struct phy_device *phydev, |
408 | char *led, |
409 | u32 default_mode) |
410 | { |
411 | struct vsc8531_private *priv = phydev->priv; |
412 | struct device *dev = &phydev->mdio.dev; |
413 | struct device_node *of_node = dev->of_node; |
414 | u32 led_mode; |
415 | int err; |
416 | |
417 | if (!of_node) |
418 | return -ENODEV; |
419 | |
420 | led_mode = default_mode; |
421 | err = of_property_read_u32(np: of_node, propname: led, out_value: &led_mode); |
422 | if (!err && !(BIT(led_mode) & priv->supp_led_modes)) { |
423 | phydev_err(phydev, "DT %s invalid\n" , led); |
424 | return -EINVAL; |
425 | } |
426 | |
427 | return led_mode; |
428 | } |
429 | |
430 | #else |
431 | static int vsc85xx_edge_rate_magic_get(struct phy_device *phydev) |
432 | { |
433 | return 0; |
434 | } |
435 | |
436 | static int vsc85xx_dt_led_mode_get(struct phy_device *phydev, |
437 | char *led, |
438 | u8 default_mode) |
439 | { |
440 | return default_mode; |
441 | } |
442 | #endif /* CONFIG_OF_MDIO */ |
443 | |
444 | static int vsc85xx_dt_led_modes_get(struct phy_device *phydev, |
445 | u32 *default_mode) |
446 | { |
447 | struct vsc8531_private *priv = phydev->priv; |
448 | char led_dt_prop[28]; |
449 | int i, ret; |
450 | |
451 | for (i = 0; i < priv->nleds; i++) { |
452 | ret = sprintf(buf: led_dt_prop, fmt: "vsc8531,led-%d-mode" , i); |
453 | if (ret < 0) |
454 | return ret; |
455 | |
456 | ret = vsc85xx_dt_led_mode_get(phydev, led: led_dt_prop, |
457 | default_mode: default_mode[i]); |
458 | if (ret < 0) |
459 | return ret; |
460 | priv->leds_mode[i] = ret; |
461 | } |
462 | |
463 | return 0; |
464 | } |
465 | |
466 | static int vsc85xx_edge_rate_cntl_set(struct phy_device *phydev, u8 edge_rate) |
467 | { |
468 | int rc; |
469 | |
470 | mutex_lock(&phydev->lock); |
471 | rc = phy_modify_paged(phydev, MSCC_PHY_PAGE_EXTENDED_2, |
472 | MSCC_PHY_WOL_MAC_CONTROL, EDGE_RATE_CNTL_MASK, |
473 | set: edge_rate << EDGE_RATE_CNTL_POS); |
474 | mutex_unlock(lock: &phydev->lock); |
475 | |
476 | return rc; |
477 | } |
478 | |
479 | static int vsc85xx_mac_if_set(struct phy_device *phydev, |
480 | phy_interface_t interface) |
481 | { |
482 | int rc; |
483 | u16 reg_val; |
484 | |
485 | mutex_lock(&phydev->lock); |
486 | reg_val = phy_read(phydev, MSCC_PHY_EXT_PHY_CNTL_1); |
487 | reg_val &= ~(MAC_IF_SELECTION_MASK); |
488 | switch (interface) { |
489 | case PHY_INTERFACE_MODE_RGMII_TXID: |
490 | case PHY_INTERFACE_MODE_RGMII_RXID: |
491 | case PHY_INTERFACE_MODE_RGMII_ID: |
492 | case PHY_INTERFACE_MODE_RGMII: |
493 | reg_val |= (MAC_IF_SELECTION_RGMII << MAC_IF_SELECTION_POS); |
494 | break; |
495 | case PHY_INTERFACE_MODE_RMII: |
496 | reg_val |= (MAC_IF_SELECTION_RMII << MAC_IF_SELECTION_POS); |
497 | break; |
498 | case PHY_INTERFACE_MODE_MII: |
499 | case PHY_INTERFACE_MODE_GMII: |
500 | reg_val |= (MAC_IF_SELECTION_GMII << MAC_IF_SELECTION_POS); |
501 | break; |
502 | default: |
503 | rc = -EINVAL; |
504 | goto out_unlock; |
505 | } |
506 | rc = phy_write(phydev, MSCC_PHY_EXT_PHY_CNTL_1, val: reg_val); |
507 | if (rc) |
508 | goto out_unlock; |
509 | |
510 | rc = genphy_soft_reset(phydev); |
511 | |
512 | out_unlock: |
513 | mutex_unlock(lock: &phydev->lock); |
514 | |
515 | return rc; |
516 | } |
517 | |
518 | /* Set the RGMII RX and TX clock skews individually, according to the PHY |
519 | * interface type, to: |
520 | * * 0.2 ns (their default, and lowest, hardware value) if delays should |
521 | * not be enabled |
522 | * * 2.0 ns (which causes the data to be sampled at exactly half way between |
523 | * clock transitions at 1000 Mbps) if delays should be enabled |
524 | */ |
525 | static int vsc85xx_update_rgmii_cntl(struct phy_device *phydev, u32 rgmii_cntl, |
526 | u16 rgmii_rx_delay_mask, |
527 | u16 rgmii_tx_delay_mask) |
528 | { |
529 | u16 rgmii_rx_delay_pos = ffs(rgmii_rx_delay_mask) - 1; |
530 | u16 rgmii_tx_delay_pos = ffs(rgmii_tx_delay_mask) - 1; |
531 | int delay_size = ARRAY_SIZE(vsc85xx_internal_delay); |
532 | struct device *dev = &phydev->mdio.dev; |
533 | u16 reg_val = 0; |
534 | u16 mask = 0; |
535 | s32 rx_delay; |
536 | s32 tx_delay; |
537 | int rc = 0; |
538 | |
539 | /* For traffic to pass, the VSC8502 family needs the RX_CLK disable bit |
540 | * to be unset for all PHY modes, so do that as part of the paged |
541 | * register modification. |
542 | * For some family members (like VSC8530/31/40/41) this bit is reserved |
543 | * and read-only, and the RX clock is enabled by default. |
544 | */ |
545 | if (rgmii_cntl == VSC8502_RGMII_CNTL) |
546 | mask |= VSC8502_RGMII_RX_CLK_DISABLE; |
547 | |
548 | if (phy_interface_is_rgmii(phydev)) |
549 | mask |= rgmii_rx_delay_mask | rgmii_tx_delay_mask; |
550 | |
551 | rx_delay = phy_get_internal_delay(phydev, dev, delay_values: vsc85xx_internal_delay, |
552 | size: delay_size, is_rx: true); |
553 | if (rx_delay < 0) { |
554 | if (phydev->interface == PHY_INTERFACE_MODE_RGMII_RXID || |
555 | phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) |
556 | rx_delay = RGMII_CLK_DELAY_2_0_NS; |
557 | else |
558 | rx_delay = RGMII_CLK_DELAY_0_2_NS; |
559 | } |
560 | |
561 | tx_delay = phy_get_internal_delay(phydev, dev, delay_values: vsc85xx_internal_delay, |
562 | size: delay_size, is_rx: false); |
563 | if (tx_delay < 0) { |
564 | if (phydev->interface == PHY_INTERFACE_MODE_RGMII_TXID || |
565 | phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) |
566 | tx_delay = RGMII_CLK_DELAY_2_0_NS; |
567 | else |
568 | tx_delay = RGMII_CLK_DELAY_0_2_NS; |
569 | } |
570 | |
571 | reg_val |= rx_delay << rgmii_rx_delay_pos; |
572 | reg_val |= tx_delay << rgmii_tx_delay_pos; |
573 | |
574 | if (mask) |
575 | rc = phy_modify_paged(phydev, MSCC_PHY_PAGE_EXTENDED_2, |
576 | regnum: rgmii_cntl, mask, set: reg_val); |
577 | |
578 | return rc; |
579 | } |
580 | |
581 | static int vsc85xx_default_config(struct phy_device *phydev) |
582 | { |
583 | phydev->mdix_ctrl = ETH_TP_MDI_AUTO; |
584 | |
585 | return vsc85xx_update_rgmii_cntl(phydev, VSC8502_RGMII_CNTL, |
586 | VSC8502_RGMII_RX_DELAY_MASK, |
587 | VSC8502_RGMII_TX_DELAY_MASK); |
588 | } |
589 | |
590 | static int vsc85xx_get_tunable(struct phy_device *phydev, |
591 | struct ethtool_tunable *tuna, void *data) |
592 | { |
593 | switch (tuna->id) { |
594 | case ETHTOOL_PHY_DOWNSHIFT: |
595 | return vsc85xx_downshift_get(phydev, count: (u8 *)data); |
596 | default: |
597 | return -EINVAL; |
598 | } |
599 | } |
600 | |
601 | static int vsc85xx_set_tunable(struct phy_device *phydev, |
602 | struct ethtool_tunable *tuna, |
603 | const void *data) |
604 | { |
605 | switch (tuna->id) { |
606 | case ETHTOOL_PHY_DOWNSHIFT: |
607 | return vsc85xx_downshift_set(phydev, count: *(u8 *)data); |
608 | default: |
609 | return -EINVAL; |
610 | } |
611 | } |
612 | |
613 | /* mdiobus lock should be locked when using this function */ |
614 | static void vsc85xx_tr_write(struct phy_device *phydev, u16 addr, u32 val) |
615 | { |
616 | __phy_write(phydev, MSCC_PHY_TR_MSB, val: val >> 16); |
617 | __phy_write(phydev, MSCC_PHY_TR_LSB, val: val & GENMASK(15, 0)); |
618 | __phy_write(phydev, MSCC_PHY_TR_CNTL, TR_WRITE | TR_ADDR(addr)); |
619 | } |
620 | |
621 | static int vsc8531_pre_init_seq_set(struct phy_device *phydev) |
622 | { |
623 | int rc; |
624 | static const struct reg_val init_seq[] = { |
625 | {0x0f90, 0x00688980}, |
626 | {0x0696, 0x00000003}, |
627 | {0x07fa, 0x0050100f}, |
628 | {0x1686, 0x00000004}, |
629 | }; |
630 | unsigned int i; |
631 | int oldpage; |
632 | |
633 | rc = phy_modify_paged(phydev, MSCC_PHY_PAGE_STANDARD, |
634 | MSCC_PHY_EXT_CNTL_STATUS, SMI_BROADCAST_WR_EN, |
635 | SMI_BROADCAST_WR_EN); |
636 | if (rc < 0) |
637 | return rc; |
638 | rc = phy_modify_paged(phydev, MSCC_PHY_PAGE_TEST, |
639 | MSCC_PHY_TEST_PAGE_24, mask: 0, set: 0x0400); |
640 | if (rc < 0) |
641 | return rc; |
642 | rc = phy_modify_paged(phydev, MSCC_PHY_PAGE_TEST, |
643 | MSCC_PHY_TEST_PAGE_5, mask: 0x0a00, set: 0x0e00); |
644 | if (rc < 0) |
645 | return rc; |
646 | rc = phy_modify_paged(phydev, MSCC_PHY_PAGE_TEST, |
647 | MSCC_PHY_TEST_PAGE_8, TR_CLK_DISABLE, TR_CLK_DISABLE); |
648 | if (rc < 0) |
649 | return rc; |
650 | |
651 | mutex_lock(&phydev->lock); |
652 | oldpage = phy_select_page(phydev, MSCC_PHY_PAGE_TR); |
653 | if (oldpage < 0) |
654 | goto out_unlock; |
655 | |
656 | for (i = 0; i < ARRAY_SIZE(init_seq); i++) |
657 | vsc85xx_tr_write(phydev, addr: init_seq[i].reg, val: init_seq[i].val); |
658 | |
659 | out_unlock: |
660 | oldpage = phy_restore_page(phydev, oldpage, ret: oldpage); |
661 | mutex_unlock(lock: &phydev->lock); |
662 | |
663 | return oldpage; |
664 | } |
665 | |
666 | static int vsc85xx_eee_init_seq_set(struct phy_device *phydev) |
667 | { |
668 | static const struct reg_val init_eee[] = { |
669 | {0x0f82, 0x0012b00a}, |
670 | {0x1686, 0x00000004}, |
671 | {0x168c, 0x00d2c46f}, |
672 | {0x17a2, 0x00000620}, |
673 | {0x16a0, 0x00eeffdd}, |
674 | {0x16a6, 0x00071448}, |
675 | {0x16a4, 0x0013132f}, |
676 | {0x16a8, 0x00000000}, |
677 | {0x0ffc, 0x00c0a028}, |
678 | {0x0fe8, 0x0091b06c}, |
679 | {0x0fea, 0x00041600}, |
680 | {0x0f80, 0x00000af4}, |
681 | {0x0fec, 0x00901809}, |
682 | {0x0fee, 0x0000a6a1}, |
683 | {0x0ffe, 0x00b01007}, |
684 | {0x16b0, 0x00eeff00}, |
685 | {0x16b2, 0x00007000}, |
686 | {0x16b4, 0x00000814}, |
687 | }; |
688 | unsigned int i; |
689 | int oldpage; |
690 | |
691 | mutex_lock(&phydev->lock); |
692 | oldpage = phy_select_page(phydev, MSCC_PHY_PAGE_TR); |
693 | if (oldpage < 0) |
694 | goto out_unlock; |
695 | |
696 | for (i = 0; i < ARRAY_SIZE(init_eee); i++) |
697 | vsc85xx_tr_write(phydev, addr: init_eee[i].reg, val: init_eee[i].val); |
698 | |
699 | out_unlock: |
700 | oldpage = phy_restore_page(phydev, oldpage, ret: oldpage); |
701 | mutex_unlock(lock: &phydev->lock); |
702 | |
703 | return oldpage; |
704 | } |
705 | |
706 | /* phydev->bus->mdio_lock should be locked when using this function */ |
707 | int phy_base_write(struct phy_device *phydev, u32 regnum, u16 val) |
708 | { |
709 | if (unlikely(!mutex_is_locked(&phydev->mdio.bus->mdio_lock))) { |
710 | dev_err(&phydev->mdio.dev, "MDIO bus lock not held!\n" ); |
711 | dump_stack(); |
712 | } |
713 | |
714 | return __phy_package_write(phydev, addr_offset: VSC88XX_BASE_ADDR, regnum, val); |
715 | } |
716 | |
717 | /* phydev->bus->mdio_lock should be locked when using this function */ |
718 | int phy_base_read(struct phy_device *phydev, u32 regnum) |
719 | { |
720 | if (unlikely(!mutex_is_locked(&phydev->mdio.bus->mdio_lock))) { |
721 | dev_err(&phydev->mdio.dev, "MDIO bus lock not held!\n" ); |
722 | dump_stack(); |
723 | } |
724 | |
725 | return __phy_package_read(phydev, addr_offset: VSC88XX_BASE_ADDR, regnum); |
726 | } |
727 | |
728 | u32 vsc85xx_csr_read(struct phy_device *phydev, |
729 | enum csr_target target, u32 reg) |
730 | { |
731 | unsigned long deadline; |
732 | u32 val, val_l, val_h; |
733 | |
734 | phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_CSR_CNTL); |
735 | |
736 | /* CSR registers are grouped under different Target IDs. |
737 | * 6-bit Target_ID is split between MSCC_EXT_PAGE_CSR_CNTL_20 and |
738 | * MSCC_EXT_PAGE_CSR_CNTL_19 registers. |
739 | * Target_ID[5:2] maps to bits[3:0] of MSCC_EXT_PAGE_CSR_CNTL_20 |
740 | * and Target_ID[1:0] maps to bits[13:12] of MSCC_EXT_PAGE_CSR_CNTL_19. |
741 | */ |
742 | |
743 | /* Setup the Target ID */ |
744 | phy_base_write(phydev, MSCC_EXT_PAGE_CSR_CNTL_20, |
745 | MSCC_PHY_CSR_CNTL_20_TARGET(target >> 2)); |
746 | |
747 | if ((target >> 2 == 0x1) || (target >> 2 == 0x3)) |
748 | /* non-MACsec access */ |
749 | target &= 0x3; |
750 | else |
751 | target = 0; |
752 | |
753 | /* Trigger CSR Action - Read into the CSR's */ |
754 | phy_base_write(phydev, MSCC_EXT_PAGE_CSR_CNTL_19, |
755 | MSCC_PHY_CSR_CNTL_19_CMD | MSCC_PHY_CSR_CNTL_19_READ | |
756 | MSCC_PHY_CSR_CNTL_19_REG_ADDR(reg) | |
757 | MSCC_PHY_CSR_CNTL_19_TARGET(target)); |
758 | |
759 | /* Wait for register access*/ |
760 | deadline = jiffies + msecs_to_jiffies(PROC_CMD_NCOMPLETED_TIMEOUT_MS); |
761 | do { |
762 | usleep_range(min: 500, max: 1000); |
763 | val = phy_base_read(phydev, MSCC_EXT_PAGE_CSR_CNTL_19); |
764 | } while (time_before(jiffies, deadline) && |
765 | !(val & MSCC_PHY_CSR_CNTL_19_CMD)); |
766 | |
767 | if (!(val & MSCC_PHY_CSR_CNTL_19_CMD)) |
768 | return 0xffffffff; |
769 | |
770 | /* Read the Least Significant Word (LSW) (17) */ |
771 | val_l = phy_base_read(phydev, MSCC_EXT_PAGE_CSR_CNTL_17); |
772 | |
773 | /* Read the Most Significant Word (MSW) (18) */ |
774 | val_h = phy_base_read(phydev, MSCC_EXT_PAGE_CSR_CNTL_18); |
775 | |
776 | phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, |
777 | MSCC_PHY_PAGE_STANDARD); |
778 | |
779 | return (val_h << 16) | val_l; |
780 | } |
781 | |
782 | int vsc85xx_csr_write(struct phy_device *phydev, |
783 | enum csr_target target, u32 reg, u32 val) |
784 | { |
785 | unsigned long deadline; |
786 | |
787 | phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_CSR_CNTL); |
788 | |
789 | /* CSR registers are grouped under different Target IDs. |
790 | * 6-bit Target_ID is split between MSCC_EXT_PAGE_CSR_CNTL_20 and |
791 | * MSCC_EXT_PAGE_CSR_CNTL_19 registers. |
792 | * Target_ID[5:2] maps to bits[3:0] of MSCC_EXT_PAGE_CSR_CNTL_20 |
793 | * and Target_ID[1:0] maps to bits[13:12] of MSCC_EXT_PAGE_CSR_CNTL_19. |
794 | */ |
795 | |
796 | /* Setup the Target ID */ |
797 | phy_base_write(phydev, MSCC_EXT_PAGE_CSR_CNTL_20, |
798 | MSCC_PHY_CSR_CNTL_20_TARGET(target >> 2)); |
799 | |
800 | /* Write the Least Significant Word (LSW) (17) */ |
801 | phy_base_write(phydev, MSCC_EXT_PAGE_CSR_CNTL_17, val: (u16)val); |
802 | |
803 | /* Write the Most Significant Word (MSW) (18) */ |
804 | phy_base_write(phydev, MSCC_EXT_PAGE_CSR_CNTL_18, val: (u16)(val >> 16)); |
805 | |
806 | if ((target >> 2 == 0x1) || (target >> 2 == 0x3)) |
807 | /* non-MACsec access */ |
808 | target &= 0x3; |
809 | else |
810 | target = 0; |
811 | |
812 | /* Trigger CSR Action - Write into the CSR's */ |
813 | phy_base_write(phydev, MSCC_EXT_PAGE_CSR_CNTL_19, |
814 | MSCC_PHY_CSR_CNTL_19_CMD | |
815 | MSCC_PHY_CSR_CNTL_19_REG_ADDR(reg) | |
816 | MSCC_PHY_CSR_CNTL_19_TARGET(target)); |
817 | |
818 | /* Wait for register access */ |
819 | deadline = jiffies + msecs_to_jiffies(PROC_CMD_NCOMPLETED_TIMEOUT_MS); |
820 | do { |
821 | usleep_range(min: 500, max: 1000); |
822 | val = phy_base_read(phydev, MSCC_EXT_PAGE_CSR_CNTL_19); |
823 | } while (time_before(jiffies, deadline) && |
824 | !(val & MSCC_PHY_CSR_CNTL_19_CMD)); |
825 | |
826 | if (!(val & MSCC_PHY_CSR_CNTL_19_CMD)) |
827 | return -ETIMEDOUT; |
828 | |
829 | phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, |
830 | MSCC_PHY_PAGE_STANDARD); |
831 | |
832 | return 0; |
833 | } |
834 | |
835 | /* bus->mdio_lock should be locked when using this function */ |
836 | static void vsc8584_csr_write(struct phy_device *phydev, u16 addr, u32 val) |
837 | { |
838 | phy_base_write(phydev, MSCC_PHY_TR_MSB, val: val >> 16); |
839 | phy_base_write(phydev, MSCC_PHY_TR_LSB, val: val & GENMASK(15, 0)); |
840 | phy_base_write(phydev, MSCC_PHY_TR_CNTL, TR_WRITE | TR_ADDR(addr)); |
841 | } |
842 | |
843 | /* bus->mdio_lock should be locked when using this function */ |
844 | int vsc8584_cmd(struct phy_device *phydev, u16 val) |
845 | { |
846 | unsigned long deadline; |
847 | u16 reg_val; |
848 | |
849 | phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, |
850 | MSCC_PHY_PAGE_EXTENDED_GPIO); |
851 | |
852 | phy_base_write(phydev, MSCC_PHY_PROC_CMD, PROC_CMD_NCOMPLETED | val); |
853 | |
854 | deadline = jiffies + msecs_to_jiffies(PROC_CMD_NCOMPLETED_TIMEOUT_MS); |
855 | do { |
856 | reg_val = phy_base_read(phydev, MSCC_PHY_PROC_CMD); |
857 | } while (time_before(jiffies, deadline) && |
858 | (reg_val & PROC_CMD_NCOMPLETED) && |
859 | !(reg_val & PROC_CMD_FAILED)); |
860 | |
861 | phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_STANDARD); |
862 | |
863 | if (reg_val & PROC_CMD_FAILED) |
864 | return -EIO; |
865 | |
866 | if (reg_val & PROC_CMD_NCOMPLETED) |
867 | return -ETIMEDOUT; |
868 | |
869 | return 0; |
870 | } |
871 | |
872 | /* bus->mdio_lock should be locked when using this function */ |
873 | static int vsc8584_micro_deassert_reset(struct phy_device *phydev, |
874 | bool patch_en) |
875 | { |
876 | u32 enable, release; |
877 | |
878 | phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, |
879 | MSCC_PHY_PAGE_EXTENDED_GPIO); |
880 | |
881 | enable = RUN_FROM_INT_ROM | MICRO_CLK_EN | DW8051_CLK_EN; |
882 | release = MICRO_NSOFT_RESET | RUN_FROM_INT_ROM | DW8051_CLK_EN | |
883 | MICRO_CLK_EN; |
884 | |
885 | if (patch_en) { |
886 | enable |= MICRO_PATCH_EN; |
887 | release |= MICRO_PATCH_EN; |
888 | |
889 | /* Clear all patches */ |
890 | phy_base_write(phydev, MSCC_INT_MEM_CNTL, READ_RAM); |
891 | } |
892 | |
893 | /* Enable 8051 Micro clock; CLEAR/SET patch present; disable PRAM clock |
894 | * override and addr. auto-incr; operate at 125 MHz |
895 | */ |
896 | phy_base_write(phydev, MSCC_DW8051_CNTL_STATUS, val: enable); |
897 | /* Release 8051 Micro SW reset */ |
898 | phy_base_write(phydev, MSCC_DW8051_CNTL_STATUS, val: release); |
899 | |
900 | phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_STANDARD); |
901 | |
902 | return 0; |
903 | } |
904 | |
905 | /* bus->mdio_lock should be locked when using this function */ |
906 | static int vsc8584_micro_assert_reset(struct phy_device *phydev) |
907 | { |
908 | int ret; |
909 | u16 reg; |
910 | |
911 | ret = vsc8584_cmd(phydev, PROC_CMD_NOP); |
912 | if (ret) |
913 | return ret; |
914 | |
915 | phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, |
916 | MSCC_PHY_PAGE_EXTENDED_GPIO); |
917 | |
918 | reg = phy_base_read(phydev, MSCC_INT_MEM_CNTL); |
919 | reg &= ~EN_PATCH_RAM_TRAP_ADDR(4); |
920 | phy_base_write(phydev, MSCC_INT_MEM_CNTL, val: reg); |
921 | |
922 | phy_base_write(phydev, MSCC_TRAP_ROM_ADDR(4), val: 0x005b); |
923 | phy_base_write(phydev, MSCC_PATCH_RAM_ADDR(4), val: 0x005b); |
924 | |
925 | reg = phy_base_read(phydev, MSCC_INT_MEM_CNTL); |
926 | reg |= EN_PATCH_RAM_TRAP_ADDR(4); |
927 | phy_base_write(phydev, MSCC_INT_MEM_CNTL, val: reg); |
928 | |
929 | phy_base_write(phydev, MSCC_PHY_PROC_CMD, PROC_CMD_NOP); |
930 | |
931 | reg = phy_base_read(phydev, MSCC_DW8051_CNTL_STATUS); |
932 | reg &= ~MICRO_NSOFT_RESET; |
933 | phy_base_write(phydev, MSCC_DW8051_CNTL_STATUS, val: reg); |
934 | |
935 | phy_base_write(phydev, MSCC_PHY_PROC_CMD, PROC_CMD_MCB_ACCESS_MAC_CONF | |
936 | PROC_CMD_SGMII_PORT(0) | PROC_CMD_NO_MAC_CONF | |
937 | PROC_CMD_READ); |
938 | |
939 | reg = phy_base_read(phydev, MSCC_INT_MEM_CNTL); |
940 | reg &= ~EN_PATCH_RAM_TRAP_ADDR(4); |
941 | phy_base_write(phydev, MSCC_INT_MEM_CNTL, val: reg); |
942 | |
943 | phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_STANDARD); |
944 | |
945 | return 0; |
946 | } |
947 | |
948 | /* bus->mdio_lock should be locked when using this function */ |
949 | static int vsc8584_get_fw_crc(struct phy_device *phydev, u16 start, u16 size, |
950 | u16 *crc) |
951 | { |
952 | int ret; |
953 | |
954 | phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_EXTENDED); |
955 | |
956 | phy_base_write(phydev, MSCC_PHY_VERIPHY_CNTL_2, val: start); |
957 | phy_base_write(phydev, MSCC_PHY_VERIPHY_CNTL_3, val: size); |
958 | |
959 | /* Start Micro command */ |
960 | ret = vsc8584_cmd(phydev, PROC_CMD_CRC16); |
961 | if (ret) |
962 | goto out; |
963 | |
964 | phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_EXTENDED); |
965 | |
966 | *crc = phy_base_read(phydev, MSCC_PHY_VERIPHY_CNTL_2); |
967 | |
968 | out: |
969 | phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_STANDARD); |
970 | |
971 | return ret; |
972 | } |
973 | |
974 | /* bus->mdio_lock should be locked when using this function */ |
975 | static int vsc8584_patch_fw(struct phy_device *phydev, |
976 | const struct firmware *fw) |
977 | { |
978 | int i, ret; |
979 | |
980 | ret = vsc8584_micro_assert_reset(phydev); |
981 | if (ret) { |
982 | dev_err(&phydev->mdio.dev, |
983 | "%s: failed to assert reset of micro\n" , __func__); |
984 | return ret; |
985 | } |
986 | |
987 | phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, |
988 | MSCC_PHY_PAGE_EXTENDED_GPIO); |
989 | |
990 | /* Hold 8051 Micro in SW Reset, Enable auto incr address and patch clock |
991 | * Disable the 8051 Micro clock |
992 | */ |
993 | phy_base_write(phydev, MSCC_DW8051_CNTL_STATUS, RUN_FROM_INT_ROM | |
994 | AUTOINC_ADDR | PATCH_RAM_CLK | MICRO_CLK_EN | |
995 | MICRO_CLK_DIVIDE(2)); |
996 | phy_base_write(phydev, MSCC_INT_MEM_CNTL, READ_PRAM | INT_MEM_WRITE_EN | |
997 | INT_MEM_DATA(2)); |
998 | phy_base_write(phydev, MSCC_INT_MEM_ADDR, val: 0x0000); |
999 | |
1000 | for (i = 0; i < fw->size; i++) |
1001 | phy_base_write(phydev, MSCC_INT_MEM_CNTL, READ_PRAM | |
1002 | INT_MEM_WRITE_EN | fw->data[i]); |
1003 | |
1004 | /* Clear internal memory access */ |
1005 | phy_base_write(phydev, MSCC_INT_MEM_CNTL, READ_RAM); |
1006 | |
1007 | phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_STANDARD); |
1008 | |
1009 | return 0; |
1010 | } |
1011 | |
1012 | /* bus->mdio_lock should be locked when using this function */ |
1013 | static bool vsc8574_is_serdes_init(struct phy_device *phydev) |
1014 | { |
1015 | u16 reg; |
1016 | bool ret; |
1017 | |
1018 | phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, |
1019 | MSCC_PHY_PAGE_EXTENDED_GPIO); |
1020 | |
1021 | reg = phy_base_read(phydev, MSCC_TRAP_ROM_ADDR(1)); |
1022 | if (reg != 0x3eb7) { |
1023 | ret = false; |
1024 | goto out; |
1025 | } |
1026 | |
1027 | reg = phy_base_read(phydev, MSCC_PATCH_RAM_ADDR(1)); |
1028 | if (reg != 0x4012) { |
1029 | ret = false; |
1030 | goto out; |
1031 | } |
1032 | |
1033 | reg = phy_base_read(phydev, MSCC_INT_MEM_CNTL); |
1034 | if (reg != EN_PATCH_RAM_TRAP_ADDR(1)) { |
1035 | ret = false; |
1036 | goto out; |
1037 | } |
1038 | |
1039 | reg = phy_base_read(phydev, MSCC_DW8051_CNTL_STATUS); |
1040 | if ((MICRO_NSOFT_RESET | RUN_FROM_INT_ROM | DW8051_CLK_EN | |
1041 | MICRO_CLK_EN) != (reg & MSCC_DW8051_VLD_MASK)) { |
1042 | ret = false; |
1043 | goto out; |
1044 | } |
1045 | |
1046 | ret = true; |
1047 | out: |
1048 | phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_STANDARD); |
1049 | |
1050 | return ret; |
1051 | } |
1052 | |
1053 | /* bus->mdio_lock should be locked when using this function */ |
1054 | static int vsc8574_config_pre_init(struct phy_device *phydev) |
1055 | { |
1056 | static const struct reg_val pre_init1[] = { |
1057 | {0x0fae, 0x000401bd}, |
1058 | {0x0fac, 0x000f000f}, |
1059 | {0x17a0, 0x00a0f147}, |
1060 | {0x0fe4, 0x00052f54}, |
1061 | {0x1792, 0x0027303d}, |
1062 | {0x07fe, 0x00000704}, |
1063 | {0x0fe0, 0x00060150}, |
1064 | {0x0f82, 0x0012b00a}, |
1065 | {0x0f80, 0x00000d74}, |
1066 | {0x02e0, 0x00000012}, |
1067 | {0x03a2, 0x00050208}, |
1068 | {0x03b2, 0x00009186}, |
1069 | {0x0fb0, 0x000e3700}, |
1070 | {0x1688, 0x00049f81}, |
1071 | {0x0fd2, 0x0000ffff}, |
1072 | {0x168a, 0x00039fa2}, |
1073 | {0x1690, 0x0020640b}, |
1074 | {0x0258, 0x00002220}, |
1075 | {0x025a, 0x00002a20}, |
1076 | {0x025c, 0x00003060}, |
1077 | {0x025e, 0x00003fa0}, |
1078 | {0x03a6, 0x0000e0f0}, |
1079 | {0x0f92, 0x00001489}, |
1080 | {0x16a2, 0x00007000}, |
1081 | {0x16a6, 0x00071448}, |
1082 | {0x16a0, 0x00eeffdd}, |
1083 | {0x0fe8, 0x0091b06c}, |
1084 | {0x0fea, 0x00041600}, |
1085 | {0x16b0, 0x00eeff00}, |
1086 | {0x16b2, 0x00007000}, |
1087 | {0x16b4, 0x00000814}, |
1088 | {0x0f90, 0x00688980}, |
1089 | {0x03a4, 0x0000d8f0}, |
1090 | {0x0fc0, 0x00000400}, |
1091 | {0x07fa, 0x0050100f}, |
1092 | {0x0796, 0x00000003}, |
1093 | {0x07f8, 0x00c3ff98}, |
1094 | {0x0fa4, 0x0018292a}, |
1095 | {0x168c, 0x00d2c46f}, |
1096 | {0x17a2, 0x00000620}, |
1097 | {0x16a4, 0x0013132f}, |
1098 | {0x16a8, 0x00000000}, |
1099 | {0x0ffc, 0x00c0a028}, |
1100 | {0x0fec, 0x00901c09}, |
1101 | {0x0fee, 0x0004a6a1}, |
1102 | {0x0ffe, 0x00b01807}, |
1103 | }; |
1104 | static const struct reg_val pre_init2[] = { |
1105 | {0x0486, 0x0008a518}, |
1106 | {0x0488, 0x006dc696}, |
1107 | {0x048a, 0x00000912}, |
1108 | {0x048e, 0x00000db6}, |
1109 | {0x049c, 0x00596596}, |
1110 | {0x049e, 0x00000514}, |
1111 | {0x04a2, 0x00410280}, |
1112 | {0x04a4, 0x00000000}, |
1113 | {0x04a6, 0x00000000}, |
1114 | {0x04a8, 0x00000000}, |
1115 | {0x04aa, 0x00000000}, |
1116 | {0x04ae, 0x007df7dd}, |
1117 | {0x04b0, 0x006d95d4}, |
1118 | {0x04b2, 0x00492410}, |
1119 | }; |
1120 | struct device *dev = &phydev->mdio.dev; |
1121 | const struct firmware *fw; |
1122 | unsigned int i; |
1123 | u16 crc, reg; |
1124 | bool serdes_init; |
1125 | int ret; |
1126 | |
1127 | phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_STANDARD); |
1128 | |
1129 | /* all writes below are broadcasted to all PHYs in the same package */ |
1130 | reg = phy_base_read(phydev, MSCC_PHY_EXT_CNTL_STATUS); |
1131 | reg |= SMI_BROADCAST_WR_EN; |
1132 | phy_base_write(phydev, MSCC_PHY_EXT_CNTL_STATUS, val: reg); |
1133 | |
1134 | phy_base_write(phydev, MII_VSC85XX_INT_MASK, val: 0); |
1135 | |
1136 | /* The below register writes are tweaking analog and electrical |
1137 | * configuration that were determined through characterization by PHY |
1138 | * engineers. These don't mean anything more than "these are the best |
1139 | * values". |
1140 | */ |
1141 | phy_base_write(phydev, MSCC_PHY_EXT_PHY_CNTL_2, val: 0x0040); |
1142 | |
1143 | phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_TEST); |
1144 | |
1145 | phy_base_write(phydev, MSCC_PHY_TEST_PAGE_20, val: 0x4320); |
1146 | phy_base_write(phydev, MSCC_PHY_TEST_PAGE_24, val: 0x0c00); |
1147 | phy_base_write(phydev, MSCC_PHY_TEST_PAGE_9, val: 0x18ca); |
1148 | phy_base_write(phydev, MSCC_PHY_TEST_PAGE_5, val: 0x1b20); |
1149 | |
1150 | reg = phy_base_read(phydev, MSCC_PHY_TEST_PAGE_8); |
1151 | reg |= TR_CLK_DISABLE; |
1152 | phy_base_write(phydev, MSCC_PHY_TEST_PAGE_8, val: reg); |
1153 | |
1154 | phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_TR); |
1155 | |
1156 | for (i = 0; i < ARRAY_SIZE(pre_init1); i++) |
1157 | vsc8584_csr_write(phydev, addr: pre_init1[i].reg, val: pre_init1[i].val); |
1158 | |
1159 | phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_EXTENDED_2); |
1160 | |
1161 | phy_base_write(phydev, MSCC_PHY_CU_PMD_TX_CNTL, val: 0x028e); |
1162 | |
1163 | phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_TR); |
1164 | |
1165 | for (i = 0; i < ARRAY_SIZE(pre_init2); i++) |
1166 | vsc8584_csr_write(phydev, addr: pre_init2[i].reg, val: pre_init2[i].val); |
1167 | |
1168 | phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_TEST); |
1169 | |
1170 | reg = phy_base_read(phydev, MSCC_PHY_TEST_PAGE_8); |
1171 | reg &= ~TR_CLK_DISABLE; |
1172 | phy_base_write(phydev, MSCC_PHY_TEST_PAGE_8, val: reg); |
1173 | |
1174 | phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_STANDARD); |
1175 | |
1176 | /* end of write broadcasting */ |
1177 | reg = phy_base_read(phydev, MSCC_PHY_EXT_CNTL_STATUS); |
1178 | reg &= ~SMI_BROADCAST_WR_EN; |
1179 | phy_base_write(phydev, MSCC_PHY_EXT_CNTL_STATUS, val: reg); |
1180 | |
1181 | ret = request_firmware(fw: &fw, MSCC_VSC8574_REVB_INT8051_FW, device: dev); |
1182 | if (ret) { |
1183 | dev_err(dev, "failed to load firmware %s, ret: %d\n" , |
1184 | MSCC_VSC8574_REVB_INT8051_FW, ret); |
1185 | return ret; |
1186 | } |
1187 | |
1188 | /* Add one byte to size for the one added by the patch_fw function */ |
1189 | ret = vsc8584_get_fw_crc(phydev, |
1190 | MSCC_VSC8574_REVB_INT8051_FW_START_ADDR, |
1191 | size: fw->size + 1, crc: &crc); |
1192 | if (ret) |
1193 | goto out; |
1194 | |
1195 | if (crc == MSCC_VSC8574_REVB_INT8051_FW_CRC) { |
1196 | serdes_init = vsc8574_is_serdes_init(phydev); |
1197 | |
1198 | if (!serdes_init) { |
1199 | ret = vsc8584_micro_assert_reset(phydev); |
1200 | if (ret) { |
1201 | dev_err(dev, |
1202 | "%s: failed to assert reset of micro\n" , |
1203 | __func__); |
1204 | goto out; |
1205 | } |
1206 | } |
1207 | } else { |
1208 | dev_dbg(dev, "FW CRC is not the expected one, patching FW\n" ); |
1209 | |
1210 | serdes_init = false; |
1211 | |
1212 | if (vsc8584_patch_fw(phydev, fw)) |
1213 | dev_warn(dev, |
1214 | "failed to patch FW, expect non-optimal device\n" ); |
1215 | } |
1216 | |
1217 | if (!serdes_init) { |
1218 | phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, |
1219 | MSCC_PHY_PAGE_EXTENDED_GPIO); |
1220 | |
1221 | phy_base_write(phydev, MSCC_TRAP_ROM_ADDR(1), val: 0x3eb7); |
1222 | phy_base_write(phydev, MSCC_PATCH_RAM_ADDR(1), val: 0x4012); |
1223 | phy_base_write(phydev, MSCC_INT_MEM_CNTL, |
1224 | EN_PATCH_RAM_TRAP_ADDR(1)); |
1225 | |
1226 | vsc8584_micro_deassert_reset(phydev, patch_en: false); |
1227 | |
1228 | /* Add one byte to size for the one added by the patch_fw |
1229 | * function |
1230 | */ |
1231 | ret = vsc8584_get_fw_crc(phydev, |
1232 | MSCC_VSC8574_REVB_INT8051_FW_START_ADDR, |
1233 | size: fw->size + 1, crc: &crc); |
1234 | if (ret) |
1235 | goto out; |
1236 | |
1237 | if (crc != MSCC_VSC8574_REVB_INT8051_FW_CRC) |
1238 | dev_warn(dev, |
1239 | "FW CRC after patching is not the expected one, expect non-optimal device\n" ); |
1240 | } |
1241 | |
1242 | phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, |
1243 | MSCC_PHY_PAGE_EXTENDED_GPIO); |
1244 | |
1245 | ret = vsc8584_cmd(phydev, PROC_CMD_1588_DEFAULT_INIT | |
1246 | PROC_CMD_PHY_INIT); |
1247 | |
1248 | out: |
1249 | phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_STANDARD); |
1250 | |
1251 | release_firmware(fw); |
1252 | |
1253 | return ret; |
1254 | } |
1255 | |
1256 | /* Access LCPLL Cfg_2 */ |
1257 | static void vsc8584_pll5g_cfg2_wr(struct phy_device *phydev, |
1258 | bool disable_fsm) |
1259 | { |
1260 | u32 rd_dat; |
1261 | |
1262 | rd_dat = vsc85xx_csr_read(phydev, target: MACRO_CTRL, PHY_S6G_PLL5G_CFG2); |
1263 | rd_dat &= ~BIT(PHY_S6G_CFG2_FSM_DIS); |
1264 | rd_dat |= (disable_fsm << PHY_S6G_CFG2_FSM_DIS); |
1265 | vsc85xx_csr_write(phydev, target: MACRO_CTRL, PHY_S6G_PLL5G_CFG2, val: rd_dat); |
1266 | } |
1267 | |
1268 | /* trigger a read to the spcified MCB */ |
1269 | static int vsc8584_mcb_rd_trig(struct phy_device *phydev, |
1270 | u32 mcb_reg_addr, u8 mcb_slave_num) |
1271 | { |
1272 | u32 rd_dat = 0; |
1273 | |
1274 | /* read MCB */ |
1275 | vsc85xx_csr_write(phydev, target: MACRO_CTRL, reg: mcb_reg_addr, |
1276 | val: (0x40000000 | (1L << mcb_slave_num))); |
1277 | |
1278 | return read_poll_timeout(vsc85xx_csr_read, rd_dat, |
1279 | !(rd_dat & 0x40000000), |
1280 | 4000, 200000, 0, |
1281 | phydev, MACRO_CTRL, mcb_reg_addr); |
1282 | } |
1283 | |
1284 | /* trigger a write to the spcified MCB */ |
1285 | static int vsc8584_mcb_wr_trig(struct phy_device *phydev, |
1286 | u32 mcb_reg_addr, |
1287 | u8 mcb_slave_num) |
1288 | { |
1289 | u32 rd_dat = 0; |
1290 | |
1291 | /* write back MCB */ |
1292 | vsc85xx_csr_write(phydev, target: MACRO_CTRL, reg: mcb_reg_addr, |
1293 | val: (0x80000000 | (1L << mcb_slave_num))); |
1294 | |
1295 | return read_poll_timeout(vsc85xx_csr_read, rd_dat, |
1296 | !(rd_dat & 0x80000000), |
1297 | 4000, 200000, 0, |
1298 | phydev, MACRO_CTRL, mcb_reg_addr); |
1299 | } |
1300 | |
1301 | /* Sequence to Reset LCPLL for the VIPER and ELISE PHY */ |
1302 | static int vsc8584_pll5g_reset(struct phy_device *phydev) |
1303 | { |
1304 | bool dis_fsm; |
1305 | int ret = 0; |
1306 | |
1307 | ret = vsc8584_mcb_rd_trig(phydev, mcb_reg_addr: 0x11, mcb_slave_num: 0); |
1308 | if (ret < 0) |
1309 | goto done; |
1310 | dis_fsm = 1; |
1311 | |
1312 | /* Reset LCPLL */ |
1313 | vsc8584_pll5g_cfg2_wr(phydev, disable_fsm: dis_fsm); |
1314 | |
1315 | /* write back LCPLL MCB */ |
1316 | ret = vsc8584_mcb_wr_trig(phydev, mcb_reg_addr: 0x11, mcb_slave_num: 0); |
1317 | if (ret < 0) |
1318 | goto done; |
1319 | |
1320 | /* 10 mSec sleep while LCPLL is hold in reset */ |
1321 | usleep_range(min: 10000, max: 20000); |
1322 | |
1323 | /* read LCPLL MCB into CSRs */ |
1324 | ret = vsc8584_mcb_rd_trig(phydev, mcb_reg_addr: 0x11, mcb_slave_num: 0); |
1325 | if (ret < 0) |
1326 | goto done; |
1327 | dis_fsm = 0; |
1328 | |
1329 | /* Release the Reset of LCPLL */ |
1330 | vsc8584_pll5g_cfg2_wr(phydev, disable_fsm: dis_fsm); |
1331 | |
1332 | /* write back LCPLL MCB */ |
1333 | ret = vsc8584_mcb_wr_trig(phydev, mcb_reg_addr: 0x11, mcb_slave_num: 0); |
1334 | if (ret < 0) |
1335 | goto done; |
1336 | |
1337 | usleep_range(min: 110000, max: 200000); |
1338 | done: |
1339 | return ret; |
1340 | } |
1341 | |
1342 | /* bus->mdio_lock should be locked when using this function */ |
1343 | static int vsc8584_config_pre_init(struct phy_device *phydev) |
1344 | { |
1345 | static const struct reg_val pre_init1[] = { |
1346 | {0x07fa, 0x0050100f}, |
1347 | {0x1688, 0x00049f81}, |
1348 | {0x0f90, 0x00688980}, |
1349 | {0x03a4, 0x0000d8f0}, |
1350 | {0x0fc0, 0x00000400}, |
1351 | {0x0f82, 0x0012b002}, |
1352 | {0x1686, 0x00000004}, |
1353 | {0x168c, 0x00d2c46f}, |
1354 | {0x17a2, 0x00000620}, |
1355 | {0x16a0, 0x00eeffdd}, |
1356 | {0x16a6, 0x00071448}, |
1357 | {0x16a4, 0x0013132f}, |
1358 | {0x16a8, 0x00000000}, |
1359 | {0x0ffc, 0x00c0a028}, |
1360 | {0x0fe8, 0x0091b06c}, |
1361 | {0x0fea, 0x00041600}, |
1362 | {0x0f80, 0x00fffaff}, |
1363 | {0x0fec, 0x00901809}, |
1364 | {0x0ffe, 0x00b01007}, |
1365 | {0x16b0, 0x00eeff00}, |
1366 | {0x16b2, 0x00007000}, |
1367 | {0x16b4, 0x00000814}, |
1368 | }; |
1369 | static const struct reg_val pre_init2[] = { |
1370 | {0x0486, 0x0008a518}, |
1371 | {0x0488, 0x006dc696}, |
1372 | {0x048a, 0x00000912}, |
1373 | }; |
1374 | const struct firmware *fw; |
1375 | struct device *dev = &phydev->mdio.dev; |
1376 | unsigned int i; |
1377 | u16 crc, reg; |
1378 | int ret; |
1379 | |
1380 | ret = vsc8584_pll5g_reset(phydev); |
1381 | if (ret < 0) { |
1382 | dev_err(dev, "failed LCPLL reset, ret: %d\n" , ret); |
1383 | return ret; |
1384 | } |
1385 | |
1386 | phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_STANDARD); |
1387 | |
1388 | /* all writes below are broadcasted to all PHYs in the same package */ |
1389 | reg = phy_base_read(phydev, MSCC_PHY_EXT_CNTL_STATUS); |
1390 | reg |= SMI_BROADCAST_WR_EN; |
1391 | phy_base_write(phydev, MSCC_PHY_EXT_CNTL_STATUS, val: reg); |
1392 | |
1393 | phy_base_write(phydev, MII_VSC85XX_INT_MASK, val: 0); |
1394 | |
1395 | reg = phy_base_read(phydev, MSCC_PHY_BYPASS_CONTROL); |
1396 | reg |= PARALLEL_DET_IGNORE_ADVERTISED; |
1397 | phy_base_write(phydev, MSCC_PHY_BYPASS_CONTROL, val: reg); |
1398 | |
1399 | /* The below register writes are tweaking analog and electrical |
1400 | * configuration that were determined through characterization by PHY |
1401 | * engineers. These don't mean anything more than "these are the best |
1402 | * values". |
1403 | */ |
1404 | phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_EXTENDED_3); |
1405 | |
1406 | phy_base_write(phydev, MSCC_PHY_SERDES_TX_CRC_ERR_CNT, val: 0x2000); |
1407 | |
1408 | phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_TEST); |
1409 | |
1410 | phy_base_write(phydev, MSCC_PHY_TEST_PAGE_5, val: 0x1f20); |
1411 | |
1412 | reg = phy_base_read(phydev, MSCC_PHY_TEST_PAGE_8); |
1413 | reg |= TR_CLK_DISABLE; |
1414 | phy_base_write(phydev, MSCC_PHY_TEST_PAGE_8, val: reg); |
1415 | |
1416 | phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_TR); |
1417 | |
1418 | phy_base_write(phydev, MSCC_PHY_TR_CNTL, TR_WRITE | TR_ADDR(0x2fa4)); |
1419 | |
1420 | reg = phy_base_read(phydev, MSCC_PHY_TR_MSB); |
1421 | reg &= ~0x007f; |
1422 | reg |= 0x0019; |
1423 | phy_base_write(phydev, MSCC_PHY_TR_MSB, val: reg); |
1424 | |
1425 | phy_base_write(phydev, MSCC_PHY_TR_CNTL, TR_WRITE | TR_ADDR(0x0fa4)); |
1426 | |
1427 | for (i = 0; i < ARRAY_SIZE(pre_init1); i++) |
1428 | vsc8584_csr_write(phydev, addr: pre_init1[i].reg, val: pre_init1[i].val); |
1429 | |
1430 | phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_EXTENDED_2); |
1431 | |
1432 | phy_base_write(phydev, MSCC_PHY_CU_PMD_TX_CNTL, val: 0x028e); |
1433 | |
1434 | phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_TR); |
1435 | |
1436 | for (i = 0; i < ARRAY_SIZE(pre_init2); i++) |
1437 | vsc8584_csr_write(phydev, addr: pre_init2[i].reg, val: pre_init2[i].val); |
1438 | |
1439 | phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_TEST); |
1440 | |
1441 | reg = phy_base_read(phydev, MSCC_PHY_TEST_PAGE_8); |
1442 | reg &= ~TR_CLK_DISABLE; |
1443 | phy_base_write(phydev, MSCC_PHY_TEST_PAGE_8, val: reg); |
1444 | |
1445 | phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_STANDARD); |
1446 | |
1447 | /* end of write broadcasting */ |
1448 | reg = phy_base_read(phydev, MSCC_PHY_EXT_CNTL_STATUS); |
1449 | reg &= ~SMI_BROADCAST_WR_EN; |
1450 | phy_base_write(phydev, MSCC_PHY_EXT_CNTL_STATUS, val: reg); |
1451 | |
1452 | ret = request_firmware(fw: &fw, MSCC_VSC8584_REVB_INT8051_FW, device: dev); |
1453 | if (ret) { |
1454 | dev_err(dev, "failed to load firmware %s, ret: %d\n" , |
1455 | MSCC_VSC8584_REVB_INT8051_FW, ret); |
1456 | return ret; |
1457 | } |
1458 | |
1459 | /* Add one byte to size for the one added by the patch_fw function */ |
1460 | ret = vsc8584_get_fw_crc(phydev, |
1461 | MSCC_VSC8584_REVB_INT8051_FW_START_ADDR, |
1462 | size: fw->size + 1, crc: &crc); |
1463 | if (ret) |
1464 | goto out; |
1465 | |
1466 | if (crc != MSCC_VSC8584_REVB_INT8051_FW_CRC) { |
1467 | dev_dbg(dev, "FW CRC is not the expected one, patching FW\n" ); |
1468 | if (vsc8584_patch_fw(phydev, fw)) |
1469 | dev_warn(dev, |
1470 | "failed to patch FW, expect non-optimal device\n" ); |
1471 | } |
1472 | |
1473 | vsc8584_micro_deassert_reset(phydev, patch_en: false); |
1474 | |
1475 | /* Add one byte to size for the one added by the patch_fw function */ |
1476 | ret = vsc8584_get_fw_crc(phydev, |
1477 | MSCC_VSC8584_REVB_INT8051_FW_START_ADDR, |
1478 | size: fw->size + 1, crc: &crc); |
1479 | if (ret) |
1480 | goto out; |
1481 | |
1482 | if (crc != MSCC_VSC8584_REVB_INT8051_FW_CRC) |
1483 | dev_warn(dev, |
1484 | "FW CRC after patching is not the expected one, expect non-optimal device\n" ); |
1485 | |
1486 | ret = vsc8584_micro_assert_reset(phydev); |
1487 | if (ret) |
1488 | goto out; |
1489 | |
1490 | /* Write patch vector 0, to skip IB cal polling */ |
1491 | phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_EXTENDED_GPIO); |
1492 | reg = MSCC_ROM_TRAP_SERDES_6G_CFG; /* ROM address to trap, for patch vector 0 */ |
1493 | ret = phy_base_write(phydev, MSCC_TRAP_ROM_ADDR(1), val: reg); |
1494 | if (ret) |
1495 | goto out; |
1496 | |
1497 | reg = MSCC_RAM_TRAP_SERDES_6G_CFG; /* RAM address to jump to, when patch vector 0 enabled */ |
1498 | ret = phy_base_write(phydev, MSCC_PATCH_RAM_ADDR(1), val: reg); |
1499 | if (ret) |
1500 | goto out; |
1501 | |
1502 | reg = phy_base_read(phydev, MSCC_INT_MEM_CNTL); |
1503 | reg |= PATCH_VEC_ZERO_EN; /* bit 8, enable patch vector 0 */ |
1504 | ret = phy_base_write(phydev, MSCC_INT_MEM_CNTL, val: reg); |
1505 | if (ret) |
1506 | goto out; |
1507 | |
1508 | vsc8584_micro_deassert_reset(phydev, patch_en: true); |
1509 | |
1510 | out: |
1511 | phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_STANDARD); |
1512 | |
1513 | release_firmware(fw); |
1514 | |
1515 | return ret; |
1516 | } |
1517 | |
1518 | static void vsc8584_get_base_addr(struct phy_device *phydev) |
1519 | { |
1520 | struct vsc8531_private *vsc8531 = phydev->priv; |
1521 | u16 val, addr; |
1522 | |
1523 | phy_lock_mdio_bus(phydev); |
1524 | __phy_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_EXTENDED); |
1525 | |
1526 | addr = __phy_read(phydev, MSCC_PHY_EXT_PHY_CNTL_4); |
1527 | addr >>= PHY_CNTL_4_ADDR_POS; |
1528 | |
1529 | val = __phy_read(phydev, MSCC_PHY_ACTIPHY_CNTL); |
1530 | |
1531 | __phy_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_STANDARD); |
1532 | phy_unlock_mdio_bus(phydev); |
1533 | |
1534 | /* In the package, there are two pairs of PHYs (PHY0 + PHY2 and |
1535 | * PHY1 + PHY3). The first PHY of each pair (PHY0 and PHY1) is |
1536 | * the base PHY for timestamping operations. |
1537 | */ |
1538 | vsc8531->ts_base_addr = phydev->mdio.addr; |
1539 | vsc8531->ts_base_phy = addr; |
1540 | |
1541 | if (val & PHY_ADDR_REVERSED) { |
1542 | vsc8531->base_addr = phydev->mdio.addr + addr; |
1543 | if (addr > 1) { |
1544 | vsc8531->ts_base_addr += 2; |
1545 | vsc8531->ts_base_phy += 2; |
1546 | } |
1547 | } else { |
1548 | vsc8531->base_addr = phydev->mdio.addr - addr; |
1549 | if (addr > 1) { |
1550 | vsc8531->ts_base_addr -= 2; |
1551 | vsc8531->ts_base_phy -= 2; |
1552 | } |
1553 | } |
1554 | |
1555 | vsc8531->addr = addr; |
1556 | } |
1557 | |
1558 | static void vsc85xx_coma_mode_release(struct phy_device *phydev) |
1559 | { |
1560 | /* The coma mode (pin or reg) provides an optional feature that |
1561 | * may be used to control when the PHYs become active. |
1562 | * Alternatively the COMA_MODE pin may be connected low |
1563 | * so that the PHYs are fully active once out of reset. |
1564 | */ |
1565 | |
1566 | /* Enable output (mode=0) and write zero to it */ |
1567 | vsc85xx_phy_write_page(phydev, MSCC_PHY_PAGE_EXTENDED_GPIO); |
1568 | __phy_modify(phydev, MSCC_PHY_GPIO_CONTROL_2, |
1569 | MSCC_PHY_COMA_MODE | MSCC_PHY_COMA_OUTPUT, set: 0); |
1570 | vsc85xx_phy_write_page(phydev, MSCC_PHY_PAGE_STANDARD); |
1571 | } |
1572 | |
1573 | static int vsc8584_config_host_serdes(struct phy_device *phydev) |
1574 | { |
1575 | struct vsc8531_private *vsc8531 = phydev->priv; |
1576 | int ret; |
1577 | u16 val; |
1578 | |
1579 | ret = phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, |
1580 | MSCC_PHY_PAGE_EXTENDED_GPIO); |
1581 | if (ret) |
1582 | return ret; |
1583 | |
1584 | val = phy_base_read(phydev, MSCC_PHY_MAC_CFG_FASTLINK); |
1585 | val &= ~MAC_CFG_MASK; |
1586 | if (phydev->interface == PHY_INTERFACE_MODE_QSGMII) { |
1587 | val |= MAC_CFG_QSGMII; |
1588 | } else if (phydev->interface == PHY_INTERFACE_MODE_SGMII) { |
1589 | val |= MAC_CFG_SGMII; |
1590 | } else { |
1591 | ret = -EINVAL; |
1592 | return ret; |
1593 | } |
1594 | |
1595 | ret = phy_base_write(phydev, MSCC_PHY_MAC_CFG_FASTLINK, val); |
1596 | if (ret) |
1597 | return ret; |
1598 | |
1599 | ret = phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, |
1600 | MSCC_PHY_PAGE_STANDARD); |
1601 | if (ret) |
1602 | return ret; |
1603 | |
1604 | val = PROC_CMD_MCB_ACCESS_MAC_CONF | PROC_CMD_RST_CONF_PORT | |
1605 | PROC_CMD_READ_MOD_WRITE_PORT; |
1606 | if (phydev->interface == PHY_INTERFACE_MODE_QSGMII) |
1607 | val |= PROC_CMD_QSGMII_MAC; |
1608 | else |
1609 | val |= PROC_CMD_SGMII_MAC; |
1610 | |
1611 | ret = vsc8584_cmd(phydev, val); |
1612 | if (ret) |
1613 | return ret; |
1614 | |
1615 | usleep_range(min: 10000, max: 20000); |
1616 | |
1617 | /* Disable SerDes for 100Base-FX */ |
1618 | ret = vsc8584_cmd(phydev, PROC_CMD_FIBER_MEDIA_CONF | |
1619 | PROC_CMD_FIBER_PORT(vsc8531->addr) | |
1620 | PROC_CMD_FIBER_DISABLE | |
1621 | PROC_CMD_READ_MOD_WRITE_PORT | |
1622 | PROC_CMD_RST_CONF_PORT | PROC_CMD_FIBER_100BASE_FX); |
1623 | if (ret) |
1624 | return ret; |
1625 | |
1626 | /* Disable SerDes for 1000Base-X */ |
1627 | ret = vsc8584_cmd(phydev, PROC_CMD_FIBER_MEDIA_CONF | |
1628 | PROC_CMD_FIBER_PORT(vsc8531->addr) | |
1629 | PROC_CMD_FIBER_DISABLE | |
1630 | PROC_CMD_READ_MOD_WRITE_PORT | |
1631 | PROC_CMD_RST_CONF_PORT | PROC_CMD_FIBER_1000BASE_X); |
1632 | if (ret) |
1633 | return ret; |
1634 | |
1635 | return vsc85xx_sd6g_config_v2(phydev); |
1636 | } |
1637 | |
1638 | static int vsc8574_config_host_serdes(struct phy_device *phydev) |
1639 | { |
1640 | struct vsc8531_private *vsc8531 = phydev->priv; |
1641 | int ret; |
1642 | u16 val; |
1643 | |
1644 | ret = phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, |
1645 | MSCC_PHY_PAGE_EXTENDED_GPIO); |
1646 | if (ret) |
1647 | return ret; |
1648 | |
1649 | val = phy_base_read(phydev, MSCC_PHY_MAC_CFG_FASTLINK); |
1650 | val &= ~MAC_CFG_MASK; |
1651 | if (phydev->interface == PHY_INTERFACE_MODE_QSGMII) { |
1652 | val |= MAC_CFG_QSGMII; |
1653 | } else if (phydev->interface == PHY_INTERFACE_MODE_SGMII) { |
1654 | val |= MAC_CFG_SGMII; |
1655 | } else if (phy_interface_is_rgmii(phydev)) { |
1656 | val |= MAC_CFG_RGMII; |
1657 | } else { |
1658 | ret = -EINVAL; |
1659 | return ret; |
1660 | } |
1661 | |
1662 | ret = phy_base_write(phydev, MSCC_PHY_MAC_CFG_FASTLINK, val); |
1663 | if (ret) |
1664 | return ret; |
1665 | |
1666 | ret = phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, |
1667 | MSCC_PHY_PAGE_STANDARD); |
1668 | if (ret) |
1669 | return ret; |
1670 | |
1671 | if (!phy_interface_is_rgmii(phydev)) { |
1672 | val = PROC_CMD_MCB_ACCESS_MAC_CONF | PROC_CMD_RST_CONF_PORT | |
1673 | PROC_CMD_READ_MOD_WRITE_PORT; |
1674 | if (phydev->interface == PHY_INTERFACE_MODE_QSGMII) |
1675 | val |= PROC_CMD_QSGMII_MAC; |
1676 | else |
1677 | val |= PROC_CMD_SGMII_MAC; |
1678 | |
1679 | ret = vsc8584_cmd(phydev, val); |
1680 | if (ret) |
1681 | return ret; |
1682 | |
1683 | usleep_range(min: 10000, max: 20000); |
1684 | } |
1685 | |
1686 | /* Disable SerDes for 100Base-FX */ |
1687 | ret = vsc8584_cmd(phydev, PROC_CMD_FIBER_MEDIA_CONF | |
1688 | PROC_CMD_FIBER_PORT(vsc8531->addr) | |
1689 | PROC_CMD_FIBER_DISABLE | |
1690 | PROC_CMD_READ_MOD_WRITE_PORT | |
1691 | PROC_CMD_RST_CONF_PORT | PROC_CMD_FIBER_100BASE_FX); |
1692 | if (ret) |
1693 | return ret; |
1694 | |
1695 | /* Disable SerDes for 1000Base-X */ |
1696 | return vsc8584_cmd(phydev, PROC_CMD_FIBER_MEDIA_CONF | |
1697 | PROC_CMD_FIBER_PORT(vsc8531->addr) | |
1698 | PROC_CMD_FIBER_DISABLE | |
1699 | PROC_CMD_READ_MOD_WRITE_PORT | |
1700 | PROC_CMD_RST_CONF_PORT | PROC_CMD_FIBER_1000BASE_X); |
1701 | } |
1702 | |
1703 | static int vsc8584_config_init(struct phy_device *phydev) |
1704 | { |
1705 | struct vsc8531_private *vsc8531 = phydev->priv; |
1706 | int ret, i; |
1707 | u16 val; |
1708 | |
1709 | phydev->mdix_ctrl = ETH_TP_MDI_AUTO; |
1710 | |
1711 | phy_lock_mdio_bus(phydev); |
1712 | |
1713 | /* Some parts of the init sequence are identical for every PHY in the |
1714 | * package. Some parts are modifying the GPIO register bank which is a |
1715 | * set of registers that are affecting all PHYs, a few resetting the |
1716 | * microprocessor common to all PHYs. The CRC check responsible of the |
1717 | * checking the firmware within the 8051 microprocessor can only be |
1718 | * accessed via the PHY whose internal address in the package is 0. |
1719 | * All PHYs' interrupts mask register has to be zeroed before enabling |
1720 | * any PHY's interrupt in this register. |
1721 | * For all these reasons, we need to do the init sequence once and only |
1722 | * once whatever is the first PHY in the package that is initialized and |
1723 | * do the correct init sequence for all PHYs that are package-critical |
1724 | * in this pre-init function. |
1725 | */ |
1726 | if (phy_package_init_once(phydev)) { |
1727 | /* The following switch statement assumes that the lowest |
1728 | * nibble of the phy_id_mask is always 0. This works because |
1729 | * the lowest nibble of the PHY_ID's below are also 0. |
1730 | */ |
1731 | WARN_ON(phydev->drv->phy_id_mask & 0xf); |
1732 | |
1733 | switch (phydev->phy_id & phydev->drv->phy_id_mask) { |
1734 | case PHY_ID_VSC8504: |
1735 | case PHY_ID_VSC8552: |
1736 | case PHY_ID_VSC8572: |
1737 | case PHY_ID_VSC8574: |
1738 | ret = vsc8574_config_pre_init(phydev); |
1739 | if (ret) |
1740 | goto err; |
1741 | ret = vsc8574_config_host_serdes(phydev); |
1742 | if (ret) |
1743 | goto err; |
1744 | break; |
1745 | case PHY_ID_VSC856X: |
1746 | case PHY_ID_VSC8575: |
1747 | case PHY_ID_VSC8582: |
1748 | case PHY_ID_VSC8584: |
1749 | ret = vsc8584_config_pre_init(phydev); |
1750 | if (ret) |
1751 | goto err; |
1752 | ret = vsc8584_config_host_serdes(phydev); |
1753 | if (ret) |
1754 | goto err; |
1755 | vsc85xx_coma_mode_release(phydev); |
1756 | break; |
1757 | default: |
1758 | ret = -EINVAL; |
1759 | break; |
1760 | } |
1761 | |
1762 | if (ret) |
1763 | goto err; |
1764 | } |
1765 | |
1766 | phy_unlock_mdio_bus(phydev); |
1767 | |
1768 | ret = vsc8584_macsec_init(phydev); |
1769 | if (ret) |
1770 | return ret; |
1771 | |
1772 | ret = vsc8584_ptp_init(phydev); |
1773 | if (ret) |
1774 | return ret; |
1775 | |
1776 | val = phy_read(phydev, MSCC_PHY_EXT_PHY_CNTL_1); |
1777 | val &= ~(MEDIA_OP_MODE_MASK | VSC8584_MAC_IF_SELECTION_MASK); |
1778 | val |= (MEDIA_OP_MODE_COPPER << MEDIA_OP_MODE_POS) | |
1779 | (VSC8584_MAC_IF_SELECTION_SGMII << VSC8584_MAC_IF_SELECTION_POS); |
1780 | ret = phy_write(phydev, MSCC_PHY_EXT_PHY_CNTL_1, val); |
1781 | if (ret) |
1782 | return ret; |
1783 | |
1784 | ret = vsc85xx_update_rgmii_cntl(phydev, VSC8572_RGMII_CNTL, |
1785 | VSC8572_RGMII_RX_DELAY_MASK, |
1786 | VSC8572_RGMII_TX_DELAY_MASK); |
1787 | if (ret) |
1788 | return ret; |
1789 | |
1790 | ret = genphy_soft_reset(phydev); |
1791 | if (ret) |
1792 | return ret; |
1793 | |
1794 | for (i = 0; i < vsc8531->nleds; i++) { |
1795 | ret = vsc85xx_led_cntl_set(phydev, led_num: i, mode: vsc8531->leds_mode[i]); |
1796 | if (ret) |
1797 | return ret; |
1798 | } |
1799 | |
1800 | return 0; |
1801 | |
1802 | err: |
1803 | phy_unlock_mdio_bus(phydev); |
1804 | return ret; |
1805 | } |
1806 | |
1807 | static irqreturn_t vsc8584_handle_interrupt(struct phy_device *phydev) |
1808 | { |
1809 | irqreturn_t ret; |
1810 | int irq_status; |
1811 | |
1812 | irq_status = phy_read(phydev, MII_VSC85XX_INT_STATUS); |
1813 | if (irq_status < 0) |
1814 | return IRQ_NONE; |
1815 | |
1816 | /* Timestamping IRQ does not set a bit in the global INT_STATUS, so |
1817 | * irq_status would be 0. |
1818 | */ |
1819 | ret = vsc8584_handle_ts_interrupt(phydev); |
1820 | if (!(irq_status & MII_VSC85XX_INT_MASK_MASK)) |
1821 | return ret; |
1822 | |
1823 | if (irq_status & MII_VSC85XX_INT_MASK_EXT) |
1824 | vsc8584_handle_macsec_interrupt(phydev); |
1825 | |
1826 | if (irq_status & MII_VSC85XX_INT_MASK_LINK_CHG) |
1827 | phy_trigger_machine(phydev); |
1828 | |
1829 | return IRQ_HANDLED; |
1830 | } |
1831 | |
1832 | static int vsc85xx_config_init(struct phy_device *phydev) |
1833 | { |
1834 | int rc, i, phy_id; |
1835 | struct vsc8531_private *vsc8531 = phydev->priv; |
1836 | |
1837 | rc = vsc85xx_default_config(phydev); |
1838 | if (rc) |
1839 | return rc; |
1840 | |
1841 | rc = vsc85xx_mac_if_set(phydev, interface: phydev->interface); |
1842 | if (rc) |
1843 | return rc; |
1844 | |
1845 | rc = vsc85xx_edge_rate_cntl_set(phydev, edge_rate: vsc8531->rate_magic); |
1846 | if (rc) |
1847 | return rc; |
1848 | |
1849 | phy_id = phydev->drv->phy_id & phydev->drv->phy_id_mask; |
1850 | if (PHY_ID_VSC8531 == phy_id || PHY_ID_VSC8541 == phy_id || |
1851 | PHY_ID_VSC8530 == phy_id || PHY_ID_VSC8540 == phy_id) { |
1852 | rc = vsc8531_pre_init_seq_set(phydev); |
1853 | if (rc) |
1854 | return rc; |
1855 | } |
1856 | |
1857 | rc = vsc85xx_eee_init_seq_set(phydev); |
1858 | if (rc) |
1859 | return rc; |
1860 | |
1861 | for (i = 0; i < vsc8531->nleds; i++) { |
1862 | rc = vsc85xx_led_cntl_set(phydev, led_num: i, mode: vsc8531->leds_mode[i]); |
1863 | if (rc) |
1864 | return rc; |
1865 | } |
1866 | |
1867 | return 0; |
1868 | } |
1869 | |
1870 | static int __phy_write_mcb_s6g(struct phy_device *phydev, u32 reg, u8 mcb, |
1871 | u32 op) |
1872 | { |
1873 | unsigned long deadline; |
1874 | u32 val; |
1875 | int ret; |
1876 | |
1877 | ret = vsc85xx_csr_write(phydev, PHY_MCB_TARGET, reg, |
1878 | val: op | (1 << mcb)); |
1879 | if (ret) |
1880 | return -EINVAL; |
1881 | |
1882 | deadline = jiffies + msecs_to_jiffies(PROC_CMD_NCOMPLETED_TIMEOUT_MS); |
1883 | do { |
1884 | usleep_range(min: 500, max: 1000); |
1885 | val = vsc85xx_csr_read(phydev, PHY_MCB_TARGET, reg); |
1886 | |
1887 | if (val == 0xffffffff) |
1888 | return -EIO; |
1889 | |
1890 | } while (time_before(jiffies, deadline) && (val & op)); |
1891 | |
1892 | if (val & op) |
1893 | return -ETIMEDOUT; |
1894 | |
1895 | return 0; |
1896 | } |
1897 | |
1898 | /* Trigger a read to the specified MCB */ |
1899 | int phy_update_mcb_s6g(struct phy_device *phydev, u32 reg, u8 mcb) |
1900 | { |
1901 | return __phy_write_mcb_s6g(phydev, reg, mcb, PHY_MCB_S6G_READ); |
1902 | } |
1903 | |
1904 | /* Trigger a write to the specified MCB */ |
1905 | int phy_commit_mcb_s6g(struct phy_device *phydev, u32 reg, u8 mcb) |
1906 | { |
1907 | return __phy_write_mcb_s6g(phydev, reg, mcb, PHY_MCB_S6G_WRITE); |
1908 | } |
1909 | |
1910 | static int vsc8514_config_host_serdes(struct phy_device *phydev) |
1911 | { |
1912 | int ret; |
1913 | u16 val; |
1914 | |
1915 | ret = phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, |
1916 | MSCC_PHY_PAGE_EXTENDED_GPIO); |
1917 | if (ret) |
1918 | return ret; |
1919 | |
1920 | val = phy_base_read(phydev, MSCC_PHY_MAC_CFG_FASTLINK); |
1921 | val &= ~MAC_CFG_MASK; |
1922 | val |= MAC_CFG_QSGMII; |
1923 | ret = phy_base_write(phydev, MSCC_PHY_MAC_CFG_FASTLINK, val); |
1924 | if (ret) |
1925 | return ret; |
1926 | |
1927 | ret = phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, |
1928 | MSCC_PHY_PAGE_STANDARD); |
1929 | if (ret) |
1930 | return ret; |
1931 | |
1932 | ret = vsc8584_cmd(phydev, PROC_CMD_NOP); |
1933 | if (ret) |
1934 | return ret; |
1935 | |
1936 | ret = vsc8584_cmd(phydev, |
1937 | PROC_CMD_MCB_ACCESS_MAC_CONF | |
1938 | PROC_CMD_RST_CONF_PORT | |
1939 | PROC_CMD_READ_MOD_WRITE_PORT | PROC_CMD_QSGMII_MAC); |
1940 | if (ret) { |
1941 | dev_err(&phydev->mdio.dev, "%s: QSGMII error: %d\n" , |
1942 | __func__, ret); |
1943 | return ret; |
1944 | } |
1945 | |
1946 | /* Apply 6G SerDes FOJI Algorithm |
1947 | * Initial condition requirement: |
1948 | * 1. hold 8051 in reset |
1949 | * 2. disable patch vector 0, in order to allow IB cal poll during FoJi |
1950 | * 3. deassert 8051 reset after change patch vector status |
1951 | * 4. proceed with FoJi (vsc85xx_sd6g_config_v2) |
1952 | */ |
1953 | vsc8584_micro_assert_reset(phydev); |
1954 | val = phy_base_read(phydev, MSCC_INT_MEM_CNTL); |
1955 | /* clear bit 8, to disable patch vector 0 */ |
1956 | val &= ~PATCH_VEC_ZERO_EN; |
1957 | ret = phy_base_write(phydev, MSCC_INT_MEM_CNTL, val); |
1958 | /* Enable 8051 clock, don't set patch present, disable PRAM clock override */ |
1959 | vsc8584_micro_deassert_reset(phydev, patch_en: false); |
1960 | |
1961 | return vsc85xx_sd6g_config_v2(phydev); |
1962 | } |
1963 | |
1964 | static int vsc8514_config_pre_init(struct phy_device *phydev) |
1965 | { |
1966 | /* These are the settings to override the silicon default |
1967 | * values to handle hardware performance of PHY. They |
1968 | * are set at Power-On state and remain until PHY Reset. |
1969 | */ |
1970 | static const struct reg_val pre_init1[] = { |
1971 | {0x0f90, 0x00688980}, |
1972 | {0x0786, 0x00000003}, |
1973 | {0x07fa, 0x0050100f}, |
1974 | {0x0f82, 0x0012b002}, |
1975 | {0x1686, 0x00000004}, |
1976 | {0x168c, 0x00d2c46f}, |
1977 | {0x17a2, 0x00000620}, |
1978 | {0x16a0, 0x00eeffdd}, |
1979 | {0x16a6, 0x00071448}, |
1980 | {0x16a4, 0x0013132f}, |
1981 | {0x16a8, 0x00000000}, |
1982 | {0x0ffc, 0x00c0a028}, |
1983 | {0x0fe8, 0x0091b06c}, |
1984 | {0x0fea, 0x00041600}, |
1985 | {0x0f80, 0x00fffaff}, |
1986 | {0x0fec, 0x00901809}, |
1987 | {0x0ffe, 0x00b01007}, |
1988 | {0x16b0, 0x00eeff00}, |
1989 | {0x16b2, 0x00007000}, |
1990 | {0x16b4, 0x00000814}, |
1991 | }; |
1992 | struct device *dev = &phydev->mdio.dev; |
1993 | unsigned int i; |
1994 | u16 reg; |
1995 | int ret; |
1996 | |
1997 | ret = vsc8584_pll5g_reset(phydev); |
1998 | if (ret < 0) { |
1999 | dev_err(dev, "failed LCPLL reset, ret: %d\n" , ret); |
2000 | return ret; |
2001 | } |
2002 | |
2003 | phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_STANDARD); |
2004 | |
2005 | /* all writes below are broadcasted to all PHYs in the same package */ |
2006 | reg = phy_base_read(phydev, MSCC_PHY_EXT_CNTL_STATUS); |
2007 | reg |= SMI_BROADCAST_WR_EN; |
2008 | phy_base_write(phydev, MSCC_PHY_EXT_CNTL_STATUS, val: reg); |
2009 | |
2010 | phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_TEST); |
2011 | |
2012 | reg = phy_base_read(phydev, MSCC_PHY_TEST_PAGE_8); |
2013 | reg |= BIT(15); |
2014 | phy_base_write(phydev, MSCC_PHY_TEST_PAGE_8, val: reg); |
2015 | |
2016 | phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_TR); |
2017 | |
2018 | for (i = 0; i < ARRAY_SIZE(pre_init1); i++) |
2019 | vsc8584_csr_write(phydev, addr: pre_init1[i].reg, val: pre_init1[i].val); |
2020 | |
2021 | phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_TEST); |
2022 | |
2023 | reg = phy_base_read(phydev, MSCC_PHY_TEST_PAGE_8); |
2024 | reg &= ~BIT(15); |
2025 | phy_base_write(phydev, MSCC_PHY_TEST_PAGE_8, val: reg); |
2026 | |
2027 | phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, MSCC_PHY_PAGE_STANDARD); |
2028 | |
2029 | reg = phy_base_read(phydev, MSCC_PHY_EXT_CNTL_STATUS); |
2030 | reg &= ~SMI_BROADCAST_WR_EN; |
2031 | phy_base_write(phydev, MSCC_PHY_EXT_CNTL_STATUS, val: reg); |
2032 | |
2033 | /* Add pre-patching commands to: |
2034 | * 1. enable 8051 clock, operate 8051 clock at 125 MHz |
2035 | * instead of HW default 62.5MHz |
2036 | * 2. write patch vector 0, to skip IB cal polling executed |
2037 | * as part of the 0x80E0 ROM command |
2038 | */ |
2039 | vsc8584_micro_deassert_reset(phydev, patch_en: false); |
2040 | |
2041 | vsc8584_micro_assert_reset(phydev); |
2042 | phy_base_write(phydev, MSCC_EXT_PAGE_ACCESS, |
2043 | MSCC_PHY_PAGE_EXTENDED_GPIO); |
2044 | /* ROM address to trap, for patch vector 0 */ |
2045 | reg = MSCC_ROM_TRAP_SERDES_6G_CFG; |
2046 | ret = phy_base_write(phydev, MSCC_TRAP_ROM_ADDR(1), val: reg); |
2047 | if (ret) |
2048 | goto err; |
2049 | /* RAM address to jump to, when patch vector 0 enabled */ |
2050 | reg = MSCC_RAM_TRAP_SERDES_6G_CFG; |
2051 | ret = phy_base_write(phydev, MSCC_PATCH_RAM_ADDR(1), val: reg); |
2052 | if (ret) |
2053 | goto err; |
2054 | reg = phy_base_read(phydev, MSCC_INT_MEM_CNTL); |
2055 | reg |= PATCH_VEC_ZERO_EN; /* bit 8, enable patch vector 0 */ |
2056 | ret = phy_base_write(phydev, MSCC_INT_MEM_CNTL, val: reg); |
2057 | if (ret) |
2058 | goto err; |
2059 | |
2060 | /* Enable 8051 clock, don't set patch present |
2061 | * yet, disable PRAM clock override |
2062 | */ |
2063 | vsc8584_micro_deassert_reset(phydev, patch_en: false); |
2064 | return ret; |
2065 | err: |
2066 | /* restore 8051 and bail w error */ |
2067 | vsc8584_micro_deassert_reset(phydev, patch_en: false); |
2068 | return ret; |
2069 | } |
2070 | |
2071 | static int vsc8514_config_init(struct phy_device *phydev) |
2072 | { |
2073 | struct vsc8531_private *vsc8531 = phydev->priv; |
2074 | int ret, i; |
2075 | |
2076 | phydev->mdix_ctrl = ETH_TP_MDI_AUTO; |
2077 | |
2078 | phy_lock_mdio_bus(phydev); |
2079 | |
2080 | /* Some parts of the init sequence are identical for every PHY in the |
2081 | * package. Some parts are modifying the GPIO register bank which is a |
2082 | * set of registers that are affecting all PHYs, a few resetting the |
2083 | * microprocessor common to all PHYs. |
2084 | * All PHYs' interrupts mask register has to be zeroed before enabling |
2085 | * any PHY's interrupt in this register. |
2086 | * For all these reasons, we need to do the init sequence once and only |
2087 | * once whatever is the first PHY in the package that is initialized and |
2088 | * do the correct init sequence for all PHYs that are package-critical |
2089 | * in this pre-init function. |
2090 | */ |
2091 | if (phy_package_init_once(phydev)) { |
2092 | ret = vsc8514_config_pre_init(phydev); |
2093 | if (ret) |
2094 | goto err; |
2095 | ret = vsc8514_config_host_serdes(phydev); |
2096 | if (ret) |
2097 | goto err; |
2098 | vsc85xx_coma_mode_release(phydev); |
2099 | } |
2100 | |
2101 | phy_unlock_mdio_bus(phydev); |
2102 | |
2103 | ret = phy_modify(phydev, MSCC_PHY_EXT_PHY_CNTL_1, MEDIA_OP_MODE_MASK, |
2104 | MEDIA_OP_MODE_COPPER << MEDIA_OP_MODE_POS); |
2105 | |
2106 | if (ret) |
2107 | return ret; |
2108 | |
2109 | ret = genphy_soft_reset(phydev); |
2110 | |
2111 | if (ret) |
2112 | return ret; |
2113 | |
2114 | for (i = 0; i < vsc8531->nleds; i++) { |
2115 | ret = vsc85xx_led_cntl_set(phydev, led_num: i, mode: vsc8531->leds_mode[i]); |
2116 | if (ret) |
2117 | return ret; |
2118 | } |
2119 | |
2120 | return ret; |
2121 | |
2122 | err: |
2123 | phy_unlock_mdio_bus(phydev); |
2124 | return ret; |
2125 | } |
2126 | |
2127 | static int vsc85xx_ack_interrupt(struct phy_device *phydev) |
2128 | { |
2129 | int rc = 0; |
2130 | |
2131 | if (phydev->interrupts == PHY_INTERRUPT_ENABLED) |
2132 | rc = phy_read(phydev, MII_VSC85XX_INT_STATUS); |
2133 | |
2134 | return (rc < 0) ? rc : 0; |
2135 | } |
2136 | |
2137 | static int vsc85xx_config_intr(struct phy_device *phydev) |
2138 | { |
2139 | int rc; |
2140 | |
2141 | if (phydev->interrupts == PHY_INTERRUPT_ENABLED) { |
2142 | rc = vsc85xx_ack_interrupt(phydev); |
2143 | if (rc) |
2144 | return rc; |
2145 | |
2146 | vsc8584_config_macsec_intr(phydev); |
2147 | vsc8584_config_ts_intr(phydev); |
2148 | |
2149 | rc = phy_write(phydev, MII_VSC85XX_INT_MASK, |
2150 | MII_VSC85XX_INT_MASK_MASK); |
2151 | } else { |
2152 | rc = phy_write(phydev, MII_VSC85XX_INT_MASK, val: 0); |
2153 | if (rc < 0) |
2154 | return rc; |
2155 | rc = phy_read(phydev, MII_VSC85XX_INT_STATUS); |
2156 | if (rc < 0) |
2157 | return rc; |
2158 | |
2159 | rc = vsc85xx_ack_interrupt(phydev); |
2160 | } |
2161 | |
2162 | return rc; |
2163 | } |
2164 | |
2165 | static irqreturn_t vsc85xx_handle_interrupt(struct phy_device *phydev) |
2166 | { |
2167 | int irq_status; |
2168 | |
2169 | irq_status = phy_read(phydev, MII_VSC85XX_INT_STATUS); |
2170 | if (irq_status < 0) { |
2171 | phy_error(phydev); |
2172 | return IRQ_NONE; |
2173 | } |
2174 | |
2175 | if (!(irq_status & MII_VSC85XX_INT_MASK_MASK)) |
2176 | return IRQ_NONE; |
2177 | |
2178 | phy_trigger_machine(phydev); |
2179 | |
2180 | return IRQ_HANDLED; |
2181 | } |
2182 | |
2183 | static int vsc85xx_config_aneg(struct phy_device *phydev) |
2184 | { |
2185 | int rc; |
2186 | |
2187 | rc = vsc85xx_mdix_set(phydev, mdix: phydev->mdix_ctrl); |
2188 | if (rc < 0) |
2189 | return rc; |
2190 | |
2191 | return genphy_config_aneg(phydev); |
2192 | } |
2193 | |
2194 | static int vsc85xx_read_status(struct phy_device *phydev) |
2195 | { |
2196 | int rc; |
2197 | |
2198 | rc = vsc85xx_mdix_get(phydev, mdix: &phydev->mdix); |
2199 | if (rc < 0) |
2200 | return rc; |
2201 | |
2202 | return genphy_read_status(phydev); |
2203 | } |
2204 | |
2205 | static int vsc8514_probe(struct phy_device *phydev) |
2206 | { |
2207 | struct vsc8531_private *vsc8531; |
2208 | u32 default_mode[4] = {VSC8531_LINK_1000_ACTIVITY, |
2209 | VSC8531_LINK_100_ACTIVITY, VSC8531_LINK_ACTIVITY, |
2210 | VSC8531_DUPLEX_COLLISION}; |
2211 | |
2212 | vsc8531 = devm_kzalloc(dev: &phydev->mdio.dev, size: sizeof(*vsc8531), GFP_KERNEL); |
2213 | if (!vsc8531) |
2214 | return -ENOMEM; |
2215 | |
2216 | phydev->priv = vsc8531; |
2217 | |
2218 | vsc8584_get_base_addr(phydev); |
2219 | devm_phy_package_join(dev: &phydev->mdio.dev, phydev, |
2220 | base_addr: vsc8531->base_addr, priv_size: 0); |
2221 | |
2222 | vsc8531->nleds = 4; |
2223 | vsc8531->supp_led_modes = VSC85XX_SUPP_LED_MODES; |
2224 | vsc8531->hw_stats = vsc85xx_hw_stats; |
2225 | vsc8531->nstats = ARRAY_SIZE(vsc85xx_hw_stats); |
2226 | vsc8531->stats = devm_kcalloc(dev: &phydev->mdio.dev, n: vsc8531->nstats, |
2227 | size: sizeof(u64), GFP_KERNEL); |
2228 | if (!vsc8531->stats) |
2229 | return -ENOMEM; |
2230 | |
2231 | return vsc85xx_dt_led_modes_get(phydev, default_mode); |
2232 | } |
2233 | |
2234 | static int vsc8574_probe(struct phy_device *phydev) |
2235 | { |
2236 | struct vsc8531_private *vsc8531; |
2237 | u32 default_mode[4] = {VSC8531_LINK_1000_ACTIVITY, |
2238 | VSC8531_LINK_100_ACTIVITY, VSC8531_LINK_ACTIVITY, |
2239 | VSC8531_DUPLEX_COLLISION}; |
2240 | |
2241 | vsc8531 = devm_kzalloc(dev: &phydev->mdio.dev, size: sizeof(*vsc8531), GFP_KERNEL); |
2242 | if (!vsc8531) |
2243 | return -ENOMEM; |
2244 | |
2245 | phydev->priv = vsc8531; |
2246 | |
2247 | vsc8584_get_base_addr(phydev); |
2248 | devm_phy_package_join(dev: &phydev->mdio.dev, phydev, |
2249 | base_addr: vsc8531->base_addr, priv_size: 0); |
2250 | |
2251 | vsc8531->nleds = 4; |
2252 | vsc8531->supp_led_modes = VSC8584_SUPP_LED_MODES; |
2253 | vsc8531->hw_stats = vsc8584_hw_stats; |
2254 | vsc8531->nstats = ARRAY_SIZE(vsc8584_hw_stats); |
2255 | vsc8531->stats = devm_kcalloc(dev: &phydev->mdio.dev, n: vsc8531->nstats, |
2256 | size: sizeof(u64), GFP_KERNEL); |
2257 | if (!vsc8531->stats) |
2258 | return -ENOMEM; |
2259 | |
2260 | return vsc85xx_dt_led_modes_get(phydev, default_mode); |
2261 | } |
2262 | |
2263 | static int vsc8584_probe(struct phy_device *phydev) |
2264 | { |
2265 | struct vsc8531_private *vsc8531; |
2266 | u32 default_mode[4] = {VSC8531_LINK_1000_ACTIVITY, |
2267 | VSC8531_LINK_100_ACTIVITY, VSC8531_LINK_ACTIVITY, |
2268 | VSC8531_DUPLEX_COLLISION}; |
2269 | int ret; |
2270 | |
2271 | if ((phydev->phy_id & MSCC_DEV_REV_MASK) != VSC8584_REVB) { |
2272 | dev_err(&phydev->mdio.dev, "Only VSC8584 revB is supported.\n" ); |
2273 | return -ENOTSUPP; |
2274 | } |
2275 | |
2276 | vsc8531 = devm_kzalloc(dev: &phydev->mdio.dev, size: sizeof(*vsc8531), GFP_KERNEL); |
2277 | if (!vsc8531) |
2278 | return -ENOMEM; |
2279 | |
2280 | phydev->priv = vsc8531; |
2281 | |
2282 | vsc8584_get_base_addr(phydev); |
2283 | devm_phy_package_join(dev: &phydev->mdio.dev, phydev, base_addr: vsc8531->base_addr, |
2284 | priv_size: sizeof(struct vsc85xx_shared_private)); |
2285 | |
2286 | vsc8531->nleds = 4; |
2287 | vsc8531->supp_led_modes = VSC8584_SUPP_LED_MODES; |
2288 | vsc8531->hw_stats = vsc8584_hw_stats; |
2289 | vsc8531->nstats = ARRAY_SIZE(vsc8584_hw_stats); |
2290 | vsc8531->stats = devm_kcalloc(dev: &phydev->mdio.dev, n: vsc8531->nstats, |
2291 | size: sizeof(u64), GFP_KERNEL); |
2292 | if (!vsc8531->stats) |
2293 | return -ENOMEM; |
2294 | |
2295 | if (phy_package_probe_once(phydev)) { |
2296 | ret = vsc8584_ptp_probe_once(phydev); |
2297 | if (ret) |
2298 | return ret; |
2299 | } |
2300 | |
2301 | ret = vsc8584_ptp_probe(phydev); |
2302 | if (ret) |
2303 | return ret; |
2304 | |
2305 | return vsc85xx_dt_led_modes_get(phydev, default_mode); |
2306 | } |
2307 | |
2308 | static int vsc85xx_probe(struct phy_device *phydev) |
2309 | { |
2310 | struct vsc8531_private *vsc8531; |
2311 | int rate_magic; |
2312 | u32 default_mode[2] = {VSC8531_LINK_1000_ACTIVITY, |
2313 | VSC8531_LINK_100_ACTIVITY}; |
2314 | |
2315 | rate_magic = vsc85xx_edge_rate_magic_get(phydev); |
2316 | if (rate_magic < 0) |
2317 | return rate_magic; |
2318 | |
2319 | vsc8531 = devm_kzalloc(dev: &phydev->mdio.dev, size: sizeof(*vsc8531), GFP_KERNEL); |
2320 | if (!vsc8531) |
2321 | return -ENOMEM; |
2322 | |
2323 | phydev->priv = vsc8531; |
2324 | |
2325 | vsc8531->rate_magic = rate_magic; |
2326 | vsc8531->nleds = 2; |
2327 | vsc8531->supp_led_modes = VSC85XX_SUPP_LED_MODES; |
2328 | vsc8531->hw_stats = vsc85xx_hw_stats; |
2329 | vsc8531->nstats = ARRAY_SIZE(vsc85xx_hw_stats); |
2330 | vsc8531->stats = devm_kcalloc(dev: &phydev->mdio.dev, n: vsc8531->nstats, |
2331 | size: sizeof(u64), GFP_KERNEL); |
2332 | if (!vsc8531->stats) |
2333 | return -ENOMEM; |
2334 | |
2335 | return vsc85xx_dt_led_modes_get(phydev, default_mode); |
2336 | } |
2337 | |
2338 | /* Microsemi VSC85xx PHYs */ |
2339 | static struct phy_driver vsc85xx_driver[] = { |
2340 | { |
2341 | .phy_id = PHY_ID_VSC8501, |
2342 | .name = "Microsemi GE VSC8501 SyncE" , |
2343 | .phy_id_mask = 0xfffffff0, |
2344 | /* PHY_BASIC_FEATURES */ |
2345 | .soft_reset = &genphy_soft_reset, |
2346 | .config_init = &vsc85xx_config_init, |
2347 | .config_aneg = &vsc85xx_config_aneg, |
2348 | .read_status = &vsc85xx_read_status, |
2349 | .handle_interrupt = vsc85xx_handle_interrupt, |
2350 | .config_intr = &vsc85xx_config_intr, |
2351 | .suspend = &genphy_suspend, |
2352 | .resume = &genphy_resume, |
2353 | .probe = &vsc85xx_probe, |
2354 | .set_wol = &vsc85xx_wol_set, |
2355 | .get_wol = &vsc85xx_wol_get, |
2356 | .get_tunable = &vsc85xx_get_tunable, |
2357 | .set_tunable = &vsc85xx_set_tunable, |
2358 | .read_page = &vsc85xx_phy_read_page, |
2359 | .write_page = &vsc85xx_phy_write_page, |
2360 | .get_sset_count = &vsc85xx_get_sset_count, |
2361 | .get_strings = &vsc85xx_get_strings, |
2362 | .get_stats = &vsc85xx_get_stats, |
2363 | }, |
2364 | { |
2365 | .phy_id = PHY_ID_VSC8502, |
2366 | .name = "Microsemi GE VSC8502 SyncE" , |
2367 | .phy_id_mask = 0xfffffff0, |
2368 | /* PHY_BASIC_FEATURES */ |
2369 | .soft_reset = &genphy_soft_reset, |
2370 | .config_init = &vsc85xx_config_init, |
2371 | .config_aneg = &vsc85xx_config_aneg, |
2372 | .read_status = &vsc85xx_read_status, |
2373 | .handle_interrupt = vsc85xx_handle_interrupt, |
2374 | .config_intr = &vsc85xx_config_intr, |
2375 | .suspend = &genphy_suspend, |
2376 | .resume = &genphy_resume, |
2377 | .probe = &vsc85xx_probe, |
2378 | .set_wol = &vsc85xx_wol_set, |
2379 | .get_wol = &vsc85xx_wol_get, |
2380 | .get_tunable = &vsc85xx_get_tunable, |
2381 | .set_tunable = &vsc85xx_set_tunable, |
2382 | .read_page = &vsc85xx_phy_read_page, |
2383 | .write_page = &vsc85xx_phy_write_page, |
2384 | .get_sset_count = &vsc85xx_get_sset_count, |
2385 | .get_strings = &vsc85xx_get_strings, |
2386 | .get_stats = &vsc85xx_get_stats, |
2387 | }, |
2388 | { |
2389 | .phy_id = PHY_ID_VSC8504, |
2390 | .name = "Microsemi GE VSC8504 SyncE" , |
2391 | .phy_id_mask = 0xfffffff0, |
2392 | /* PHY_GBIT_FEATURES */ |
2393 | .soft_reset = &genphy_soft_reset, |
2394 | .config_init = &vsc8584_config_init, |
2395 | .config_aneg = &vsc85xx_config_aneg, |
2396 | .aneg_done = &genphy_aneg_done, |
2397 | .read_status = &vsc85xx_read_status, |
2398 | .handle_interrupt = vsc85xx_handle_interrupt, |
2399 | .config_intr = &vsc85xx_config_intr, |
2400 | .suspend = &genphy_suspend, |
2401 | .resume = &genphy_resume, |
2402 | .probe = &vsc8574_probe, |
2403 | .set_wol = &vsc85xx_wol_set, |
2404 | .get_wol = &vsc85xx_wol_get, |
2405 | .get_tunable = &vsc85xx_get_tunable, |
2406 | .set_tunable = &vsc85xx_set_tunable, |
2407 | .read_page = &vsc85xx_phy_read_page, |
2408 | .write_page = &vsc85xx_phy_write_page, |
2409 | .get_sset_count = &vsc85xx_get_sset_count, |
2410 | .get_strings = &vsc85xx_get_strings, |
2411 | .get_stats = &vsc85xx_get_stats, |
2412 | }, |
2413 | { |
2414 | .phy_id = PHY_ID_VSC8514, |
2415 | .name = "Microsemi GE VSC8514 SyncE" , |
2416 | .phy_id_mask = 0xfffffff0, |
2417 | .soft_reset = &genphy_soft_reset, |
2418 | .config_init = &vsc8514_config_init, |
2419 | .config_aneg = &vsc85xx_config_aneg, |
2420 | .read_status = &vsc85xx_read_status, |
2421 | .handle_interrupt = vsc85xx_handle_interrupt, |
2422 | .config_intr = &vsc85xx_config_intr, |
2423 | .suspend = &genphy_suspend, |
2424 | .resume = &genphy_resume, |
2425 | .probe = &vsc8514_probe, |
2426 | .set_wol = &vsc85xx_wol_set, |
2427 | .get_wol = &vsc85xx_wol_get, |
2428 | .get_tunable = &vsc85xx_get_tunable, |
2429 | .set_tunable = &vsc85xx_set_tunable, |
2430 | .read_page = &vsc85xx_phy_read_page, |
2431 | .write_page = &vsc85xx_phy_write_page, |
2432 | .get_sset_count = &vsc85xx_get_sset_count, |
2433 | .get_strings = &vsc85xx_get_strings, |
2434 | .get_stats = &vsc85xx_get_stats, |
2435 | }, |
2436 | { |
2437 | .phy_id = PHY_ID_VSC8530, |
2438 | .name = "Microsemi FE VSC8530" , |
2439 | .phy_id_mask = 0xfffffff0, |
2440 | /* PHY_BASIC_FEATURES */ |
2441 | .soft_reset = &genphy_soft_reset, |
2442 | .config_init = &vsc85xx_config_init, |
2443 | .config_aneg = &vsc85xx_config_aneg, |
2444 | .read_status = &vsc85xx_read_status, |
2445 | .handle_interrupt = vsc85xx_handle_interrupt, |
2446 | .config_intr = &vsc85xx_config_intr, |
2447 | .suspend = &genphy_suspend, |
2448 | .resume = &genphy_resume, |
2449 | .probe = &vsc85xx_probe, |
2450 | .set_wol = &vsc85xx_wol_set, |
2451 | .get_wol = &vsc85xx_wol_get, |
2452 | .get_tunable = &vsc85xx_get_tunable, |
2453 | .set_tunable = &vsc85xx_set_tunable, |
2454 | .read_page = &vsc85xx_phy_read_page, |
2455 | .write_page = &vsc85xx_phy_write_page, |
2456 | .get_sset_count = &vsc85xx_get_sset_count, |
2457 | .get_strings = &vsc85xx_get_strings, |
2458 | .get_stats = &vsc85xx_get_stats, |
2459 | }, |
2460 | { |
2461 | .phy_id = PHY_ID_VSC8531, |
2462 | .name = "Microsemi VSC8531" , |
2463 | .phy_id_mask = 0xfffffff0, |
2464 | /* PHY_GBIT_FEATURES */ |
2465 | .soft_reset = &genphy_soft_reset, |
2466 | .config_init = &vsc85xx_config_init, |
2467 | .config_aneg = &vsc85xx_config_aneg, |
2468 | .read_status = &vsc85xx_read_status, |
2469 | .handle_interrupt = vsc85xx_handle_interrupt, |
2470 | .config_intr = &vsc85xx_config_intr, |
2471 | .suspend = &genphy_suspend, |
2472 | .resume = &genphy_resume, |
2473 | .probe = &vsc85xx_probe, |
2474 | .set_wol = &vsc85xx_wol_set, |
2475 | .get_wol = &vsc85xx_wol_get, |
2476 | .get_tunable = &vsc85xx_get_tunable, |
2477 | .set_tunable = &vsc85xx_set_tunable, |
2478 | .read_page = &vsc85xx_phy_read_page, |
2479 | .write_page = &vsc85xx_phy_write_page, |
2480 | .get_sset_count = &vsc85xx_get_sset_count, |
2481 | .get_strings = &vsc85xx_get_strings, |
2482 | .get_stats = &vsc85xx_get_stats, |
2483 | }, |
2484 | { |
2485 | .phy_id = PHY_ID_VSC8540, |
2486 | .name = "Microsemi FE VSC8540 SyncE" , |
2487 | .phy_id_mask = 0xfffffff0, |
2488 | /* PHY_BASIC_FEATURES */ |
2489 | .soft_reset = &genphy_soft_reset, |
2490 | .config_init = &vsc85xx_config_init, |
2491 | .config_aneg = &vsc85xx_config_aneg, |
2492 | .read_status = &vsc85xx_read_status, |
2493 | .handle_interrupt = vsc85xx_handle_interrupt, |
2494 | .config_intr = &vsc85xx_config_intr, |
2495 | .suspend = &genphy_suspend, |
2496 | .resume = &genphy_resume, |
2497 | .probe = &vsc85xx_probe, |
2498 | .set_wol = &vsc85xx_wol_set, |
2499 | .get_wol = &vsc85xx_wol_get, |
2500 | .get_tunable = &vsc85xx_get_tunable, |
2501 | .set_tunable = &vsc85xx_set_tunable, |
2502 | .read_page = &vsc85xx_phy_read_page, |
2503 | .write_page = &vsc85xx_phy_write_page, |
2504 | .get_sset_count = &vsc85xx_get_sset_count, |
2505 | .get_strings = &vsc85xx_get_strings, |
2506 | .get_stats = &vsc85xx_get_stats, |
2507 | }, |
2508 | { |
2509 | .phy_id = PHY_ID_VSC8541, |
2510 | .name = "Microsemi VSC8541 SyncE" , |
2511 | .phy_id_mask = 0xfffffff0, |
2512 | /* PHY_GBIT_FEATURES */ |
2513 | .soft_reset = &genphy_soft_reset, |
2514 | .config_init = &vsc85xx_config_init, |
2515 | .config_aneg = &vsc85xx_config_aneg, |
2516 | .read_status = &vsc85xx_read_status, |
2517 | .handle_interrupt = vsc85xx_handle_interrupt, |
2518 | .config_intr = &vsc85xx_config_intr, |
2519 | .suspend = &genphy_suspend, |
2520 | .resume = &genphy_resume, |
2521 | .probe = &vsc85xx_probe, |
2522 | .set_wol = &vsc85xx_wol_set, |
2523 | .get_wol = &vsc85xx_wol_get, |
2524 | .get_tunable = &vsc85xx_get_tunable, |
2525 | .set_tunable = &vsc85xx_set_tunable, |
2526 | .read_page = &vsc85xx_phy_read_page, |
2527 | .write_page = &vsc85xx_phy_write_page, |
2528 | .get_sset_count = &vsc85xx_get_sset_count, |
2529 | .get_strings = &vsc85xx_get_strings, |
2530 | .get_stats = &vsc85xx_get_stats, |
2531 | }, |
2532 | { |
2533 | .phy_id = PHY_ID_VSC8552, |
2534 | .name = "Microsemi GE VSC8552 SyncE" , |
2535 | .phy_id_mask = 0xfffffff0, |
2536 | /* PHY_GBIT_FEATURES */ |
2537 | .soft_reset = &genphy_soft_reset, |
2538 | .config_init = &vsc8584_config_init, |
2539 | .config_aneg = &vsc85xx_config_aneg, |
2540 | .read_status = &vsc85xx_read_status, |
2541 | .handle_interrupt = vsc85xx_handle_interrupt, |
2542 | .config_intr = &vsc85xx_config_intr, |
2543 | .suspend = &genphy_suspend, |
2544 | .resume = &genphy_resume, |
2545 | .probe = &vsc8574_probe, |
2546 | .set_wol = &vsc85xx_wol_set, |
2547 | .get_wol = &vsc85xx_wol_get, |
2548 | .get_tunable = &vsc85xx_get_tunable, |
2549 | .set_tunable = &vsc85xx_set_tunable, |
2550 | .read_page = &vsc85xx_phy_read_page, |
2551 | .write_page = &vsc85xx_phy_write_page, |
2552 | .get_sset_count = &vsc85xx_get_sset_count, |
2553 | .get_strings = &vsc85xx_get_strings, |
2554 | .get_stats = &vsc85xx_get_stats, |
2555 | }, |
2556 | { |
2557 | .phy_id = PHY_ID_VSC856X, |
2558 | .name = "Microsemi GE VSC856X SyncE" , |
2559 | .phy_id_mask = 0xfffffff0, |
2560 | /* PHY_GBIT_FEATURES */ |
2561 | .soft_reset = &genphy_soft_reset, |
2562 | .config_init = &vsc8584_config_init, |
2563 | .config_aneg = &vsc85xx_config_aneg, |
2564 | .read_status = &vsc85xx_read_status, |
2565 | .handle_interrupt = vsc85xx_handle_interrupt, |
2566 | .config_intr = &vsc85xx_config_intr, |
2567 | .suspend = &genphy_suspend, |
2568 | .resume = &genphy_resume, |
2569 | .probe = &vsc8584_probe, |
2570 | .get_tunable = &vsc85xx_get_tunable, |
2571 | .set_tunable = &vsc85xx_set_tunable, |
2572 | .read_page = &vsc85xx_phy_read_page, |
2573 | .write_page = &vsc85xx_phy_write_page, |
2574 | .get_sset_count = &vsc85xx_get_sset_count, |
2575 | .get_strings = &vsc85xx_get_strings, |
2576 | .get_stats = &vsc85xx_get_stats, |
2577 | }, |
2578 | { |
2579 | .phy_id = PHY_ID_VSC8572, |
2580 | .name = "Microsemi GE VSC8572 SyncE" , |
2581 | .phy_id_mask = 0xfffffff0, |
2582 | /* PHY_GBIT_FEATURES */ |
2583 | .soft_reset = &genphy_soft_reset, |
2584 | .config_init = &vsc8584_config_init, |
2585 | .config_aneg = &vsc85xx_config_aneg, |
2586 | .aneg_done = &genphy_aneg_done, |
2587 | .read_status = &vsc85xx_read_status, |
2588 | .handle_interrupt = &vsc8584_handle_interrupt, |
2589 | .config_intr = &vsc85xx_config_intr, |
2590 | .suspend = &genphy_suspend, |
2591 | .resume = &genphy_resume, |
2592 | .probe = &vsc8574_probe, |
2593 | .set_wol = &vsc85xx_wol_set, |
2594 | .get_wol = &vsc85xx_wol_get, |
2595 | .get_tunable = &vsc85xx_get_tunable, |
2596 | .set_tunable = &vsc85xx_set_tunable, |
2597 | .read_page = &vsc85xx_phy_read_page, |
2598 | .write_page = &vsc85xx_phy_write_page, |
2599 | .get_sset_count = &vsc85xx_get_sset_count, |
2600 | .get_strings = &vsc85xx_get_strings, |
2601 | .get_stats = &vsc85xx_get_stats, |
2602 | }, |
2603 | { |
2604 | .phy_id = PHY_ID_VSC8574, |
2605 | .name = "Microsemi GE VSC8574 SyncE" , |
2606 | .phy_id_mask = 0xfffffff0, |
2607 | /* PHY_GBIT_FEATURES */ |
2608 | .soft_reset = &genphy_soft_reset, |
2609 | .config_init = &vsc8584_config_init, |
2610 | .config_aneg = &vsc85xx_config_aneg, |
2611 | .aneg_done = &genphy_aneg_done, |
2612 | .read_status = &vsc85xx_read_status, |
2613 | .handle_interrupt = vsc85xx_handle_interrupt, |
2614 | .config_intr = &vsc85xx_config_intr, |
2615 | .suspend = &genphy_suspend, |
2616 | .resume = &genphy_resume, |
2617 | .probe = &vsc8574_probe, |
2618 | .set_wol = &vsc85xx_wol_set, |
2619 | .get_wol = &vsc85xx_wol_get, |
2620 | .get_tunable = &vsc85xx_get_tunable, |
2621 | .set_tunable = &vsc85xx_set_tunable, |
2622 | .read_page = &vsc85xx_phy_read_page, |
2623 | .write_page = &vsc85xx_phy_write_page, |
2624 | .get_sset_count = &vsc85xx_get_sset_count, |
2625 | .get_strings = &vsc85xx_get_strings, |
2626 | .get_stats = &vsc85xx_get_stats, |
2627 | }, |
2628 | { |
2629 | .phy_id = PHY_ID_VSC8575, |
2630 | .name = "Microsemi GE VSC8575 SyncE" , |
2631 | .phy_id_mask = 0xfffffff0, |
2632 | /* PHY_GBIT_FEATURES */ |
2633 | .soft_reset = &genphy_soft_reset, |
2634 | .config_init = &vsc8584_config_init, |
2635 | .config_aneg = &vsc85xx_config_aneg, |
2636 | .aneg_done = &genphy_aneg_done, |
2637 | .read_status = &vsc85xx_read_status, |
2638 | .handle_interrupt = &vsc8584_handle_interrupt, |
2639 | .config_intr = &vsc85xx_config_intr, |
2640 | .suspend = &genphy_suspend, |
2641 | .resume = &genphy_resume, |
2642 | .probe = &vsc8584_probe, |
2643 | .get_tunable = &vsc85xx_get_tunable, |
2644 | .set_tunable = &vsc85xx_set_tunable, |
2645 | .read_page = &vsc85xx_phy_read_page, |
2646 | .write_page = &vsc85xx_phy_write_page, |
2647 | .get_sset_count = &vsc85xx_get_sset_count, |
2648 | .get_strings = &vsc85xx_get_strings, |
2649 | .get_stats = &vsc85xx_get_stats, |
2650 | }, |
2651 | { |
2652 | .phy_id = PHY_ID_VSC8582, |
2653 | .name = "Microsemi GE VSC8582 SyncE" , |
2654 | .phy_id_mask = 0xfffffff0, |
2655 | /* PHY_GBIT_FEATURES */ |
2656 | .soft_reset = &genphy_soft_reset, |
2657 | .config_init = &vsc8584_config_init, |
2658 | .config_aneg = &vsc85xx_config_aneg, |
2659 | .aneg_done = &genphy_aneg_done, |
2660 | .read_status = &vsc85xx_read_status, |
2661 | .handle_interrupt = &vsc8584_handle_interrupt, |
2662 | .config_intr = &vsc85xx_config_intr, |
2663 | .suspend = &genphy_suspend, |
2664 | .resume = &genphy_resume, |
2665 | .probe = &vsc8584_probe, |
2666 | .get_tunable = &vsc85xx_get_tunable, |
2667 | .set_tunable = &vsc85xx_set_tunable, |
2668 | .read_page = &vsc85xx_phy_read_page, |
2669 | .write_page = &vsc85xx_phy_write_page, |
2670 | .get_sset_count = &vsc85xx_get_sset_count, |
2671 | .get_strings = &vsc85xx_get_strings, |
2672 | .get_stats = &vsc85xx_get_stats, |
2673 | }, |
2674 | { |
2675 | .phy_id = PHY_ID_VSC8584, |
2676 | .name = "Microsemi GE VSC8584 SyncE" , |
2677 | .phy_id_mask = 0xfffffff0, |
2678 | /* PHY_GBIT_FEATURES */ |
2679 | .soft_reset = &genphy_soft_reset, |
2680 | .config_init = &vsc8584_config_init, |
2681 | .config_aneg = &vsc85xx_config_aneg, |
2682 | .aneg_done = &genphy_aneg_done, |
2683 | .read_status = &vsc85xx_read_status, |
2684 | .handle_interrupt = &vsc8584_handle_interrupt, |
2685 | .config_intr = &vsc85xx_config_intr, |
2686 | .suspend = &genphy_suspend, |
2687 | .resume = &genphy_resume, |
2688 | .probe = &vsc8584_probe, |
2689 | .get_tunable = &vsc85xx_get_tunable, |
2690 | .set_tunable = &vsc85xx_set_tunable, |
2691 | .read_page = &vsc85xx_phy_read_page, |
2692 | .write_page = &vsc85xx_phy_write_page, |
2693 | .get_sset_count = &vsc85xx_get_sset_count, |
2694 | .get_strings = &vsc85xx_get_strings, |
2695 | .get_stats = &vsc85xx_get_stats, |
2696 | .link_change_notify = &vsc85xx_link_change_notify, |
2697 | } |
2698 | |
2699 | }; |
2700 | |
2701 | module_phy_driver(vsc85xx_driver); |
2702 | |
2703 | static struct mdio_device_id __maybe_unused vsc85xx_tbl[] = { |
2704 | { PHY_ID_MATCH_VENDOR(PHY_VENDOR_MSCC) }, |
2705 | { } |
2706 | }; |
2707 | |
2708 | MODULE_DEVICE_TABLE(mdio, vsc85xx_tbl); |
2709 | |
2710 | MODULE_DESCRIPTION("Microsemi VSC85xx PHY driver" ); |
2711 | MODULE_AUTHOR("Nagaraju Lakkaraju" ); |
2712 | MODULE_LICENSE("Dual MIT/GPL" ); |
2713 | |
2714 | MODULE_FIRMWARE(MSCC_VSC8584_REVB_INT8051_FW); |
2715 | MODULE_FIRMWARE(MSCC_VSC8574_REVB_INT8051_FW); |
2716 | |