1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Driver for Vitesse PHYs 4 * 5 * Author: Kriston Carson 6 */ 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 0x01 17 #define MII_VSC82X4_EXT_PAGE_16E 0x10 18 #define MII_VSC82X4_EXT_PAGE_17E 0x11 19 #define MII_VSC82X4_EXT_PAGE_18E 0x12 20 21 /* Vitesse Extended Control Register 1 */ 22 #define MII_VSC8244_EXT_CON1 0x17 23 #define MII_VSC8244_EXTCON1_INIT 0x0000 24 #define MII_VSC8244_EXTCON1_TX_SKEW_MASK 0x0c00 25 #define MII_VSC8244_EXTCON1_RX_SKEW_MASK 0x0300 26 #define MII_VSC8244_EXTCON1_TX_SKEW 0x0800 27 #define MII_VSC8244_EXTCON1_RX_SKEW 0x0200 28 29 /* Vitesse Interrupt Mask Register */ 30 #define MII_VSC8244_IMASK 0x19 31 #define MII_VSC8244_IMASK_IEN 0x8000 32 #define MII_VSC8244_IMASK_SPEED 0x4000 33 #define MII_VSC8244_IMASK_LINK 0x2000 34 #define MII_VSC8244_IMASK_DUPLEX 0x1000 35 #define MII_VSC8244_IMASK_MASK 0xf000 36 37 #define MII_VSC8221_IMASK_MASK 0xa000 38 39 /* Vitesse Interrupt Status Register */ 40 #define MII_VSC8244_ISTAT 0x1a 41 #define MII_VSC8244_ISTAT_STATUS 0x8000 42 #define MII_VSC8244_ISTAT_SPEED 0x4000 43 #define MII_VSC8244_ISTAT_LINK 0x2000 44 #define MII_VSC8244_ISTAT_DUPLEX 0x1000 45 #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_LINK 50 51 /* Vitesse Auxiliary Control/Status Register */ 52 #define MII_VSC8244_AUX_CONSTAT 0x1c 53 #define MII_VSC8244_AUXCONSTAT_INIT 0x0000 54 #define MII_VSC8244_AUXCONSTAT_DUPLEX 0x0020 55 #define MII_VSC8244_AUXCONSTAT_SPEED 0x0018 56 #define MII_VSC8244_AUXCONSTAT_GBIT 0x0010 57 #define MII_VSC8244_AUXCONSTAT_100 0x0008 58 59 #define MII_VSC8221_AUXCONSTAT_INIT 0x0004 /* need to set this bit? */ 60 #define MII_VSC8221_AUXCONSTAT_RESERVED 0x0004 61 62 /* Vitesse Extended Page Access Register */ 63 #define MII_VSC82X4_EXT_PAGE_ACCESS 0x1f 64 65 /* Vitesse VSC73XX Extended Control Register */ 66 #define MII_VSC73XX_PHY_CTRL_EXT3 0x14 67 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 5 72 #define MII_VSC73XX_DOWNSHIFT_INVAL 1 73 74 /* Vitesse VSC8601 Extended PHY Control Register 1 */ 75 #define MII_VSC8601_EPHY_CTL 0x17 76 #define MII_VSC8601_EPHY_CTL_RGMII_SKEW (1 << 8) 77 78 #define PHY_ID_VSC8234 0x000fc620 79 #define PHY_ID_VSC8244 0x000fc6c0 80 #define PHY_ID_VSC8572 0x000704d0 81 #define PHY_ID_VSC8601 0x00070420 82 #define PHY_ID_VSC7385 0x00070450 83 #define PHY_ID_VSC7388 0x00070480 84 #define PHY_ID_VSC7395 0x00070550 85 #define PHY_ID_VSC7398 0x00070580 86 #define PHY_ID_VSC8662 0x00070660 87 #define PHY_ID_VSC8221 0x000fc550 88 #define PHY_ID_VSC8211 0x000fc4b0 89 90 MODULE_DESCRIPTION("Vitesse PHY driver"); 91 MODULE_AUTHOR("Kriston Carson"); 92 MODULE_LICENSE("GPL"); 93 94 static int vsc824x_add_skew(struct phy_device *phydev) 95 { 96 int err; 97 int extcon; 98 99 extcon = phy_read(phydev, MII_VSC8244_EXT_CON1); 100 101 if (extcon < 0) 102 return extcon; 103 104 extcon &= ~(MII_VSC8244_EXTCON1_TX_SKEW_MASK | 105 MII_VSC8244_EXTCON1_RX_SKEW_MASK); 106 107 extcon |= (MII_VSC8244_EXTCON1_TX_SKEW | 108 MII_VSC8244_EXTCON1_RX_SKEW); 109 110 err = phy_write(phydev, MII_VSC8244_EXT_CON1, extcon); 111 112 return err; 113 } 114 115 static int vsc824x_config_init(struct phy_device *phydev) 116 { 117 int err; 118 119 err = phy_write(phydev, MII_VSC8244_AUX_CONSTAT, 120 MII_VSC8244_AUXCONSTAT_INIT); 121 if (err < 0) 122 return err; 123 124 if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) 125 err = vsc824x_add_skew(phydev); 126 127 return err; 128 } 129 130 #define VSC73XX_EXT_PAGE_ACCESS 0x1f 131 132 static int vsc73xx_read_page(struct phy_device *phydev) 133 { 134 return __phy_read(phydev, VSC73XX_EXT_PAGE_ACCESS); 135 } 136 137 static int vsc73xx_write_page(struct phy_device *phydev, int page) 138 { 139 return __phy_write(phydev, VSC73XX_EXT_PAGE_ACCESS, page); 140 } 141 142 static int vsc73xx_get_downshift(struct phy_device *phydev, u8 *data) 143 { 144 int val, enable, cnt; 145 146 val = phy_read_paged(phydev, MII_VSC73XX_EXT_PAGE_1E, 147 MII_VSC73XX_PHY_CTRL_EXT3); 148 if (val < 0) 149 return val; 150 151 enable = FIELD_GET(MII_VSC73XX_PHY_CTRL_EXT3_DOWNSHIFT_EN, val); 152 cnt = FIELD_GET(MII_VSC73XX_PHY_CTRL_EXT3_DOWNSHIFT_CNT, val) + 2; 153 154 *data = enable ? cnt : DOWNSHIFT_DEV_DISABLE; 155 156 return 0; 157 } 158 159 static int vsc73xx_set_downshift(struct phy_device *phydev, u8 cnt) 160 { 161 u16 mask, val; 162 int ret; 163 164 if (cnt > MII_VSC73XX_DOWNSHIFT_MAX) 165 return -E2BIG; 166 else if (cnt == MII_VSC73XX_DOWNSHIFT_INVAL) 167 return -EINVAL; 168 169 mask = MII_VSC73XX_PHY_CTRL_EXT3_DOWNSHIFT_EN; 170 171 if (!cnt) { 172 val = 0; 173 } else { 174 mask |= MII_VSC73XX_PHY_CTRL_EXT3_DOWNSHIFT_CNT; 175 val = MII_VSC73XX_PHY_CTRL_EXT3_DOWNSHIFT_EN | 176 FIELD_PREP(MII_VSC73XX_PHY_CTRL_EXT3_DOWNSHIFT_CNT, 177 cnt - 2); 178 } 179 180 ret = phy_modify_paged(phydev, MII_VSC73XX_EXT_PAGE_1E, 181 MII_VSC73XX_PHY_CTRL_EXT3, mask, val); 182 if (ret < 0) 183 return ret; 184 185 return genphy_soft_reset(phydev); 186 } 187 188 static int vsc73xx_get_tunable(struct phy_device *phydev, 189 struct ethtool_tunable *tuna, void *data) 190 { 191 switch (tuna->id) { 192 case ETHTOOL_PHY_DOWNSHIFT: 193 return vsc73xx_get_downshift(phydev, data); 194 default: 195 return -EOPNOTSUPP; 196 } 197 } 198 199 static int vsc73xx_set_tunable(struct phy_device *phydev, 200 struct ethtool_tunable *tuna, const void *data) 201 { 202 switch (tuna->id) { 203 case ETHTOOL_PHY_DOWNSHIFT: 204 return vsc73xx_set_downshift(phydev, *(const u8 *)data); 205 default: 206 return -EOPNOTSUPP; 207 } 208 } 209 210 static void vsc73xx_config_init(struct phy_device *phydev) 211 { 212 /* Receiver init */ 213 phy_write(phydev, 0x1f, 0x2a30); 214 phy_modify(phydev, 0x0c, 0x0300, 0x0200); 215 phy_write(phydev, 0x1f, 0x0000); 216 217 /* Config LEDs 0x61 */ 218 phy_modify(phydev, MII_TPISTATUS, 0xff00, 0x0061); 219 220 /* Enable downshift by default */ 221 vsc73xx_set_downshift(phydev, MII_VSC73XX_DOWNSHIFT_MAX); 222 } 223 224 static int vsc738x_config_init(struct phy_device *phydev) 225 { 226 u16 rev; 227 /* This magic sequence appear in the application note 228 * "VSC7385/7388 PHY Configuration". 229 * 230 * Maybe one day we will get to know what it all means. 231 */ 232 phy_write(phydev, 0x1f, 0x2a30); 233 phy_modify(phydev, 0x08, 0x0200, 0x0200); 234 phy_write(phydev, 0x1f, 0x52b5); 235 phy_write(phydev, 0x10, 0xb68a); 236 phy_modify(phydev, 0x12, 0xff07, 0x0003); 237 phy_modify(phydev, 0x11, 0x00ff, 0x00a2); 238 phy_write(phydev, 0x10, 0x968a); 239 phy_write(phydev, 0x1f, 0x2a30); 240 phy_modify(phydev, 0x08, 0x0200, 0x0000); 241 phy_write(phydev, 0x1f, 0x0000); 242 243 /* Read revision */ 244 rev = phy_read(phydev, MII_PHYSID2); 245 rev &= 0x0f; 246 247 /* Special quirk for revision 0 */ 248 if (rev == 0) { 249 phy_write(phydev, 0x1f, 0x2a30); 250 phy_modify(phydev, 0x08, 0x0200, 0x0200); 251 phy_write(phydev, 0x1f, 0x52b5); 252 phy_write(phydev, 0x12, 0x0000); 253 phy_write(phydev, 0x11, 0x0689); 254 phy_write(phydev, 0x10, 0x8f92); 255 phy_write(phydev, 0x1f, 0x52b5); 256 phy_write(phydev, 0x12, 0x0000); 257 phy_write(phydev, 0x11, 0x0e35); 258 phy_write(phydev, 0x10, 0x9786); 259 phy_write(phydev, 0x1f, 0x2a30); 260 phy_modify(phydev, 0x08, 0x0200, 0x0000); 261 phy_write(phydev, 0x17, 0xff80); 262 phy_write(phydev, 0x17, 0x0000); 263 } 264 265 phy_write(phydev, 0x1f, 0x0000); 266 phy_write(phydev, 0x12, 0x0048); 267 268 if (rev == 0) { 269 phy_write(phydev, 0x1f, 0x2a30); 270 phy_write(phydev, 0x14, 0x6600); 271 phy_write(phydev, 0x1f, 0x0000); 272 phy_write(phydev, 0x18, 0xa24e); 273 } else { 274 phy_write(phydev, 0x1f, 0x2a30); 275 phy_modify(phydev, 0x16, 0x0fc0, 0x0240); 276 phy_modify(phydev, 0x14, 0x6000, 0x4000); 277 /* bits 14-15 in extended register 0x14 controls DACG amplitude 278 * 6 = -8%, 2 is hardware default 279 */ 280 phy_write(phydev, 0x1f, 0x0001); 281 phy_modify(phydev, 0x14, 0xe000, 0x6000); 282 phy_write(phydev, 0x1f, 0x0000); 283 } 284 285 vsc73xx_config_init(phydev); 286 287 return 0; 288 } 289 290 static int vsc739x_config_init(struct phy_device *phydev) 291 { 292 /* This magic sequence appears in the VSC7395 SparX-G5e application 293 * note "VSC7395/VSC7398 PHY Configuration" 294 * 295 * Maybe one day we will get to know what it all means. 296 */ 297 phy_write(phydev, 0x1f, 0x2a30); 298 phy_modify(phydev, 0x08, 0x0200, 0x0200); 299 phy_write(phydev, 0x1f, 0x52b5); 300 phy_write(phydev, 0x10, 0xb68a); 301 phy_modify(phydev, 0x12, 0xff07, 0x0003); 302 phy_modify(phydev, 0x11, 0x00ff, 0x00a2); 303 phy_write(phydev, 0x10, 0x968a); 304 phy_write(phydev, 0x1f, 0x2a30); 305 phy_modify(phydev, 0x08, 0x0200, 0x0000); 306 phy_write(phydev, 0x1f, 0x0000); 307 308 phy_write(phydev, 0x1f, 0x0000); 309 phy_write(phydev, 0x12, 0x0048); 310 phy_write(phydev, 0x1f, 0x2a30); 311 phy_modify(phydev, 0x16, 0x0fc0, 0x0240); 312 phy_modify(phydev, 0x14, 0x6000, 0x4000); 313 phy_write(phydev, 0x1f, 0x0001); 314 phy_modify(phydev, 0x14, 0xe000, 0x6000); 315 phy_write(phydev, 0x1f, 0x0000); 316 317 vsc73xx_config_init(phydev); 318 319 return 0; 320 } 321 322 static int vsc73xx_config_aneg(struct phy_device *phydev) 323 { 324 /* The VSC73xx switches does not like to be instructed to 325 * do autonegotiation in any way, it prefers that you just go 326 * with the power-on/reset defaults. Writing some registers will 327 * just make autonegotiation permanently fail. 328 */ 329 return 0; 330 } 331 332 /* This adds a skew for both TX and RX clocks, so the skew should only be 333 * applied to "rgmii-id" interfaces. It may not work as expected 334 * on "rgmii-txid", "rgmii-rxid" or "rgmii" interfaces. 335 */ 336 static int vsc8601_add_skew(struct phy_device *phydev) 337 { 338 int ret; 339 340 ret = phy_read(phydev, MII_VSC8601_EPHY_CTL); 341 if (ret < 0) 342 return ret; 343 344 ret |= MII_VSC8601_EPHY_CTL_RGMII_SKEW; 345 return phy_write(phydev, MII_VSC8601_EPHY_CTL, ret); 346 } 347 348 static int vsc8601_config_init(struct phy_device *phydev) 349 { 350 int ret = 0; 351 352 if (phydev->interface == PHY_INTERFACE_MODE_RGMII_ID) 353 ret = vsc8601_add_skew(phydev); 354 355 if (ret < 0) 356 return ret; 357 358 return 0; 359 } 360 361 static int vsc82xx_config_intr(struct phy_device *phydev) 362 { 363 int err; 364 365 if (phydev->interrupts == PHY_INTERRUPT_ENABLED) 366 /* Don't bother to ACK the interrupts since the 824x cannot 367 * clear the interrupts if they are disabled. 368 */ 369 err = phy_write(phydev, MII_VSC8244_IMASK, 370 (phydev->drv->phy_id == PHY_ID_VSC8234 || 371 phydev->drv->phy_id == PHY_ID_VSC8244 || 372 phydev->drv->phy_id == PHY_ID_VSC8572 || 373 phydev->drv->phy_id == PHY_ID_VSC8601) ? 374 MII_VSC8244_IMASK_MASK : 375 MII_VSC8221_IMASK_MASK); 376 else { 377 /* The Vitesse PHY cannot clear the interrupt 378 * once it has disabled them, so we clear them first 379 */ 380 err = phy_read(phydev, MII_VSC8244_ISTAT); 381 382 if (err < 0) 383 return err; 384 385 err = phy_write(phydev, MII_VSC8244_IMASK, 0); 386 } 387 388 return err; 389 } 390 391 static irqreturn_t vsc82xx_handle_interrupt(struct phy_device *phydev) 392 { 393 int irq_status, irq_mask; 394 395 if (phydev->drv->phy_id == PHY_ID_VSC8244 || 396 phydev->drv->phy_id == PHY_ID_VSC8572 || 397 phydev->drv->phy_id == PHY_ID_VSC8601) 398 irq_mask = MII_VSC8244_ISTAT_MASK; 399 else 400 irq_mask = MII_VSC8221_ISTAT_MASK; 401 402 irq_status = phy_read(phydev, MII_VSC8244_ISTAT); 403 if (irq_status < 0) { 404 phy_error(phydev); 405 return IRQ_NONE; 406 } 407 408 if (!(irq_status & irq_mask)) 409 return IRQ_NONE; 410 411 phy_trigger_machine(phydev); 412 413 return IRQ_HANDLED; 414 } 415 416 static int vsc8221_config_init(struct phy_device *phydev) 417 { 418 int err; 419 420 err = phy_write(phydev, MII_VSC8244_AUX_CONSTAT, 421 MII_VSC8221_AUXCONSTAT_INIT); 422 return err; 423 424 /* Perhaps we should set EXT_CON1 based on the interface? 425 * Options are 802.3Z SerDes or SGMII 426 */ 427 } 428 429 /* vsc82x4_config_autocross_enable - Enable auto MDI/MDI-X for forced links 430 * @phydev: target phy_device struct 431 * 432 * Enable auto MDI/MDI-X when in 10/100 forced link speeds by writing 433 * special values in the VSC8234/VSC8244 extended reserved registers 434 */ 435 static int vsc82x4_config_autocross_enable(struct phy_device *phydev) 436 { 437 int ret; 438 439 if (phydev->autoneg == AUTONEG_ENABLE || phydev->speed > SPEED_100) 440 return 0; 441 442 /* map extended registers set 0x10 - 0x1e */ 443 ret = phy_write(phydev, MII_VSC82X4_EXT_PAGE_ACCESS, 0x52b5); 444 if (ret >= 0) 445 ret = phy_write(phydev, MII_VSC82X4_EXT_PAGE_18E, 0x0012); 446 if (ret >= 0) 447 ret = phy_write(phydev, MII_VSC82X4_EXT_PAGE_17E, 0x2803); 448 if (ret >= 0) 449 ret = phy_write(phydev, MII_VSC82X4_EXT_PAGE_16E, 0x87fa); 450 /* map standard registers set 0x10 - 0x1e */ 451 if (ret >= 0) 452 ret = phy_write(phydev, MII_VSC82X4_EXT_PAGE_ACCESS, 0x0000); 453 else 454 phy_write(phydev, MII_VSC82X4_EXT_PAGE_ACCESS, 0x0000); 455 456 return ret; 457 } 458 459 /* vsc82x4_config_aneg - restart auto-negotiation or write BMCR 460 * @phydev: target phy_device struct 461 * 462 * Description: If auto-negotiation is enabled, we configure the 463 * advertising, and then restart auto-negotiation. If it is not 464 * enabled, then we write the BMCR and also start the auto 465 * MDI/MDI-X feature 466 */ 467 static int vsc82x4_config_aneg(struct phy_device *phydev) 468 { 469 int ret; 470 471 /* Enable auto MDI/MDI-X when in 10/100 forced link speeds by 472 * writing special values in the VSC8234 extended reserved registers 473 */ 474 if (phydev->autoneg != AUTONEG_ENABLE && phydev->speed <= SPEED_100) { 475 ret = genphy_setup_forced(phydev); 476 477 if (ret < 0) /* error */ 478 return ret; 479 480 return vsc82x4_config_autocross_enable(phydev); 481 } 482 483 return genphy_config_aneg(phydev); 484 } 485 486 /* Vitesse 82xx */ 487 static struct phy_driver vsc82xx_driver[] = { 488 { 489 .phy_id = PHY_ID_VSC8234, 490 .name = "Vitesse VSC8234", 491 .phy_id_mask = 0x000ffff0, 492 /* PHY_GBIT_FEATURES */ 493 .config_init = &vsc824x_config_init, 494 .config_aneg = &vsc82x4_config_aneg, 495 .config_intr = &vsc82xx_config_intr, 496 .handle_interrupt = &vsc82xx_handle_interrupt, 497 }, { 498 .phy_id = PHY_ID_VSC8244, 499 .name = "Vitesse VSC8244", 500 .phy_id_mask = 0x000fffc0, 501 /* PHY_GBIT_FEATURES */ 502 .config_init = &vsc824x_config_init, 503 .config_aneg = &vsc82x4_config_aneg, 504 .config_intr = &vsc82xx_config_intr, 505 .handle_interrupt = &vsc82xx_handle_interrupt, 506 }, { 507 .phy_id = PHY_ID_VSC8572, 508 .name = "Vitesse VSC8572", 509 .phy_id_mask = 0x000ffff0, 510 /* PHY_GBIT_FEATURES */ 511 .config_init = &vsc824x_config_init, 512 .config_aneg = &vsc82x4_config_aneg, 513 .config_intr = &vsc82xx_config_intr, 514 .handle_interrupt = &vsc82xx_handle_interrupt, 515 }, { 516 .phy_id = PHY_ID_VSC8601, 517 .name = "Vitesse VSC8601", 518 .phy_id_mask = 0x000ffff0, 519 /* PHY_GBIT_FEATURES */ 520 .config_init = &vsc8601_config_init, 521 .config_intr = &vsc82xx_config_intr, 522 .handle_interrupt = &vsc82xx_handle_interrupt, 523 }, { 524 .phy_id = PHY_ID_VSC7385, 525 .name = "Vitesse VSC7385", 526 .phy_id_mask = 0x000ffff0, 527 /* PHY_GBIT_FEATURES */ 528 .config_init = vsc738x_config_init, 529 .config_aneg = vsc73xx_config_aneg, 530 .read_page = vsc73xx_read_page, 531 .write_page = vsc73xx_write_page, 532 .get_tunable = vsc73xx_get_tunable, 533 .set_tunable = vsc73xx_set_tunable, 534 }, { 535 .phy_id = PHY_ID_VSC7388, 536 .name = "Vitesse VSC7388", 537 .phy_id_mask = 0x000ffff0, 538 /* PHY_GBIT_FEATURES */ 539 .config_init = vsc738x_config_init, 540 .config_aneg = vsc73xx_config_aneg, 541 .read_page = vsc73xx_read_page, 542 .write_page = vsc73xx_write_page, 543 .get_tunable = vsc73xx_get_tunable, 544 .set_tunable = vsc73xx_set_tunable, 545 }, { 546 .phy_id = PHY_ID_VSC7395, 547 .name = "Vitesse VSC7395", 548 .phy_id_mask = 0x000ffff0, 549 /* PHY_GBIT_FEATURES */ 550 .config_init = vsc739x_config_init, 551 .config_aneg = vsc73xx_config_aneg, 552 .read_page = vsc73xx_read_page, 553 .write_page = vsc73xx_write_page, 554 .get_tunable = vsc73xx_get_tunable, 555 .set_tunable = vsc73xx_set_tunable, 556 }, { 557 .phy_id = PHY_ID_VSC7398, 558 .name = "Vitesse VSC7398", 559 .phy_id_mask = 0x000ffff0, 560 /* PHY_GBIT_FEATURES */ 561 .config_init = vsc739x_config_init, 562 .config_aneg = vsc73xx_config_aneg, 563 .read_page = vsc73xx_read_page, 564 .write_page = vsc73xx_write_page, 565 .get_tunable = vsc73xx_get_tunable, 566 .set_tunable = vsc73xx_set_tunable, 567 }, { 568 .phy_id = PHY_ID_VSC8662, 569 .name = "Vitesse VSC8662", 570 .phy_id_mask = 0x000ffff0, 571 /* PHY_GBIT_FEATURES */ 572 .config_init = &vsc824x_config_init, 573 .config_aneg = &vsc82x4_config_aneg, 574 .config_intr = &vsc82xx_config_intr, 575 .handle_interrupt = &vsc82xx_handle_interrupt, 576 }, { 577 /* Vitesse 8221 */ 578 .phy_id = PHY_ID_VSC8221, 579 .phy_id_mask = 0x000ffff0, 580 .name = "Vitesse VSC8221", 581 /* PHY_GBIT_FEATURES */ 582 .config_init = &vsc8221_config_init, 583 .config_intr = &vsc82xx_config_intr, 584 .handle_interrupt = &vsc82xx_handle_interrupt, 585 }, { 586 /* Vitesse 8211 */ 587 .phy_id = PHY_ID_VSC8211, 588 .phy_id_mask = 0x000ffff0, 589 .name = "Vitesse VSC8211", 590 /* PHY_GBIT_FEATURES */ 591 .config_init = &vsc8221_config_init, 592 .config_intr = &vsc82xx_config_intr, 593 .handle_interrupt = &vsc82xx_handle_interrupt, 594 } }; 595 596 module_phy_driver(vsc82xx_driver); 597 598 static struct mdio_device_id __maybe_unused vitesse_tbl[] = { 599 { PHY_ID_VSC8234, 0x000ffff0 }, 600 { PHY_ID_VSC8244, 0x000fffc0 }, 601 { PHY_ID_VSC8572, 0x000ffff0 }, 602 { PHY_ID_VSC7385, 0x000ffff0 }, 603 { PHY_ID_VSC7388, 0x000ffff0 }, 604 { PHY_ID_VSC7395, 0x000ffff0 }, 605 { PHY_ID_VSC7398, 0x000ffff0 }, 606 { PHY_ID_VSC8662, 0x000ffff0 }, 607 { PHY_ID_VSC8221, 0x000ffff0 }, 608 { PHY_ID_VSC8211, 0x000ffff0 }, 609 { } 610 }; 611 612 MODULE_DEVICE_TABLE(mdio, vitesse_tbl); 613