diff options
| author | Freddy Xin <freddy@asix.com.tw> | 2014-07-31 07:06:35 -0400 |
|---|---|---|
| committer | David S. Miller <davem@davemloft.net> | 2014-08-01 01:07:38 -0400 |
| commit | e98d69ba464868a5d6b0b43730658810a29ff825 (patch) | |
| tree | 2335182f6c800f3e72d0a68265cd687d0e550c37 /drivers/net/usb | |
| parent | 74e83b23f2619db057c9e3c3ec4b7090f883bc5e (diff) | |
AX88179_178A: Add ethtool ops for EEE support
Add functions to support ethtool EEE manipulating, and the EEE
is disabled in default setting to enhance the compatibility
with certain switch.
Signed-off-by: Freddy Xin <freddy@asix.com.tw>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'drivers/net/usb')
| -rw-r--r-- | drivers/net/usb/ax88179_178a.c | 264 |
1 files changed, 264 insertions, 0 deletions
diff --git a/drivers/net/usb/ax88179_178a.c b/drivers/net/usb/ax88179_178a.c index 054e59ca6946..be4275721039 100644 --- a/drivers/net/usb/ax88179_178a.c +++ b/drivers/net/usb/ax88179_178a.c | |||
| @@ -23,6 +23,8 @@ | |||
| 23 | #include <linux/usb.h> | 23 | #include <linux/usb.h> |
| 24 | #include <linux/crc32.h> | 24 | #include <linux/crc32.h> |
| 25 | #include <linux/usb/usbnet.h> | 25 | #include <linux/usb/usbnet.h> |
| 26 | #include <uapi/linux/mdio.h> | ||
| 27 | #include <linux/mdio.h> | ||
| 26 | 28 | ||
| 27 | #define AX88179_PHY_ID 0x03 | 29 | #define AX88179_PHY_ID 0x03 |
| 28 | #define AX_EEPROM_LEN 0x100 | 30 | #define AX_EEPROM_LEN 0x100 |
| @@ -170,8 +172,12 @@ | |||
| 170 | #define GMII_PHY_PAGE_SELECT 0x1f | 172 | #define GMII_PHY_PAGE_SELECT 0x1f |
| 171 | #define GMII_PHY_PGSEL_EXT 0x0007 | 173 | #define GMII_PHY_PGSEL_EXT 0x0007 |
| 172 | #define GMII_PHY_PGSEL_PAGE0 0x0000 | 174 | #define GMII_PHY_PGSEL_PAGE0 0x0000 |
| 175 | #define GMII_PHY_PGSEL_PAGE3 0x0003 | ||
| 176 | #define GMII_PHY_PGSEL_PAGE5 0x0005 | ||
| 173 | 177 | ||
| 174 | struct ax88179_data { | 178 | struct ax88179_data { |
| 179 | u8 eee_enabled; | ||
| 180 | u8 eee_active; | ||
| 175 | u16 rxctl; | 181 | u16 rxctl; |
| 176 | u16 reserved; | 182 | u16 reserved; |
| 177 | }; | 183 | }; |
| @@ -373,6 +379,60 @@ static void ax88179_mdio_write(struct net_device *netdev, int phy_id, int loc, | |||
| 373 | ax88179_write_cmd(dev, AX_ACCESS_PHY, phy_id, (__u16)loc, 2, &res); | 379 | ax88179_write_cmd(dev, AX_ACCESS_PHY, phy_id, (__u16)loc, 2, &res); |
| 374 | } | 380 | } |
| 375 | 381 | ||
| 382 | static inline int ax88179_phy_mmd_indirect(struct usbnet *dev, u16 prtad, | ||
| 383 | u16 devad) | ||
| 384 | { | ||
| 385 | u16 tmp16; | ||
| 386 | int ret; | ||
| 387 | |||
| 388 | tmp16 = devad; | ||
| 389 | ret = ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, | ||
| 390 | MII_MMD_CTRL, 2, &tmp16); | ||
| 391 | |||
| 392 | tmp16 = prtad; | ||
| 393 | ret = ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, | ||
| 394 | MII_MMD_DATA, 2, &tmp16); | ||
| 395 | |||
| 396 | tmp16 = devad | MII_MMD_CTRL_NOINCR; | ||
| 397 | ret = ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, | ||
| 398 | MII_MMD_CTRL, 2, &tmp16); | ||
| 399 | |||
| 400 | return ret; | ||
| 401 | } | ||
| 402 | |||
| 403 | static int | ||
| 404 | ax88179_phy_read_mmd_indirect(struct usbnet *dev, u16 prtad, u16 devad) | ||
| 405 | { | ||
| 406 | int ret; | ||
| 407 | u16 tmp16; | ||
| 408 | |||
| 409 | ax88179_phy_mmd_indirect(dev, prtad, devad); | ||
| 410 | |||
| 411 | ret = ax88179_read_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, | ||
| 412 | MII_MMD_DATA, 2, &tmp16); | ||
| 413 | if (ret < 0) | ||
| 414 | return ret; | ||
| 415 | |||
| 416 | return tmp16; | ||
| 417 | } | ||
| 418 | |||
| 419 | static int | ||
| 420 | ax88179_phy_write_mmd_indirect(struct usbnet *dev, u16 prtad, u16 devad, | ||
| 421 | u16 data) | ||
| 422 | { | ||
| 423 | int ret; | ||
| 424 | |||
| 425 | ax88179_phy_mmd_indirect(dev, prtad, devad); | ||
| 426 | |||
| 427 | ret = ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, | ||
| 428 | MII_MMD_DATA, 2, &data); | ||
| 429 | |||
| 430 | if (ret < 0) | ||
| 431 | return ret; | ||
| 432 | |||
| 433 | return 0; | ||
| 434 | } | ||
| 435 | |||
| 376 | static int ax88179_suspend(struct usb_interface *intf, pm_message_t message) | 436 | static int ax88179_suspend(struct usb_interface *intf, pm_message_t message) |
| 377 | { | 437 | { |
| 378 | struct usbnet *dev = usb_get_intfdata(intf); | 438 | struct usbnet *dev = usb_get_intfdata(intf); |
| @@ -572,6 +632,185 @@ static int ax88179_set_settings(struct net_device *net, struct ethtool_cmd *cmd) | |||
| 572 | return mii_ethtool_sset(&dev->mii, cmd); | 632 | return mii_ethtool_sset(&dev->mii, cmd); |
| 573 | } | 633 | } |
| 574 | 634 | ||
| 635 | static int | ||
| 636 | ax88179_ethtool_get_eee(struct usbnet *dev, struct ethtool_eee *data) | ||
| 637 | { | ||
| 638 | int val; | ||
| 639 | |||
| 640 | /* Get Supported EEE */ | ||
| 641 | val = ax88179_phy_read_mmd_indirect(dev, MDIO_PCS_EEE_ABLE, | ||
| 642 | MDIO_MMD_PCS); | ||
| 643 | if (val < 0) | ||
| 644 | return val; | ||
| 645 | data->supported = mmd_eee_cap_to_ethtool_sup_t(val); | ||
| 646 | |||
| 647 | /* Get advertisement EEE */ | ||
| 648 | val = ax88179_phy_read_mmd_indirect(dev, MDIO_AN_EEE_ADV, | ||
| 649 | MDIO_MMD_AN); | ||
| 650 | if (val < 0) | ||
| 651 | return val; | ||
| 652 | data->advertised = mmd_eee_adv_to_ethtool_adv_t(val); | ||
| 653 | |||
| 654 | /* Get LP advertisement EEE */ | ||
| 655 | val = ax88179_phy_read_mmd_indirect(dev, MDIO_AN_EEE_LPABLE, | ||
| 656 | MDIO_MMD_AN); | ||
| 657 | if (val < 0) | ||
| 658 | return val; | ||
| 659 | data->lp_advertised = mmd_eee_adv_to_ethtool_adv_t(val); | ||
| 660 | |||
| 661 | return 0; | ||
| 662 | } | ||
| 663 | |||
| 664 | static int | ||
| 665 | ax88179_ethtool_set_eee(struct usbnet *dev, struct ethtool_eee *data) | ||
| 666 | { | ||
| 667 | u16 tmp16 = ethtool_adv_to_mmd_eee_adv_t(data->advertised); | ||
| 668 | |||
| 669 | return ax88179_phy_write_mmd_indirect(dev, MDIO_AN_EEE_ADV, | ||
| 670 | MDIO_MMD_AN, tmp16); | ||
| 671 | } | ||
| 672 | |||
| 673 | static int ax88179_chk_eee(struct usbnet *dev) | ||
| 674 | { | ||
| 675 | struct ethtool_cmd ecmd = { .cmd = ETHTOOL_GSET }; | ||
| 676 | struct ax88179_data *priv = (struct ax88179_data *)dev->data; | ||
| 677 | |||
| 678 | mii_ethtool_gset(&dev->mii, &ecmd); | ||
| 679 | |||
| 680 | if (ecmd.duplex & DUPLEX_FULL) { | ||
| 681 | int eee_lp, eee_cap, eee_adv; | ||
| 682 | u32 lp, cap, adv, supported = 0; | ||
| 683 | |||
| 684 | eee_cap = ax88179_phy_read_mmd_indirect(dev, | ||
| 685 | MDIO_PCS_EEE_ABLE, | ||
| 686 | MDIO_MMD_PCS); | ||
| 687 | if (eee_cap < 0) { | ||
| 688 | priv->eee_active = 0; | ||
| 689 | return false; | ||
| 690 | } | ||
| 691 | |||
| 692 | cap = mmd_eee_cap_to_ethtool_sup_t(eee_cap); | ||
| 693 | if (!cap) { | ||
| 694 | priv->eee_active = 0; | ||
| 695 | return false; | ||
| 696 | } | ||
| 697 | |||
| 698 | eee_lp = ax88179_phy_read_mmd_indirect(dev, | ||
| 699 | MDIO_AN_EEE_LPABLE, | ||
| 700 | MDIO_MMD_AN); | ||
| 701 | if (eee_lp < 0) { | ||
| 702 | priv->eee_active = 0; | ||
| 703 | return false; | ||
| 704 | } | ||
| 705 | |||
| 706 | eee_adv = ax88179_phy_read_mmd_indirect(dev, | ||
| 707 | MDIO_AN_EEE_ADV, | ||
| 708 | MDIO_MMD_AN); | ||
| 709 | |||
| 710 | if (eee_adv < 0) { | ||
| 711 | priv->eee_active = 0; | ||
| 712 | return false; | ||
| 713 | } | ||
| 714 | |||
| 715 | adv = mmd_eee_adv_to_ethtool_adv_t(eee_adv); | ||
| 716 | lp = mmd_eee_adv_to_ethtool_adv_t(eee_lp); | ||
| 717 | supported = (ecmd.speed == SPEED_1000) ? | ||
| 718 | SUPPORTED_1000baseT_Full : | ||
| 719 | SUPPORTED_100baseT_Full; | ||
| 720 | |||
| 721 | if (!(lp & adv & supported)) { | ||
| 722 | priv->eee_active = 0; | ||
| 723 | return false; | ||
| 724 | } | ||
| 725 | |||
| 726 | priv->eee_active = 1; | ||
| 727 | return true; | ||
| 728 | } | ||
| 729 | |||
| 730 | priv->eee_active = 0; | ||
| 731 | return false; | ||
| 732 | } | ||
| 733 | |||
| 734 | static void ax88179_disable_eee(struct usbnet *dev) | ||
| 735 | { | ||
| 736 | u16 tmp16; | ||
| 737 | |||
| 738 | tmp16 = GMII_PHY_PGSEL_PAGE3; | ||
| 739 | ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, | ||
| 740 | GMII_PHY_PAGE_SELECT, 2, &tmp16); | ||
| 741 | |||
| 742 | tmp16 = 0x3246; | ||
| 743 | ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, | ||
| 744 | MII_PHYADDR, 2, &tmp16); | ||
| 745 | |||
| 746 | tmp16 = GMII_PHY_PGSEL_PAGE0; | ||
| 747 | ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, | ||
| 748 | GMII_PHY_PAGE_SELECT, 2, &tmp16); | ||
| 749 | } | ||
| 750 | |||
| 751 | static void ax88179_enable_eee(struct usbnet *dev) | ||
| 752 | { | ||
| 753 | u16 tmp16; | ||
| 754 | |||
| 755 | tmp16 = GMII_PHY_PGSEL_PAGE3; | ||
| 756 | ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, | ||
| 757 | GMII_PHY_PAGE_SELECT, 2, &tmp16); | ||
| 758 | |||
| 759 | tmp16 = 0x3247; | ||
| 760 | ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, | ||
| 761 | MII_PHYADDR, 2, &tmp16); | ||
| 762 | |||
| 763 | tmp16 = GMII_PHY_PGSEL_PAGE5; | ||
| 764 | ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, | ||
| 765 | GMII_PHY_PAGE_SELECT, 2, &tmp16); | ||
| 766 | |||
| 767 | tmp16 = 0x0680; | ||
| 768 | ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, | ||
| 769 | MII_BMSR, 2, &tmp16); | ||
| 770 | |||
| 771 | tmp16 = GMII_PHY_PGSEL_PAGE0; | ||
| 772 | ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, | ||
| 773 | GMII_PHY_PAGE_SELECT, 2, &tmp16); | ||
| 774 | } | ||
| 775 | |||
| 776 | static int ax88179_get_eee(struct net_device *net, struct ethtool_eee *edata) | ||
| 777 | { | ||
| 778 | struct usbnet *dev = netdev_priv(net); | ||
| 779 | struct ax88179_data *priv = (struct ax88179_data *)dev->data; | ||
| 780 | |||
| 781 | edata->eee_enabled = priv->eee_enabled; | ||
| 782 | edata->eee_active = priv->eee_active; | ||
| 783 | |||
| 784 | return ax88179_ethtool_get_eee(dev, edata); | ||
| 785 | } | ||
| 786 | |||
| 787 | static int ax88179_set_eee(struct net_device *net, struct ethtool_eee *edata) | ||
| 788 | { | ||
| 789 | struct usbnet *dev = netdev_priv(net); | ||
| 790 | struct ax88179_data *priv = (struct ax88179_data *)dev->data; | ||
| 791 | int ret = -EOPNOTSUPP; | ||
| 792 | |||
| 793 | priv->eee_enabled = edata->eee_enabled; | ||
| 794 | if (!priv->eee_enabled) { | ||
| 795 | ax88179_disable_eee(dev); | ||
| 796 | } else { | ||
| 797 | priv->eee_enabled = ax88179_chk_eee(dev); | ||
| 798 | if (!priv->eee_enabled) | ||
| 799 | return -EOPNOTSUPP; | ||
| 800 | |||
| 801 | ax88179_enable_eee(dev); | ||
| 802 | } | ||
| 803 | |||
| 804 | ret = ax88179_ethtool_set_eee(dev, edata); | ||
| 805 | if (ret) | ||
| 806 | return ret; | ||
| 807 | |||
| 808 | mii_nway_restart(&dev->mii); | ||
| 809 | |||
| 810 | usbnet_link_change(dev, 0, 0); | ||
| 811 | |||
| 812 | return ret; | ||
| 813 | } | ||
| 575 | 814 | ||
| 576 | static int ax88179_ioctl(struct net_device *net, struct ifreq *rq, int cmd) | 815 | static int ax88179_ioctl(struct net_device *net, struct ifreq *rq, int cmd) |
| 577 | { | 816 | { |
| @@ -589,6 +828,8 @@ static const struct ethtool_ops ax88179_ethtool_ops = { | |||
| 589 | .get_eeprom = ax88179_get_eeprom, | 828 | .get_eeprom = ax88179_get_eeprom, |
| 590 | .get_settings = ax88179_get_settings, | 829 | .get_settings = ax88179_get_settings, |
| 591 | .set_settings = ax88179_set_settings, | 830 | .set_settings = ax88179_set_settings, |
| 831 | .get_eee = ax88179_get_eee, | ||
| 832 | .set_eee = ax88179_set_eee, | ||
| 592 | .nway_reset = usbnet_nway_reset, | 833 | .nway_reset = usbnet_nway_reset, |
| 593 | }; | 834 | }; |
| 594 | 835 | ||
| @@ -980,6 +1221,7 @@ static int ax88179_bind(struct usbnet *dev, struct usb_interface *intf) | |||
| 980 | u16 *tmp16; | 1221 | u16 *tmp16; |
| 981 | u8 *tmp; | 1222 | u8 *tmp; |
| 982 | struct ax88179_data *ax179_data = (struct ax88179_data *)dev->data; | 1223 | struct ax88179_data *ax179_data = (struct ax88179_data *)dev->data; |
| 1224 | struct ethtool_eee eee_data; | ||
| 983 | 1225 | ||
| 984 | usbnet_get_endpoints(dev, intf); | 1226 | usbnet_get_endpoints(dev, intf); |
| 985 | 1227 | ||
| @@ -1062,6 +1304,15 @@ static int ax88179_bind(struct usbnet *dev, struct usb_interface *intf) | |||
| 1062 | 1304 | ||
| 1063 | ax88179_led_setting(dev); | 1305 | ax88179_led_setting(dev); |
| 1064 | 1306 | ||
| 1307 | ax179_data->eee_enabled = 0; | ||
| 1308 | ax179_data->eee_active = 0; | ||
| 1309 | |||
| 1310 | ax88179_disable_eee(dev); | ||
| 1311 | |||
| 1312 | ax88179_ethtool_get_eee(dev, &eee_data); | ||
| 1313 | eee_data.advertised = 0; | ||
| 1314 | ax88179_ethtool_set_eee(dev, &eee_data); | ||
| 1315 | |||
| 1065 | /* Restart autoneg */ | 1316 | /* Restart autoneg */ |
| 1066 | mii_nway_restart(&dev->mii); | 1317 | mii_nway_restart(&dev->mii); |
| 1067 | 1318 | ||
| @@ -1261,6 +1512,8 @@ static int ax88179_link_reset(struct usbnet *dev) | |||
| 1261 | ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE, | 1512 | ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE, |
| 1262 | 2, 2, &mode); | 1513 | 2, 2, &mode); |
| 1263 | 1514 | ||
| 1515 | ax179_data->eee_enabled = ax88179_chk_eee(dev); | ||
| 1516 | |||
| 1264 | netif_carrier_on(dev->net); | 1517 | netif_carrier_on(dev->net); |
| 1265 | 1518 | ||
| 1266 | return 0; | 1519 | return 0; |
| @@ -1271,6 +1524,8 @@ static int ax88179_reset(struct usbnet *dev) | |||
| 1271 | u8 buf[5]; | 1524 | u8 buf[5]; |
| 1272 | u16 *tmp16; | 1525 | u16 *tmp16; |
| 1273 | u8 *tmp; | 1526 | u8 *tmp; |
| 1527 | struct ax88179_data *ax179_data = (struct ax88179_data *)dev->data; | ||
| 1528 | struct ethtool_eee eee_data; | ||
| 1274 | 1529 | ||
| 1275 | tmp16 = (u16 *)buf; | 1530 | tmp16 = (u16 *)buf; |
| 1276 | tmp = (u8 *)buf; | 1531 | tmp = (u8 *)buf; |
| @@ -1340,6 +1595,15 @@ static int ax88179_reset(struct usbnet *dev) | |||
| 1340 | 1595 | ||
| 1341 | ax88179_led_setting(dev); | 1596 | ax88179_led_setting(dev); |
| 1342 | 1597 | ||
| 1598 | ax179_data->eee_enabled = 0; | ||
| 1599 | ax179_data->eee_active = 0; | ||
| 1600 | |||
| 1601 | ax88179_disable_eee(dev); | ||
| 1602 | |||
| 1603 | ax88179_ethtool_get_eee(dev, &eee_data); | ||
| 1604 | eee_data.advertised = 0; | ||
| 1605 | ax88179_ethtool_set_eee(dev, &eee_data); | ||
| 1606 | |||
| 1343 | /* Restart autoneg */ | 1607 | /* Restart autoneg */ |
| 1344 | mii_nway_restart(&dev->mii); | 1608 | mii_nway_restart(&dev->mii); |
| 1345 | 1609 | ||
