Commit e98d69ba464868a5d6b0b43730658810a29ff825
Committed by
David S. Miller
1 parent
74e83b23f2
Exists in
ti-lsk-linux-4.1.y
and in
10 other branches
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>
Showing 1 changed file with 264 additions and 0 deletions Side-by-side Diff
drivers/net/usb/ax88179_178a.c
... | ... | @@ -23,6 +23,8 @@ |
23 | 23 | #include <linux/usb.h> |
24 | 24 | #include <linux/crc32.h> |
25 | 25 | #include <linux/usb/usbnet.h> |
26 | +#include <uapi/linux/mdio.h> | |
27 | +#include <linux/mdio.h> | |
26 | 28 | |
27 | 29 | #define AX88179_PHY_ID 0x03 |
28 | 30 | #define AX_EEPROM_LEN 0x100 |
29 | 31 | |
... | ... | @@ -170,8 +172,12 @@ |
170 | 172 | #define GMII_PHY_PAGE_SELECT 0x1f |
171 | 173 | #define GMII_PHY_PGSEL_EXT 0x0007 |
172 | 174 | #define GMII_PHY_PGSEL_PAGE0 0x0000 |
175 | + #define GMII_PHY_PGSEL_PAGE3 0x0003 | |
176 | + #define GMII_PHY_PGSEL_PAGE5 0x0005 | |
173 | 177 | |
174 | 178 | struct ax88179_data { |
179 | + u8 eee_enabled; | |
180 | + u8 eee_active; | |
175 | 181 | u16 rxctl; |
176 | 182 | u16 reserved; |
177 | 183 | }; |
... | ... | @@ -373,6 +379,60 @@ |
373 | 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 | 436 | static int ax88179_suspend(struct usb_interface *intf, pm_message_t message) |
377 | 437 | { |
378 | 438 | struct usbnet *dev = usb_get_intfdata(intf); |
379 | 439 | |
... | ... | @@ -572,7 +632,186 @@ |
572 | 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; | |
575 | 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 | +} | |
814 | + | |
576 | 815 | static int ax88179_ioctl(struct net_device *net, struct ifreq *rq, int cmd) |
577 | 816 | { |
578 | 817 | struct usbnet *dev = netdev_priv(net); |
... | ... | @@ -589,6 +828,8 @@ |
589 | 828 | .get_eeprom = ax88179_get_eeprom, |
590 | 829 | .get_settings = ax88179_get_settings, |
591 | 830 | .set_settings = ax88179_set_settings, |
831 | + .get_eee = ax88179_get_eee, | |
832 | + .set_eee = ax88179_set_eee, | |
592 | 833 | .nway_reset = usbnet_nway_reset, |
593 | 834 | }; |
594 | 835 | |
... | ... | @@ -980,6 +1221,7 @@ |
980 | 1221 | u16 *tmp16; |
981 | 1222 | u8 *tmp; |
982 | 1223 | struct ax88179_data *ax179_data = (struct ax88179_data *)dev->data; |
1224 | + struct ethtool_eee eee_data; | |
983 | 1225 | |
984 | 1226 | usbnet_get_endpoints(dev, intf); |
985 | 1227 | |
... | ... | @@ -1062,6 +1304,15 @@ |
1062 | 1304 | |
1063 | 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 | 1316 | /* Restart autoneg */ |
1066 | 1317 | mii_nway_restart(&dev->mii); |
1067 | 1318 | |
... | ... | @@ -1261,6 +1512,8 @@ |
1261 | 1512 | ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE, |
1262 | 1513 | 2, 2, &mode); |
1263 | 1514 | |
1515 | + ax179_data->eee_enabled = ax88179_chk_eee(dev); | |
1516 | + | |
1264 | 1517 | netif_carrier_on(dev->net); |
1265 | 1518 | |
1266 | 1519 | return 0; |
... | ... | @@ -1271,6 +1524,8 @@ |
1271 | 1524 | u8 buf[5]; |
1272 | 1525 | u16 *tmp16; |
1273 | 1526 | u8 *tmp; |
1527 | + struct ax88179_data *ax179_data = (struct ax88179_data *)dev->data; | |
1528 | + struct ethtool_eee eee_data; | |
1274 | 1529 | |
1275 | 1530 | tmp16 = (u16 *)buf; |
1276 | 1531 | tmp = (u8 *)buf; |
... | ... | @@ -1339,6 +1594,15 @@ |
1339 | 1594 | 2, 2, tmp16); |
1340 | 1595 | |
1341 | 1596 | ax88179_led_setting(dev); |
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); | |
1342 | 1606 | |
1343 | 1607 | /* Restart autoneg */ |
1344 | 1608 | mii_nway_restart(&dev->mii); |