Linux Audio

Check our new training course

Loading...
Note: File does not exist in v6.8.
  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
 17enum rgmii_rx_clock_delay {
 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
 26};
 27
 28/* Microsemi VSC85xx PHY registers */
 29/* IEEE 802. Std Registers */
 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
 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
 43#define MII_VSC85XX_INT_MASK		  25
 44#define MII_VSC85XX_INT_MASK_MASK	  0xa000
 45#define MII_VSC85XX_INT_MASK_WOL	  0x0040
 46#define MII_VSC85XX_INT_STATUS		  26
 47
 48#define MSCC_PHY_WOL_MAC_CONTROL          27
 49#define EDGE_RATE_CNTL_POS                5
 50#define EDGE_RATE_CNTL_MASK               0x00E0
 51
 52#define MSCC_PHY_DEV_AUX_CNTL		  28
 53#define HP_AUTO_MDIX_X_OVER_IND_MASK	  0x2000
 54
 55#define MSCC_EXT_PAGE_ACCESS		  31
 56#define MSCC_PHY_PAGE_STANDARD		  0x0000 /* Standard registers */
 57#define MSCC_PHY_PAGE_EXTENDED		  0x0001 /* Extended registers */
 58#define MSCC_PHY_PAGE_EXTENDED_2	  0x0002 /* Extended reg - page 2 */
 59
 60/* Extended Page 1 Registers */
 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
 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
 71/* Extended Page 2 Registers */
 72#define MSCC_PHY_RGMII_CNTL		  20
 73#define RGMII_RX_CLK_DELAY_MASK		  0x0070
 74#define RGMII_RX_CLK_DELAY_POS		  4
 75
 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
 87/* Microsemi PHY ID's */
 88#define PHY_ID_VSC8530			  0x00070560
 89#define PHY_ID_VSC8531			  0x00070570
 90#define PHY_ID_VSC8540			  0x00070760
 91#define PHY_ID_VSC8541			  0x00070770
 92
 93#define MSCC_VDDMAC_1500		  1500
 94#define MSCC_VDDMAC_1800		  1800
 95#define MSCC_VDDMAC_2500		  2500
 96#define MSCC_VDDMAC_3300		  3300
 97
 98#define DOWNSHIFT_COUNT_MAX		  5
 99
