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