1 // SPDX-License-Identifier: GPL-2.0+ 2 /* 3 * Core PHY library, taken from phy.c 4 */ 5 #include <linux/export.h> 6 #include <linux/phy.h> 7 8 const char *phy_speed_to_str(int speed) 9 { 10 switch (speed) { 11 case SPEED_10: 12 return "10Mbps"; 13 case SPEED_100: 14 return "100Mbps"; 15 case SPEED_1000: 16 return "1Gbps"; 17 case SPEED_2500: 18 return "2.5Gbps"; 19 case SPEED_5000: 20 return "5Gbps"; 21 case SPEED_10000: 22 return "10Gbps"; 23 case SPEED_14000: 24 return "14Gbps"; 25 case SPEED_20000: 26 return "20Gbps"; 27 case SPEED_25000: 28 return "25Gbps"; 29 case SPEED_40000: 30 return "40Gbps"; 31 case SPEED_50000: 32 return "50Gbps"; 33 case SPEED_56000: 34 return "56Gbps"; 35 case SPEED_100000: 36 return "100Gbps"; 37 case SPEED_UNKNOWN: 38 return "Unknown"; 39 default: 40 return "Unsupported (update phy-core.c)"; 41 } 42 } 43 EXPORT_SYMBOL_GPL(phy_speed_to_str); 44 45 const char *phy_duplex_to_str(unsigned int duplex) 46 { 47 if (duplex == DUPLEX_HALF) 48 return "Half"; 49 if (duplex == DUPLEX_FULL) 50 return "Full"; 51 if (duplex == DUPLEX_UNKNOWN) 52 return "Unknown"; 53 return "Unsupported (update phy-core.c)"; 54 } 55 EXPORT_SYMBOL_GPL(phy_duplex_to_str); 56 57 /* A mapping of all SUPPORTED settings to speed/duplex. This table 58 * must be grouped by speed and sorted in descending match priority 59 * - iow, descending speed. */ 60 static const struct phy_setting settings[] = { 61 /* 100G */ 62 { 63 .speed = SPEED_100000, 64 .duplex = DUPLEX_FULL, 65 .bit = ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT, 66 }, 67 { 68 .speed = SPEED_100000, 69 .duplex = DUPLEX_FULL, 70 .bit = ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT, 71 }, 72 { 73 .speed = SPEED_100000, 74 .duplex = DUPLEX_FULL, 75 .bit = ETHTOOL_LINK_MODE_100000baseLR4_ER4_Full_BIT, 76 }, 77 { 78 .speed = SPEED_100000, 79 .duplex = DUPLEX_FULL, 80 .bit = ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT, 81 }, 82 /* 56G */ 83 { 84 .speed = SPEED_56000, 85 .duplex = DUPLEX_FULL, 86 .bit = ETHTOOL_LINK_MODE_56000baseCR4_Full_BIT, 87 }, 88 { 89 .speed = SPEED_56000, 90 .duplex = DUPLEX_FULL, 91 .bit = ETHTOOL_LINK_MODE_56000baseKR4_Full_BIT, 92 }, 93 { 94 .speed = SPEED_56000, 95 .duplex = DUPLEX_FULL, 96 .bit = ETHTOOL_LINK_MODE_56000baseLR4_Full_BIT, 97 }, 98 { 99 .speed = SPEED_56000, 100 .duplex = DUPLEX_FULL, 101 .bit = ETHTOOL_LINK_MODE_56000baseSR4_Full_BIT, 102 }, 103 /* 50G */ 104 { 105 .speed = SPEED_50000, 106 .duplex = DUPLEX_FULL, 107 .bit = ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT, 108 }, 109 { 110 .speed = SPEED_50000, 111 .duplex = DUPLEX_FULL, 112 .bit = ETHTOOL_LINK_MODE_50000baseKR2_Full_BIT, 113 }, 114 { 115 .speed = SPEED_50000, 116 .duplex = DUPLEX_FULL, 117 .bit = ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT, 118 }, 119 /* 40G */ 120 { 121 .speed = SPEED_40000, 122 .duplex = DUPLEX_FULL, 123 .bit = ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT, 124 }, 125 { 126 .speed = SPEED_40000, 127 .duplex = DUPLEX_FULL, 128 .bit = ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT, 129 }, 130 { 131 .speed = SPEED_40000, 132 .duplex = DUPLEX_FULL, 133 .bit = ETHTOOL_LINK_MODE_40000baseLR4_Full_BIT, 134 }, 135 { 136 .speed = SPEED_40000, 137 .duplex = DUPLEX_FULL, 138 .bit = ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT, 139 }, 140 /* 25G */ 141 { 142 .speed = SPEED_25000, 143 .duplex = DUPLEX_FULL, 144 .bit = ETHTOOL_LINK_MODE_25000baseCR_Full_BIT, 145 }, 146 { 147 .speed = SPEED_25000, 148 .duplex = DUPLEX_FULL, 149 .bit = ETHTOOL_LINK_MODE_25000baseKR_Full_BIT, 150 }, 151 { 152 .speed = SPEED_25000, 153 .duplex = DUPLEX_FULL, 154 .bit = ETHTOOL_LINK_MODE_25000baseSR_Full_BIT, 155 }, 156 157 /* 20G */ 158 { 159 .speed = SPEED_20000, 160 .duplex = DUPLEX_FULL, 161 .bit = ETHTOOL_LINK_MODE_20000baseKR2_Full_BIT, 162 }, 163 { 164 .speed = SPEED_20000, 165 .duplex = DUPLEX_FULL, 166 .bit = ETHTOOL_LINK_MODE_20000baseMLD2_Full_BIT, 167 }, 168 /* 10G */ 169 { 170 .speed = SPEED_10000, 171 .duplex = DUPLEX_FULL, 172 .bit = ETHTOOL_LINK_MODE_10000baseCR_Full_BIT, 173 }, 174 { 175 .speed = SPEED_10000, 176 .duplex = DUPLEX_FULL, 177 .bit = ETHTOOL_LINK_MODE_10000baseER_Full_BIT, 178 }, 179 { 180 .speed = SPEED_10000, 181 .duplex = DUPLEX_FULL, 182 .bit = ETHTOOL_LINK_MODE_10000baseKR_Full_BIT, 183 }, 184 { 185 .speed = SPEED_10000, 186 .duplex = DUPLEX_FULL, 187 .bit = ETHTOOL_LINK_MODE_10000baseKX4_Full_BIT, 188 }, 189 { 190 .speed = SPEED_10000, 191 .duplex = DUPLEX_FULL, 192 .bit = ETHTOOL_LINK_MODE_10000baseLR_Full_BIT, 193 }, 194 { 195 .speed = SPEED_10000, 196 .duplex = DUPLEX_FULL, 197 .bit = ETHTOOL_LINK_MODE_10000baseLRM_Full_BIT, 198 }, 199 { 200 .speed = SPEED_10000, 201 .duplex = DUPLEX_FULL, 202 .bit = ETHTOOL_LINK_MODE_10000baseR_FEC_BIT, 203 }, 204 { 205 .speed = SPEED_10000, 206 .duplex = DUPLEX_FULL, 207 .bit = ETHTOOL_LINK_MODE_10000baseSR_Full_BIT, 208 }, 209 { 210 .speed = SPEED_10000, 211 .duplex = DUPLEX_FULL, 212 .bit = ETHTOOL_LINK_MODE_10000baseT_Full_BIT, 213 }, 214 /* 5G */ 215 { 216 .speed = SPEED_5000, 217 .duplex = DUPLEX_FULL, 218 .bit = ETHTOOL_LINK_MODE_5000baseT_Full_BIT, 219 }, 220 221 /* 2.5G */ 222 { 223 .speed = SPEED_2500, 224 .duplex = DUPLEX_FULL, 225 .bit = ETHTOOL_LINK_MODE_2500baseT_Full_BIT, 226 }, 227 { 228 .speed = SPEED_2500, 229 .duplex = DUPLEX_FULL, 230 .bit = ETHTOOL_LINK_MODE_2500baseX_Full_BIT, 231 }, 232 /* 1G */ 233 { 234 .speed = SPEED_1000, 235 .duplex = DUPLEX_FULL, 236 .bit = ETHTOOL_LINK_MODE_1000baseKX_Full_BIT, 237 }, 238 { 239 .speed = SPEED_1000, 240 .duplex = DUPLEX_FULL, 241 .bit = ETHTOOL_LINK_MODE_1000baseT_Full_BIT, 242 }, 243 { 244 .speed = SPEED_1000, 245 .duplex = DUPLEX_HALF, 246 .bit = ETHTOOL_LINK_MODE_1000baseT_Half_BIT, 247 }, 248 { 249 .speed = SPEED_1000, 250 .duplex = DUPLEX_FULL, 251 .bit = ETHTOOL_LINK_MODE_1000baseX_Full_BIT, 252 }, 253 /* 100M */ 254 { 255 .speed = SPEED_100, 256 .duplex = DUPLEX_FULL, 257 .bit = ETHTOOL_LINK_MODE_100baseT_Full_BIT, 258 }, 259 { 260 .speed = SPEED_100, 261 .duplex = DUPLEX_HALF, 262 .bit = ETHTOOL_LINK_MODE_100baseT_Half_BIT, 263 }, 264 /* 10M */ 265 { 266 .speed = SPEED_10, 267 .duplex = DUPLEX_FULL, 268 .bit = ETHTOOL_LINK_MODE_10baseT_Full_BIT, 269 }, 270 { 271 .speed = SPEED_10, 272 .duplex = DUPLEX_HALF, 273 .bit = ETHTOOL_LINK_MODE_10baseT_Half_BIT, 274 }, 275 }; 276 277 /** 278 * phy_lookup_setting - lookup a PHY setting 279 * @speed: speed to match 280 * @duplex: duplex to match 281 * @mask: allowed link modes 282 * @exact: an exact match is required 283 * 284 * Search the settings array for a setting that matches the speed and 285 * duplex, and which is supported. 286 * 287 * If @exact is unset, either an exact match or %NULL for no match will 288 * be returned. 289 * 290 * If @exact is set, an exact match, the fastest supported setting at 291 * or below the specified speed, the slowest supported setting, or if 292 * they all fail, %NULL will be returned. 293 */ 294 const struct phy_setting * 295 phy_lookup_setting(int speed, int duplex, const unsigned long *mask, bool exact) 296 { 297 const struct phy_setting *p, *match = NULL, *last = NULL; 298 int i; 299 300 for (i = 0, p = settings; i < ARRAY_SIZE(settings); i++, p++) { 301 if (p->bit < __ETHTOOL_LINK_MODE_MASK_NBITS && 302 test_bit(p->bit, mask)) { 303 last = p; 304 if (p->speed == speed && p->duplex == duplex) { 305 /* Exact match for speed and duplex */ 306 match = p; 307 break; 308 } else if (!exact) { 309 if (!match && p->speed <= speed) 310 /* Candidate */ 311 match = p; 312 313 if (p->speed < speed) 314 break; 315 } 316 } 317 } 318 319 if (!match && !exact) 320 match = last; 321 322 return match; 323 } 324 EXPORT_SYMBOL_GPL(phy_lookup_setting); 325 326 size_t phy_speeds(unsigned int *speeds, size_t size, 327 unsigned long *mask) 328 { 329 size_t count; 330 int i; 331 332 for (i = 0, count = 0; i < ARRAY_SIZE(settings) && count < size; i++) 333 if (settings[i].bit < __ETHTOOL_LINK_MODE_MASK_NBITS && 334 test_bit(settings[i].bit, mask) && 335 (count == 0 || speeds[count - 1] != settings[i].speed)) 336 speeds[count++] = settings[i].speed; 337 338 return count; 339 } 340 341 /** 342 * phy_resolve_aneg_linkmode - resolve the advertisements into phy settings 343 * @phydev: The phy_device struct 344 * 345 * Resolve our and the link partner advertisements into their corresponding 346 * speed and duplex. If full duplex was negotiated, extract the pause mode 347 * from the link partner mask. 348 */ 349 void phy_resolve_aneg_linkmode(struct phy_device *phydev) 350 { 351 __ETHTOOL_DECLARE_LINK_MODE_MASK(common); 352 353 linkmode_and(common, phydev->lp_advertising, phydev->advertising); 354 355 if (linkmode_test_bit(ETHTOOL_LINK_MODE_10000baseT_Full_BIT, common)) { 356 phydev->speed = SPEED_10000; 357 phydev->duplex = DUPLEX_FULL; 358 } else if (linkmode_test_bit(ETHTOOL_LINK_MODE_5000baseT_Full_BIT, 359 common)) { 360 phydev->speed = SPEED_5000; 361 phydev->duplex = DUPLEX_FULL; 362 } else if (linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, 363 common)) { 364 phydev->speed = SPEED_2500; 365 phydev->duplex = DUPLEX_FULL; 366 } else if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, 367 common)) { 368 phydev->speed = SPEED_1000; 369 phydev->duplex = DUPLEX_FULL; 370 } else if (linkmode_test_bit(ETHTOOL_LINK_MODE_1000baseT_Half_BIT, 371 common)) { 372 phydev->speed = SPEED_1000; 373 phydev->duplex = DUPLEX_HALF; 374 } else if (linkmode_test_bit(ETHTOOL_LINK_MODE_100baseT_Full_BIT, 375 common)) { 376 phydev->speed = SPEED_100; 377 phydev->duplex = DUPLEX_FULL; 378 } else if (linkmode_test_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT, 379 common)) { 380 phydev->speed = SPEED_100; 381 phydev->duplex = DUPLEX_HALF; 382 } else if (linkmode_test_bit(ETHTOOL_LINK_MODE_10baseT_Full_BIT, 383 common)) { 384 phydev->speed = SPEED_10; 385 phydev->duplex = DUPLEX_FULL; 386 } else if (linkmode_test_bit(ETHTOOL_LINK_MODE_10baseT_Half_BIT, 387 common)) { 388 phydev->speed = SPEED_10; 389 phydev->duplex = DUPLEX_HALF; 390 } 391 392 if (phydev->duplex == DUPLEX_FULL) { 393 phydev->pause = linkmode_test_bit(ETHTOOL_LINK_MODE_Pause_BIT, 394 phydev->lp_advertising); 395 phydev->asym_pause = linkmode_test_bit( 396 ETHTOOL_LINK_MODE_Asym_Pause_BIT, 397 phydev->lp_advertising); 398 } 399 } 400 EXPORT_SYMBOL_GPL(phy_resolve_aneg_linkmode); 401 402 static void mmd_phy_indirect(struct mii_bus *bus, int phy_addr, int devad, 403 u16 regnum) 404 { 405 /* Write the desired MMD Devad */ 406 __mdiobus_write(bus, phy_addr, MII_MMD_CTRL, devad); 407 408 /* Write the desired MMD register address */ 409 __mdiobus_write(bus, phy_addr, MII_MMD_DATA, regnum); 410 411 /* Select the Function : DATA with no post increment */ 412 __mdiobus_write(bus, phy_addr, MII_MMD_CTRL, 413 devad | MII_MMD_CTRL_NOINCR); 414 } 415 416 /** 417 * phy_read_mmd - Convenience function for reading a register 418 * from an MMD on a given PHY. 419 * @phydev: The phy_device struct 420 * @devad: The MMD to read from (0..31) 421 * @regnum: The register on the MMD to read (0..65535) 422 * 423 * Same rules as for phy_read(); 424 */ 425 int phy_read_mmd(struct phy_device *phydev, int devad, u32 regnum) 426 { 427 int val; 428 429 if (regnum > (u16)~0 || devad > 32) 430 return -EINVAL; 431 432 if (phydev->drv->read_mmd) { 433 val = phydev->drv->read_mmd(phydev, devad, regnum); 434 } else if (phydev->is_c45) { 435 u32 addr = MII_ADDR_C45 | (devad << 16) | (regnum & 0xffff); 436 437 val = mdiobus_read(phydev->mdio.bus, phydev->mdio.addr, addr); 438 } else { 439 struct mii_bus *bus = phydev->mdio.bus; 440 int phy_addr = phydev->mdio.addr; 441 442 mutex_lock(&bus->mdio_lock); 443 mmd_phy_indirect(bus, phy_addr, devad, regnum); 444 445 /* Read the content of the MMD's selected register */ 446 val = __mdiobus_read(bus, phy_addr, MII_MMD_DATA); 447 mutex_unlock(&bus->mdio_lock); 448 } 449 return val; 450 } 451 EXPORT_SYMBOL(phy_read_mmd); 452 453 /** 454 * phy_write_mmd - Convenience function for writing a register 455 * on an MMD on a given PHY. 456 * @phydev: The phy_device struct 457 * @devad: The MMD to read from 458 * @regnum: The register on the MMD to read 459 * @val: value to write to @regnum 460 * 461 * Same rules as for phy_write(); 462 */ 463 int phy_write_mmd(struct phy_device *phydev, int devad, u32 regnum, u16 val) 464 { 465 int ret; 466 467 if (regnum > (u16)~0 || devad > 32) 468 return -EINVAL; 469 470 if (phydev->drv->write_mmd) { 471 ret = phydev->drv->write_mmd(phydev, devad, regnum, val); 472 } else if (phydev->is_c45) { 473 u32 addr = MII_ADDR_C45 | (devad << 16) | (regnum & 0xffff); 474 475 ret = mdiobus_write(phydev->mdio.bus, phydev->mdio.addr, 476 addr, val); 477 } else { 478 struct mii_bus *bus = phydev->mdio.bus; 479 int phy_addr = phydev->mdio.addr; 480 481 mutex_lock(&bus->mdio_lock); 482 mmd_phy_indirect(bus, phy_addr, devad, regnum); 483 484 /* Write the data into MMD's selected register */ 485 __mdiobus_write(bus, phy_addr, MII_MMD_DATA, val); 486 mutex_unlock(&bus->mdio_lock); 487 488 ret = 0; 489 } 490 return ret; 491 } 492 EXPORT_SYMBOL(phy_write_mmd); 493 494 /** 495 * __phy_modify() - Convenience function for modifying a PHY register 496 * @phydev: a pointer to a &struct phy_device 497 * @regnum: register number 498 * @mask: bit mask of bits to clear 499 * @set: bit mask of bits to set 500 * 501 * Unlocked helper function which allows a PHY register to be modified as 502 * new register value = (old register value & ~mask) | set 503 */ 504 int __phy_modify(struct phy_device *phydev, u32 regnum, u16 mask, u16 set) 505 { 506 int ret; 507 508 ret = __phy_read(phydev, regnum); 509 if (ret < 0) 510 return ret; 511 512 ret = __phy_write(phydev, regnum, (ret & ~mask) | set); 513 514 return ret < 0 ? ret : 0; 515 } 516 EXPORT_SYMBOL_GPL(__phy_modify); 517 518 /** 519 * phy_modify - Convenience function for modifying a given PHY register 520 * @phydev: the phy_device struct 521 * @regnum: register number to write 522 * @mask: bit mask of bits to clear 523 * @set: new value of bits set in mask to write to @regnum 524 * 525 * NOTE: MUST NOT be called from interrupt context, 526 * because the bus read/write functions may wait for an interrupt 527 * to conclude the operation. 528 */ 529 int phy_modify(struct phy_device *phydev, u32 regnum, u16 mask, u16 set) 530 { 531 int ret; 532 533 mutex_lock(&phydev->mdio.bus->mdio_lock); 534 ret = __phy_modify(phydev, regnum, mask, set); 535 mutex_unlock(&phydev->mdio.bus->mdio_lock); 536 537 return ret; 538 } 539 EXPORT_SYMBOL_GPL(phy_modify); 540 541 static int __phy_read_page(struct phy_device *phydev) 542 { 543 return phydev->drv->read_page(phydev); 544 } 545 546 static int __phy_write_page(struct phy_device *phydev, int page) 547 { 548 return phydev->drv->write_page(phydev, page); 549 } 550 551 /** 552 * phy_save_page() - take the bus lock and save the current page 553 * @phydev: a pointer to a &struct phy_device 554 * 555 * Take the MDIO bus lock, and return the current page number. On error, 556 * returns a negative errno. phy_restore_page() must always be called 557 * after this, irrespective of success or failure of this call. 558 */ 559 int phy_save_page(struct phy_device *phydev) 560 { 561 mutex_lock(&phydev->mdio.bus->mdio_lock); 562 return __phy_read_page(phydev); 563 } 564 EXPORT_SYMBOL_GPL(phy_save_page); 565 566 /** 567 * phy_select_page() - take the bus lock, save the current page, and set a page 568 * @phydev: a pointer to a &struct phy_device 569 * @page: desired page 570 * 571 * Take the MDIO bus lock to protect against concurrent access, save the 572 * current PHY page, and set the current page. On error, returns a 573 * negative errno, otherwise returns the previous page number. 574 * phy_restore_page() must always be called after this, irrespective 575 * of success or failure of this call. 576 */ 577 int phy_select_page(struct phy_device *phydev, int page) 578 { 579 int ret, oldpage; 580 581 oldpage = ret = phy_save_page(phydev); 582 if (ret < 0) 583 return ret; 584 585 if (oldpage != page) { 586 ret = __phy_write_page(phydev, page); 587 if (ret < 0) 588 return ret; 589 } 590 591 return oldpage; 592 } 593 EXPORT_SYMBOL_GPL(phy_select_page); 594 595 /** 596 * phy_restore_page() - restore the page register and release the bus lock 597 * @phydev: a pointer to a &struct phy_device 598 * @oldpage: the old page, return value from phy_save_page() or phy_select_page() 599 * @ret: operation's return code 600 * 601 * Release the MDIO bus lock, restoring @oldpage if it is a valid page. 602 * This function propagates the earliest error code from the group of 603 * operations. 604 * 605 * Returns: 606 * @oldpage if it was a negative value, otherwise 607 * @ret if it was a negative errno value, otherwise 608 * phy_write_page()'s negative value if it were in error, otherwise 609 * @ret. 610 */ 611 int phy_restore_page(struct phy_device *phydev, int oldpage, int ret) 612 { 613 int r; 614 615 if (oldpage >= 0) { 616 r = __phy_write_page(phydev, oldpage); 617 618 /* Propagate the operation return code if the page write 619 * was successful. 620 */ 621 if (ret >= 0 && r < 0) 622 ret = r; 623 } else { 624 /* Propagate the phy page selection error code */ 625 ret = oldpage; 626 } 627 628 mutex_unlock(&phydev->mdio.bus->mdio_lock); 629 630 return ret; 631 } 632 EXPORT_SYMBOL_GPL(phy_restore_page); 633 634 /** 635 * phy_read_paged() - Convenience function for reading a paged register 636 * @phydev: a pointer to a &struct phy_device 637 * @page: the page for the phy 638 * @regnum: register number 639 * 640 * Same rules as for phy_read(). 641 */ 642 int phy_read_paged(struct phy_device *phydev, int page, u32 regnum) 643 { 644 int ret = 0, oldpage; 645 646 oldpage = phy_select_page(phydev, page); 647 if (oldpage >= 0) 648 ret = __phy_read(phydev, regnum); 649 650 return phy_restore_page(phydev, oldpage, ret); 651 } 652 EXPORT_SYMBOL(phy_read_paged); 653 654 /** 655 * phy_write_paged() - Convenience function for writing a paged register 656 * @phydev: a pointer to a &struct phy_device 657 * @page: the page for the phy 658 * @regnum: register number 659 * @val: value to write 660 * 661 * Same rules as for phy_write(). 662 */ 663 int phy_write_paged(struct phy_device *phydev, int page, u32 regnum, u16 val) 664 { 665 int ret = 0, oldpage; 666 667 oldpage = phy_select_page(phydev, page); 668 if (oldpage >= 0) 669 ret = __phy_write(phydev, regnum, val); 670 671 return phy_restore_page(phydev, oldpage, ret); 672 } 673 EXPORT_SYMBOL(phy_write_paged); 674 675 /** 676 * phy_modify_paged() - Convenience function for modifying a paged register 677 * @phydev: a pointer to a &struct phy_device 678 * @page: the page for the phy 679 * @regnum: register number 680 * @mask: bit mask of bits to clear 681 * @set: bit mask of bits to set 682 * 683 * Same rules as for phy_read() and phy_write(). 684 */ 685 int phy_modify_paged(struct phy_device *phydev, int page, u32 regnum, 686 u16 mask, u16 set) 687 { 688 int ret = 0, oldpage; 689 690 oldpage = phy_select_page(phydev, page); 691 if (oldpage >= 0) 692 ret = __phy_modify(phydev, regnum, mask, set); 693 694 return phy_restore_page(phydev, oldpage, ret); 695 } 696 EXPORT_SYMBOL(phy_modify_paged); 697