100struct vsc8531_private {
101	int rate_magic;
102};
103
104#ifdef CONFIG_OF_MDIO
105struct vsc8531_edge_rate_table {
106	u16 vddmac;
107	u8 slowdown[8];
108};
109
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
118static int vsc85xx_phy_page_set(struct phy_device *phydev, u8 page)
119{
120	int rc;
121
122	rc = phy_write(phydev, MSCC_EXT_PAGE_ACCESS, page);
123	return rc;
124}
125
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
179static int vsc85xx_downshift_get(struct phy_device *phydev, u8 *count)
180{
181	int rc;
182	u16 reg_val;
183
184	rc = vsc85xx_phy_page_set(phydev, MSCC_PHY_PAGE_EXTENDED);
185	if (rc != 0)
186		goto out;
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
196out:
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
216	rc = vsc85xx_phy_page_set(phydev, MSCC_PHY_PAGE_EXTENDED);
217	if (rc != 0)
218		goto out;
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)
225		goto out;
226
227	rc = vsc85xx_phy_page_set(phydev, MSCC_PHY_PAGE_STANDARD);
228
229out:
230	return rc;
231}
232
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
344#ifdef CONFIG_OF_MDIO
345static int vsc85xx_edge_rate_magic_get(struct phy_device *phydev)
346{
347	u8 sd;
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);
353
354	if (!of_node)
355		return -ENODEV;
356
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;
377}
378#endif /* CONFIG_OF_MDIO */
379
380static int vsc85xx_edge_rate_cntl_set(struct phy_device *phydev, u8 edge_rate)
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
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
439static int vsc85xx_default_config(struct phy_device *phydev)
440{
441	int rc;
442	u16 reg_val;
443
444	phydev->mdix_ctrl = ETH_TP_MDI_AUTO;
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;
449
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);
455
456out_unlock:
457	mutex_unlock(&phydev->lock);
458
459	return rc;
460}
461
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
485static int vsc85xx_config_init(struct phy_device *phydev)
486{
487	int rc;
488	struct vsc8531_private *vsc8531 = phydev->priv;
489
490	rc = vsc85xx_default_config(phydev);
491	if (rc)
492		return rc;
493
494	rc = vsc85xx_mac_if_set(phydev, phydev->interface);
495	if (rc)
496		return rc;
497
498	rc = vsc85xx_edge_rate_cntl_set(phydev, vsc8531->rate_magic);
499	if (rc)
500		return rc;
501
502	rc = genphy_config_init(phydev);
503
504	return rc;
505}
506
507static int vsc85xx_ack_interrupt(struct phy_device *phydev)
508{
509	int rc = 0;
510
511	if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
512		rc = phy_read(phydev, MII_VSC85XX_INT_STATUS);
513
514	return (rc < 0) ? rc : 0;
515}
516
517static int vsc85xx_config_intr(struct phy_device *phydev)
518{
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;
532}
533
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
556static int vsc85xx_probe(struct phy_device *phydev)
557{
558	int rate_magic;
559	struct vsc8531_private *vsc8531;
560
561	rate_magic = vsc85xx_edge_rate_magic_get(phydev);
562	if (rate_magic < 0)
563		return rate_magic;
564
565	vsc8531 = devm_kzalloc(&phydev->mdio.dev, sizeof(*vsc8531), GFP_KERNEL);
566	if (!vsc8531)
567		return -ENOMEM;
568
569	phydev->priv = vsc8531;
570
571	vsc8531->rate_magic = rate_magic;
572
573	return 0;
574}
575
576/* Microsemi VSC85xx PHYs */
577static struct phy_driver vsc85xx_driver[] = {
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,
586	.config_aneg    = &vsc85xx_config_aneg,
587	.aneg_done	= &genphy_aneg_done,
588	.read_status	= &vsc85xx_read_status,
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,
596	.get_tunable	= &vsc85xx_get_tunable,
597	.set_tunable	= &vsc85xx_set_tunable,
598},
599{
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,
607	.config_aneg    = &vsc85xx_config_aneg,
608	.aneg_done	= &genphy_aneg_done,
609	.read_status	= &vsc85xx_read_status,
610	.ack_interrupt  = &vsc85xx_ack_interrupt,
611	.config_intr    = &vsc85xx_config_intr,
612	.suspend	= &genphy_suspend,
613	.resume		= &genphy_resume,
614	.probe		= &vsc85xx_probe,
615	.set_wol	= &vsc85xx_wol_set,
616	.get_wol	= &vsc85xx_wol_get,
617	.get_tunable	= &vsc85xx_get_tunable,
618	.set_tunable	= &vsc85xx_set_tunable,
619},
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,
628	.config_aneg	= &vsc85xx_config_aneg,
629	.aneg_done	= &genphy_aneg_done,
630	.read_status	= &vsc85xx_read_status,
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,
638	.get_tunable	= &vsc85xx_get_tunable,
639	.set_tunable	= &vsc85xx_set_tunable,
640},
641{
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,
649	.config_aneg    = &vsc85xx_config_aneg,
650	.aneg_done	= &genphy_aneg_done,
651	.read_status	= &vsc85xx_read_status,
652	.ack_interrupt  = &vsc85xx_ack_interrupt,
653	.config_intr    = &vsc85xx_config_intr,
654	.suspend	= &genphy_suspend,
655	.resume		= &genphy_resume,
656	.probe		= &vsc85xx_probe,
657	.set_wol	= &vsc85xx_wol_set,
658	.get_wol	= &vsc85xx_wol_get,
659	.get_tunable	= &vsc85xx_get_tunable,
660	.set_tunable	= &vsc85xx_set_tunable,
661}
662
663};
664
665module_phy_driver(vsc85xx_driver);
666
667static struct mdio_device_id __maybe_unused vsc85xx_tbl[] = {
668	{ PHY_ID_VSC8530, 0xfffffff0, },
669	{ PHY_ID_VSC8531, 0xfffffff0, },
670	{ PHY_ID_VSC8540, 0xfffffff0, },
671	{ PHY_ID_VSC8541, 0xfffffff0, },
672	{ }
673};
674
675MODULE_DEVICE_TABLE(mdio, vsc85xx_tbl);
676
677MODULE_DESCRIPTION("Microsemi VSC85xx PHY driver");
678MODULE_AUTHOR("Nagaraju Lakkaraju");
679MODULE_LICENSE("Dual MIT/GPL");