Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Merge branch 'ethtool-phy-downshift'

Allan W. Nielsen says:

====================
Adding PHY-Tunables and downshift support

(This is a re-post of the v3 patch set with a new cover letter - I was not
aware that the cover letters was used a commit comments in merge commits).

This series add support for PHY tunables, and uses this facility to
configure downshifting. The downshifting mechanism is implemented for MSCC
phys.

This series tries to address the comments provided back in mid October when
this feature was posted along with fast-link-failure. Fast-link-failure has
been separated out, but we would like to pick continue on that if/when we
agree on how the phy-tunables and downshifting should be done.

The proposed generic interface is similar to
ETHTOOL_GTUNABLE/ETHTOOL_STUNABLE, it uses the same type
(ethtool_tunable/tunable_type_id) but a new enum (phy_tunable_id) is added
to reflect the PHY tunable.

The implementation just call the newly added function pointers in
get_tunable/set_tunable phy_device structure.

To configure downshifting, the ethtool_tunable structure is used. 'id' must
be set to 'ETHTOOL_PHY_DOWNSHIFT', 'type_id' must be set to
'ETHTOOL_TUNABLE_U8' and 'data' value configure the amount of downshift
re-tries.

If configured to DOWNSHIFT_DEV_DISABLE, then downshift is disabled If
configured to DOWNSHIFT_DEV_DEFAULT_COUNT, then it is up to the device to
choose a device-specific re-try count.

Tested on Beaglebone Black with VSC 8531 PHY.

Change set:
v0:

- Link Speed downshift and Fast Link failure-2 features coded by using
Device tree.
v1:
- Split the Downshift and FLF2 features in different set of patches.
- Removed DT access and implemented IOCTL access suggested by Andrew.
- Added function pointers in get_tunable/set_tunable phy_device structure
v2:
- Added trace message with a hist is printed when downshifting clould not
be eanbled with the requested count
- (ethtool) Syntax is changed from "--set-phy-tunable downshift on|off|%d"
to "--set-phy-tunable [downshift on|off [count N]]" - as requested by
Andrew.
v3:
- Fixed Spelling in "net: phy: Add downshift get/set support in Microsemi
PHYs driver"
====================

Signed-off-by: David S. Miller <davem@davemloft.net>

