Commit e98d69ba464868a5d6b0b43730658810a29ff825

Authored by Freddy Xin
Committed by David S. Miller
1 parent 74e83b23f2

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