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 | |
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>
-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 | ||