brintos

brintos / linux-shallow public Read only

0
0
Text · 18.4 KiB · 2377179 Raw
692 lines · c
1// SPDX-License-Identifier: GPL-2.0+2/*3 * Driver for Vitesse PHYs4 *5 * Author: Kriston Carson6 */7 8#include <linux/kernel.h>9#include <linux/module.h>10#include <linux/mii.h>11#include <linux/ethtool.h>12#include <linux/phy.h>13#include <linux/bitfield.h>14 15/* Vitesse Extended Page Magic Register(s) */16#define MII_VSC73XX_EXT_PAGE_1E		0x0117#define MII_VSC82X4_EXT_PAGE_16E	0x1018#define MII_VSC82X4_EXT_PAGE_17E	0x1119#define MII_VSC82X4_EXT_PAGE_18E	0x1220 21/* Vitesse Extended Control Register 1 */22#define MII_VSC8244_EXT_CON1           0x1723#define MII_VSC8244_EXTCON1_INIT       0x000024#define MII_VSC8244_EXTCON1_TX_SKEW_MASK	0x0c0025#define MII_VSC8244_EXTCON1_RX_SKEW_MASK	0x030026#define MII_VSC8244_EXTCON1_TX_SKEW	0x080027#define MII_VSC8244_EXTCON1_RX_SKEW	0x020028 29/* Vitesse Interrupt Mask Register */30#define MII_VSC8244_IMASK		0x1931#define MII_VSC8244_IMASK_IEN		0x800032#define MII_VSC8244_IMASK_SPEED		0x400033#define MII_VSC8244_IMASK_LINK		0x200034#define MII_VSC8244_IMASK_DUPLEX	0x100035#define MII_VSC8244_IMASK_MASK		0xf00036 37#define MII_VSC8221_IMASK_MASK		0xa00038 39/* Vitesse Interrupt Status Register */40#define MII_VSC8244_ISTAT		0x1a41#define MII_VSC8244_ISTAT_STATUS	0x800042#define MII_VSC8244_ISTAT_SPEED		0x400043#define MII_VSC8244_ISTAT_LINK		0x200044#define MII_VSC8244_ISTAT_DUPLEX	0x100045#define MII_VSC8244_ISTAT_MASK		(MII_VSC8244_ISTAT_SPEED | \46					 MII_VSC8244_ISTAT_LINK | \47					 MII_VSC8244_ISTAT_DUPLEX)48 49#define MII_VSC8221_ISTAT_MASK		MII_VSC8244_ISTAT_LINK50 51/* Vitesse Auxiliary Control/Status Register */52#define MII_VSC8244_AUX_CONSTAT		0x1c53#define MII_VSC8244_AUXCONSTAT_INIT	0x000054#define MII_VSC8244_AUXCONSTAT_DUPLEX	0x002055#define MII_VSC8244_AUXCONSTAT_SPEED	0x001856#define MII_VSC8244_AUXCONSTAT_GBIT	0x001057#define MII_VSC8244_AUXCONSTAT_100	0x000858 59#define MII_VSC8221_AUXCONSTAT_INIT	0x0004 /* need to set this bit? */60#define MII_VSC8221_AUXCONSTAT_RESERVED	0x000461 62/* Vitesse Extended Page Access Register */63#define MII_VSC82X4_EXT_PAGE_ACCESS	0x1f64 65/* Vitesse VSC73XX Extended Control Register */66#define MII_VSC73XX_PHY_CTRL_EXT3		0x1467 68#define MII_VSC73XX_PHY_CTRL_EXT3_DOWNSHIFT_EN	BIT(4)69#define MII_VSC73XX_PHY_CTRL_EXT3_DOWNSHIFT_CNT	GENMASK(3, 2)70#define MII_VSC73XX_PHY_CTRL_EXT3_DOWNSHIFT_STA	BIT(1)71#define MII_VSC73XX_DOWNSHIFT_MAX		572#define MII_VSC73XX_DOWNSHIFT_INVAL		173 74/* VSC73XX PHY_BYPASS_CTRL register*/75#define MII_VSC73XX_PHY_BYPASS_CTRL		MII_DCOUNTER76#define MII_VSC73XX_PBC_TX_DIS			BIT(15)77#define MII_VSC73XX_PBC_FOR_SPD_AUTO_MDIX_DIS	BIT(7)78#define MII_VSC73XX_PBC_PAIR_SWAP_DIS		BIT(5)79#define MII_VSC73XX_PBC_POL_INV_DIS		BIT(4)80#define MII_VSC73XX_PBC_PARALLEL_DET_DIS	BIT(3)81#define MII_VSC73XX_PBC_AUTO_NP_EXCHANGE_DIS	BIT(1)82 83/* VSC73XX PHY_AUX_CTRL_STAT register */84#define MII_VSC73XX_PHY_AUX_CTRL_STAT	MII_NCONFIG85#define MII_VSC73XX_PACS_NO_MDI_X_IND	BIT(13)86 87/* Vitesse VSC8601 Extended PHY Control Register 1 */88#define MII_VSC8601_EPHY_CTL		0x1789#define MII_VSC8601_EPHY_CTL_RGMII_SKEW	(1 << 8)90 91#define PHY_ID_VSC8234			0x000fc62092#define PHY_ID_VSC8244			0x000fc6c093#define PHY_ID_VSC8572			0x000704d094#define PHY_ID_VSC8601			0x0007042095#define PHY_ID_VSC7385			0x0007045096#define PHY_ID_VSC7388			0x0007048097#define PHY_ID_VSC7395			0x0007055098#define PHY_ID_VSC7398			0x0007058099#define PHY_ID_VSC8662			0x00070660100#define PHY_ID_VSC8221			0x000fc550101#define PHY_ID_VSC8211			0x000fc4b0102 103MODULE_DESCRIPTION("Vitesse PHY driver");104MODULE_AUTHOR("Kriston Carson");105MODULE_LICENSE("GPL");106 107static int vsc824x_add_skew(struct phy_device *phydev)108{109	int err;110	int extcon;111 112	extcon = phy_read(phydev, MII_VSC8244_EXT_CON1);113 114	if (extcon < 0)115		return extcon;116 117	extcon &= ~(MII_VSC8244_EXTCON1_TX_SKEW_MASK |118			MII_VSC8244_EXTCON1_RX_SKEW_MASK);119 120	extcon |= (MII_VSC8244_EXTCON1_TX_SKEW |121			MII_VSC8244_EXTCON1_RX_SKEW);122 123	err = phy_write(phydev, MII_VSC8244_EXT_CON1, extcon);124 125	return err;126}127 128static int vsc824x_config_init(struct phy_device *phydev)129{130	int err;131 132	err = phy_write(phydev, MII_VSC8244_AUX_CONSTAT,133			MII_VSC8244_AUXCONSTAT_INIT);134	if (err < 0)135		return err;136 137	if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID)138		err = vsc824x_add_skew(phydev);139 140	return err;141}142 143#define VSC73XX_EXT_PAGE_ACCESS 0x1f144 145static int vsc73xx_read_page(struct phy_device *phydev)146{147	return __phy_read(phydev, VSC73XX_EXT_PAGE_ACCESS);148}149 150static int vsc73xx_write_page(struct phy_device *phydev, int page)151{152	return __phy_write(phydev, VSC73XX_EXT_PAGE_ACCESS, page);153}154 155static int vsc73xx_get_downshift(struct phy_device *phydev, u8 *data)156{157	int val, enable, cnt;158 159	val = phy_read_paged(phydev, MII_VSC73XX_EXT_PAGE_1E,160			     MII_VSC73XX_PHY_CTRL_EXT3);161	if (val < 0)162		return val;163 164	enable = FIELD_GET(MII_VSC73XX_PHY_CTRL_EXT3_DOWNSHIFT_EN, val);165	cnt = FIELD_GET(MII_VSC73XX_PHY_CTRL_EXT3_DOWNSHIFT_CNT, val) + 2;166 167	*data = enable ? cnt : DOWNSHIFT_DEV_DISABLE;168 169	return 0;170}171 172static int vsc73xx_set_downshift(struct phy_device *phydev, u8 cnt)173{174	u16 mask, val;175	int ret;176 177	if (cnt > MII_VSC73XX_DOWNSHIFT_MAX)178		return -E2BIG;179	else if (cnt == MII_VSC73XX_DOWNSHIFT_INVAL)180		return -EINVAL;181 182	mask = MII_VSC73XX_PHY_CTRL_EXT3_DOWNSHIFT_EN;183 184	if (!cnt) {185		val = 0;186	} else {187		mask |= MII_VSC73XX_PHY_CTRL_EXT3_DOWNSHIFT_CNT;188		val = MII_VSC73XX_PHY_CTRL_EXT3_DOWNSHIFT_EN |189		      FIELD_PREP(MII_VSC73XX_PHY_CTRL_EXT3_DOWNSHIFT_CNT,190				 cnt - 2);191	}192 193	ret = phy_modify_paged(phydev, MII_VSC73XX_EXT_PAGE_1E,194			       MII_VSC73XX_PHY_CTRL_EXT3, mask, val);195	if (ret < 0)196		return ret;197 198	return genphy_soft_reset(phydev);199}200 201static int vsc73xx_get_tunable(struct phy_device *phydev,202			       struct ethtool_tunable *tuna, void *data)203{204	switch (tuna->id) {205	case ETHTOOL_PHY_DOWNSHIFT:206		return vsc73xx_get_downshift(phydev, data);207	default:208		return -EOPNOTSUPP;209	}210}211 212static int vsc73xx_set_tunable(struct phy_device *phydev,213			       struct ethtool_tunable *tuna, const void *data)214{215	switch (tuna->id) {216	case ETHTOOL_PHY_DOWNSHIFT:217		return vsc73xx_set_downshift(phydev, *(const u8 *)data);218	default:219		return -EOPNOTSUPP;220	}221}222 223static void vsc73xx_config_init(struct phy_device *phydev)224{225	/* Receiver init */226	phy_write(phydev, 0x1f, 0x2a30);227	phy_modify(phydev, 0x0c, 0x0300, 0x0200);228	phy_write(phydev, 0x1f, 0x0000);229 230	/* Config LEDs 0x61 */231	phy_modify(phydev, MII_TPISTATUS, 0xff00, 0x0061);232 233	/* Enable downshift by default */234	vsc73xx_set_downshift(phydev, MII_VSC73XX_DOWNSHIFT_MAX);235 236	/* Set Auto MDI-X by default */237	phydev->mdix_ctrl = ETH_TP_MDI_AUTO;238}239 240static int vsc738x_config_init(struct phy_device *phydev)241{242	u16 rev;243	/* This magic sequence appear in the application note244	 * "VSC7385/7388 PHY Configuration".245	 *246	 * Maybe one day we will get to know what it all means.247	 */248	phy_write(phydev, 0x1f, 0x2a30);249	phy_modify(phydev, 0x08, 0x0200, 0x0200);250	phy_write(phydev, 0x1f, 0x52b5);251	phy_write(phydev, 0x10, 0xb68a);252	phy_modify(phydev, 0x12, 0xff07, 0x0003);253	phy_modify(phydev, 0x11, 0x00ff, 0x00a2);254	phy_write(phydev, 0x10, 0x968a);255	phy_write(phydev, 0x1f, 0x2a30);256	phy_modify(phydev, 0x08, 0x0200, 0x0000);257	phy_write(phydev, 0x1f, 0x0000);258 259	/* Read revision */260	rev = phy_read(phydev, MII_PHYSID2);261	rev &= 0x0f;262 263	/* Special quirk for revision 0 */264	if (rev == 0) {265		phy_write(phydev, 0x1f, 0x2a30);266		phy_modify(phydev, 0x08, 0x0200, 0x0200);267		phy_write(phydev, 0x1f, 0x52b5);268		phy_write(phydev, 0x12, 0x0000);269		phy_write(phydev, 0x11, 0x0689);270		phy_write(phydev, 0x10, 0x8f92);271		phy_write(phydev, 0x1f, 0x52b5);272		phy_write(phydev, 0x12, 0x0000);273		phy_write(phydev, 0x11, 0x0e35);274		phy_write(phydev, 0x10, 0x9786);275		phy_write(phydev, 0x1f, 0x2a30);276		phy_modify(phydev, 0x08, 0x0200, 0x0000);277		phy_write(phydev, 0x17, 0xff80);278		phy_write(phydev, 0x17, 0x0000);279	}280 281	phy_write(phydev, 0x1f, 0x0000);282	phy_write(phydev, 0x12, 0x0048);283 284	if (rev == 0) {285		phy_write(phydev, 0x1f, 0x2a30);286		phy_write(phydev, 0x14, 0x6600);287		phy_write(phydev, 0x1f, 0x0000);288		phy_write(phydev, 0x18, 0xa24e);289	} else {290		phy_write(phydev, 0x1f, 0x2a30);291		phy_modify(phydev, 0x16, 0x0fc0, 0x0240);292		phy_modify(phydev, 0x14, 0x6000, 0x4000);293		/* bits 14-15 in extended register 0x14 controls DACG amplitude294		 * 6 = -8%, 2 is hardware default295		 */296		phy_write(phydev, 0x1f, 0x0001);297		phy_modify(phydev, 0x14, 0xe000, 0x6000);298		phy_write(phydev, 0x1f, 0x0000);299	}300 301	vsc73xx_config_init(phydev);302 303	return 0;304}305 306static int vsc739x_config_init(struct phy_device *phydev)307{308	/* This magic sequence appears in the VSC7395 SparX-G5e application309	 * note "VSC7395/VSC7398 PHY Configuration"310	 *311	 * Maybe one day we will get to know what it all means.312	 */313	phy_write(phydev, 0x1f, 0x2a30);314	phy_modify(phydev, 0x08, 0x0200, 0x0200);315	phy_write(phydev, 0x1f, 0x52b5);316	phy_write(phydev, 0x10, 0xb68a);317	phy_modify(phydev, 0x12, 0xff07, 0x0003);318	phy_modify(phydev, 0x11, 0x00ff, 0x00a2);319	phy_write(phydev, 0x10, 0x968a);320	phy_write(phydev, 0x1f, 0x2a30);321	phy_modify(phydev, 0x08, 0x0200, 0x0000);322	phy_write(phydev, 0x1f, 0x0000);323 324	phy_write(phydev, 0x1f, 0x0000);325	phy_write(phydev, 0x12, 0x0048);326	phy_write(phydev, 0x1f, 0x2a30);327	phy_modify(phydev, 0x16, 0x0fc0, 0x0240);328	phy_modify(phydev, 0x14, 0x6000, 0x4000);329	phy_write(phydev, 0x1f, 0x0001);330	phy_modify(phydev, 0x14, 0xe000, 0x6000);331	phy_write(phydev, 0x1f, 0x0000);332 333	vsc73xx_config_init(phydev);334 335	return 0;336}337 338static int vsc73xx_mdix_set(struct phy_device *phydev, u8 mdix)339{340	int ret;341	u16 val;342 343	val = phy_read(phydev, MII_VSC73XX_PHY_BYPASS_CTRL);344 345	switch (mdix) {346	case ETH_TP_MDI:347		val |= MII_VSC73XX_PBC_FOR_SPD_AUTO_MDIX_DIS |348		       MII_VSC73XX_PBC_PAIR_SWAP_DIS |349		       MII_VSC73XX_PBC_POL_INV_DIS;350		break;351	case ETH_TP_MDI_X:352		/* When MDI-X auto configuration is disabled, is possible353		 * to force only MDI mode. Let's use autoconfig for forced354		 * MDIX mode.355		 */356	case ETH_TP_MDI_AUTO:357		val &= ~(MII_VSC73XX_PBC_FOR_SPD_AUTO_MDIX_DIS |358			 MII_VSC73XX_PBC_PAIR_SWAP_DIS |359			 MII_VSC73XX_PBC_POL_INV_DIS);360		break;361	default:362		return -EINVAL;363	}364 365	ret = phy_write(phydev, MII_VSC73XX_PHY_BYPASS_CTRL, val);366	if (ret)367		return ret;368 369	return genphy_restart_aneg(phydev);370}371 372static int vsc73xx_config_aneg(struct phy_device *phydev)373{374	int ret;375 376	ret = vsc73xx_mdix_set(phydev, phydev->mdix_ctrl);377	if (ret)378		return ret;379 380	return genphy_config_aneg(phydev);381}382 383static int vsc73xx_mdix_get(struct phy_device *phydev, u8 *mdix)384{385	u16 reg_val;386 387	reg_val = phy_read(phydev, MII_VSC73XX_PHY_AUX_CTRL_STAT);388	if (reg_val & MII_VSC73XX_PACS_NO_MDI_X_IND)389		*mdix = ETH_TP_MDI;390	else391		*mdix = ETH_TP_MDI_X;392 393	return 0;394}395 396static int vsc73xx_read_status(struct phy_device *phydev)397{398	int ret;399 400	ret = vsc73xx_mdix_get(phydev, &phydev->mdix);401	if (ret < 0)402		return ret;403 404	return genphy_read_status(phydev);405}406 407/* This adds a skew for both TX and RX clocks, so the skew should only be408 * applied to "rgmii-id" interfaces. It may not work as expected409 * on "rgmii-txid", "rgmii-rxid" or "rgmii" interfaces.410 */411static int vsc8601_add_skew(struct phy_device *phydev)412{413	int ret;414 415	ret = phy_read(phydev, MII_VSC8601_EPHY_CTL);416	if (ret < 0)417		return ret;418 419	ret |= MII_VSC8601_EPHY_CTL_RGMII_SKEW;420	return phy_write(phydev, MII_VSC8601_EPHY_CTL, ret);421}422 423static int vsc8601_config_init(struct phy_device *phydev)424{425	int ret = 0;426 427	if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID)428		ret = vsc8601_add_skew(phydev);429 430	if (ret < 0)431		return ret;432 433	return 0;434}435 436static int vsc82xx_config_intr(struct phy_device *phydev)437{438	int err;439 440	if (phydev->interrupts == PHY_INTERRUPT_ENABLED)441		/* Don't bother to ACK the interrupts since the 824x cannot442		 * clear the interrupts if they are disabled.443		 */444		err = phy_write(phydev, MII_VSC8244_IMASK,445			(phydev->drv->phy_id == PHY_ID_VSC8234 ||446			 phydev->drv->phy_id == PHY_ID_VSC8244 ||447			 phydev->drv->phy_id == PHY_ID_VSC8572 ||448			 phydev->drv->phy_id == PHY_ID_VSC8601) ?449				MII_VSC8244_IMASK_MASK :450				MII_VSC8221_IMASK_MASK);451	else {452		/* The Vitesse PHY cannot clear the interrupt453		 * once it has disabled them, so we clear them first454		 */455		err = phy_read(phydev, MII_VSC8244_ISTAT);456 457		if (err < 0)458			return err;459 460		err = phy_write(phydev, MII_VSC8244_IMASK, 0);461	}462 463	return err;464}465 466static irqreturn_t vsc82xx_handle_interrupt(struct phy_device *phydev)467{468	int irq_status, irq_mask;469 470	if (phydev->drv->phy_id == PHY_ID_VSC8244 ||471	    phydev->drv->phy_id == PHY_ID_VSC8572 ||472	    phydev->drv->phy_id == PHY_ID_VSC8601)473		irq_mask = MII_VSC8244_ISTAT_MASK;474	else475		irq_mask = MII_VSC8221_ISTAT_MASK;476 477	irq_status = phy_read(phydev, MII_VSC8244_ISTAT);478	if (irq_status < 0) {479		phy_error(phydev);480		return IRQ_NONE;481	}482 483	if (!(irq_status & irq_mask))484		return IRQ_NONE;485 486	phy_trigger_machine(phydev);487 488	return IRQ_HANDLED;489}490 491static int vsc8221_config_init(struct phy_device *phydev)492{493	int err;494 495	err = phy_write(phydev, MII_VSC8244_AUX_CONSTAT,496			MII_VSC8221_AUXCONSTAT_INIT);497	return err;498 499	/* Perhaps we should set EXT_CON1 based on the interface?500	 * Options are 802.3Z SerDes or SGMII501	 */502}503 504/* vsc82x4_config_autocross_enable - Enable auto MDI/MDI-X for forced links505 * @phydev: target phy_device struct506 *507 * Enable auto MDI/MDI-X when in 10/100 forced link speeds by writing508 * special values in the VSC8234/VSC8244 extended reserved registers509 */510static int vsc82x4_config_autocross_enable(struct phy_device *phydev)511{512	int ret;513 514	if (phydev->autoneg == AUTONEG_ENABLE || phydev->speed > SPEED_100)515		return 0;516 517	/* map extended registers set 0x10 - 0x1e */518	ret = phy_write(phydev, MII_VSC82X4_EXT_PAGE_ACCESS, 0x52b5);519	if (ret >= 0)520		ret = phy_write(phydev, MII_VSC82X4_EXT_PAGE_18E, 0x0012);521	if (ret >= 0)522		ret = phy_write(phydev, MII_VSC82X4_EXT_PAGE_17E, 0x2803);523	if (ret >= 0)524		ret = phy_write(phydev, MII_VSC82X4_EXT_PAGE_16E, 0x87fa);525	/* map standard registers set 0x10 - 0x1e */526	if (ret >= 0)527		ret = phy_write(phydev, MII_VSC82X4_EXT_PAGE_ACCESS, 0x0000);528	else529		phy_write(phydev, MII_VSC82X4_EXT_PAGE_ACCESS, 0x0000);530 531	return ret;532}533 534/* vsc82x4_config_aneg - restart auto-negotiation or write BMCR535 * @phydev: target phy_device struct536 *537 * Description: If auto-negotiation is enabled, we configure the538 *   advertising, and then restart auto-negotiation.  If it is not539 *   enabled, then we write the BMCR and also start the auto540 *   MDI/MDI-X feature541 */542static int vsc82x4_config_aneg(struct phy_device *phydev)543{544	int ret;545 546	/* Enable auto MDI/MDI-X when in 10/100 forced link speeds by547	 * writing special values in the VSC8234 extended reserved registers548	 */549	if (phydev->autoneg != AUTONEG_ENABLE && phydev->speed <= SPEED_100) {550		ret = genphy_setup_forced(phydev);551 552		if (ret < 0) /* error */553			return ret;554 555		return vsc82x4_config_autocross_enable(phydev);556	}557 558	return genphy_config_aneg(phydev);559}560 561/* Vitesse 82xx */562static struct phy_driver vsc82xx_driver[] = {563{564	.phy_id         = PHY_ID_VSC8234,565	.name           = "Vitesse VSC8234",566	.phy_id_mask    = 0x000ffff0,567	/* PHY_GBIT_FEATURES */568	.config_init    = &vsc824x_config_init,569	.config_aneg    = &vsc82x4_config_aneg,570	.config_intr    = &vsc82xx_config_intr,571	.handle_interrupt = &vsc82xx_handle_interrupt,572}, {573	.phy_id		= PHY_ID_VSC8244,574	.name		= "Vitesse VSC8244",575	.phy_id_mask	= 0x000fffc0,576	/* PHY_GBIT_FEATURES */577	.config_init	= &vsc824x_config_init,578	.config_aneg	= &vsc82x4_config_aneg,579	.config_intr	= &vsc82xx_config_intr,580	.handle_interrupt = &vsc82xx_handle_interrupt,581}, {582	.phy_id         = PHY_ID_VSC8572,583	.name           = "Vitesse VSC8572",584	.phy_id_mask    = 0x000ffff0,585	/* PHY_GBIT_FEATURES */586	.config_init    = &vsc824x_config_init,587	.config_aneg    = &vsc82x4_config_aneg,588	.config_intr    = &vsc82xx_config_intr,589	.handle_interrupt = &vsc82xx_handle_interrupt,590}, {591	.phy_id         = PHY_ID_VSC8601,592	.name           = "Vitesse VSC8601",593	.phy_id_mask    = 0x000ffff0,594	/* PHY_GBIT_FEATURES */595	.config_init    = &vsc8601_config_init,596	.config_intr    = &vsc82xx_config_intr,597	.handle_interrupt = &vsc82xx_handle_interrupt,598}, {599	.phy_id         = PHY_ID_VSC7385,600	.name           = "Vitesse VSC7385",601	.phy_id_mask    = 0x000ffff0,602	/* PHY_GBIT_FEATURES */603	.config_init    = vsc738x_config_init,604	.config_aneg    = vsc73xx_config_aneg,605	.read_status	= vsc73xx_read_status,606	.read_page      = vsc73xx_read_page,607	.write_page     = vsc73xx_write_page,608	.get_tunable    = vsc73xx_get_tunable,609	.set_tunable    = vsc73xx_set_tunable,610}, {611	.phy_id         = PHY_ID_VSC7388,612	.name           = "Vitesse VSC7388",613	.phy_id_mask    = 0x000ffff0,614	/* PHY_GBIT_FEATURES */615	.config_init    = vsc738x_config_init,616	.config_aneg    = vsc73xx_config_aneg,617	.read_status	= vsc73xx_read_status,618	.read_page      = vsc73xx_read_page,619	.write_page     = vsc73xx_write_page,620	.get_tunable    = vsc73xx_get_tunable,621	.set_tunable    = vsc73xx_set_tunable,622}, {623	.phy_id         = PHY_ID_VSC7395,624	.name           = "Vitesse VSC7395",625	.phy_id_mask    = 0x000ffff0,626	/* PHY_GBIT_FEATURES */627	.config_init    = vsc739x_config_init,628	.config_aneg    = vsc73xx_config_aneg,629	.read_status	= vsc73xx_read_status,630	.read_page      = vsc73xx_read_page,631	.write_page     = vsc73xx_write_page,632	.get_tunable    = vsc73xx_get_tunable,633	.set_tunable    = vsc73xx_set_tunable,634}, {635	.phy_id         = PHY_ID_VSC7398,636	.name           = "Vitesse VSC7398",637	.phy_id_mask    = 0x000ffff0,638	/* PHY_GBIT_FEATURES */639	.config_init    = vsc739x_config_init,640	.config_aneg    = vsc73xx_config_aneg,641	.read_status	= vsc73xx_read_status,642	.read_page      = vsc73xx_read_page,643	.write_page     = vsc73xx_write_page,644	.get_tunable    = vsc73xx_get_tunable,645	.set_tunable    = vsc73xx_set_tunable,646}, {647	.phy_id         = PHY_ID_VSC8662,648	.name           = "Vitesse VSC8662",649	.phy_id_mask    = 0x000ffff0,650	/* PHY_GBIT_FEATURES */651	.config_init    = &vsc824x_config_init,652	.config_aneg    = &vsc82x4_config_aneg,653	.config_intr    = &vsc82xx_config_intr,654	.handle_interrupt = &vsc82xx_handle_interrupt,655}, {656	/* Vitesse 8221 */657	.phy_id		= PHY_ID_VSC8221,658	.phy_id_mask	= 0x000ffff0,659	.name		= "Vitesse VSC8221",660	/* PHY_GBIT_FEATURES */661	.config_init	= &vsc8221_config_init,662	.config_intr	= &vsc82xx_config_intr,663	.handle_interrupt = &vsc82xx_handle_interrupt,664}, {665	/* Vitesse 8211 */666	.phy_id		= PHY_ID_VSC8211,667	.phy_id_mask	= 0x000ffff0,668	.name		= "Vitesse VSC8211",669	/* PHY_GBIT_FEATURES */670	.config_init	= &vsc8221_config_init,671	.config_intr	= &vsc82xx_config_intr,672	.handle_interrupt = &vsc82xx_handle_interrupt,673} };674 675module_phy_driver(vsc82xx_driver);676 677static struct mdio_device_id __maybe_unused vitesse_tbl[] = {678	{ PHY_ID_VSC8234, 0x000ffff0 },679	{ PHY_ID_VSC8244, 0x000fffc0 },680	{ PHY_ID_VSC8572, 0x000ffff0 },681	{ PHY_ID_VSC7385, 0x000ffff0 },682	{ PHY_ID_VSC7388, 0x000ffff0 },683	{ PHY_ID_VSC7395, 0x000ffff0 },684	{ PHY_ID_VSC7398, 0x000ffff0 },685	{ PHY_ID_VSC8662, 0x000ffff0 },686	{ PHY_ID_VSC8221, 0x000ffff0 },687	{ PHY_ID_VSC8211, 0x000ffff0 },688	{ }689};690 691MODULE_DEVICE_TABLE(mdio, vitesse_tbl);692