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