Skip to content

Commit

Permalink
AX88179_178A: Add ethtool ops for EEE support
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Freddy Xin authored and David S. Miller committed Aug 1, 2014
1 parent 74e83b2 commit e98d69b
Showing 1 changed file with 264 additions and 0 deletions.
264 changes: 264 additions & 0 deletions drivers/net/usb/ax88179_178a.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
#include <linux/usb.h>
#include <linux/crc32.h>
#include <linux/usb/usbnet.h>
#include <uapi/linux/mdio.h>
#include <linux/mdio.h>

#define AX88179_PHY_ID 0x03
#define AX_EEPROM_LEN 0x100
Expand Down Expand Up @@ -170,8 +172,12 @@
#define GMII_PHY_PAGE_SELECT 0x1f
#define GMII_PHY_PGSEL_EXT 0x0007
#define GMII_PHY_PGSEL_PAGE0 0x0000
#define GMII_PHY_PGSEL_PAGE3 0x0003
#define GMII_PHY_PGSEL_PAGE5 0x0005

struct ax88179_data {
u8 eee_enabled;
u8 eee_active;
u16 rxctl;
u16 reserved;
};
Expand Down Expand Up @@ -373,6 +379,60 @@ static void ax88179_mdio_write(struct net_device *netdev, int phy_id, int loc,
ax88179_write_cmd(dev, AX_ACCESS_PHY, phy_id, (__u16)loc, 2, &res);
}

static inline int ax88179_phy_mmd_indirect(struct usbnet *dev, u16 prtad,
u16 devad)
{
u16 tmp16;
int ret;

tmp16 = devad;
ret = ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
MII_MMD_CTRL, 2, &tmp16);

tmp16 = prtad;
ret = ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
MII_MMD_DATA, 2, &tmp16);

tmp16 = devad | MII_MMD_CTRL_NOINCR;
ret = ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
MII_MMD_CTRL, 2, &tmp16);

return ret;
}

static int
ax88179_phy_read_mmd_indirect(struct usbnet *dev, u16 prtad, u16 devad)
{
int ret;
u16 tmp16;

ax88179_phy_mmd_indirect(dev, prtad, devad);

ret = ax88179_read_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
MII_MMD_DATA, 2, &tmp16);
if (ret < 0)
return ret;

return tmp16;
}

static int
ax88179_phy_write_mmd_indirect(struct usbnet *dev, u16 prtad, u16 devad,
u16 data)
{
int ret;

ax88179_phy_mmd_indirect(dev, prtad, devad);

ret = ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
MII_MMD_DATA, 2, &data);

if (ret < 0)
return ret;

return 0;
}

static int ax88179_suspend(struct usb_interface *intf, pm_message_t message)
{
struct usbnet *dev = usb_get_intfdata(intf);
Expand Down Expand Up @@ -572,6 +632,185 @@ static int ax88179_set_settings(struct net_device *net, struct ethtool_cmd *cmd)
return mii_ethtool_sset(&dev->mii, cmd);
}

static int
ax88179_ethtool_get_eee(struct usbnet *dev, struct ethtool_eee *data)
{
int val;

/* Get Supported EEE */
val = ax88179_phy_read_mmd_indirect(dev, MDIO_PCS_EEE_ABLE,
MDIO_MMD_PCS);
if (val < 0)
return val;
data->supported = mmd_eee_cap_to_ethtool_sup_t(val);

/* Get advertisement EEE */
val = ax88179_phy_read_mmd_indirect(dev, MDIO_AN_EEE_ADV,
MDIO_MMD_AN);
if (val < 0)
return val;
data->advertised = mmd_eee_adv_to_ethtool_adv_t(val);

/* Get LP advertisement EEE */
val = ax88179_phy_read_mmd_indirect(dev, MDIO_AN_EEE_LPABLE,
MDIO_MMD_AN);
if (val < 0)
return val;
data->lp_advertised = mmd_eee_adv_to_ethtool_adv_t(val);

return 0;
}

static int
ax88179_ethtool_set_eee(struct usbnet *dev, struct ethtool_eee *data)
{
u16 tmp16 = ethtool_adv_to_mmd_eee_adv_t(data->advertised);

return ax88179_phy_write_mmd_indirect(dev, MDIO_AN_EEE_ADV,
MDIO_MMD_AN, tmp16);
}

static int ax88179_chk_eee(struct usbnet *dev)
{
struct ethtool_cmd ecmd = { .cmd = ETHTOOL_GSET };
struct ax88179_data *priv = (struct ax88179_data *)dev->data;

mii_ethtool_gset(&dev->mii, &ecmd);

if (ecmd.duplex & DUPLEX_FULL) {
int eee_lp, eee_cap, eee_adv;
u32 lp, cap, adv, supported = 0;

eee_cap = ax88179_phy_read_mmd_indirect(dev,
MDIO_PCS_EEE_ABLE,
MDIO_MMD_PCS);
if (eee_cap < 0) {
priv->eee_active = 0;
return false;
}

cap = mmd_eee_cap_to_ethtool_sup_t(eee_cap);
if (!cap) {
priv->eee_active = 0;
return false;
}

eee_lp = ax88179_phy_read_mmd_indirect(dev,
MDIO_AN_EEE_LPABLE,
MDIO_MMD_AN);
if (eee_lp < 0) {
priv->eee_active = 0;
return false;
}

eee_adv = ax88179_phy_read_mmd_indirect(dev,
MDIO_AN_EEE_ADV,
MDIO_MMD_AN);

if (eee_adv < 0) {
priv->eee_active = 0;
return false;
}

adv = mmd_eee_adv_to_ethtool_adv_t(eee_adv);
lp = mmd_eee_adv_to_ethtool_adv_t(eee_lp);
supported = (ecmd.speed == SPEED_1000) ?
SUPPORTED_1000baseT_Full :
SUPPORTED_100baseT_Full;

if (!(lp & adv & supported)) {
priv->eee_active = 0;
return false;
}

priv->eee_active = 1;
return true;
}

priv->eee_active = 0;
return false;
}

