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