+217 -1
+100
drivers/net/phy/mscc.c
··· 46 46 47 47 #define MSCC_EXT_PAGE_ACCESS 31 48 48 #define MSCC_PHY_PAGE_STANDARD 0x0000 /* Standard registers */ 49 + #define MSCC_PHY_PAGE_EXTENDED 0x0001 /* Extended registers */ 49 50 #define MSCC_PHY_PAGE_EXTENDED_2 0x0002 /* Extended reg - page 2 */ 51 + 52 + /* Extended Page 1 Registers */ 53 + #define MSCC_PHY_ACTIPHY_CNTL 20 54 + #define DOWNSHIFT_CNTL_MASK 0x001C 55 + #define DOWNSHIFT_EN 0x0010 56 + #define DOWNSHIFT_CNTL_POS 2 50 57 51 58 /* Extended Page 2 Registers */ 52 59 #define MSCC_PHY_RGMII_CNTL 20 ··· 82 75 #define MSCC_VDDMAC_2500 2500 83 76 #define MSCC_VDDMAC_3300 3300 84 77 78 + #define DOWNSHIFT_COUNT_MAX 5 79 + 85 80 struct vsc8531_private { 86 81 int rate_magic; 87 82 }; ··· 107 98 int rc; 108 99 109 100 rc = phy_write(phydev, MSCC_EXT_PAGE_ACCESS, page); 101 + return rc; 102 + } 103 + 104 + static int vsc85xx_downshift_get(struct phy_device *phydev, u8 *count) 105 + { 106 + int rc; 107 + u16 reg_val; 108 + 109 + mutex_lock(&phydev->lock); 110 + rc = vsc85xx_phy_page_set(phydev, MSCC_PHY_PAGE_EXTENDED); 111 + if (rc != 0) 112 + goto out_unlock; 113 + 114 + reg_val = phy_read(phydev, MSCC_PHY_ACTIPHY_CNTL); 115 + reg_val &= DOWNSHIFT_CNTL_MASK; 116 + if (!(reg_val & DOWNSHIFT_EN)) 117 + *count = DOWNSHIFT_DEV_DISABLE; 118 + else 119 + *count = ((reg_val & ~DOWNSHIFT_EN) >> DOWNSHIFT_CNTL_POS) + 2; 120 + rc = vsc85xx_phy_page_set(phydev, MSCC_PHY_PAGE_STANDARD); 121 + 122 + out_unlock: 123 + mutex_unlock(&phydev->lock); 124 + 125 + return rc; 126 + } 127 + 128 + static int vsc85xx_downshift_set(struct phy_device *phydev, u8 count) 129 + { 130 + int rc; 131 + u16 reg_val; 132 + 133 + if (count == DOWNSHIFT_DEV_DEFAULT_COUNT) { 134 + /* Default downshift count 3 (i.e. Bit3:2 = 0b01) */ 135 + count = ((1 << DOWNSHIFT_CNTL_POS) | DOWNSHIFT_EN); 136 + } else if (count > DOWNSHIFT_COUNT_MAX || count == 1) { 137 + phydev_err(phydev, "Downshift count should be 2,3,4 or 5\n"); 138 + return -ERANGE; 139 + } else if (count) { 140 + /* Downshift count is either 2,3,4 or 5 */ 141 + count = (((count - 2) << DOWNSHIFT_CNTL_POS) | DOWNSHIFT_EN); 142 + } 143 + 144 + mutex_lock(&phydev->lock); 145 + rc = vsc85xx_phy_page_set(phydev, MSCC_PHY_PAGE_EXTENDED); 146 + if (rc != 0) 147 + goto out_unlock; 148 + 149 + reg_val = phy_read(phydev, MSCC_PHY_ACTIPHY_CNTL); 150 + reg_val &= ~(DOWNSHIFT_CNTL_MASK); 151 + reg_val |= count; 152 + rc = phy_write(phydev, MSCC_PHY_ACTIPHY_CNTL, reg_val); 153 + if (rc != 0) 154 + goto out_unlock; 155 + 156 + rc = vsc85xx_phy_page_set(phydev, MSCC_PHY_PAGE_STANDARD); 157 + 158 + out_unlock: 159 + mutex_unlock(&phydev->lock); 160 + 110 161 return rc; 111 162 } 112 163 ··· 398 329 return rc; 399 330 } 400 331 332 + static int vsc85xx_get_tunable(struct phy_device *phydev, 333 + struct ethtool_tunable *tuna, void *data) 334 + { 335 + switch (tuna->id) { 336 + case ETHTOOL_PHY_DOWNSHIFT: 337 + return vsc85xx_downshift_get(phydev, (u8 *)data); 338 + default: 339 + return -EINVAL; 340 + } 341 + } 342 + 343 + static int vsc85xx_set_tunable(struct phy_device *phydev, 344 + struct ethtool_tunable *tuna, 345 + const void *data) 346 + { 347 + switch (tuna->id) { 348 + case ETHTOOL_PHY_DOWNSHIFT: 349 + return vsc85xx_downshift_set(phydev, *(u8 *)data); 350 + default: 351 + return -EINVAL; 352 + } 353 + } 354 + 401 355 static int vsc85xx_config_init(struct phy_device *phydev) 402 356 { 403 357 int rc; ··· 510 418 .probe = &vsc85xx_probe, 511 419 .set_wol = &vsc85xx_wol_set, 512 420 .get_wol = &vsc85xx_wol_get, 421 + .get_tunable = &vsc85xx_get_tunable, 422 + .set_tunable = &vsc85xx_set_tunable, 513 423 }, 514 424 { 515 425 .phy_id = PHY_ID_VSC8531, ··· 531 437 .probe = &vsc85xx_probe, 532 438 .set_wol = &vsc85xx_wol_set, 533 439 .get_wol = &vsc85xx_wol_get, 440 + .get_tunable = &vsc85xx_get_tunable, 441 + .set_tunable = &vsc85xx_set_tunable, 534 442 }, 535 443 { 536 444 .phy_id = PHY_ID_VSC8540, ··· 552 456 .probe = &vsc85xx_probe, 553 457 .set_wol = &vsc85xx_wol_set, 554 458 .get_wol = &vsc85xx_wol_get, 459 + .get_tunable = &vsc85xx_get_tunable, 460 + .set_tunable = &vsc85xx_set_tunable, 555 461 }, 556 462 { 557 463 .phy_id = PHY_ID_VSC8541, ··· 573 475 .probe = &vsc85xx_probe, 574 476 .set_wol = &vsc85xx_wol_set, 575 477 .get_wol = &vsc85xx_wol_get, 478 + .get_tunable = &vsc85xx_get_tunable, 479 + .set_tunable = &vsc85xx_set_tunable, 576 480 } 577 481 578 482 };
+7
include/linux/phy.h
··· 611 611 void (*get_strings)(struct phy_device *dev, u8 *data); 612 612 void (*get_stats)(struct phy_device *dev, 613 613 struct ethtool_stats *stats, u64 *data); 614 + 615 + /* Get and Set PHY tunables */ 616 + int (*get_tunable)(struct phy_device *dev, 617 + struct ethtool_tunable *tuna, void *data); 618 + int (*set_tunable)(struct phy_device *dev, 619 + struct ethtool_tunable *tuna, 620 + const void *data); 614 621 }; 615 622 #define to_phy_driver(d) container_of(to_mdio_common_driver(d), \ 616 623 struct phy_driver, mdiodrv)
+17 -1
include/uapi/linux/ethtool.h
··· 248 248 void *data[0]; 249 249 }; 250 250 251 + #define DOWNSHIFT_DEV_DEFAULT_COUNT 0xff 252 + #define DOWNSHIFT_DEV_DISABLE 0 253 + 254 + enum phy_tunable_id { 255 + ETHTOOL_PHY_ID_UNSPEC, 256 + ETHTOOL_PHY_DOWNSHIFT, 257 + /* 258 + * Add your fresh new phy tunable attribute above and remember to update 259 + * phy_tunable_strings[] in net/core/ethtool.c 260 + */ 261 + __ETHTOOL_PHY_TUNABLE_COUNT, 262 + }; 263 + 251 264 /** 252 265 * struct ethtool_regs - hardware register dump 253 266 * @cmd: Command number = %ETHTOOL_GREGS ··· 561 548 * @ETH_SS_FEATURES: Device feature names 562 549 * @ETH_SS_RSS_HASH_FUNCS: RSS hush function names 563 550 * @ETH_SS_PHY_STATS: Statistic names, for use with %ETHTOOL_GPHYSTATS 551 + * @ETH_SS_PHY_TUNABLES: PHY tunable names 564 552 */ 565 553 enum ethtool_stringset { 566 554 ETH_SS_TEST = 0, ··· 572 558 ETH_SS_RSS_HASH_FUNCS, 573 559 ETH_SS_TUNABLES, 574 560 ETH_SS_PHY_STATS, 561 + ETH_SS_PHY_TUNABLES, 575 562 }; 576 563 577 564 /** ··· 1328 1313 1329 1314 #define ETHTOOL_GLINKSETTINGS 0x0000004c /* Get ethtool_link_settings */ 1330 1315 #define ETHTOOL_SLINKSETTINGS 0x0000004d /* Set ethtool_link_settings */ 1331 - 1316 + #define ETHTOOL_PHY_GTUNABLE 0x0000004e /* Get PHY tunable configuration */ 1317 + #define ETHTOOL_PHY_STUNABLE 0x0000004f /* Set PHY tunable configuration */ 1332 1318 1333 1319 /* compatibility with older code */ 1334 1320 #define SPARC_ETH_GSET ETHTOOL_GSET
+93
net/core/ethtool.c
··· 119 119 [ETHTOOL_TX_COPYBREAK] = "tx-copybreak", 120 120 }; 121 121 122 + static const char 123 + phy_tunable_strings[__ETHTOOL_PHY_TUNABLE_COUNT][ETH_GSTRING_LEN] = { 124 + [ETHTOOL_ID_UNSPEC] = "Unspec", 125 + [ETHTOOL_PHY_DOWNSHIFT] = "phy-downshift", 126 + }; 127 + 122 128 static int ethtool_get_features(struct net_device *dev, void __user *useraddr) 123 129 { 124 130 struct ethtool_gfeatures cmd = { ··· 233 227 if (sset == ETH_SS_TUNABLES) 234 228 return ARRAY_SIZE(tunable_strings); 235 229 230 + if (sset == ETH_SS_PHY_TUNABLES) 231 + return ARRAY_SIZE(phy_tunable_strings); 232 + 236 233 if (sset == ETH_SS_PHY_STATS) { 237 234 if (dev->phydev) 238 235 return phy_get_sset_count(dev->phydev); ··· 262 253 sizeof(rss_hash_func_strings)); 263 254 else if (stringset == ETH_SS_TUNABLES) 264 255 memcpy(data, tunable_strings, sizeof(tunable_strings)); 256 + else if (stringset == ETH_SS_PHY_TUNABLES) 257 + memcpy(data, phy_tunable_strings, sizeof(phy_tunable_strings)); 265 258 else if (stringset == ETH_SS_PHY_STATS) { 266 259 struct phy_device *phydev = dev->phydev; 267 260 ··· 2433 2422 }; 2434 2423 } 2435 2424 2425 + static int ethtool_phy_tunable_valid(const struct ethtool_tunable *tuna) 2426 + { 2427 + switch (tuna->id) { 2428 + case ETHTOOL_PHY_DOWNSHIFT: 2429 + if (tuna->len != sizeof(u8) || 2430 + tuna->type_id != ETHTOOL_TUNABLE_U8) 2431 + return -EINVAL; 2432 + break; 2433 + default: 2434 + return -EINVAL; 2435 + } 2436 + 2437 + return 0; 2438 + } 2439 + 2440 + static int get_phy_tunable(struct net_device *dev, void __user *useraddr) 2441 + { 2442 + int ret; 2443 + struct ethtool_tunable tuna; 2444 + struct phy_device *phydev = dev->phydev; 2445 + void *data; 2446 + 2447 + if (!(phydev && phydev->drv && phydev->drv->get_tunable)) 2448 + return -EOPNOTSUPP; 2449 + 2450 + if (copy_from_user(&tuna, useraddr, sizeof(tuna))) 2451 + return -EFAULT; 2452 + ret = ethtool_phy_tunable_valid(&tuna); 2453 + if (ret) 2454 + return ret; 2455 + data = kmalloc(tuna.len, GFP_USER); 2456 + if (!data) 2457 + return -ENOMEM; 2458 + ret = phydev->drv->get_tunable(phydev, &tuna, data); 2459 + if (ret) 2460 + goto out; 2461 + useraddr += sizeof(tuna); 2462 + ret = -EFAULT; 2463 + if (copy_to_user(useraddr, data, tuna.len)) 2464 + goto out; 2465 + ret = 0; 2466 + 2467 + out: 2468 + kfree(data); 2469 + return ret; 2470 + } 2471 + 2472 + static int set_phy_tunable(struct net_device *dev, void __user *useraddr) 2473 + { 2474 + int ret; 2475 + struct ethtool_tunable tuna; 2476 + struct phy_device *phydev = dev->phydev; 2477 + void *data; 2478 + 2479 + if (!(phydev && phydev->drv && phydev->drv->set_tunable)) 2480 + return -EOPNOTSUPP; 2481 + if (copy_from_user(&tuna, useraddr, sizeof(tuna))) 2482 + return -EFAULT; 2483 + ret = ethtool_phy_tunable_valid(&tuna); 2484 + if (ret) 2485 + return ret; 2486 + data = kmalloc(tuna.len, GFP_USER); 2487 + if (!data) 2488 + return -ENOMEM; 2489 + useraddr += sizeof(tuna); 2490 + ret = -EFAULT; 2491 + if (copy_from_user(data, useraddr, tuna.len)) 2492 + goto out; 2493 + ret = phydev->drv->set_tunable(phydev, &tuna, data); 2494 + 2495 + out: 2496 + kfree(data); 2497 + return ret; 2498 + } 2499 + 2436 2500 /* The main entry point in this file. Called from net/core/dev_ioctl.c */ 2437 2501 2438 2502 int dev_ethtool(struct net *net, struct ifreq *ifr) ··· 2565 2479 case ETHTOOL_GET_TS_INFO: 2566 2480 case ETHTOOL_GEEE: 2567 2481 case ETHTOOL_GTUNABLE: 2482 + case ETHTOOL_PHY_GTUNABLE: 2568 2483 break; 2569 2484 default: 2570 2485 if (!ns_capable(net->user_ns, CAP_NET_ADMIN)) ··· 2770 2683 break; 2771 2684 case ETHTOOL_SLINKSETTINGS: 2772 2685 rc = ethtool_set_link_ksettings(dev, useraddr); 2686 + break; 2687 + case ETHTOOL_PHY_GTUNABLE: 2688 + rc = get_phy_tunable(dev, useraddr); 2689 + break; 2690 + case ETHTOOL_PHY_STUNABLE: 2691 + rc = set_phy_tunable(dev, useraddr); 2773 2692 break; 2774 2693 default: 2775 2694 rc = -EOPNOTSUPP;