Linux Audio

Check our new training course

Loading...
Note: File does not exist in v6.13.7.
  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
 18enum 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_1_MODE_SEL_MASK		  0x00F0
 58#define LED_0_MODE_SEL_MASK		  0x000F
 59#define LED_1_MODE_SEL_POS		  4
 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
106struct vsc8531_private {
107	int rate_magic;
108	u8 led_0_mode;
109	u8 led_1_mode;
110};
111
112#ifdef CONFIG_OF_MDIO
113struct vsc8531_edge_rate_table {
114	u16 vddmac;
115	u8 slowdown[8];
116};
117
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
126static int vsc85xx_phy_page_set(struct phy_device *phydev, u8 page)
127{
128	int rc;
129
130	rc = phy_write(phydev, MSCC_EXT_PAGE_ACCESS, page);
131	return rc;
132}
133
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
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
210static int vsc85xx_downshift_get(struct phy_device *phydev, u8 *count)
211{
212	int rc;
213	u16 reg_val;
214
215	rc = vsc85xx_phy_page_set(phydev, MSCC_PHY_PAGE_EXTENDED);
216	if (rc != 0)
217		goto out;
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
227out:
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
247	rc = vsc85xx_phy_page_set(phydev, MSCC_PHY_PAGE_EXTENDED);
248	if (rc != 0)
249		goto out;
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)
256		goto out;
257
258	rc = vsc85xx_phy_page_set(phydev, MSCC_PHY_PAGE_STANDARD);
259
260out:
261	return rc;
262}
263
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
375#ifdef CONFIG_OF_MDIO
376static int vsc85xx_edge_rate_magic_get(struct phy_device *phydev)
377{
378	u8 sd;
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);
384
385	if (!of_node)
386		return -ENODEV;
387
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}
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
427#else
428static int vsc85xx_edge_rate_magic_get(struct phy_device *phydev)
429{
430	return 0;
431}
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}
439#endif /* CONFIG_OF_MDIO */
440
441static int vsc85xx_edge_rate_cntl_set(struct phy_device *phydev, u8 edge_rate)
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
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
500static int vsc85xx_default_config(struct phy_device *phydev)
501{
502	int rc;
503	u16 reg_val;
504
505	phydev->mdix_ctrl = ETH_TP_MDI_AUTO;
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;
510
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);
516
517out_unlock:
518	mutex_unlock(&phydev->lock);
519
520	return rc;
521}
522
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
546static int vsc85xx_config_init(struct phy_device *phydev)
547{
548	int rc;
549	struct vsc8531_private *vsc8531 = phydev->priv;
550
551	rc = vsc85xx_default_config(phydev);
552	if (rc)
553		return rc;
554
555	rc = vsc85xx_mac_if_set(phydev, phydev->interface);
556	if (rc)
557		return rc;
558
559	rc = vsc85xx_edge_rate_cntl_set(phydev, vsc8531->rate_magic);
560	if (rc)
561		return rc;
562
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
571	rc = genphy_config_init(phydev);
572
573	return rc;
574}
575
576static int vsc85xx_ack_interrupt(struct phy_device *phydev)
577{
578	int rc = 0;
579
580	if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
581		rc = phy_read(phydev, MII_VSC85XX_INT_STATUS);
582
583	return (rc < 0) ? rc : 0;
584}
585
586static int vsc85xx_config_intr(struct phy_device *phydev)
587{
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;
601}
602
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
625static int vsc85xx_probe(struct phy_device *phydev)
626{
627	struct vsc8531_private *vsc8531;
628	int rate_magic;
629	int led_mode;
630
631	rate_magic = vsc85xx_edge_rate_magic_get(phydev);
632	if (rate_magic < 0)
633		return rate_magic;
634
635	vsc8531 = devm_kzalloc(&phydev->mdio.dev, sizeof(*vsc8531), GFP_KERNEL);
636	if (!vsc8531)
637		return -ENOMEM;
638
639	phydev->priv = vsc8531;
640
641	vsc8531->rate_magic = rate_magic;
642
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
656	return 0;
657}
658
659/* Microsemi VSC85xx PHYs */
660static struct phy_driver vsc85xx_driver[] = {
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,
669	.config_aneg    = &vsc85xx_config_aneg,
670	.aneg_done	= &genphy_aneg_done,
671	.read_status	= &vsc85xx_read_status,
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,
679	.get_tunable	= &vsc85xx_get_tunable,
680	.set_tunable	= &vsc85xx_set_tunable,
681},
682{
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,
690	.config_aneg    = &vsc85xx_config_aneg,
691	.aneg_done	= &genphy_aneg_done,
692	.read_status	= &vsc85xx_read_status,
693	.ack_interrupt  = &vsc85xx_ack_interrupt,
694	.config_intr    = &vsc85xx_config_intr,
695	.suspend	= &genphy_suspend,
696	.resume		= &genphy_resume,
697	.probe		= &vsc85xx_probe,
698	.set_wol	= &vsc85xx_wol_set,
699	.get_wol	= &vsc85xx_wol_get,
700	.get_tunable	= &vsc85xx_get_tunable,
701	.set_tunable	= &vsc85xx_set_tunable,
702},
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,
711	.config_aneg	= &vsc85xx_config_aneg,
712	.aneg_done	= &genphy_aneg_done,
713	.read_status	= &vsc85xx_read_status,
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,
721	.get_tunable	= &vsc85xx_get_tunable,
722	.set_tunable	= &vsc85xx_set_tunable,
723},
724{
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,
732	.config_aneg    = &vsc85xx_config_aneg,
733	.aneg_done	= &genphy_aneg_done,
734	.read_status	= &vsc85xx_read_status,
735	.ack_interrupt  = &vsc85xx_ack_interrupt,
736	.config_intr    = &vsc85xx_config_intr,
737	.suspend	= &genphy_suspend,
738	.resume		= &genphy_resume,
739	.probe		= &vsc85xx_probe,
740	.set_wol	= &vsc85xx_wol_set,
741	.get_wol	= &vsc85xx_wol_get,
742	.get_tunable	= &vsc85xx_get_tunable,
743	.set_tunable	= &vsc85xx_set_tunable,
744}
745
746};
747
748module_phy_driver(vsc85xx_driver);
749
750static struct mdio_device_id __maybe_unused vsc85xx_tbl[] = {
751	{ PHY_ID_VSC8530, 0xfffffff0, },
752	{ PHY_ID_VSC8531, 0xfffffff0, },
753	{ PHY_ID_VSC8540, 0xfffffff0, },
754	{ PHY_ID_VSC8541, 0xfffffff0, },
755	{ }
756};
757
758MODULE_DEVICE_TABLE(mdio, vsc85xx_tbl);
759
760MODULE_DESCRIPTION("Microsemi VSC85xx PHY driver");
761MODULE_AUTHOR("Nagaraju Lakkaraju");
762MODULE_LICENSE("Dual MIT/GPL");