Merge branch 'scsi-target-for-v4.10' of git://git.kernel.org/pub/scm/linux/kernel...
[GitHub/LineageOS/android_kernel_motorola_exynos9610.git] / drivers / net / phy / mscc.c
CommitLineData
d50736a8
RL
1/*
2 * Driver for Microsemi VSC85xx PHYs
3 *
4 * Author: Nagaraju Lakkaraju
5 * License: Dual MIT/GPL
6 * Copyright (c) 2016 Microsemi Corporation
7 */
8
9#include <linux/kernel.h>
10#include <linux/module.h>
11#include <linux/mdio.h>
12#include <linux/mii.h>
13#include <linux/phy.h>
a4cc96d1 14#include <linux/of.h>
0a55c12f 15#include <linux/netdevice.h>
d50736a8
RL
16
17enum rgmii_rx_clock_delay {
4ffd03f5
RL
18 RGMII_RX_CLK_DELAY_0_2_NS = 0,
19 RGMII_RX_CLK_DELAY_0_8_NS = 1,
20 RGMII_RX_CLK_DELAY_1_1_NS = 2,
21 RGMII_RX_CLK_DELAY_1_7_NS = 3,
22 RGMII_RX_CLK_DELAY_2_0_NS = 4,
23 RGMII_RX_CLK_DELAY_2_3_NS = 5,
24 RGMII_RX_CLK_DELAY_2_6_NS = 6,
25 RGMII_RX_CLK_DELAY_3_4_NS = 7
d50736a8
RL
26};
27
1a21101d
RL
28/* Microsemi VSC85xx PHY registers */
29/* IEEE 802. Std Registers */
233275ec
RL
30#define MSCC_PHY_BYPASS_CONTROL 18
31#define DISABLE_HP_AUTO_MDIX_MASK 0x0080
32#define DISABLE_PAIR_SWAP_CORR_MASK 0x0020
33#define DISABLE_POLARITY_CORR_MASK 0x0010
34
1a21101d
RL
35#define MSCC_PHY_EXT_PHY_CNTL_1 23
36#define MAC_IF_SELECTION_MASK 0x1800
37#define MAC_IF_SELECTION_GMII 0
38#define MAC_IF_SELECTION_RMII 1
39#define MAC_IF_SELECTION_RGMII 2
40#define MAC_IF_SELECTION_POS 11
41#define FAR_END_LOOPBACK_MODE_MASK 0x0008
42
4ffd03f5
RL
43#define MII_VSC85XX_INT_MASK 25
44#define MII_VSC85XX_INT_MASK_MASK 0xa000
0a55c12f 45#define MII_VSC85XX_INT_MASK_WOL 0x0040
4ffd03f5 46#define MII_VSC85XX_INT_STATUS 26
d50736a8 47
a4cc96d1
RL
48#define MSCC_PHY_WOL_MAC_CONTROL 27
49#define EDGE_RATE_CNTL_POS 5
50#define EDGE_RATE_CNTL_MASK 0x00E0
51
233275ec
RL
52#define MSCC_PHY_DEV_AUX_CNTL 28
53#define HP_AUTO_MDIX_X_OVER_IND_MASK 0x2000
54
4ffd03f5
RL
55#define MSCC_EXT_PAGE_ACCESS 31
56#define MSCC_PHY_PAGE_STANDARD 0x0000 /* Standard registers */
310d9ad5 57#define MSCC_PHY_PAGE_EXTENDED 0x0001 /* Extended registers */
4ffd03f5 58#define MSCC_PHY_PAGE_EXTENDED_2 0x0002 /* Extended reg - page 2 */
d50736a8 59
310d9ad5 60/* Extended Page 1 Registers */
233275ec
RL
61#define MSCC_PHY_EXT_MODE_CNTL 19
62#define FORCE_MDI_CROSSOVER_MASK 0x000C
63#define FORCE_MDI_CROSSOVER_MDIX 0x000C
64#define FORCE_MDI_CROSSOVER_MDI 0x0008
65
310d9ad5
RL
66#define MSCC_PHY_ACTIPHY_CNTL 20
67#define DOWNSHIFT_CNTL_MASK 0x001C
68#define DOWNSHIFT_EN 0x0010
69#define DOWNSHIFT_CNTL_POS 2
70
d50736a8 71/* Extended Page 2 Registers */
4ffd03f5
RL
72#define MSCC_PHY_RGMII_CNTL 20
73#define RGMII_RX_CLK_DELAY_MASK 0x0070
74#define RGMII_RX_CLK_DELAY_POS 4
d50736a8 75
0a55c12f
RL
76#define MSCC_PHY_WOL_LOWER_MAC_ADDR 21
77#define MSCC_PHY_WOL_MID_MAC_ADDR 22
78#define MSCC_PHY_WOL_UPPER_MAC_ADDR 23
79#define MSCC_PHY_WOL_LOWER_PASSWD 24
80#define MSCC_PHY_WOL_MID_PASSWD 25
81#define MSCC_PHY_WOL_UPPER_PASSWD 26
82
83#define MSCC_PHY_WOL_MAC_CONTROL 27
84#define SECURE_ON_ENABLE 0x8000
85#define SECURE_ON_PASSWD_LEN_4 0x4000
86
d50736a8 87/* Microsemi PHY ID's */
af1fee98 88#define PHY_ID_VSC8530 0x00070560
4ffd03f5 89#define PHY_ID_VSC8531 0x00070570
af1fee98 90#define PHY_ID_VSC8540 0x00070760
4ffd03f5 91#define PHY_ID_VSC8541 0x00070770
d50736a8 92
4f58e6dc
AN
93#define MSCC_VDDMAC_1500 1500
94#define MSCC_VDDMAC_1800 1800
95#define MSCC_VDDMAC_2500 2500
96#define MSCC_VDDMAC_3300 3300
a4cc96d1 97
310d9ad5
RL
98#define DOWNSHIFT_COUNT_MAX 5
99
4f58e6dc
AN
100struct vsc8531_private {
101 int rate_magic;
a4cc96d1
RL
102};
103
4f58e6dc
AN
104#ifdef CONFIG_OF_MDIO
105struct vsc8531_edge_rate_table {
a4cc96d1 106 u16 vddmac;
4f58e6dc 107 u8 slowdown[8];
a4cc96d1
RL
108};
109
4f58e6dc
AN
110static const struct vsc8531_edge_rate_table edge_table[] = {
111 {MSCC_VDDMAC_3300, { 0, 2, 4, 7, 10, 17, 29, 53} },
112 {MSCC_VDDMAC_2500, { 0, 3, 6, 10, 14, 23, 37, 63} },
113 {MSCC_VDDMAC_1800, { 0, 5, 9, 16, 23, 35, 52, 76} },
114 {MSCC_VDDMAC_1500, { 0, 6, 14, 21, 29, 42, 58, 77} },
115};
116#endif /* CONFIG_OF_MDIO */
117
d50736a8
RL
118static int vsc85xx_phy_page_set(struct phy_device *phydev, u8 page)
119{
4ffd03f5 120 int rc;
d50736a8 121
4ffd03f5
RL
122 rc = phy_write(phydev, MSCC_EXT_PAGE_ACCESS, page);
123 return rc;
d50736a8
RL
124}
125
233275ec
RL
126static int vsc85xx_mdix_get(struct phy_device *phydev, u8 *mdix)
127{
128 u16 reg_val;
129
130 reg_val = phy_read(phydev, MSCC_PHY_DEV_AUX_CNTL);
131 if (reg_val & HP_AUTO_MDIX_X_OVER_IND_MASK)
132 *mdix = ETH_TP_MDI_X;
133 else
134 *mdix = ETH_TP_MDI;
135
136 return 0;
137}
138
139static int vsc85xx_mdix_set(struct phy_device *phydev, u8 mdix)
140{
141 int rc;
142 u16 reg_val;
143
144 reg_val = phy_read(phydev, MSCC_PHY_BYPASS_CONTROL);
145 if ((mdix == ETH_TP_MDI) || (mdix == ETH_TP_MDI_X)) {
146 reg_val |= (DISABLE_PAIR_SWAP_CORR_MASK |
147 DISABLE_POLARITY_CORR_MASK |
148 DISABLE_HP_AUTO_MDIX_MASK);
149 } else {
150 reg_val &= ~(DISABLE_PAIR_SWAP_CORR_MASK |
151 DISABLE_POLARITY_CORR_MASK |
152 DISABLE_HP_AUTO_MDIX_MASK);
153 }
154 rc = phy_write(phydev, MSCC_PHY_BYPASS_CONTROL, reg_val);
155 if (rc != 0)
156 return rc;
157
158 rc = vsc85xx_phy_page_set(phydev, MSCC_PHY_PAGE_EXTENDED);
159 if (rc != 0)
160 return rc;
161
162 reg_val = phy_read(phydev, MSCC_PHY_EXT_MODE_CNTL);
163 reg_val &= ~(FORCE_MDI_CROSSOVER_MASK);
164 if (mdix == ETH_TP_MDI)
165 reg_val |= FORCE_MDI_CROSSOVER_MDI;
166 else if (mdix == ETH_TP_MDI_X)
167 reg_val |= FORCE_MDI_CROSSOVER_MDIX;
168 rc = phy_write(phydev, MSCC_PHY_EXT_MODE_CNTL, reg_val);
169 if (rc != 0)
170 return rc;
171
172 rc = vsc85xx_phy_page_set(phydev, MSCC_PHY_PAGE_STANDARD);
173 if (rc != 0)
174 return rc;
175
176 return genphy_restart_aneg(phydev);
177}
178
310d9ad5
RL
179static int vsc85xx_downshift_get(struct phy_device *phydev, u8 *count)
180{
181 int rc;
182 u16 reg_val;
183
310d9ad5
RL
184 rc = vsc85xx_phy_page_set(phydev, MSCC_PHY_PAGE_EXTENDED);
185 if (rc != 0)
4b65246b 186 goto out;
310d9ad5
RL
187
188 reg_val = phy_read(phydev, MSCC_PHY_ACTIPHY_CNTL);
189 reg_val &= DOWNSHIFT_CNTL_MASK;
190 if (!(reg_val & DOWNSHIFT_EN))
191 *count = DOWNSHIFT_DEV_DISABLE;
192 else
193 *count = ((reg_val & ~DOWNSHIFT_EN) >> DOWNSHIFT_CNTL_POS) + 2;
194 rc = vsc85xx_phy_page_set(phydev, MSCC_PHY_PAGE_STANDARD);
195
4b65246b 196out:
310d9ad5
RL
197 return rc;
198}
199
200static int vsc85xx_downshift_set(struct phy_device *phydev, u8 count)
201{
202 int rc;
203 u16 reg_val;
204
205 if (count == DOWNSHIFT_DEV_DEFAULT_COUNT) {
206 /* Default downshift count 3 (i.e. Bit3:2 = 0b01) */
207 count = ((1 << DOWNSHIFT_CNTL_POS) | DOWNSHIFT_EN);
208 } else if (count > DOWNSHIFT_COUNT_MAX || count == 1) {
209 phydev_err(phydev, "Downshift count should be 2,3,4 or 5\n");
210 return -ERANGE;
211 } else if (count) {
212 /* Downshift count is either 2,3,4 or 5 */
213 count = (((count - 2) << DOWNSHIFT_CNTL_POS) | DOWNSHIFT_EN);
214 }
215
310d9ad5
RL
216 rc = vsc85xx_phy_page_set(phydev, MSCC_PHY_PAGE_EXTENDED);
217 if (rc != 0)
4b65246b 218 goto out;
310d9ad5
RL
219
220 reg_val = phy_read(phydev, MSCC_PHY_ACTIPHY_CNTL);
221 reg_val &= ~(DOWNSHIFT_CNTL_MASK);
222 reg_val |= count;
223 rc = phy_write(phydev, MSCC_PHY_ACTIPHY_CNTL, reg_val);
224 if (rc != 0)
4b65246b 225 goto out;
310d9ad5
RL
226
227 rc = vsc85xx_phy_page_set(phydev, MSCC_PHY_PAGE_STANDARD);
228
4b65246b 229out:
310d9ad5
RL
230 return rc;
231}
232
0a55c12f
RL
233static int vsc85xx_wol_set(struct phy_device *phydev,
234 struct ethtool_wolinfo *wol)
235{
236 int rc;
237 u16 reg_val;
238 u8 i;
239 u16 pwd[3] = {0, 0, 0};
240 struct ethtool_wolinfo *wol_conf = wol;
241 u8 *mac_addr = phydev->attached_dev->dev_addr;
242
243 mutex_lock(&phydev->lock);
244 rc = vsc85xx_phy_page_set(phydev, MSCC_PHY_PAGE_EXTENDED_2);
245 if (rc != 0)
246 goto out_unlock;
247
248 if (wol->wolopts & WAKE_MAGIC) {
249 /* Store the device address for the magic packet */
250 for (i = 0; i < ARRAY_SIZE(pwd); i++)
251 pwd[i] = mac_addr[5 - (i * 2 + 1)] << 8 |
252 mac_addr[5 - i * 2];
253 phy_write(phydev, MSCC_PHY_WOL_LOWER_MAC_ADDR, pwd[0]);
254 phy_write(phydev, MSCC_PHY_WOL_MID_MAC_ADDR, pwd[1]);
255 phy_write(phydev, MSCC_PHY_WOL_UPPER_MAC_ADDR, pwd[2]);
256 } else {
257 phy_write(phydev, MSCC_PHY_WOL_LOWER_MAC_ADDR, 0);
258 phy_write(phydev, MSCC_PHY_WOL_MID_MAC_ADDR, 0);
259 phy_write(phydev, MSCC_PHY_WOL_UPPER_MAC_ADDR, 0);
260 }
261
262 if (wol_conf->wolopts & WAKE_MAGICSECURE) {
263 for (i = 0; i < ARRAY_SIZE(pwd); i++)
264 pwd[i] = wol_conf->sopass[5 - (i * 2 + 1)] << 8 |
265 wol_conf->sopass[5 - i * 2];
266 phy_write(phydev, MSCC_PHY_WOL_LOWER_PASSWD, pwd[0]);
267 phy_write(phydev, MSCC_PHY_WOL_MID_PASSWD, pwd[1]);
268 phy_write(phydev, MSCC_PHY_WOL_UPPER_PASSWD, pwd[2]);
269 } else {
270 phy_write(phydev, MSCC_PHY_WOL_LOWER_PASSWD, 0);
271 phy_write(phydev, MSCC_PHY_WOL_MID_PASSWD, 0);
272 phy_write(phydev, MSCC_PHY_WOL_UPPER_PASSWD, 0);
273 }
274
275 reg_val = phy_read(phydev, MSCC_PHY_WOL_MAC_CONTROL);
276 if (wol_conf->wolopts & WAKE_MAGICSECURE)
277 reg_val |= SECURE_ON_ENABLE;
278 else
279 reg_val &= ~SECURE_ON_ENABLE;
280 phy_write(phydev, MSCC_PHY_WOL_MAC_CONTROL, reg_val);
281
282 rc = vsc85xx_phy_page_set(phydev, MSCC_PHY_PAGE_STANDARD);
283 if (rc != 0)
284 goto out_unlock;
285
286 if (wol->wolopts & WAKE_MAGIC) {
287 /* Enable the WOL interrupt */
288 reg_val = phy_read(phydev, MII_VSC85XX_INT_MASK);
289 reg_val |= MII_VSC85XX_INT_MASK_WOL;
290 rc = phy_write(phydev, MII_VSC85XX_INT_MASK, reg_val);
291 if (rc != 0)
292 goto out_unlock;
293 } else {
294 /* Disable the WOL interrupt */
295 reg_val = phy_read(phydev, MII_VSC85XX_INT_MASK);
296 reg_val &= (~MII_VSC85XX_INT_MASK_WOL);
297 rc = phy_write(phydev, MII_VSC85XX_INT_MASK, reg_val);
298 if (rc != 0)
299 goto out_unlock;
300 }
301 /* Clear WOL iterrupt status */
302 reg_val = phy_read(phydev, MII_VSC85XX_INT_STATUS);
303
304out_unlock:
305 mutex_unlock(&phydev->lock);
306
307 return rc;
308}
309
310static void vsc85xx_wol_get(struct phy_device *phydev,
311 struct ethtool_wolinfo *wol)
312{
313 int rc;
314 u16 reg_val;
315 u8 i;
316 u16 pwd[3] = {0, 0, 0};
317 struct ethtool_wolinfo *wol_conf = wol;
318
319 mutex_lock(&phydev->lock);
320 rc = vsc85xx_phy_page_set(phydev, MSCC_PHY_PAGE_EXTENDED_2);
321 if (rc != 0)
322 goto out_unlock;
323
324 reg_val = phy_read(phydev, MSCC_PHY_WOL_MAC_CONTROL);
325 if (reg_val & SECURE_ON_ENABLE)
326 wol_conf->wolopts |= WAKE_MAGICSECURE;
327 if (wol_conf->wolopts & WAKE_MAGICSECURE) {
328 pwd[0] = phy_read(phydev, MSCC_PHY_WOL_LOWER_PASSWD);
329 pwd[1] = phy_read(phydev, MSCC_PHY_WOL_MID_PASSWD);
330 pwd[2] = phy_read(phydev, MSCC_PHY_WOL_UPPER_PASSWD);
331 for (i = 0; i < ARRAY_SIZE(pwd); i++) {
332 wol_conf->sopass[5 - i * 2] = pwd[i] & 0x00ff;
333 wol_conf->sopass[5 - (i * 2 + 1)] = (pwd[i] & 0xff00)
334 >> 8;
335 }
336 }
337
338 rc = vsc85xx_phy_page_set(phydev, MSCC_PHY_PAGE_STANDARD);
339
340out_unlock:
341 mutex_unlock(&phydev->lock);
342}
343
4f58e6dc
AN
344#ifdef CONFIG_OF_MDIO
345static int vsc85xx_edge_rate_magic_get(struct phy_device *phydev)
a4cc96d1 346{
a4cc96d1 347 u8 sd;
4f58e6dc
AN
348 u16 vdd;
349 int rc, i, j;
350 struct device *dev = &phydev->mdio.dev;
351 struct device_node *of_node = dev->of_node;
352 u8 sd_array_size = ARRAY_SIZE(edge_table[0].slowdown);
a4cc96d1 353
4f58e6dc
AN
354 if (!of_node)
355 return -ENODEV;
a4cc96d1 356
4f58e6dc
AN
357 rc = of_property_read_u16(of_node, "vsc8531,vddmac", &vdd);
358 if (rc != 0)
359 vdd = MSCC_VDDMAC_3300;
360
361 rc = of_property_read_u8(of_node, "vsc8531,edge-slowdown", &sd);
362 if (rc != 0)
363 sd = 0;
364
365 for (i = 0; i < ARRAY_SIZE(edge_table); i++)
366 if (edge_table[i].vddmac == vdd)
367 for (j = 0; j < sd_array_size; j++)
368 if (edge_table[i].slowdown[j] == sd)
369 return (sd_array_size - j - 1);
370
371 return -EINVAL;
372}
373#else
374static int vsc85xx_edge_rate_magic_get(struct phy_device *phydev)
375{
376 return 0;
a4cc96d1 377}
4f58e6dc 378#endif /* CONFIG_OF_MDIO */
a4cc96d1 379
4f58e6dc 380static int vsc85xx_edge_rate_cntl_set(struct phy_device *phydev, u8 edge_rate)
a4cc96d1
RL
381{
382 int rc;
383 u16 reg_val;
384
385 mutex_lock(&phydev->lock);
386 rc = vsc85xx_phy_page_set(phydev, MSCC_PHY_PAGE_EXTENDED_2);
387 if (rc != 0)
388 goto out_unlock;
389 reg_val = phy_read(phydev, MSCC_PHY_WOL_MAC_CONTROL);
390 reg_val &= ~(EDGE_RATE_CNTL_MASK);
391 reg_val |= (edge_rate << EDGE_RATE_CNTL_POS);
392 rc = phy_write(phydev, MSCC_PHY_WOL_MAC_CONTROL, reg_val);
393 if (rc != 0)
394 goto out_unlock;
395 rc = vsc85xx_phy_page_set(phydev, MSCC_PHY_PAGE_STANDARD);
396
397out_unlock:
398 mutex_unlock(&phydev->lock);
399
400 return rc;
401}
402
1a21101d
RL
403static int vsc85xx_mac_if_set(struct phy_device *phydev,
404 phy_interface_t interface)
405{
406 int rc;
407 u16 reg_val;
408
409 mutex_lock(&phydev->lock);
410 reg_val = phy_read(phydev, MSCC_PHY_EXT_PHY_CNTL_1);
411 reg_val &= ~(MAC_IF_SELECTION_MASK);
412 switch (interface) {
413 case PHY_INTERFACE_MODE_RGMII:
414 reg_val |= (MAC_IF_SELECTION_RGMII << MAC_IF_SELECTION_POS);
415 break;
416 case PHY_INTERFACE_MODE_RMII:
417 reg_val |= (MAC_IF_SELECTION_RMII << MAC_IF_SELECTION_POS);
418 break;
419 case PHY_INTERFACE_MODE_MII:
420 case PHY_INTERFACE_MODE_GMII:
421 reg_val |= (MAC_IF_SELECTION_GMII << MAC_IF_SELECTION_POS);
422 break;
423 default:
424 rc = -EINVAL;
425 goto out_unlock;
426 }
427 rc = phy_write(phydev, MSCC_PHY_EXT_PHY_CNTL_1, reg_val);
428 if (rc != 0)
429 goto out_unlock;
430
431 rc = genphy_soft_reset(phydev);
432
433out_unlock:
434 mutex_unlock(&phydev->lock);
435
436 return rc;
437}
438
d50736a8
RL
439static int vsc85xx_default_config(struct phy_device *phydev)
440{
4ffd03f5
RL
441 int rc;
442 u16 reg_val;
d50736a8 443
233275ec 444 phydev->mdix_ctrl = ETH_TP_MDI_AUTO;
4ffd03f5
RL
445 mutex_lock(&phydev->lock);
446 rc = vsc85xx_phy_page_set(phydev, MSCC_PHY_PAGE_EXTENDED_2);
447 if (rc != 0)
448 goto out_unlock;
d50736a8 449
4ffd03f5
RL
450 reg_val = phy_read(phydev, MSCC_PHY_RGMII_CNTL);
451 reg_val &= ~(RGMII_RX_CLK_DELAY_MASK);
452 reg_val |= (RGMII_RX_CLK_DELAY_1_1_NS << RGMII_RX_CLK_DELAY_POS);
453 phy_write(phydev, MSCC_PHY_RGMII_CNTL, reg_val);
454 rc = vsc85xx_phy_page_set(phydev, MSCC_PHY_PAGE_STANDARD);
d50736a8
RL
455
456out_unlock:
4ffd03f5 457 mutex_unlock(&phydev->lock);
d50736a8 458
4ffd03f5 459 return rc;
d50736a8
RL
460}
461
310d9ad5
RL
462static int vsc85xx_get_tunable(struct phy_device *phydev,
463 struct ethtool_tunable *tuna, void *data)
464{
465 switch (tuna->id) {
466 case ETHTOOL_PHY_DOWNSHIFT:
467 return vsc85xx_downshift_get(phydev, (u8 *)data);
468 default:
469 return -EINVAL;
470 }
471}
472
473static int vsc85xx_set_tunable(struct phy_device *phydev,
474 struct ethtool_tunable *tuna,
475 const void *data)
476{
477 switch (tuna->id) {
478 case ETHTOOL_PHY_DOWNSHIFT:
479 return vsc85xx_downshift_set(phydev, *(u8 *)data);
480 default:
481 return -EINVAL;
482 }
483}
484
d50736a8
RL
485static int vsc85xx_config_init(struct phy_device *phydev)
486{
4ffd03f5 487 int rc;
a4cc96d1 488 struct vsc8531_private *vsc8531 = phydev->priv;
d50736a8 489
4ffd03f5
RL
490 rc = vsc85xx_default_config(phydev);
491 if (rc)
492 return rc;
1a21101d
RL
493
494 rc = vsc85xx_mac_if_set(phydev, phydev->interface);
495 if (rc)
496 return rc;
497
4f58e6dc 498 rc = vsc85xx_edge_rate_cntl_set(phydev, vsc8531->rate_magic);
a4cc96d1
RL
499 if (rc)
500 return rc;
501
4ffd03f5 502 rc = genphy_config_init(phydev);
d50736a8 503
4ffd03f5 504 return rc;
d50736a8
RL
505}
506
507static int vsc85xx_ack_interrupt(struct phy_device *phydev)
508{
4ffd03f5 509 int rc = 0;
d50736a8 510
4ffd03f5
RL
511 if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
512 rc = phy_read(phydev, MII_VSC85XX_INT_STATUS);
d50736a8 513
4ffd03f5 514 return (rc < 0) ? rc : 0;
d50736a8
RL
515}
516
517static int vsc85xx_config_intr(struct phy_device *phydev)
518{
4ffd03f5
RL
519 int rc;
520
521 if (phydev->interrupts == PHY_INTERRUPT_ENABLED) {
522 rc = phy_write(phydev, MII_VSC85XX_INT_MASK,
523 MII_VSC85XX_INT_MASK_MASK);
524 } else {
525 rc = phy_write(phydev, MII_VSC85XX_INT_MASK, 0);
526 if (rc < 0)
527 return rc;
528 rc = phy_read(phydev, MII_VSC85XX_INT_STATUS);
529 }
530
531 return rc;
d50736a8
RL
532}
533
233275ec
RL
534static int vsc85xx_config_aneg(struct phy_device *phydev)
535{
536 int rc;
537
538 rc = vsc85xx_mdix_set(phydev, phydev->mdix_ctrl);
539 if (rc < 0)
540 return rc;
541
542 return genphy_config_aneg(phydev);
543}
544
545static int vsc85xx_read_status(struct phy_device *phydev)
546{
547 int rc;
548
549 rc = vsc85xx_mdix_get(phydev, &phydev->mdix);
550 if (rc < 0)
551 return rc;
552
553 return genphy_read_status(phydev);
554}
555
a4cc96d1
RL
556static int vsc85xx_probe(struct phy_device *phydev)
557{
4f58e6dc 558 int rate_magic;
a4cc96d1
RL
559 struct vsc8531_private *vsc8531;
560
4f58e6dc
AN
561 rate_magic = vsc85xx_edge_rate_magic_get(phydev);
562 if (rate_magic < 0)
563 return rate_magic;
564
a4cc96d1
RL
565 vsc8531 = devm_kzalloc(&phydev->mdio.dev, sizeof(*vsc8531), GFP_KERNEL);
566 if (!vsc8531)
567 return -ENOMEM;
568
569 phydev->priv = vsc8531;
570
4f58e6dc
AN
571 vsc8531->rate_magic = rate_magic;
572
a4cc96d1
RL
573 return 0;
574}
575
d50736a8
RL
576/* Microsemi VSC85xx PHYs */
577static struct phy_driver vsc85xx_driver[] = {
af1fee98
RL
578{
579 .phy_id = PHY_ID_VSC8530,
580 .name = "Microsemi FE VSC8530",
581 .phy_id_mask = 0xfffffff0,
582 .features = PHY_BASIC_FEATURES,
583 .flags = PHY_HAS_INTERRUPT,
584 .soft_reset = &genphy_soft_reset,
585 .config_init = &vsc85xx_config_init,
233275ec 586 .config_aneg = &vsc85xx_config_aneg,
af1fee98 587 .aneg_done = &genphy_aneg_done,
233275ec 588 .read_status = &vsc85xx_read_status,
af1fee98
RL
589 .ack_interrupt = &vsc85xx_ack_interrupt,
590 .config_intr = &vsc85xx_config_intr,
591 .suspend = &genphy_suspend,
592 .resume = &genphy_resume,
593 .probe = &vsc85xx_probe,
594 .set_wol = &vsc85xx_wol_set,
595 .get_wol = &vsc85xx_wol_get,
310d9ad5
RL
596 .get_tunable = &vsc85xx_get_tunable,
597 .set_tunable = &vsc85xx_set_tunable,
af1fee98 598},
d50736a8 599{
4ffd03f5
RL
600 .phy_id = PHY_ID_VSC8531,
601 .name = "Microsemi VSC8531",
602 .phy_id_mask = 0xfffffff0,
603 .features = PHY_GBIT_FEATURES,
604 .flags = PHY_HAS_INTERRUPT,
605 .soft_reset = &genphy_soft_reset,
606 .config_init = &vsc85xx_config_init,
233275ec 607 .config_aneg = &vsc85xx_config_aneg,
4ffd03f5 608 .aneg_done = &genphy_aneg_done,
233275ec 609 .read_status = &vsc85xx_read_status,
4ffd03f5
RL
610 .ack_interrupt = &vsc85xx_ack_interrupt,
611 .config_intr = &vsc85xx_config_intr,
612 .suspend = &genphy_suspend,
613 .resume = &genphy_resume,
4f58e6dc
AN
614 .probe = &vsc85xx_probe,
615 .set_wol = &vsc85xx_wol_set,
616 .get_wol = &vsc85xx_wol_get,
310d9ad5
RL
617 .get_tunable = &vsc85xx_get_tunable,
618 .set_tunable = &vsc85xx_set_tunable,
d50736a8 619},
af1fee98
RL
620{
621 .phy_id = PHY_ID_VSC8540,
622 .name = "Microsemi FE VSC8540 SyncE",
623 .phy_id_mask = 0xfffffff0,
624 .features = PHY_BASIC_FEATURES,
625 .flags = PHY_HAS_INTERRUPT,
626 .soft_reset = &genphy_soft_reset,
627 .config_init = &vsc85xx_config_init,
233275ec 628 .config_aneg = &vsc85xx_config_aneg,
af1fee98 629 .aneg_done = &genphy_aneg_done,
233275ec 630 .read_status = &vsc85xx_read_status,
af1fee98
RL
631 .ack_interrupt = &vsc85xx_ack_interrupt,
632 .config_intr = &vsc85xx_config_intr,
633 .suspend = &genphy_suspend,
634 .resume = &genphy_resume,
635 .probe = &vsc85xx_probe,
636 .set_wol = &vsc85xx_wol_set,
637 .get_wol = &vsc85xx_wol_get,
310d9ad5
RL
638 .get_tunable = &vsc85xx_get_tunable,
639 .set_tunable = &vsc85xx_set_tunable,
af1fee98 640},
d50736a8 641{
4ffd03f5
RL
642 .phy_id = PHY_ID_VSC8541,
643 .name = "Microsemi VSC8541 SyncE",
644 .phy_id_mask = 0xfffffff0,
645 .features = PHY_GBIT_FEATURES,
646 .flags = PHY_HAS_INTERRUPT,
647 .soft_reset = &genphy_soft_reset,
648 .config_init = &vsc85xx_config_init,
233275ec 649 .config_aneg = &vsc85xx_config_aneg,
4ffd03f5 650 .aneg_done = &genphy_aneg_done,
233275ec 651 .read_status = &vsc85xx_read_status,
4ffd03f5
RL
652 .ack_interrupt = &vsc85xx_ack_interrupt,
653 .config_intr = &vsc85xx_config_intr,
654 .suspend = &genphy_suspend,
655 .resume = &genphy_resume,
4f58e6dc
AN
656 .probe = &vsc85xx_probe,
657 .set_wol = &vsc85xx_wol_set,
658 .get_wol = &vsc85xx_wol_get,
310d9ad5
RL
659 .get_tunable = &vsc85xx_get_tunable,
660 .set_tunable = &vsc85xx_set_tunable,
d50736a8
RL
661}
662
663};
664
665module_phy_driver(vsc85xx_driver);
666
667static struct mdio_device_id __maybe_unused vsc85xx_tbl[] = {
af1fee98 668 { PHY_ID_VSC8530, 0xfffffff0, },
4ffd03f5 669 { PHY_ID_VSC8531, 0xfffffff0, },
af1fee98 670 { PHY_ID_VSC8540, 0xfffffff0, },
4ffd03f5
RL
671 { PHY_ID_VSC8541, 0xfffffff0, },
672 { }
d50736a8
RL
673};
674
675MODULE_DEVICE_TABLE(mdio, vsc85xx_tbl);
676
677MODULE_DESCRIPTION("Microsemi VSC85xx PHY driver");
678MODULE_AUTHOR("Nagaraju Lakkaraju");
679MODULE_LICENSE("Dual MIT/GPL");