static void ax88179_disable_eee(struct usbnet *dev)
{
u16 tmp16;

tmp16 = GMII_PHY_PGSEL_PAGE3;
ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
GMII_PHY_PAGE_SELECT, 2, &tmp16);

tmp16 = 0x3246;
ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
MII_PHYADDR, 2, &tmp16);

tmp16 = GMII_PHY_PGSEL_PAGE0;
ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
GMII_PHY_PAGE_SELECT, 2, &tmp16);
}

static void ax88179_enable_eee(struct usbnet *dev)
{
u16 tmp16;

tmp16 = GMII_PHY_PGSEL_PAGE3;
ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
GMII_PHY_PAGE_SELECT, 2, &tmp16);

tmp16 = 0x3247;
ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
MII_PHYADDR, 2, &tmp16);

tmp16 = GMII_PHY_PGSEL_PAGE5;
ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
GMII_PHY_PAGE_SELECT, 2, &tmp16);

tmp16 = 0x0680;
ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
MII_BMSR, 2, &tmp16);

tmp16 = GMII_PHY_PGSEL_PAGE0;
ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
GMII_PHY_PAGE_SELECT, 2, &tmp16);
}

static int ax88179_get_eee(struct net_device *net, struct ethtool_eee *edata)
{
struct usbnet *dev = netdev_priv(net);
struct ax88179_data *priv = (struct ax88179_data *)dev->data;

edata->eee_enabled = priv->eee_enabled;
edata->eee_active = priv->eee_active;

return ax88179_ethtool_get_eee(dev, edata);
}

static int ax88179_set_eee(struct net_device *net, struct ethtool_eee *edata)
{
struct usbnet *dev = netdev_priv(net);
struct ax88179_data *priv = (struct ax88179_data *)dev->data;
int ret = -EOPNOTSUPP;

priv->eee_enabled = edata->eee_enabled;
if (!priv->eee_enabled) {
ax88179_disable_eee(dev);
} else {
priv->eee_enabled = ax88179_chk_eee(dev);
if (!priv->eee_enabled)
return -EOPNOTSUPP;

ax88179_enable_eee(dev);
}

ret = ax88179_ethtool_set_eee(dev, edata);
if (ret)
return ret;

mii_nway_restart(&dev->mii);

usbnet_link_change(dev, 0, 0);

return ret;
}

static int ax88179_ioctl(struct net_device *net, struct ifreq *rq, int cmd)
{
Expand All @@ -589,6 +828,8 @@ static const struct ethtool_ops ax88179_ethtool_ops = {
.get_eeprom = ax88179_get_eeprom,
.get_settings = ax88179_get_settings,
.set_settings = ax88179_set_settings,
.get_eee = ax88179_get_eee,
.set_eee = ax88179_set_eee,
.nway_reset = usbnet_nway_reset,
};

Expand Down Expand Up @@ -980,6 +1221,7 @@ static int ax88179_bind(struct usbnet *dev, struct usb_interface *intf)
u16 *tmp16;
u8 *tmp;
struct ax88179_data *ax179_data = (struct ax88179_data *)dev->data;
struct ethtool_eee eee_data;

usbnet_get_endpoints(dev, intf);

Expand Down Expand Up @@ -1062,6 +1304,15 @@ static int ax88179_bind(struct usbnet *dev, struct usb_interface *intf)

ax88179_led_setting(dev);

ax179_data->eee_enabled = 0;
ax179_data->eee_active = 0;

ax88179_disable_eee(dev);

ax88179_ethtool_get_eee(dev, &eee_data);
eee_data.advertised = 0;
ax88179_ethtool_set_eee(dev, &eee_data);

/* Restart autoneg */
mii_nway_restart(&dev->mii);

Expand Down Expand Up @@ -1261,6 +1512,8 @@ static int ax88179_link_reset(struct usbnet *dev)
ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,
2, 2, &mode);

ax179_data->eee_enabled = ax88179_chk_eee(dev);

netif_carrier_on(dev->net);

return 0;
Expand All @@ -1271,6 +1524,8 @@ static int ax88179_reset(struct usbnet *dev)
u8 buf[5];
u16 *tmp16;
u8 *tmp;
struct ax88179_data *ax179_data = (struct ax88179_data *)dev->data;
struct ethtool_eee eee_data;

tmp16 = (u16 *)buf;
tmp = (u8 *)buf;
Expand Down Expand Up @@ -1340,6 +1595,15 @@ static int ax88179_reset(struct usbnet *dev)

ax88179_led_setting(dev);

ax179_data->eee_enabled = 0;
ax179_data->eee_active = 0;

ax88179_disable_eee(dev);

ax88179_ethtool_get_eee(dev, &eee_data);
eee_data.advertised = 0;
ax88179_ethtool_set_eee(dev, &eee_data);

/* Restart autoneg */
mii_nway_restart(&dev->mii);

Expand Down

0 comments on commit e98d69b

Please sign in to comment.