linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH 1/3] net: asix: Add in_pm parameter
@ 2016-07-25 17:40 robert.foss
  2016-07-25 17:40 ` [PATCH 2/3] net: asix: Avoid looping when the device is disconnected robert.foss
                   ` (3 more replies)
  0 siblings, 4 replies; 9+ messages in thread
From: robert.foss @ 2016-07-25 17:40 UTC (permalink / raw)
  To: davem, grundler, Mark_Craske, Dean_Jenkins, vpalatin, ivecera,
	linux, wk.tsai, changchias, linux-usb, netdev, linux-kernel
  Cc: enric.balletbo, tomeu.vizoso

From: Grant Grundler <grundler@chromium.org>

In order to R/W registers in suspend/resume functions, in_pm flags are
added to some functions to determine whether the nopm version of usb
functions is called.

Save BMCR and ANAR PHY registers in suspend function and restore them
in resume function.

Reset HW in resume function to ensure the PHY works correctly.

Signed-off-by: Grant Grundler <grundler@chromium.org>
---
 drivers/net/usb/asix.h         |  40 +++--
 drivers/net/usb/asix_common.c  | 180 +++++++++++++++-----
 drivers/net/usb/asix_devices.c | 373 ++++++++++++++++++++++++++++++++---------
 drivers/net/usb/ax88172a.c     |  29 ++--
 4 files changed, 472 insertions(+), 150 deletions(-)

diff --git a/drivers/net/usb/asix.h b/drivers/net/usb/asix.h
index a2d3ea6..d109242 100644
--- a/drivers/net/usb/asix.h
+++ b/drivers/net/usb/asix.h
@@ -46,6 +46,7 @@
 #define AX_CMD_SET_SW_MII		0x06
 #define AX_CMD_READ_MII_REG		0x07
 #define AX_CMD_WRITE_MII_REG		0x08
+#define AX_CMD_STATMNGSTS_REG		0x09
 #define AX_CMD_SET_HW_MII		0x0a
 #define AX_CMD_READ_EEPROM		0x0b
 #define AX_CMD_WRITE_EEPROM		0x0c
@@ -71,6 +72,17 @@
 #define AX_CMD_SW_RESET			0x20
 #define AX_CMD_SW_PHY_STATUS		0x21
 #define AX_CMD_SW_PHY_SELECT		0x22
+#define AX_QCTCTRL			0x2A
+
+#define AX_CHIPCODE_MASK		0x70
+#define AX_AX88772_CHIPCODE		0x00
+#define AX_AX88772A_CHIPCODE		0x10
+#define AX_AX88772B_CHIPCODE		0x20
+#define AX_HOST_EN			0x01
+
+#define AX_PHYSEL_PSEL			0x01
+#define AX_PHYSEL_SSMII			0
+#define AX_PHYSEL_SSEN			0x10
 
 #define AX_PHY_SELECT_MASK		(BIT(3) | BIT(2))
 #define AX_PHY_SELECT_INTERNAL		0
@@ -173,6 +185,10 @@ struct asix_rx_fixup_info {
 };
 
 struct asix_common_private {
+	void (*resume)(struct usbnet *dev);
+	void (*suspend)(struct usbnet *dev);
+	u16 presvd_phy_advertise;
+	u16 presvd_phy_bmcr;
 	struct asix_rx_fixup_info rx_fixup_info;
 };
 
@@ -182,10 +198,10 @@ extern const struct driver_info ax88172a_info;
 #define FLAG_EEPROM_MAC		(1UL << 0)  /* init device MAC from eeprom */
 
 int asix_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,
-		  u16 size, void *data);
+		  u16 size, void *data, int in_pm);
 
 int asix_write_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,
-		   u16 size, void *data);
+		   u16 size, void *data, int in_pm);
 
 void asix_write_cmd_async(struct usbnet *dev, u8 cmd, u16 value,
 			  u16 index, u16 size, void *data);
@@ -197,27 +213,31 @@ int asix_rx_fixup_common(struct usbnet *dev, struct sk_buff *skb);
 struct sk_buff *asix_tx_fixup(struct usbnet *dev, struct sk_buff *skb,
 			      gfp_t flags);
 
-int asix_set_sw_mii(struct usbnet *dev);
-int asix_set_hw_mii(struct usbnet *dev);
+int asix_set_sw_mii(struct usbnet *dev, int in_pm);
+int asix_set_hw_mii(struct usbnet *dev, int in_pm);
 
 int asix_read_phy_addr(struct usbnet *dev, int internal);
 int asix_get_phy_addr(struct usbnet *dev);
 
-int asix_sw_reset(struct usbnet *dev, u8 flags);
+int asix_sw_reset(struct usbnet *dev, u8 flags, int in_pm);
 
-u16 asix_read_rx_ctl(struct usbnet *dev);
-int asix_write_rx_ctl(struct usbnet *dev, u16 mode);
+u16 asix_read_rx_ctl(struct usbnet *dev, int in_pm);
+int asix_write_rx_ctl(struct usbnet *dev, u16 mode, int in_pm);
 
-u16 asix_read_medium_status(struct usbnet *dev);
-int asix_write_medium_mode(struct usbnet *dev, u16 mode);
+u16 asix_read_medium_status(struct usbnet *dev, int in_pm);
+int asix_write_medium_mode(struct usbnet *dev, u16 mode, int in_pm);
 
-int asix_write_gpio(struct usbnet *dev, u16 value, int sleep);
+int asix_write_gpio(struct usbnet *dev, u16 value, int sleep, int in_pm);
 
 void asix_set_multicast(struct net_device *net);
 
 int asix_mdio_read(struct net_device *netdev, int phy_id, int loc);
 void asix_mdio_write(struct net_device *netdev, int phy_id, int loc, int val);
 
+int asix_mdio_read_nopm(struct net_device *netdev, int phy_id, int loc);
+void asix_mdio_write_nopm(struct net_device *netdev, int phy_id, int loc,
+			  int val);
+
 void asix_get_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo);
 int asix_set_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo);
 
diff --git a/drivers/net/usb/asix_common.c b/drivers/net/usb/asix_common.c
index bd9acff..f0ccf76 100644
--- a/drivers/net/usb/asix_common.c
+++ b/drivers/net/usb/asix_common.c
@@ -22,24 +22,49 @@
 #include "asix.h"
 
 int asix_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,
-		  u16 size, void *data)
+		  u16 size, void *data, int in_pm)
 {
 	int ret;
-	ret = usbnet_read_cmd(dev, cmd,
-			       USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
-			       value, index, data, size);
+	int (*fn)(struct usbnet *, u8, u8, u16, u16, void *, u16);
+
+	BUG_ON(!dev);
+
+	if (!in_pm)
+		fn = usbnet_read_cmd;
+	else
+		fn = usbnet_read_cmd_nopm;
+
+	ret = fn(dev, cmd, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+		 value, index, data, size);
+
+	if (unlikely(ret < 0))
+		netdev_warn(dev->net, "Failed to read reg index 0x%04x: %d\n",
+			    index, ret);
 
-	if (ret != size && ret >= 0)
-		return -EINVAL;
 	return ret;
 }
 
 int asix_write_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,
-		   u16 size, void *data)
+		   u16 size, void *data, int in_pm)
 {
-	return usbnet_write_cmd(dev, cmd,
-				USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
-				value, index, data, size);
+	int ret;
+	int (*fn)(struct usbnet *, u8, u8, u16, u16, const void *, u16);
+
+	BUG_ON(!dev);
+
+	if (!in_pm)
+		fn = usbnet_write_cmd;
+	else
+		fn = usbnet_write_cmd_nopm;
+
+	ret = fn(dev, cmd, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+		 value, index, data, size);
+
+	if (unlikely(ret < 0))
+		netdev_warn(dev->net, "Failed to write reg index 0x%04x: %d\n",
+			    index, ret);
+
+	return ret;
 }
 
 void asix_write_cmd_async(struct usbnet *dev, u8 cmd, u16 value, u16 index,
@@ -225,19 +250,20 @@ struct sk_buff *asix_tx_fixup(struct usbnet *dev, struct sk_buff *skb,
 	return skb;
 }
 
-int asix_set_sw_mii(struct usbnet *dev)
+int asix_set_sw_mii(struct usbnet *dev, int in_pm)
 {
 	int ret;
-	ret = asix_write_cmd(dev, AX_CMD_SET_SW_MII, 0x0000, 0, 0, NULL);
+	ret = asix_write_cmd(dev, AX_CMD_SET_SW_MII, 0x0000, 0, 0, NULL, in_pm);
+
 	if (ret < 0)
 		netdev_err(dev->net, "Failed to enable software MII access\n");
 	return ret;
 }
 
-int asix_set_hw_mii(struct usbnet *dev)
+int asix_set_hw_mii(struct usbnet *dev, int in_pm)
 {
 	int ret;
-	ret = asix_write_cmd(dev, AX_CMD_SET_HW_MII, 0x0000, 0, 0, NULL);
+	ret = asix_write_cmd(dev, AX_CMD_SET_HW_MII, 0x0000, 0, 0, NULL, in_pm);
 	if (ret < 0)
 		netdev_err(dev->net, "Failed to enable hardware MII access\n");
 	return ret;
@@ -247,7 +273,7 @@ int asix_read_phy_addr(struct usbnet *dev, int internal)
 {
 	int offset = (internal ? 1 : 0);
 	u8 buf[2];
-	int ret = asix_read_cmd(dev, AX_CMD_READ_PHY_ID, 0, 0, 2, buf);
+	int ret = asix_read_cmd(dev, AX_CMD_READ_PHY_ID, 0, 0, 2, buf, 0);
 
 	netdev_dbg(dev->net, "asix_get_phy_addr()\n");
 
@@ -270,21 +296,21 @@ int asix_get_phy_addr(struct usbnet *dev)
 }
 
 
-int asix_sw_reset(struct usbnet *dev, u8 flags)
+int asix_sw_reset(struct usbnet *dev, u8 flags, int in_pm)
 {
 	int ret;
 
-        ret = asix_write_cmd(dev, AX_CMD_SW_RESET, flags, 0, 0, NULL);
+	ret = asix_write_cmd(dev, AX_CMD_SW_RESET, flags, 0, 0, NULL, in_pm);
 	if (ret < 0)
 		netdev_err(dev->net, "Failed to send software reset: %02x\n", ret);
 
 	return ret;
 }
 
-u16 asix_read_rx_ctl(struct usbnet *dev)
+u16 asix_read_rx_ctl(struct usbnet *dev, int in_pm)
 {
 	__le16 v;
-	int ret = asix_read_cmd(dev, AX_CMD_READ_RX_CTL, 0, 0, 2, &v);
+	int ret = asix_read_cmd(dev, AX_CMD_READ_RX_CTL, 0, 0, 2, &v, in_pm);
 
 	if (ret < 0) {
 		netdev_err(dev->net, "Error reading RX_CTL register: %02x\n", ret);
@@ -295,12 +321,12 @@ out:
 	return ret;
 }
 
-int asix_write_rx_ctl(struct usbnet *dev, u16 mode)
+int asix_write_rx_ctl(struct usbnet *dev, u16 mode, int in_pm)
 {
 	int ret;
 
 	netdev_dbg(dev->net, "asix_write_rx_ctl() - mode = 0x%04x\n", mode);
-	ret = asix_write_cmd(dev, AX_CMD_WRITE_RX_CTL, mode, 0, 0, NULL);
+	ret = asix_write_cmd(dev, AX_CMD_WRITE_RX_CTL, mode, 0, 0, NULL, in_pm);
 	if (ret < 0)
 		netdev_err(dev->net, "Failed to write RX_CTL mode to 0x%04x: %02x\n",
 			   mode, ret);
@@ -308,10 +334,11 @@ int asix_write_rx_ctl(struct usbnet *dev, u16 mode)
 	return ret;
 }
 
-u16 asix_read_medium_status(struct usbnet *dev)
+u16 asix_read_medium_status(struct usbnet *dev, int in_pm)
 {
 	__le16 v;
-	int ret = asix_read_cmd(dev, AX_CMD_READ_MEDIUM_STATUS, 0, 0, 2, &v);
+	int ret = asix_read_cmd(dev, AX_CMD_READ_MEDIUM_STATUS,
+				0, 0, 2, &v, in_pm);
 
 	if (ret < 0) {
 		netdev_err(dev->net, "Error reading Medium Status register: %02x\n",
@@ -323,12 +350,13 @@ u16 asix_read_medium_status(struct usbnet *dev)
 
 }
 
-int asix_write_medium_mode(struct usbnet *dev, u16 mode)
+int asix_write_medium_mode(struct usbnet *dev, u16 mode, int in_pm)
 {
 	int ret;
 
 	netdev_dbg(dev->net, "asix_write_medium_mode() - mode = 0x%04x\n", mode);
-	ret = asix_write_cmd(dev, AX_CMD_WRITE_MEDIUM_MODE, mode, 0, 0, NULL);
+	ret = asix_write_cmd(dev, AX_CMD_WRITE_MEDIUM_MODE,
+			     mode, 0, 0, NULL, in_pm);
 	if (ret < 0)
 		netdev_err(dev->net, "Failed to write Medium Mode mode to 0x%04x: %02x\n",
 			   mode, ret);
@@ -336,12 +364,12 @@ int asix_write_medium_mode(struct usbnet *dev, u16 mode)
 	return ret;
 }
 
-int asix_write_gpio(struct usbnet *dev, u16 value, int sleep)
+int asix_write_gpio(struct usbnet *dev, u16 value, int sleep, int in_pm)
 {
 	int ret;
 
 	netdev_dbg(dev->net, "asix_write_gpio() - value = 0x%04x\n", value);
-	ret = asix_write_cmd(dev, AX_CMD_WRITE_GPIOS, value, 0, 0, NULL);
+	ret = asix_write_cmd(dev, AX_CMD_WRITE_GPIOS, value, 0, 0, NULL, in_pm);
 	if (ret < 0)
 		netdev_err(dev->net, "Failed to write GPIO value 0x%04x: %02x\n",
 			   value, ret);
@@ -398,16 +426,23 @@ int asix_mdio_read(struct net_device *netdev, int phy_id, int loc)
 {
 	struct usbnet *dev = netdev_priv(netdev);
 	__le16 res;
+	u8 smsr;
+	int i = 0;
 
 	mutex_lock(&dev->phy_mutex);
-	asix_set_sw_mii(dev);
+	do {
+		asix_set_sw_mii(dev, 0);
+		usleep_range(1000, 1100);
+		asix_read_cmd(dev, AX_CMD_STATMNGSTS_REG, 0, 0, 1, &smsr, 0);
+	} while (!(smsr & AX_HOST_EN) && (i++ < 30));
+
 	asix_read_cmd(dev, AX_CMD_READ_MII_REG, phy_id,
-				(__u16)loc, 2, &res);
-	asix_set_hw_mii(dev);
+				(__u16)loc, 2, &res, 0);
+	asix_set_hw_mii(dev, 0);
 	mutex_unlock(&dev->phy_mutex);
 
 	netdev_dbg(dev->net, "asix_mdio_read() phy_id=0x%02x, loc=0x%02x, returns=0x%04x\n",
-		   phy_id, loc, le16_to_cpu(res));
+			phy_id, loc, le16_to_cpu(res));
 
 	return le16_to_cpu(res);
 }
@@ -416,13 +451,71 @@ void asix_mdio_write(struct net_device *netdev, int phy_id, int loc, int val)
 {
 	struct usbnet *dev = netdev_priv(netdev);
 	__le16 res = cpu_to_le16(val);
+	u8 smsr;
+	int i = 0;
 
 	netdev_dbg(dev->net, "asix_mdio_write() phy_id=0x%02x, loc=0x%02x, val=0x%04x\n",
-		   phy_id, loc, val);
+			phy_id, loc, val);
+
+	mutex_lock(&dev->phy_mutex);
+	do {
+		asix_set_sw_mii(dev, 0);
+		usleep_range(1000, 1100);
+		asix_read_cmd(dev, AX_CMD_STATMNGSTS_REG, 0, 0, 1, &smsr, 0);
+	} while (!(smsr & AX_HOST_EN) && (i++ < 30));
+
+	asix_write_cmd(dev, AX_CMD_WRITE_MII_REG, phy_id,
+		       (__u16)loc, 2, &res, 0);
+	asix_set_hw_mii(dev, 0);
+	mutex_unlock(&dev->phy_mutex);
+}
+
+int asix_mdio_read_nopm(struct net_device *netdev, int phy_id, int loc)
+{
+	struct usbnet *dev = netdev_priv(netdev);
+	__le16 res;
+	u8 smsr;
+	int i = 0;
+
+	mutex_lock(&dev->phy_mutex);
+	do {
+		asix_set_sw_mii(dev, 1);
+		usleep_range(1000, 1100);
+		asix_read_cmd(dev, AX_CMD_STATMNGSTS_REG, 0, 0, 1, &smsr, 1);
+	} while (!(smsr & AX_HOST_EN) && (i++ < 30));
+
+	asix_read_cmd(dev, AX_CMD_READ_MII_REG, phy_id,
+		      (__u16)loc, 2, &res, 1);
+	asix_set_hw_mii(dev, 1);
+	mutex_unlock(&dev->phy_mutex);
+
+	netdev_dbg(dev->net, "asix_mdio_read_nopm() phy_id=0x%02x, loc=0x%02x, returns=0x%04x\n",
+			phy_id, loc, le16_to_cpu(res));
+
+	return le16_to_cpu(res);
+}
+
+void
+asix_mdio_write_nopm(struct net_device *netdev, int phy_id, int loc, int val)
+{
+	struct usbnet *dev = netdev_priv(netdev);
+	__le16 res = cpu_to_le16(val);
+	u8 smsr;
+	int i = 0;
+
+	netdev_dbg(dev->net, "asix_mdio_write() phy_id=0x%02x, loc=0x%02x, val=0x%04x\n",
+			phy_id, loc, val);
+
 	mutex_lock(&dev->phy_mutex);
-	asix_set_sw_mii(dev);
-	asix_write_cmd(dev, AX_CMD_WRITE_MII_REG, phy_id, (__u16)loc, 2, &res);
-	asix_set_hw_mii(dev);
+	do {
+		asix_set_sw_mii(dev, 1);
+		usleep_range(1000, 1100);
+		asix_read_cmd(dev, AX_CMD_STATMNGSTS_REG, 0, 0, 1, &smsr, 1);
+	} while (!(smsr & AX_HOST_EN) && (i++ < 30));
+
+	asix_write_cmd(dev, AX_CMD_WRITE_MII_REG, phy_id,
+		       (__u16)loc, 2, &res, 1);
+	asix_set_hw_mii(dev, 1);
 	mutex_unlock(&dev->phy_mutex);
 }
 
@@ -431,7 +524,8 @@ void asix_get_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo)
 	struct usbnet *dev = netdev_priv(net);
 	u8 opt;
 
-	if (asix_read_cmd(dev, AX_CMD_READ_MONITOR_MODE, 0, 0, 1, &opt) < 0) {
+	if (asix_read_cmd(dev, AX_CMD_READ_MONITOR_MODE,
+			  0, 0, 1, &opt, 0) < 0) {
 		wolinfo->supported = 0;
 		wolinfo->wolopts = 0;
 		return;
@@ -455,7 +549,7 @@ int asix_set_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo)
 		opt |= AX_MONITOR_MAGIC;
 
 	if (asix_write_cmd(dev, AX_CMD_WRITE_MONITOR_MODE,
-			      opt, 0, 0, NULL) < 0)
+			      opt, 0, 0, NULL, 0) < 0)
 		return -EINVAL;
 
 	return 0;
@@ -490,7 +584,7 @@ int asix_get_eeprom(struct net_device *net, struct ethtool_eeprom *eeprom,
 	/* ax8817x returns 2 bytes from eeprom on read */
 	for (i = first_word; i <= last_word; i++) {
 		if (asix_read_cmd(dev, AX_CMD_READ_EEPROM, i, 0, 2,
-				  &(eeprom_buff[i - first_word])) < 0) {
+				  &eeprom_buff[i - first_word], 0) < 0) {
 			kfree(eeprom_buff);
 			return -EIO;
 		}
@@ -531,7 +625,7 @@ int asix_set_eeprom(struct net_device *net, struct ethtool_eeprom *eeprom,
 	   the EEPROM */
 	if (eeprom->offset & 1) {
 		ret = asix_read_cmd(dev, AX_CMD_READ_EEPROM, first_word, 0, 2,
-				    &(eeprom_buff[0]));
+				    &eeprom_buff[0], 0);
 		if (ret < 0) {
 			netdev_err(net, "Failed to read EEPROM at offset 0x%02x.\n", first_word);
 			goto free;
@@ -540,7 +634,7 @@ int asix_set_eeprom(struct net_device *net, struct ethtool_eeprom *eeprom,
 
 	if ((eeprom->offset + eeprom->len) & 1) {
 		ret = asix_read_cmd(dev, AX_CMD_READ_EEPROM, last_word, 0, 2,
-				    &(eeprom_buff[last_word - first_word]));
+				    &eeprom_buff[last_word - first_word], 0);
 		if (ret < 0) {
 			netdev_err(net, "Failed to read EEPROM at offset 0x%02x.\n", last_word);
 			goto free;
@@ -550,7 +644,7 @@ int asix_set_eeprom(struct net_device *net, struct ethtool_eeprom *eeprom,
 	memcpy((u8 *)eeprom_buff + (eeprom->offset & 1), data, eeprom->len);
 
 	/* write data to EEPROM */
-	ret = asix_write_cmd(dev, AX_CMD_WRITE_ENABLE, 0x0000, 0, 0, NULL);
+	ret = asix_write_cmd(dev, AX_CMD_WRITE_ENABLE, 0x0000, 0, 0, NULL, 0);
 	if (ret < 0) {
 		netdev_err(net, "Failed to enable EEPROM write\n");
 		goto free;
@@ -561,7 +655,7 @@ int asix_set_eeprom(struct net_device *net, struct ethtool_eeprom *eeprom,
 		netdev_dbg(net, "write to EEPROM at offset 0x%02x, data 0x%04x\n",
 			   i, eeprom_buff[i - first_word]);
 		ret = asix_write_cmd(dev, AX_CMD_WRITE_EEPROM, i,
-				     eeprom_buff[i - first_word], 0, NULL);
+				     eeprom_buff[i - first_word], 0, NULL, 0);
 		if (ret < 0) {
 			netdev_err(net, "Failed to write EEPROM at offset 0x%02x.\n",
 				   i);
@@ -570,7 +664,7 @@ int asix_set_eeprom(struct net_device *net, struct ethtool_eeprom *eeprom,
 		msleep(20);
 	}
 
-	ret = asix_write_cmd(dev, AX_CMD_WRITE_DISABLE, 0x0000, 0, 0, NULL);
+	ret = asix_write_cmd(dev, AX_CMD_WRITE_DISABLE, 0x0000, 0, 0, NULL, 0);
 	if (ret < 0) {
 		netdev_err(net, "Failed to disable EEPROM write\n");
 		goto free;
diff --git a/drivers/net/usb/asix_devices.c b/drivers/net/usb/asix_devices.c
index 5cabefc..aaa4290 100644
--- a/drivers/net/usb/asix_devices.c
+++ b/drivers/net/usb/asix_devices.c
@@ -184,7 +184,7 @@ static int ax88172_link_reset(struct usbnet *dev)
 	netdev_dbg(dev->net, "ax88172_link_reset() speed: %u duplex: %d setting mode to 0x%04x\n",
 		   ethtool_cmd_speed(&ecmd), ecmd.duplex, mode);
 
-	asix_write_medium_mode(dev, mode);
+	asix_write_medium_mode(dev, mode, 0);
 
 	return 0;
 }
@@ -213,18 +213,19 @@ static int ax88172_bind(struct usbnet *dev, struct usb_interface *intf)
 	/* Toggle the GPIOs in a manufacturer/model specific way */
 	for (i = 2; i >= 0; i--) {
 		ret = asix_write_cmd(dev, AX_CMD_WRITE_GPIOS,
-				(gpio_bits >> (i * 8)) & 0xff, 0, 0, NULL);
+				(gpio_bits >> (i * 8)) & 0xff, 0, 0, NULL, 0);
 		if (ret < 0)
 			goto out;
 		msleep(5);
 	}
 
-	ret = asix_write_rx_ctl(dev, 0x80);
+	ret = asix_write_rx_ctl(dev, 0x80, 0);
 	if (ret < 0)
 		goto out;
 
 	/* Get the MAC address */
-	ret = asix_read_cmd(dev, AX88172_CMD_READ_NODE_ID, 0, 0, ETH_ALEN, buf);
+	ret = asix_read_cmd(dev, AX88172_CMD_READ_NODE_ID,
+			    0, 0, ETH_ALEN, buf, 0);
 	if (ret < 0) {
 		netdev_dbg(dev->net, "read AX_CMD_READ_NODE_ID failed: %d\n",
 			   ret);
@@ -290,7 +291,7 @@ static int ax88772_link_reset(struct usbnet *dev)
 	netdev_dbg(dev->net, "ax88772_link_reset() speed: %u duplex: %d setting mode to 0x%04x\n",
 		   ethtool_cmd_speed(&ecmd), ecmd.duplex, mode);
 
-	asix_write_medium_mode(dev, mode);
+	asix_write_medium_mode(dev, mode, 0);
 
 	return 0;
 }
@@ -298,78 +299,192 @@ static int ax88772_link_reset(struct usbnet *dev)
 static int ax88772_reset(struct usbnet *dev)
 {
 	struct asix_data *data = (struct asix_data *)&dev->data;
+	int ret;
+
+	/* Rewrite MAC address */
+	ether_addr_copy(data->mac_addr, dev->net->dev_addr);
+	ret = asix_write_cmd(dev, AX_CMD_WRITE_NODE_ID, 0, 0,
+			     ETH_ALEN, data->mac_addr, 0);
+	if (ret < 0)
+		goto out;
+
+	/* Set RX_CTL to default values with 2k buffer, and enable cactus */
+	ret = asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL, 0);
+	if (ret < 0)
+		goto out;
+
+	asix_write_medium_mode(dev, AX88772_MEDIUM_DEFAULT, 0);
+	if (ret < 0)
+		goto out;
+
+	return 0;
+
+out:
+	return ret;
+}
+
+static int ax88772_hw_reset(struct usbnet *dev, int in_pm)
+{
+	struct asix_data *data = (struct asix_data *)&dev->data;
 	int ret, embd_phy;
 	u16 rx_ctl;
 
-	ret = asix_write_gpio(dev,
-			AX_GPIO_RSE | AX_GPIO_GPO_2 | AX_GPIO_GPO2EN, 5);
+	ret = asix_write_gpio(dev, AX_GPIO_RSE | AX_GPIO_GPO_2 |
+			      AX_GPIO_GPO2EN, 5, in_pm);
 	if (ret < 0)
 		goto out;
 
-	embd_phy = ((asix_get_phy_addr(dev) & 0x1f) == 0x10 ? 1 : 0);
+	embd_phy = ((dev->mii.phy_id & 0x1f) == 0x10 ? 1 : 0);
 
-	ret = asix_write_cmd(dev, AX_CMD_SW_PHY_SELECT, embd_phy, 0, 0, NULL);
+	ret = asix_write_cmd(dev, AX_CMD_SW_PHY_SELECT, embd_phy,
+			     0, 0, NULL, in_pm);
 	if (ret < 0) {
 		netdev_dbg(dev->net, "Select PHY #1 failed: %d\n", ret);
 		goto out;
 	}
 
-	ret = asix_sw_reset(dev, AX_SWRESET_IPPD | AX_SWRESET_PRL);
-	if (ret < 0)
-		goto out;
+	if (embd_phy) {
+		ret = asix_sw_reset(dev, AX_SWRESET_IPPD, in_pm);
+		if (ret < 0)
+			goto out;
 
-	msleep(150);
+		usleep_range(10000, 11000);
 
-	ret = asix_sw_reset(dev, AX_SWRESET_CLEAR);
-	if (ret < 0)
-		goto out;
+		ret = asix_sw_reset(dev, AX_SWRESET_CLEAR, in_pm);
+		if (ret < 0)
+			goto out;
 
-	msleep(150);
+		msleep(60);
 
-	if (embd_phy) {
-		ret = asix_sw_reset(dev, AX_SWRESET_IPRL);
+		ret = asix_sw_reset(dev, AX_SWRESET_IPRL | AX_SWRESET_PRL,
+				    in_pm);
 		if (ret < 0)
 			goto out;
 	} else {
-		ret = asix_sw_reset(dev, AX_SWRESET_PRTE);
+		ret = asix_sw_reset(dev, AX_SWRESET_IPPD | AX_SWRESET_PRL,
+				    in_pm);
 		if (ret < 0)
 			goto out;
 	}
 
 	msleep(150);
-	rx_ctl = asix_read_rx_ctl(dev);
-	netdev_dbg(dev->net, "RX_CTL is 0x%04x after software reset\n", rx_ctl);
-	ret = asix_write_rx_ctl(dev, 0x0000);
+
+	if (in_pm && (!asix_mdio_read_nopm(dev->net, dev->mii.phy_id,
+					   MII_PHYSID1))){
+		ret = -EIO;
+		goto out;
+	}
+
+	ret = asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL, in_pm);
+	if (ret < 0)
+		goto out;
+
+	ret = asix_write_medium_mode(dev, AX88772_MEDIUM_DEFAULT, in_pm);
 	if (ret < 0)
 		goto out;
 
-	rx_ctl = asix_read_rx_ctl(dev);
-	netdev_dbg(dev->net, "RX_CTL is 0x%04x setting to 0x0000\n", rx_ctl);
+	ret = asix_write_cmd(dev, AX_CMD_WRITE_IPG0,
+			     AX88772_IPG0_DEFAULT | AX88772_IPG1_DEFAULT,
+			     AX88772_IPG2_DEFAULT, 0, NULL, in_pm);
+	if (ret < 0) {
+		netdev_dbg(dev->net, "Write IPG,IPG1,IPG2 failed: %d\n", ret);
+		goto out;
+	}
 
-	ret = asix_sw_reset(dev, AX_SWRESET_PRL);
+	/* Rewrite MAC address */
+	ether_addr_copy(data->mac_addr, dev->net->dev_addr);
+	ret = asix_write_cmd(dev, AX_CMD_WRITE_NODE_ID, 0, 0,
+			     ETH_ALEN, data->mac_addr, in_pm);
 	if (ret < 0)
 		goto out;
 
-	msleep(150);
+	/* Set RX_CTL to default values with 2k buffer, and enable cactus */
+	ret = asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL, in_pm);
+	if (ret < 0)
+		goto out;
+
+	rx_ctl = asix_read_rx_ctl(dev, in_pm);
+	netdev_dbg(dev->net, "RX_CTL is 0x%04x after all initializations\n",
+		   rx_ctl);
+
+	rx_ctl = asix_read_medium_status(dev, in_pm);
+	netdev_dbg(dev->net,
+		   "Medium Status is 0x%04x after all initializations\n",
+		   rx_ctl);
+
+	return 0;
+
+out:
+	return ret;
+}
+
+static int ax88772a_hw_reset(struct usbnet *dev, int in_pm)
+{
+	struct asix_data *data = (struct asix_data *)&dev->data;
+	int ret, embd_phy;
+	u16 rx_ctl;
+	u8 chipcode = 0;
 
-	ret = asix_sw_reset(dev, AX_SWRESET_IPRL | AX_SWRESET_PRL);
+	ret = asix_write_gpio(dev, AX_GPIO_RSE, 5, in_pm);
 	if (ret < 0)
 		goto out;
 
-	msleep(150);
+	embd_phy = ((dev->mii.phy_id & 0x1f) == 0x10 ? 1 : 0);
 
-	asix_mdio_write(dev->net, dev->mii.phy_id, MII_BMCR, BMCR_RESET);
-	asix_mdio_write(dev->net, dev->mii.phy_id, MII_ADVERTISE,
-			ADVERTISE_ALL | ADVERTISE_CSMA);
-	mii_nway_restart(&dev->mii);
+	ret = asix_write_cmd(dev, AX_CMD_SW_PHY_SELECT, embd_phy |
+			     AX_PHYSEL_SSEN, 0, 0, NULL, in_pm);
+	if (ret < 0) {
+		netdev_dbg(dev->net, "Select PHY #1 failed: %d\n", ret);
+		goto out;
+	}
+	usleep_range(10000, 11000);
 
-	ret = asix_write_medium_mode(dev, AX88772_MEDIUM_DEFAULT);
+	ret = asix_sw_reset(dev, AX_SWRESET_IPPD | AX_SWRESET_IPRL, in_pm);
 	if (ret < 0)
 		goto out;
 
+	usleep_range(10000, 11000);
+
+	ret = asix_sw_reset(dev, AX_SWRESET_IPRL, in_pm);
+	if (ret < 0)
+		goto out;
+
+	msleep(160);
+
+	ret = asix_sw_reset(dev, AX_SWRESET_CLEAR, in_pm);
+	if (ret < 0)
+		goto out;
+
+	ret = asix_sw_reset(dev, AX_SWRESET_IPRL, in_pm);
+	if (ret < 0)
+		goto out;
+
+	msleep(200);
+
+	if (in_pm && (!asix_mdio_read_nopm(dev->net, dev->mii.phy_id,
+					   MII_PHYSID1))) {
+		ret = -1;
+		goto out;
+	}
+
+	ret = asix_read_cmd(dev, AX_CMD_STATMNGSTS_REG, 0,
+			    0, 1, &chipcode, in_pm);
+	if (ret < 0)
+		goto out;
+
+	if ((chipcode & AX_CHIPCODE_MASK) == AX_AX88772B_CHIPCODE) {
+		ret = asix_write_cmd(dev, AX_QCTCTRL, 0x8000, 0x8001,
+				     0, NULL, in_pm);
+		if (ret < 0) {
+			netdev_dbg(dev->net, "Write BQ setting failed: %d\n",
+				   ret);
+			goto out;
+		}
+	}
+
 	ret = asix_write_cmd(dev, AX_CMD_WRITE_IPG0,
 				AX88772_IPG0_DEFAULT | AX88772_IPG1_DEFAULT,
-				AX88772_IPG2_DEFAULT, 0, NULL);
+				AX88772_IPG2_DEFAULT, 0, NULL, in_pm);
 	if (ret < 0) {
 		netdev_dbg(dev->net, "Write IPG,IPG1,IPG2 failed: %d\n", ret);
 		goto out;
@@ -378,20 +493,29 @@ static int ax88772_reset(struct usbnet *dev)
 	/* Rewrite MAC address */
 	memcpy(data->mac_addr, dev->net->dev_addr, ETH_ALEN);
 	ret = asix_write_cmd(dev, AX_CMD_WRITE_NODE_ID, 0, 0, ETH_ALEN,
-							data->mac_addr);
+							data->mac_addr, in_pm);
+	if (ret < 0)
+		goto out;
+
+	/* Set RX_CTL to default values with 2k buffer, and enable cactus */
+	ret = asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL, in_pm);
 	if (ret < 0)
 		goto out;
 
+	ret = asix_write_medium_mode(dev, AX88772_MEDIUM_DEFAULT, in_pm);
+	if (ret < 0)
+		return ret;
+
 	/* Set RX_CTL to default values with 2k buffer, and enable cactus */
-	ret = asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL);
+	ret = asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL, in_pm);
 	if (ret < 0)
 		goto out;
 
-	rx_ctl = asix_read_rx_ctl(dev);
+	rx_ctl = asix_read_rx_ctl(dev, in_pm);
 	netdev_dbg(dev->net, "RX_CTL is 0x%04x after all initializations\n",
 		   rx_ctl);
 
-	rx_ctl = asix_read_medium_status(dev);
+	rx_ctl = asix_read_medium_status(dev, in_pm);
 	netdev_dbg(dev->net,
 		   "Medium Status is 0x%04x after all initializations\n",
 		   rx_ctl);
@@ -400,7 +524,6 @@ static int ax88772_reset(struct usbnet *dev)
 
 out:
 	return ret;
-
 }
 
 static const struct net_device_ops ax88772_netdev_ops = {
@@ -415,11 +538,87 @@ static const struct net_device_ops ax88772_netdev_ops = {
 	.ndo_set_rx_mode        = asix_set_multicast,
 };
 
+static void ax88772_suspend(struct usbnet *dev)
+{
+	struct asix_common_private *priv = dev->driver_priv;
+
+	/* Preserve BMCR for restoring */
+	priv->presvd_phy_bmcr =
+		asix_mdio_read_nopm(dev->net, dev->mii.phy_id, MII_BMCR);
+
+	/* Preserve ANAR for restoring */
+	priv->presvd_phy_advertise =
+		asix_mdio_read_nopm(dev->net, dev->mii.phy_id, MII_ADVERTISE);
+}
+
+static int asix_suspend(struct usb_interface *intf, pm_message_t message)
+{
+	struct usbnet *dev = usb_get_intfdata(intf);
+	struct asix_common_private *priv = dev->driver_priv;
+
+	if (priv->suspend)
+		priv->suspend(dev);
+
+	return usbnet_suspend(intf, message);
+}
+
+static void ax88772_restore_phy(struct usbnet *dev)
+{
+	struct asix_common_private *priv = dev->driver_priv;
+
+	if (priv->presvd_phy_advertise) {
+		/* Restore Advertisement control reg */
+		asix_mdio_write_nopm(dev->net, dev->mii.phy_id, MII_ADVERTISE,
+				     priv->presvd_phy_advertise);
+
+		/* Restore BMCR */
+		asix_mdio_write_nopm(dev->net, dev->mii.phy_id, MII_BMCR,
+				     priv->presvd_phy_bmcr);
+
+		priv->presvd_phy_advertise = 0;
+		priv->presvd_phy_bmcr = 0;
+	}
+}
+
+static void ax88772_resume(struct usbnet *dev)
+{
+	int i;
+
+	for (i = 0; i < 3; i++)
+		if (!ax88772_hw_reset(dev, 1))
+			break;
+	ax88772_restore_phy(dev);
+}
+
+static void ax88772a_resume(struct usbnet *dev)
+{
+	int i;
+
+	for (i = 0; i < 3; i++) {
+		if (!ax88772a_hw_reset(dev, 1))
+			break;
+	}
+
+	ax88772_restore_phy(dev);
+}
+
+static int asix_resume(struct usb_interface *intf)
+{
+	struct usbnet *dev = usb_get_intfdata(intf);
+	struct asix_common_private *priv = dev->driver_priv;
+
+	if (priv->resume)
+		priv->resume(dev);
+
+	return usbnet_resume(intf);
+}
+
 static int ax88772_bind(struct usbnet *dev, struct usb_interface *intf)
 {
-	int ret, embd_phy, i;
-	u8 buf[ETH_ALEN];
+	int ret, i;
+	u8 buf[ETH_ALEN], chipcode = 0;
 	u32 phyid;
+	struct asix_common_private *priv;
 
 	usbnet_get_endpoints(dev,intf);
 
@@ -427,13 +626,13 @@ static int ax88772_bind(struct usbnet *dev, struct usb_interface *intf)
 	if (dev->driver_info->data & FLAG_EEPROM_MAC) {
 		for (i = 0; i < (ETH_ALEN >> 1); i++) {
 			ret = asix_read_cmd(dev, AX_CMD_READ_EEPROM, 0x04 + i,
-					0, 2, buf + i * 2);
+					    0, 2, buf + i * 2, 0);
 			if (ret < 0)
 				break;
 		}
 	} else {
 		ret = asix_read_cmd(dev, AX_CMD_READ_NODE_ID,
-				0, 0, ETH_ALEN, buf);
+				0, 0, ETH_ALEN, buf, 0);
 	}
 
 	if (ret < 0) {
@@ -456,16 +655,11 @@ static int ax88772_bind(struct usbnet *dev, struct usb_interface *intf)
 	dev->net->needed_headroom = 4; /* cf asix_tx_fixup() */
 	dev->net->needed_tailroom = 4; /* cf asix_tx_fixup() */
 
-	embd_phy = ((dev->mii.phy_id & 0x1f) == 0x10 ? 1 : 0);
+	asix_read_cmd(dev, AX_CMD_STATMNGSTS_REG, 0, 0, 1, &chipcode, 0);
+	chipcode &= AX_CHIPCODE_MASK;
 
-	/* Reset the PHY to normal operation mode */
-	ret = asix_write_cmd(dev, AX_CMD_SW_PHY_SELECT, embd_phy, 0, 0, NULL);
-	if (ret < 0) {
-		netdev_dbg(dev->net, "Select PHY #1 failed: %d\n", ret);
-		return ret;
-	}
-
-	ax88772_reset(dev);
+	(chipcode == AX_AX88772_CHIPCODE) ? ax88772_hw_reset(dev, 0) :
+					    ax88772a_hw_reset(dev, 0);
 
 	/* Read PHYID register *AFTER* the PHY was reset properly */
 	phyid = asix_get_phyid(dev);
@@ -482,6 +676,18 @@ static int ax88772_bind(struct usbnet *dev, struct usb_interface *intf)
 	if (!dev->driver_priv)
 		return -ENOMEM;
 
+	priv = dev->driver_priv;
+
+	priv->presvd_phy_bmcr = 0;
+	priv->presvd_phy_advertise = 0;
+	if (chipcode == AX_AX88772_CHIPCODE) {
+		priv->resume = ax88772_resume;
+		priv->suspend = ax88772_suspend;
+	} else {
+		priv->resume = ax88772a_resume;
+		priv->suspend = ax88772_suspend;
+	}
+
 	return 0;
 }
 
@@ -593,12 +799,12 @@ static int ax88178_reset(struct usbnet *dev)
 	int gpio0 = 0;
 	u32 phyid;
 
-	asix_read_cmd(dev, AX_CMD_READ_GPIOS, 0, 0, 1, &status);
+	asix_read_cmd(dev, AX_CMD_READ_GPIOS, 0, 0, 1, &status, 0);
 	netdev_dbg(dev->net, "GPIO Status: 0x%04x\n", status);
 
-	asix_write_cmd(dev, AX_CMD_WRITE_ENABLE, 0, 0, 0, NULL);
-	asix_read_cmd(dev, AX_CMD_READ_EEPROM, 0x0017, 0, 2, &eeprom);
-	asix_write_cmd(dev, AX_CMD_WRITE_DISABLE, 0, 0, 0, NULL);
+	asix_write_cmd(dev, AX_CMD_WRITE_ENABLE, 0, 0, 0, NULL, 0);
+	asix_read_cmd(dev, AX_CMD_READ_EEPROM, 0x0017, 0, 2, &eeprom, 0);
+	asix_write_cmd(dev, AX_CMD_WRITE_DISABLE, 0, 0, 0, NULL, 0);
 
 	netdev_dbg(dev->net, "EEPROM index 0x17 is 0x%04x\n", eeprom);
 
@@ -614,15 +820,16 @@ static int ax88178_reset(struct usbnet *dev)
 	netdev_dbg(dev->net, "GPIO0: %d, PhyMode: %d\n", gpio0, data->phymode);
 
 	/* Power up external GigaPHY through AX88178 GPIO pin */
-	asix_write_gpio(dev, AX_GPIO_RSE | AX_GPIO_GPO_1 | AX_GPIO_GPO1EN, 40);
+	asix_write_gpio(dev, AX_GPIO_RSE | AX_GPIO_GPO_1 |
+			AX_GPIO_GPO1EN, 40, 0);
 	if ((le16_to_cpu(eeprom) >> 8) != 1) {
-		asix_write_gpio(dev, 0x003c, 30);
-		asix_write_gpio(dev, 0x001c, 300);
-		asix_write_gpio(dev, 0x003c, 30);
+		asix_write_gpio(dev, 0x003c, 30, 0);
+		asix_write_gpio(dev, 0x001c, 300, 0);
+		asix_write_gpio(dev, 0x003c, 30, 0);
 	} else {
 		netdev_dbg(dev->net, "gpio phymode == 1 path\n");
-		asix_write_gpio(dev, AX_GPIO_GPO1EN, 30);
-		asix_write_gpio(dev, AX_GPIO_GPO1EN | AX_GPIO_GPO_1, 30);
+		asix_write_gpio(dev, AX_GPIO_GPO1EN, 30, 0);
+		asix_write_gpio(dev, AX_GPIO_GPO1EN | AX_GPIO_GPO_1, 30, 0);
 	}
 
 	/* Read PHYID register *AFTER* powering up PHY */
@@ -630,15 +837,15 @@ static int ax88178_reset(struct usbnet *dev)
 	netdev_dbg(dev->net, "PHYID=0x%08x\n", phyid);
 
 	/* Set AX88178 to enable MII/GMII/RGMII interface for external PHY */
-	asix_write_cmd(dev, AX_CMD_SW_PHY_SELECT, 0, 0, 0, NULL);
+	asix_write_cmd(dev, AX_CMD_SW_PHY_SELECT, 0, 0, 0, NULL, 0);
 
-	asix_sw_reset(dev, 0);
+	asix_sw_reset(dev, 0, 0);
 	msleep(150);
 
-	asix_sw_reset(dev, AX_SWRESET_PRL | AX_SWRESET_IPPD);
+	asix_sw_reset(dev, AX_SWRESET_PRL | AX_SWRESET_IPPD, 0);
 	msleep(150);
 
-	asix_write_rx_ctl(dev, 0);
+	asix_write_rx_ctl(dev, 0, 0);
 
 	if (data->phymode == PHY_MODE_MARVELL) {
 		marvell_phy_init(dev);
@@ -655,18 +862,18 @@ static int ax88178_reset(struct usbnet *dev)
 
 	mii_nway_restart(&dev->mii);
 
-	ret = asix_write_medium_mode(dev, AX88178_MEDIUM_DEFAULT);
+	ret = asix_write_medium_mode(dev, AX88178_MEDIUM_DEFAULT, 0);
 	if (ret < 0)
 		return ret;
 
 	/* Rewrite MAC address */
 	memcpy(data->mac_addr, dev->net->dev_addr, ETH_ALEN);
 	ret = asix_write_cmd(dev, AX_CMD_WRITE_NODE_ID, 0, 0, ETH_ALEN,
-							data->mac_addr);
+							data->mac_addr, 0);
 	if (ret < 0)
 		return ret;
 
-	ret = asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL);
+	ret = asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL, 0);
 	if (ret < 0)
 		return ret;
 
@@ -704,7 +911,7 @@ static int ax88178_link_reset(struct usbnet *dev)
 	netdev_dbg(dev->net, "ax88178_link_reset() speed: %u duplex: %d setting mode to 0x%04x\n",
 		   speed, ecmd.duplex, mode);
 
-	asix_write_medium_mode(dev, mode);
+	asix_write_medium_mode(dev, mode, 0);
 
 	if (data->phymode == PHY_MODE_MARVELL && data->ledmode)
 		marvell_led_status(dev, speed);
@@ -733,15 +940,15 @@ static void ax88178_set_mfb(struct usbnet *dev)
 		mfb = AX_RX_CTL_MFB_16384;
 	}
 
-	rxctl = asix_read_rx_ctl(dev);
-	asix_write_rx_ctl(dev, (rxctl & ~AX_RX_CTL_MFB_16384) | mfb);
+	rxctl = asix_read_rx_ctl(dev, 0);
+	asix_write_rx_ctl(dev, (rxctl & ~AX_RX_CTL_MFB_16384) | mfb, 0);
 
-	medium = asix_read_medium_status(dev);
+	medium = asix_read_medium_status(dev, 0);
 	if (dev->net->mtu > 1500)
 		medium |= AX_MEDIUM_JFE;
 	else
 		medium &= ~AX_MEDIUM_JFE;
-	asix_write_medium_mode(dev, medium);
+	asix_write_medium_mode(dev, medium, 0);
 
 	if (dev->rx_urb_size > old_rx_urb_size)
 		usbnet_unlink_rx_urbs(dev);
@@ -790,7 +997,7 @@ static int ax88178_bind(struct usbnet *dev, struct usb_interface *intf)
 	usbnet_get_endpoints(dev,intf);
 
 	/* Get the MAC address */
-	ret = asix_read_cmd(dev, AX_CMD_READ_NODE_ID, 0, 0, ETH_ALEN, buf);
+	ret = asix_read_cmd(dev, AX_CMD_READ_NODE_ID, 0, 0, ETH_ALEN, buf, 0);
 	if (ret < 0) {
 		netdev_dbg(dev->net, "Failed to read MAC address: %d\n", ret);
 		return ret;
@@ -811,10 +1018,10 @@ static int ax88178_bind(struct usbnet *dev, struct usb_interface *intf)
 	dev->net->ethtool_ops = &ax88178_ethtool_ops;
 
 	/* Blink LEDS so users know driver saw dongle */
-	asix_sw_reset(dev, 0);
+	asix_sw_reset(dev, 0, 0);
 	msleep(150);
 
-	asix_sw_reset(dev, AX_SWRESET_PRL | AX_SWRESET_IPPD);
+	asix_sw_reset(dev, AX_SWRESET_PRL | AX_SWRESET_IPPD, 0);
 	msleep(150);
 
 	/* Asix framing packs multiple eth frames into a 2K usb bulk transfer */
@@ -877,7 +1084,7 @@ static const struct driver_info ax88772_info = {
 	.unbind = ax88772_unbind,
 	.status = asix_status,
 	.link_reset = ax88772_link_reset,
-	.reset = ax88772_link_reset,
+	.reset = ax88772_reset,
 	.flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_LINK_INTR | FLAG_MULTI_PACKET,
 	.rx_fixup = asix_rx_fixup_common,
 	.tx_fixup = asix_tx_fixup,
@@ -1005,7 +1212,7 @@ static const struct usb_device_id	products [] = {
 }, {
 	// Lenovo U2L100P 10/100
 	USB_DEVICE (0x17ef, 0x7203),
-	.driver_info = (unsigned long) &ax88772_info,
+	.driver_info = (unsigned long)&ax88772b_info,
 }, {
 	// ASIX AX88772B 10/100
 	USB_DEVICE (0x0b95, 0x772b),
@@ -1073,7 +1280,7 @@ static const struct usb_device_id	products [] = {
 }, {
 	// Asus USB Ethernet Adapter
 	USB_DEVICE (0x0b95, 0x7e2b),
-	.driver_info = (unsigned long) &ax88772_info,
+	.driver_info = (unsigned long)&ax88772b_info,
 }, {
 	/* ASIX 88172a demo board */
 	USB_DEVICE(0x0b95, 0x172a),
@@ -1095,8 +1302,8 @@ static struct usb_driver asix_driver = {
 	.name =		DRIVER_NAME,
 	.id_table =	products,
 	.probe =	usbnet_probe,
-	.suspend =	usbnet_suspend,
-	.resume =	usbnet_resume,
+	.suspend =	asix_suspend,
+	.resume =	asix_resume,
 	.disconnect =	usbnet_disconnect,
 	.supports_autosuspend = 1,
 	.disable_hub_initiated_lpm = 1,
diff --git a/drivers/net/usb/ax88172a.c b/drivers/net/usb/ax88172a.c
index 5f18fcb..c2394f1 100644
--- a/drivers/net/usb/ax88172a.c
+++ b/drivers/net/usb/ax88172a.c
@@ -81,7 +81,7 @@ static void ax88172a_adjust_link(struct net_device *netdev)
 	}
 
 	if (mode != priv->oldmode) {
-		asix_write_medium_mode(dev, mode);
+		asix_write_medium_mode(dev, mode, 0);
 		priv->oldmode = mode;
 		netdev_dbg(netdev, "speed %u duplex %d, setting mode to 0x%04x\n",
 			   phydev->speed, phydev->duplex, mode);
@@ -205,18 +205,19 @@ static int ax88172a_reset_phy(struct usbnet *dev, int embd_phy)
 {
 	int ret;
 
-	ret = asix_sw_reset(dev, AX_SWRESET_IPPD);
+	ret = asix_sw_reset(dev, AX_SWRESET_IPPD, 0);
 	if (ret < 0)
 		goto err;
 
 	msleep(150);
-	ret = asix_sw_reset(dev, AX_SWRESET_CLEAR);
+	ret = asix_sw_reset(dev, AX_SWRESET_CLEAR, 0);
 	if (ret < 0)
 		goto err;
 
 	msleep(150);
 
-	ret = asix_sw_reset(dev, embd_phy ? AX_SWRESET_IPRL : AX_SWRESET_IPPD);
+	ret = asix_sw_reset(dev, embd_phy ? AX_SWRESET_IPRL : AX_SWRESET_IPPD,
+			    0);
 	if (ret < 0)
 		goto err;
 
@@ -242,7 +243,7 @@ static int ax88172a_bind(struct usbnet *dev, struct usb_interface *intf)
 	dev->driver_priv = priv;
 
 	/* Get the MAC address */
-	ret = asix_read_cmd(dev, AX_CMD_READ_NODE_ID, 0, 0, ETH_ALEN, buf);
+	ret = asix_read_cmd(dev, AX_CMD_READ_NODE_ID, 0, 0, ETH_ALEN, buf, 0);
 	if (ret < 0) {
 		netdev_err(dev->net, "Failed to read MAC address: %d\n", ret);
 		goto free;
@@ -253,7 +254,7 @@ static int ax88172a_bind(struct usbnet *dev, struct usb_interface *intf)
 	dev->net->ethtool_ops = &ax88172a_ethtool_ops;
 
 	/* are we using the internal or the external phy? */
-	ret = asix_read_cmd(dev, AX_CMD_SW_PHY_STATUS, 0, 0, 1, buf);
+	ret = asix_read_cmd(dev, AX_CMD_SW_PHY_STATUS, 0, 0, 1, buf, 0);
 	if (ret < 0) {
 		netdev_err(dev->net, "Failed to read software interface selection register: %d\n",
 			   ret);
@@ -332,20 +333,20 @@ static int ax88172a_reset(struct usbnet *dev)
 	ax88172a_reset_phy(dev, priv->use_embdphy);
 
 	msleep(150);
-	rx_ctl = asix_read_rx_ctl(dev);
+	rx_ctl = asix_read_rx_ctl(dev, 0);
 	netdev_dbg(dev->net, "RX_CTL is 0x%04x after software reset\n", rx_ctl);
-	ret = asix_write_rx_ctl(dev, 0x0000);
+	ret = asix_write_rx_ctl(dev, 0x0000, 0);
 	if (ret < 0)
 		goto out;
 
-	rx_ctl = asix_read_rx_ctl(dev);
+	rx_ctl = asix_read_rx_ctl(dev, 0);
 	netdev_dbg(dev->net, "RX_CTL is 0x%04x setting to 0x0000\n", rx_ctl);
 
 	msleep(150);
 
 	ret = asix_write_cmd(dev, AX_CMD_WRITE_IPG0,
 			     AX88772_IPG0_DEFAULT | AX88772_IPG1_DEFAULT,
-			     AX88772_IPG2_DEFAULT, 0, NULL);
+			     AX88772_IPG2_DEFAULT, 0, NULL, 0);
 	if (ret < 0) {
 		netdev_err(dev->net, "Write IPG,IPG1,IPG2 failed: %d\n", ret);
 		goto out;
@@ -354,20 +355,20 @@ static int ax88172a_reset(struct usbnet *dev)
 	/* Rewrite MAC address */
 	memcpy(data->mac_addr, dev->net->dev_addr, ETH_ALEN);
 	ret = asix_write_cmd(dev, AX_CMD_WRITE_NODE_ID, 0, 0, ETH_ALEN,
-			     data->mac_addr);
+			     data->mac_addr, 0);
 	if (ret < 0)
 		goto out;
 
 	/* Set RX_CTL to default values with 2k buffer, and enable cactus */
-	ret = asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL);
+	ret = asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL, 0);
 	if (ret < 0)
 		goto out;
 
-	rx_ctl = asix_read_rx_ctl(dev);
+	rx_ctl = asix_read_rx_ctl(dev, 0);
 	netdev_dbg(dev->net, "RX_CTL is 0x%04x after all initializations\n",
 		   rx_ctl);
 
-	rx_ctl = asix_read_medium_status(dev);
+	rx_ctl = asix_read_medium_status(dev, 0);
 	netdev_dbg(dev->net, "Medium Status is 0x%04x after all initializations\n",
 		   rx_ctl);
 
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH 2/3] net: asix: Avoid looping when the device is disconnected
  2016-07-25 17:40 [PATCH 1/3] net: asix: Add in_pm parameter robert.foss
@ 2016-07-25 17:40 ` robert.foss
  2016-07-25 21:37   ` Grant Grundler
  2016-07-25 17:40 ` [PATCH 3/3] net: asix: Fix AX88772x resume failures robert.foss
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 9+ messages in thread
From: robert.foss @ 2016-07-25 17:40 UTC (permalink / raw)
  To: davem, grundler, Mark_Craske, Dean_Jenkins, vpalatin, ivecera,
	linux, wk.tsai, changchias, linux-usb, netdev, linux-kernel
  Cc: enric.balletbo, tomeu.vizoso

From: Vincent Palatin <vpalatin@chromium.org>

Check the answers from the USB stack and avoid re-sending multiple times
the request if the device has disappeared.

Signed-off-by: Vincent Palatin <vpalatin@chromium.org>
---
 drivers/net/usb/asix_common.c  | 56 +++++++++++++++++++++++++++++++++---------
 drivers/net/usb/asix_devices.c |  2 ++
 2 files changed, 46 insertions(+), 12 deletions(-)

diff --git a/drivers/net/usb/asix_common.c b/drivers/net/usb/asix_common.c
index f0ccf76..0339560 100644
--- a/drivers/net/usb/asix_common.c
+++ b/drivers/net/usb/asix_common.c
@@ -428,13 +428,21 @@ int asix_mdio_read(struct net_device *netdev, int phy_id, int loc)
 	__le16 res;
 	u8 smsr;
 	int i = 0;
+	int ret;
 
 	mutex_lock(&dev->phy_mutex);
 	do {
-		asix_set_sw_mii(dev, 0);
+		ret = asix_set_sw_mii(dev, 0);
+		if (ret == -ENODEV)
+			break;
 		usleep_range(1000, 1100);
-		asix_read_cmd(dev, AX_CMD_STATMNGSTS_REG, 0, 0, 1, &smsr, 0);
-	} while (!(smsr & AX_HOST_EN) && (i++ < 30));
+		ret = asix_read_cmd(dev, AX_CMD_STATMNGSTS_REG,
+				    0, 0, 1, &smsr, 0);
+	} while (!(smsr & AX_HOST_EN) && (i++ < 30) && (ret != -ENODEV));
+	if (ret == -ENODEV) {
+		mutex_unlock(&dev->phy_mutex);
+		return ret;
+	}
 
 	asix_read_cmd(dev, AX_CMD_READ_MII_REG, phy_id,
 				(__u16)loc, 2, &res, 0);
@@ -453,16 +461,24 @@ void asix_mdio_write(struct net_device *netdev, int phy_id, int loc, int val)
 	__le16 res = cpu_to_le16(val);
 	u8 smsr;
 	int i = 0;
+	int ret;
 
 	netdev_dbg(dev->net, "asix_mdio_write() phy_id=0x%02x, loc=0x%02x, val=0x%04x\n",
 			phy_id, loc, val);
 
 	mutex_lock(&dev->phy_mutex);
 	do {
-		asix_set_sw_mii(dev, 0);
+		ret = asix_set_sw_mii(dev, 0);
+		if (ret == -ENODEV)
+			break;
 		usleep_range(1000, 1100);
-		asix_read_cmd(dev, AX_CMD_STATMNGSTS_REG, 0, 0, 1, &smsr, 0);
-	} while (!(smsr & AX_HOST_EN) && (i++ < 30));
+		ret = asix_read_cmd(dev, AX_CMD_STATMNGSTS_REG,
+				    0, 0, 1, &smsr, 0);
+	} while (!(smsr & AX_HOST_EN) && (i++ < 30) && (ret != -ENODEV));
+	if (ret == -ENODEV) {
+		mutex_unlock(&dev->phy_mutex);
+		return;
+	}
 
 	asix_write_cmd(dev, AX_CMD_WRITE_MII_REG, phy_id,
 		       (__u16)loc, 2, &res, 0);
@@ -476,13 +492,21 @@ int asix_mdio_read_nopm(struct net_device *netdev, int phy_id, int loc)
 	__le16 res;
 	u8 smsr;
 	int i = 0;
+	int ret;
 
 	mutex_lock(&dev->phy_mutex);
 	do {
-		asix_set_sw_mii(dev, 1);
+		ret = asix_set_sw_mii(dev, 1);
+		if (ret == -ENODEV)
+			break;
 		usleep_range(1000, 1100);
-		asix_read_cmd(dev, AX_CMD_STATMNGSTS_REG, 0, 0, 1, &smsr, 1);
-	} while (!(smsr & AX_HOST_EN) && (i++ < 30));
+		ret = asix_read_cmd(dev, AX_CMD_STATMNGSTS_REG,
+				    0, 0, 1, &smsr, 1);
+	} while (!(smsr & AX_HOST_EN) && (i++ < 30) && (ret != -ENODEV));
+	if (ret == -ENODEV) {
+		mutex_unlock(&dev->phy_mutex);
+		return ret;
+	}
 
 	asix_read_cmd(dev, AX_CMD_READ_MII_REG, phy_id,
 		      (__u16)loc, 2, &res, 1);
@@ -502,16 +526,24 @@ asix_mdio_write_nopm(struct net_device *netdev, int phy_id, int loc, int val)
 	__le16 res = cpu_to_le16(val);
 	u8 smsr;
 	int i = 0;
+	int ret;
 
 	netdev_dbg(dev->net, "asix_mdio_write() phy_id=0x%02x, loc=0x%02x, val=0x%04x\n",
 			phy_id, loc, val);
 
 	mutex_lock(&dev->phy_mutex);
 	do {
-		asix_set_sw_mii(dev, 1);
+		ret = asix_set_sw_mii(dev, 1);
+		if (ret == -ENODEV)
+			break;
 		usleep_range(1000, 1100);
-		asix_read_cmd(dev, AX_CMD_STATMNGSTS_REG, 0, 0, 1, &smsr, 1);
-	} while (!(smsr & AX_HOST_EN) && (i++ < 30));
+		ret = asix_read_cmd(dev, AX_CMD_STATMNGSTS_REG,
+				    0, 0, 1, &smsr, 1);
+	} while (!(smsr & AX_HOST_EN) && (i++ < 30) && (ret != -ENODEV));
+	if (ret == -ENODEV) {
+		mutex_unlock(&dev->phy_mutex);
+		return;
+	}
 
 	asix_write_cmd(dev, AX_CMD_WRITE_MII_REG, phy_id,
 		       (__u16)loc, 2, &res, 1);
diff --git a/drivers/net/usb/asix_devices.c b/drivers/net/usb/asix_devices.c
index aaa4290..ebeb730 100644
--- a/drivers/net/usb/asix_devices.c
+++ b/drivers/net/usb/asix_devices.c
@@ -79,6 +79,8 @@ static u32 asix_get_phyid(struct usbnet *dev)
 	/* Poll for the rare case the FW or phy isn't ready yet.  */
 	for (i = 0; i < 100; i++) {
 		phy_reg = asix_mdio_read(dev->net, dev->mii.phy_id, MII_PHYSID1);
+		if (phy_reg < 0)
+			return 0;
 		if (phy_reg != 0 && phy_reg != 0xFFFF)
 			break;
 		mdelay(1);
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* [PATCH 3/3] net: asix: Fix AX88772x resume failures
  2016-07-25 17:40 [PATCH 1/3] net: asix: Add in_pm parameter robert.foss
  2016-07-25 17:40 ` [PATCH 2/3] net: asix: Avoid looping when the device is disconnected robert.foss
@ 2016-07-25 17:40 ` robert.foss
  2016-07-25 21:45   ` Grant Grundler
  2016-07-25 21:26 ` [PATCH 1/3] net: asix: Add in_pm parameter Grant Grundler
  2016-07-26  4:45 ` David Miller
  3 siblings, 1 reply; 9+ messages in thread
From: robert.foss @ 2016-07-25 17:40 UTC (permalink / raw)
  To: davem, grundler, Mark_Craske, Dean_Jenkins, vpalatin, ivecera,
	linux, wk.tsai, changchias, linux-usb, netdev, linux-kernel
  Cc: enric.balletbo, tomeu.vizoso

From: WK Tsai <wk.tsai@nvidia.com>

The change fixes AX88772x resume failure by
- Restore incorrect AX88772A PHY registers when resetting
- Need to stop MAC operation when suspending
- Need to restart MII when restoring PHY

Signed-off-by: WK Tsai <wk.tsai@nvidia.com>
---
 drivers/net/usb/asix_devices.c | 47 +++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 46 insertions(+), 1 deletion(-)

diff --git a/drivers/net/usb/asix_devices.c b/drivers/net/usb/asix_devices.c
index ebeb730..083dc2e 100644
--- a/drivers/net/usb/asix_devices.c
+++ b/drivers/net/usb/asix_devices.c
@@ -35,6 +35,15 @@
 
 #define	PHY_MODE_RTL8211CL	0x000C
 
+#define AX88772A_PHY14H		0x14
+#define AX88772A_PHY14H_DEFAULT 0x442C
+
+#define AX88772A_PHY15H		0x15
+#define AX88772A_PHY15H_DEFAULT 0x03C8
+
+#define AX88772A_PHY16H		0x16
+#define AX88772A_PHY16H_DEFAULT 0x4044
+
 struct ax88172_int_data {
 	__le16 res1;
 	u8 link;
@@ -424,7 +433,7 @@ static int ax88772a_hw_reset(struct usbnet *dev, int in_pm)
 {
 	struct asix_data *data = (struct asix_data *)&dev->data;
 	int ret, embd_phy;
-	u16 rx_ctl;
+	u16 rx_ctl, phy14h, phy15h, phy16h;
 	u8 chipcode = 0;
 
 	ret = asix_write_gpio(dev, AX_GPIO_RSE, 5, in_pm);
@@ -482,6 +491,32 @@ static int ax88772a_hw_reset(struct usbnet *dev, int in_pm)
 				   ret);
 			goto out;
 		}
+	} else if ((chipcode & AX_CHIPCODE_MASK) == AX_AX88772A_CHIPCODE) {
+		/* Check if the PHY registers have default settings */
+		phy14h = asix_mdio_read_nopm(dev->net, dev->mii.phy_id,
+					     AX88772A_PHY14H);
+		phy15h = asix_mdio_read_nopm(dev->net, dev->mii.phy_id,
+					     AX88772A_PHY15H);
+		phy16h = asix_mdio_read_nopm(dev->net, dev->mii.phy_id,
+					     AX88772A_PHY16H);
+
+		netdev_dbg(dev->net,
+			   "772a_hw_reset: MR20=0x%x MR21=0x%x MR22=0x%x\n",
+			   phy14h, phy15h, phy16h);
+
+		/* Restore PHY registers default setting if not */
+		if (phy14h != AX88772A_PHY14H_DEFAULT)
+			asix_mdio_write_nopm(dev->net, dev->mii.phy_id,
+					     AX88772A_PHY14H,
+					     AX88772A_PHY14H_DEFAULT);
+		if (phy15h != AX88772A_PHY15H_DEFAULT)
+			asix_mdio_write_nopm(dev->net, dev->mii.phy_id,
+					     AX88772A_PHY15H,
+					     AX88772A_PHY15H_DEFAULT);
+		if (phy16h != AX88772A_PHY16H_DEFAULT)
+			asix_mdio_write_nopm(dev->net, dev->mii.phy_id,
+					     AX88772A_PHY16H,
+					     AX88772A_PHY16H_DEFAULT);
 	}
 
 	ret = asix_write_cmd(dev, AX_CMD_WRITE_IPG0,
@@ -543,6 +578,15 @@ static const struct net_device_ops ax88772_netdev_ops = {
 static void ax88772_suspend(struct usbnet *dev)
 {
 	struct asix_common_private *priv = dev->driver_priv;
+	u16 medium;
+
+	/* Stop MAC operation */
+	medium = asix_read_medium_status(dev, 0);
+	medium &= ~AX_MEDIUM_RE;
+	asix_write_medium_mode(dev, medium, 0);
+
+	netdev_dbg(dev->net, "ax88772_suspend: medium=0x%04x\n",
+		   asix_read_medium_status(dev, 0));
 
 	/* Preserve BMCR for restoring */
 	priv->presvd_phy_bmcr =
@@ -577,6 +621,7 @@ static void ax88772_restore_phy(struct usbnet *dev)
 		asix_mdio_write_nopm(dev->net, dev->mii.phy_id, MII_BMCR,
 				     priv->presvd_phy_bmcr);
 
+		mii_nway_restart(&dev->mii);
 		priv->presvd_phy_advertise = 0;
 		priv->presvd_phy_bmcr = 0;
 	}
-- 
2.7.4

^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Re: [PATCH 1/3] net: asix: Add in_pm parameter
  2016-07-25 17:40 [PATCH 1/3] net: asix: Add in_pm parameter robert.foss
  2016-07-25 17:40 ` [PATCH 2/3] net: asix: Avoid looping when the device is disconnected robert.foss
  2016-07-25 17:40 ` [PATCH 3/3] net: asix: Fix AX88772x resume failures robert.foss
@ 2016-07-25 21:26 ` Grant Grundler
  2016-07-26 21:14   ` Robert Foss
  2016-07-26  4:45 ` David Miller
  3 siblings, 1 reply; 9+ messages in thread
From: Grant Grundler @ 2016-07-25 21:26 UTC (permalink / raw)
  To: robert.foss
  Cc: David Miller, Grant Grundler, Mark_Craske, Dean_Jenkins,
	Vincent Palatin, ivecera, linux, WK TSAI, changchias, linux-usb,
	netdev, LKML, enric.balletbo, tomeu.vizoso

[as plain text this time...]

Robert,

On Mon, Jul 25, 2016 at 10:40 AM,  <robert.foss@collabora.com> wrote:
> From: Grant Grundler <grundler@chromium.org>

For the record, I believe I am not the author of these patches.

I believe the original author is
    Signed-off-by: Freddy Xin <freddy@asix.com.tw>

as recorded in the following code reviews (and testing) that I was
responsible for:
    https://chromium-review.googlesource.com/#/q/owner:%22Grant+Grundler%22+status:merged+asix+in_pm

And I would certainly be happy to see this code go upstream and
expected ASIX would submit this change upstream.

> In order to R/W registers in suspend/resume functions, in_pm flags are
> added to some functions to determine whether the nopm version of usb
> functions is called.

FTR, current  drivers/net/usb/ax88179_178a.c uses "in_pm" as well.

> Save BMCR and ANAR PHY registers in suspend function and restore them
> in resume function.
>
> Reset HW in resume function to ensure the PHY works correctly.
>
> Signed-off-by: Grant Grundler <grundler@chromium.org>

BTW, I have two additional changes for AX88772x support sitting in my
"needs more work" queue (for quite a while already):
   https://chromium-review.googlesource.com/#/c/229620/
   "asix: autoneg will set WRITE_MEDIUM reg"

   https://chromium-review.googlesource.com/#/c/231162/
   "net: asix: see 802.3 spec for phy reset"

I would certainly approve if _anyone_ picked these up, tested them,
and then submitted them to netdev.

cheers,
grant

> ---
>  drivers/net/usb/asix.h         |  40 +++--
>  drivers/net/usb/asix_common.c  | 180 +++++++++++++++-----
>  drivers/net/usb/asix_devices.c | 373 ++++++++++++++++++++++++++++++++---------
>  drivers/net/usb/ax88172a.c     |  29 ++--
>  4 files changed, 472 insertions(+), 150 deletions(-)
>
> diff --git a/drivers/net/usb/asix.h b/drivers/net/usb/asix.h
> index a2d3ea6..d109242 100644
> --- a/drivers/net/usb/asix.h
> +++ b/drivers/net/usb/asix.h
> @@ -46,6 +46,7 @@
>  #define AX_CMD_SET_SW_MII              0x06
>  #define AX_CMD_READ_MII_REG            0x07
>  #define AX_CMD_WRITE_MII_REG           0x08
> +#define AX_CMD_STATMNGSTS_REG          0x09
>  #define AX_CMD_SET_HW_MII              0x0a
>  #define AX_CMD_READ_EEPROM             0x0b
>  #define AX_CMD_WRITE_EEPROM            0x0c
> @@ -71,6 +72,17 @@
>  #define AX_CMD_SW_RESET                        0x20
>  #define AX_CMD_SW_PHY_STATUS           0x21
>  #define AX_CMD_SW_PHY_SELECT           0x22
> +#define AX_QCTCTRL                     0x2A
> +
> +#define AX_CHIPCODE_MASK               0x70
> +#define AX_AX88772_CHIPCODE            0x00
> +#define AX_AX88772A_CHIPCODE           0x10
> +#define AX_AX88772B_CHIPCODE           0x20
> +#define AX_HOST_EN                     0x01
> +
> +#define AX_PHYSEL_PSEL                 0x01
> +#define AX_PHYSEL_SSMII                        0
> +#define AX_PHYSEL_SSEN                 0x10
>
>  #define AX_PHY_SELECT_MASK             (BIT(3) | BIT(2))
>  #define AX_PHY_SELECT_INTERNAL         0
> @@ -173,6 +185,10 @@ struct asix_rx_fixup_info {
>  };
>
>  struct asix_common_private {
> +       void (*resume)(struct usbnet *dev);
> +       void (*suspend)(struct usbnet *dev);
> +       u16 presvd_phy_advertise;
> +       u16 presvd_phy_bmcr;
>         struct asix_rx_fixup_info rx_fixup_info;
>  };
>
> @@ -182,10 +198,10 @@ extern const struct driver_info ax88172a_info;
>  #define FLAG_EEPROM_MAC                (1UL << 0)  /* init device MAC from eeprom */
>
>  int asix_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,
> -                 u16 size, void *data);
> +                 u16 size, void *data, int in_pm);
>
>  int asix_write_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,
> -                  u16 size, void *data);
> +                  u16 size, void *data, int in_pm);
>
>  void asix_write_cmd_async(struct usbnet *dev, u8 cmd, u16 value,
>                           u16 index, u16 size, void *data);
> @@ -197,27 +213,31 @@ int asix_rx_fixup_common(struct usbnet *dev, struct sk_buff *skb);
>  struct sk_buff *asix_tx_fixup(struct usbnet *dev, struct sk_buff *skb,
>                               gfp_t flags);
>
> -int asix_set_sw_mii(struct usbnet *dev);
> -int asix_set_hw_mii(struct usbnet *dev);
> +int asix_set_sw_mii(struct usbnet *dev, int in_pm);
> +int asix_set_hw_mii(struct usbnet *dev, int in_pm);
>
>  int asix_read_phy_addr(struct usbnet *dev, int internal);
>  int asix_get_phy_addr(struct usbnet *dev);
>
> -int asix_sw_reset(struct usbnet *dev, u8 flags);
> +int asix_sw_reset(struct usbnet *dev, u8 flags, int in_pm);
>
> -u16 asix_read_rx_ctl(struct usbnet *dev);
> -int asix_write_rx_ctl(struct usbnet *dev, u16 mode);
> +u16 asix_read_rx_ctl(struct usbnet *dev, int in_pm);
> +int asix_write_rx_ctl(struct usbnet *dev, u16 mode, int in_pm);
>
> -u16 asix_read_medium_status(struct usbnet *dev);
> -int asix_write_medium_mode(struct usbnet *dev, u16 mode);
> +u16 asix_read_medium_status(struct usbnet *dev, int in_pm);
> +int asix_write_medium_mode(struct usbnet *dev, u16 mode, int in_pm);
>
> -int asix_write_gpio(struct usbnet *dev, u16 value, int sleep);
> +int asix_write_gpio(struct usbnet *dev, u16 value, int sleep, int in_pm);
>
>  void asix_set_multicast(struct net_device *net);
>
>  int asix_mdio_read(struct net_device *netdev, int phy_id, int loc);
>  void asix_mdio_write(struct net_device *netdev, int phy_id, int loc, int val);
>
> +int asix_mdio_read_nopm(struct net_device *netdev, int phy_id, int loc);
> +void asix_mdio_write_nopm(struct net_device *netdev, int phy_id, int loc,
> +                         int val);
> +
>  void asix_get_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo);
>  int asix_set_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo);
>
> diff --git a/drivers/net/usb/asix_common.c b/drivers/net/usb/asix_common.c
> index bd9acff..f0ccf76 100644
> --- a/drivers/net/usb/asix_common.c
> +++ b/drivers/net/usb/asix_common.c
> @@ -22,24 +22,49 @@
>  #include "asix.h"
>
>  int asix_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,
> -                 u16 size, void *data)
> +                 u16 size, void *data, int in_pm)
>  {
>         int ret;
> -       ret = usbnet_read_cmd(dev, cmd,
> -                              USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
> -                              value, index, data, size);
> +       int (*fn)(struct usbnet *, u8, u8, u16, u16, void *, u16);
> +
> +       BUG_ON(!dev);
> +
> +       if (!in_pm)
> +               fn = usbnet_read_cmd;
> +       else
> +               fn = usbnet_read_cmd_nopm;
> +
> +       ret = fn(dev, cmd, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
> +                value, index, data, size);
> +
> +       if (unlikely(ret < 0))
> +               netdev_warn(dev->net, "Failed to read reg index 0x%04x: %d\n",
> +                           index, ret);
>
> -       if (ret != size && ret >= 0)
> -               return -EINVAL;
>         return ret;
>  }
>
>  int asix_write_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,
> -                  u16 size, void *data)
> +                  u16 size, void *data, int in_pm)
>  {
> -       return usbnet_write_cmd(dev, cmd,
> -                               USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
> -                               value, index, data, size);
> +       int ret;
> +       int (*fn)(struct usbnet *, u8, u8, u16, u16, const void *, u16);
> +
> +       BUG_ON(!dev);
> +
> +       if (!in_pm)
> +               fn = usbnet_write_cmd;
> +       else
> +               fn = usbnet_write_cmd_nopm;
> +
> +       ret = fn(dev, cmd, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
> +                value, index, data, size);
> +
> +       if (unlikely(ret < 0))
> +               netdev_warn(dev->net, "Failed to write reg index 0x%04x: %d\n",
> +                           index, ret);
> +
> +       return ret;
>  }
>
>  void asix_write_cmd_async(struct usbnet *dev, u8 cmd, u16 value, u16 index,
> @@ -225,19 +250,20 @@ struct sk_buff *asix_tx_fixup(struct usbnet *dev, struct sk_buff *skb,
>         return skb;
>  }
>
> -int asix_set_sw_mii(struct usbnet *dev)
> +int asix_set_sw_mii(struct usbnet *dev, int in_pm)
>  {
>         int ret;
> -       ret = asix_write_cmd(dev, AX_CMD_SET_SW_MII, 0x0000, 0, 0, NULL);
> +       ret = asix_write_cmd(dev, AX_CMD_SET_SW_MII, 0x0000, 0, 0, NULL, in_pm);
> +
>         if (ret < 0)
>                 netdev_err(dev->net, "Failed to enable software MII access\n");
>         return ret;
>  }
>
> -int asix_set_hw_mii(struct usbnet *dev)
> +int asix_set_hw_mii(struct usbnet *dev, int in_pm)
>  {
>         int ret;
> -       ret = asix_write_cmd(dev, AX_CMD_SET_HW_MII, 0x0000, 0, 0, NULL);
> +       ret = asix_write_cmd(dev, AX_CMD_SET_HW_MII, 0x0000, 0, 0, NULL, in_pm);
>         if (ret < 0)
>                 netdev_err(dev->net, "Failed to enable hardware MII access\n");
>         return ret;
> @@ -247,7 +273,7 @@ int asix_read_phy_addr(struct usbnet *dev, int internal)
>  {
>         int offset = (internal ? 1 : 0);
>         u8 buf[2];
> -       int ret = asix_read_cmd(dev, AX_CMD_READ_PHY_ID, 0, 0, 2, buf);
> +       int ret = asix_read_cmd(dev, AX_CMD_READ_PHY_ID, 0, 0, 2, buf, 0);
>
>         netdev_dbg(dev->net, "asix_get_phy_addr()\n");
>
> @@ -270,21 +296,21 @@ int asix_get_phy_addr(struct usbnet *dev)
>  }
>
>
> -int asix_sw_reset(struct usbnet *dev, u8 flags)
> +int asix_sw_reset(struct usbnet *dev, u8 flags, int in_pm)
>  {
>         int ret;
>
> -        ret = asix_write_cmd(dev, AX_CMD_SW_RESET, flags, 0, 0, NULL);
> +       ret = asix_write_cmd(dev, AX_CMD_SW_RESET, flags, 0, 0, NULL, in_pm);
>         if (ret < 0)
>                 netdev_err(dev->net, "Failed to send software reset: %02x\n", ret);
>
>         return ret;
>  }
>
> -u16 asix_read_rx_ctl(struct usbnet *dev)
> +u16 asix_read_rx_ctl(struct usbnet *dev, int in_pm)
>  {
>         __le16 v;
> -       int ret = asix_read_cmd(dev, AX_CMD_READ_RX_CTL, 0, 0, 2, &v);
> +       int ret = asix_read_cmd(dev, AX_CMD_READ_RX_CTL, 0, 0, 2, &v, in_pm);
>
>         if (ret < 0) {
>                 netdev_err(dev->net, "Error reading RX_CTL register: %02x\n", ret);
> @@ -295,12 +321,12 @@ out:
>         return ret;
>  }
>
> -int asix_write_rx_ctl(struct usbnet *dev, u16 mode)
> +int asix_write_rx_ctl(struct usbnet *dev, u16 mode, int in_pm)
>  {
>         int ret;
>
>         netdev_dbg(dev->net, "asix_write_rx_ctl() - mode = 0x%04x\n", mode);
> -       ret = asix_write_cmd(dev, AX_CMD_WRITE_RX_CTL, mode, 0, 0, NULL);
> +       ret = asix_write_cmd(dev, AX_CMD_WRITE_RX_CTL, mode, 0, 0, NULL, in_pm);
>         if (ret < 0)
>                 netdev_err(dev->net, "Failed to write RX_CTL mode to 0x%04x: %02x\n",
>                            mode, ret);
> @@ -308,10 +334,11 @@ int asix_write_rx_ctl(struct usbnet *dev, u16 mode)
>         return ret;
>  }
>
> -u16 asix_read_medium_status(struct usbnet *dev)
> +u16 asix_read_medium_status(struct usbnet *dev, int in_pm)
>  {
>         __le16 v;
> -       int ret = asix_read_cmd(dev, AX_CMD_READ_MEDIUM_STATUS, 0, 0, 2, &v);
> +       int ret = asix_read_cmd(dev, AX_CMD_READ_MEDIUM_STATUS,
> +                               0, 0, 2, &v, in_pm);
>
>         if (ret < 0) {
>                 netdev_err(dev->net, "Error reading Medium Status register: %02x\n",
> @@ -323,12 +350,13 @@ u16 asix_read_medium_status(struct usbnet *dev)
>
>  }
>
> -int asix_write_medium_mode(struct usbnet *dev, u16 mode)
> +int asix_write_medium_mode(struct usbnet *dev, u16 mode, int in_pm)
>  {
>         int ret;
>
>         netdev_dbg(dev->net, "asix_write_medium_mode() - mode = 0x%04x\n", mode);
> -       ret = asix_write_cmd(dev, AX_CMD_WRITE_MEDIUM_MODE, mode, 0, 0, NULL);
> +       ret = asix_write_cmd(dev, AX_CMD_WRITE_MEDIUM_MODE,
> +                            mode, 0, 0, NULL, in_pm);
>         if (ret < 0)
>                 netdev_err(dev->net, "Failed to write Medium Mode mode to 0x%04x: %02x\n",
>                            mode, ret);
> @@ -336,12 +364,12 @@ int asix_write_medium_mode(struct usbnet *dev, u16 mode)
>         return ret;
>  }
>
> -int asix_write_gpio(struct usbnet *dev, u16 value, int sleep)
> +int asix_write_gpio(struct usbnet *dev, u16 value, int sleep, int in_pm)
>  {
>         int ret;
>
>         netdev_dbg(dev->net, "asix_write_gpio() - value = 0x%04x\n", value);
> -       ret = asix_write_cmd(dev, AX_CMD_WRITE_GPIOS, value, 0, 0, NULL);
> +       ret = asix_write_cmd(dev, AX_CMD_WRITE_GPIOS, value, 0, 0, NULL, in_pm);
>         if (ret < 0)
>                 netdev_err(dev->net, "Failed to write GPIO value 0x%04x: %02x\n",
>                            value, ret);
> @@ -398,16 +426,23 @@ int asix_mdio_read(struct net_device *netdev, int phy_id, int loc)
>  {
>         struct usbnet *dev = netdev_priv(netdev);
>         __le16 res;
> +       u8 smsr;
> +       int i = 0;
>
>         mutex_lock(&dev->phy_mutex);
> -       asix_set_sw_mii(dev);
> +       do {
> +               asix_set_sw_mii(dev, 0);
> +               usleep_range(1000, 1100);
> +               asix_read_cmd(dev, AX_CMD_STATMNGSTS_REG, 0, 0, 1, &smsr, 0);
> +       } while (!(smsr & AX_HOST_EN) && (i++ < 30));
> +
>         asix_read_cmd(dev, AX_CMD_READ_MII_REG, phy_id,
> -                               (__u16)loc, 2, &res);
> -       asix_set_hw_mii(dev);
> +                               (__u16)loc, 2, &res, 0);
> +       asix_set_hw_mii(dev, 0);
>         mutex_unlock(&dev->phy_mutex);
>
>         netdev_dbg(dev->net, "asix_mdio_read() phy_id=0x%02x, loc=0x%02x, returns=0x%04x\n",
> -                  phy_id, loc, le16_to_cpu(res));
> +                       phy_id, loc, le16_to_cpu(res));
>
>         return le16_to_cpu(res);
>  }
> @@ -416,13 +451,71 @@ void asix_mdio_write(struct net_device *netdev, int phy_id, int loc, int val)
>  {
>         struct usbnet *dev = netdev_priv(netdev);
>         __le16 res = cpu_to_le16(val);
> +       u8 smsr;
> +       int i = 0;
>
>         netdev_dbg(dev->net, "asix_mdio_write() phy_id=0x%02x, loc=0x%02x, val=0x%04x\n",
> -                  phy_id, loc, val);
> +                       phy_id, loc, val);
> +
> +       mutex_lock(&dev->phy_mutex);
> +       do {
> +               asix_set_sw_mii(dev, 0);
> +               usleep_range(1000, 1100);
> +               asix_read_cmd(dev, AX_CMD_STATMNGSTS_REG, 0, 0, 1, &smsr, 0);
> +       } while (!(smsr & AX_HOST_EN) && (i++ < 30));
> +
> +       asix_write_cmd(dev, AX_CMD_WRITE_MII_REG, phy_id,
> +                      (__u16)loc, 2, &res, 0);
> +       asix_set_hw_mii(dev, 0);
> +       mutex_unlock(&dev->phy_mutex);
> +}
> +
> +int asix_mdio_read_nopm(struct net_device *netdev, int phy_id, int loc)
> +{
> +       struct usbnet *dev = netdev_priv(netdev);
> +       __le16 res;
> +       u8 smsr;
> +       int i = 0;
> +
> +       mutex_lock(&dev->phy_mutex);
> +       do {
> +               asix_set_sw_mii(dev, 1);
> +               usleep_range(1000, 1100);
> +               asix_read_cmd(dev, AX_CMD_STATMNGSTS_REG, 0, 0, 1, &smsr, 1);
> +       } while (!(smsr & AX_HOST_EN) && (i++ < 30));
> +
> +       asix_read_cmd(dev, AX_CMD_READ_MII_REG, phy_id,
> +                     (__u16)loc, 2, &res, 1);
> +       asix_set_hw_mii(dev, 1);
> +       mutex_unlock(&dev->phy_mutex);
> +
> +       netdev_dbg(dev->net, "asix_mdio_read_nopm() phy_id=0x%02x, loc=0x%02x, returns=0x%04x\n",
> +                       phy_id, loc, le16_to_cpu(res));
> +
> +       return le16_to_cpu(res);
> +}
> +
> +void
> +asix_mdio_write_nopm(struct net_device *netdev, int phy_id, int loc, int val)
> +{
> +       struct usbnet *dev = netdev_priv(netdev);
> +       __le16 res = cpu_to_le16(val);
> +       u8 smsr;
> +       int i = 0;
> +
> +       netdev_dbg(dev->net, "asix_mdio_write() phy_id=0x%02x, loc=0x%02x, val=0x%04x\n",
> +                       phy_id, loc, val);
> +
>         mutex_lock(&dev->phy_mutex);
> -       asix_set_sw_mii(dev);
> -       asix_write_cmd(dev, AX_CMD_WRITE_MII_REG, phy_id, (__u16)loc, 2, &res);
> -       asix_set_hw_mii(dev);
> +       do {
> +               asix_set_sw_mii(dev, 1);
> +               usleep_range(1000, 1100);
> +               asix_read_cmd(dev, AX_CMD_STATMNGSTS_REG, 0, 0, 1, &smsr, 1);
> +       } while (!(smsr & AX_HOST_EN) && (i++ < 30));
> +
> +       asix_write_cmd(dev, AX_CMD_WRITE_MII_REG, phy_id,
> +                      (__u16)loc, 2, &res, 1);
> +       asix_set_hw_mii(dev, 1);
>         mutex_unlock(&dev->phy_mutex);
>  }
>
> @@ -431,7 +524,8 @@ void asix_get_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo)
>         struct usbnet *dev = netdev_priv(net);
>         u8 opt;
>
> -       if (asix_read_cmd(dev, AX_CMD_READ_MONITOR_MODE, 0, 0, 1, &opt) < 0) {
> +       if (asix_read_cmd(dev, AX_CMD_READ_MONITOR_MODE,
> +                         0, 0, 1, &opt, 0) < 0) {
>                 wolinfo->supported = 0;
>                 wolinfo->wolopts = 0;
>                 return;
> @@ -455,7 +549,7 @@ int asix_set_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo)
>                 opt |= AX_MONITOR_MAGIC;
>
>         if (asix_write_cmd(dev, AX_CMD_WRITE_MONITOR_MODE,
> -                             opt, 0, 0, NULL) < 0)
> +                             opt, 0, 0, NULL, 0) < 0)
>                 return -EINVAL;
>
>         return 0;
> @@ -490,7 +584,7 @@ int asix_get_eeprom(struct net_device *net, struct ethtool_eeprom *eeprom,
>         /* ax8817x returns 2 bytes from eeprom on read */
>         for (i = first_word; i <= last_word; i++) {
>                 if (asix_read_cmd(dev, AX_CMD_READ_EEPROM, i, 0, 2,
> -                                 &(eeprom_buff[i - first_word])) < 0) {
> +                                 &eeprom_buff[i - first_word], 0) < 0) {
>                         kfree(eeprom_buff);
>                         return -EIO;
>                 }
> @@ -531,7 +625,7 @@ int asix_set_eeprom(struct net_device *net, struct ethtool_eeprom *eeprom,
>            the EEPROM */
>         if (eeprom->offset & 1) {
>                 ret = asix_read_cmd(dev, AX_CMD_READ_EEPROM, first_word, 0, 2,
> -                                   &(eeprom_buff[0]));
> +                                   &eeprom_buff[0], 0);
>                 if (ret < 0) {
>                         netdev_err(net, "Failed to read EEPROM at offset 0x%02x.\n", first_word);
>                         goto free;
> @@ -540,7 +634,7 @@ int asix_set_eeprom(struct net_device *net, struct ethtool_eeprom *eeprom,
>
>         if ((eeprom->offset + eeprom->len) & 1) {
>                 ret = asix_read_cmd(dev, AX_CMD_READ_EEPROM, last_word, 0, 2,
> -                                   &(eeprom_buff[last_word - first_word]));
> +                                   &eeprom_buff[last_word - first_word], 0);
>                 if (ret < 0) {
>                         netdev_err(net, "Failed to read EEPROM at offset 0x%02x.\n", last_word);
>                         goto free;
> @@ -550,7 +644,7 @@ int asix_set_eeprom(struct net_device *net, struct ethtool_eeprom *eeprom,
>         memcpy((u8 *)eeprom_buff + (eeprom->offset & 1), data, eeprom->len);
>
>         /* write data to EEPROM */
> -       ret = asix_write_cmd(dev, AX_CMD_WRITE_ENABLE, 0x0000, 0, 0, NULL);
> +       ret = asix_write_cmd(dev, AX_CMD_WRITE_ENABLE, 0x0000, 0, 0, NULL, 0);
>         if (ret < 0) {
>                 netdev_err(net, "Failed to enable EEPROM write\n");
>                 goto free;
> @@ -561,7 +655,7 @@ int asix_set_eeprom(struct net_device *net, struct ethtool_eeprom *eeprom,
>                 netdev_dbg(net, "write to EEPROM at offset 0x%02x, data 0x%04x\n",
>                            i, eeprom_buff[i - first_word]);
>                 ret = asix_write_cmd(dev, AX_CMD_WRITE_EEPROM, i,
> -                                    eeprom_buff[i - first_word], 0, NULL);
> +                                    eeprom_buff[i - first_word], 0, NULL, 0);
>                 if (ret < 0) {
>                         netdev_err(net, "Failed to write EEPROM at offset 0x%02x.\n",
>                                    i);
> @@ -570,7 +664,7 @@ int asix_set_eeprom(struct net_device *net, struct ethtool_eeprom *eeprom,
>                 msleep(20);
>         }
>
> -       ret = asix_write_cmd(dev, AX_CMD_WRITE_DISABLE, 0x0000, 0, 0, NULL);
> +       ret = asix_write_cmd(dev, AX_CMD_WRITE_DISABLE, 0x0000, 0, 0, NULL, 0);
>         if (ret < 0) {
>                 netdev_err(net, "Failed to disable EEPROM write\n");
>                 goto free;
> diff --git a/drivers/net/usb/asix_devices.c b/drivers/net/usb/asix_devices.c
> index 5cabefc..aaa4290 100644
> --- a/drivers/net/usb/asix_devices.c
> +++ b/drivers/net/usb/asix_devices.c
> @@ -184,7 +184,7 @@ static int ax88172_link_reset(struct usbnet *dev)
>         netdev_dbg(dev->net, "ax88172_link_reset() speed: %u duplex: %d setting mode to 0x%04x\n",
>                    ethtool_cmd_speed(&ecmd), ecmd.duplex, mode);
>
> -       asix_write_medium_mode(dev, mode);
> +       asix_write_medium_mode(dev, mode, 0);
>
>         return 0;
>  }
> @@ -213,18 +213,19 @@ static int ax88172_bind(struct usbnet *dev, struct usb_interface *intf)
>         /* Toggle the GPIOs in a manufacturer/model specific way */
>         for (i = 2; i >= 0; i--) {
>                 ret = asix_write_cmd(dev, AX_CMD_WRITE_GPIOS,
> -                               (gpio_bits >> (i * 8)) & 0xff, 0, 0, NULL);
> +                               (gpio_bits >> (i * 8)) & 0xff, 0, 0, NULL, 0);
>                 if (ret < 0)
>                         goto out;
>                 msleep(5);
>         }
>
> -       ret = asix_write_rx_ctl(dev, 0x80);
> +       ret = asix_write_rx_ctl(dev, 0x80, 0);
>         if (ret < 0)
>                 goto out;
>
>         /* Get the MAC address */
> -       ret = asix_read_cmd(dev, AX88172_CMD_READ_NODE_ID, 0, 0, ETH_ALEN, buf);
> +       ret = asix_read_cmd(dev, AX88172_CMD_READ_NODE_ID,
> +                           0, 0, ETH_ALEN, buf, 0);
>         if (ret < 0) {
>                 netdev_dbg(dev->net, "read AX_CMD_READ_NODE_ID failed: %d\n",
>                            ret);
> @@ -290,7 +291,7 @@ static int ax88772_link_reset(struct usbnet *dev)
>         netdev_dbg(dev->net, "ax88772_link_reset() speed: %u duplex: %d setting mode to 0x%04x\n",
>                    ethtool_cmd_speed(&ecmd), ecmd.duplex, mode);
>
> -       asix_write_medium_mode(dev, mode);
> +       asix_write_medium_mode(dev, mode, 0);
>
>         return 0;
>  }
> @@ -298,78 +299,192 @@ static int ax88772_link_reset(struct usbnet *dev)
>  static int ax88772_reset(struct usbnet *dev)
>  {
>         struct asix_data *data = (struct asix_data *)&dev->data;
> +       int ret;
> +
> +       /* Rewrite MAC address */
> +       ether_addr_copy(data->mac_addr, dev->net->dev_addr);
> +       ret = asix_write_cmd(dev, AX_CMD_WRITE_NODE_ID, 0, 0,
> +                            ETH_ALEN, data->mac_addr, 0);
> +       if (ret < 0)
> +               goto out;
> +
> +       /* Set RX_CTL to default values with 2k buffer, and enable cactus */
> +       ret = asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL, 0);
> +       if (ret < 0)
> +               goto out;
> +
> +       asix_write_medium_mode(dev, AX88772_MEDIUM_DEFAULT, 0);
> +       if (ret < 0)
> +               goto out;
> +
> +       return 0;
> +
> +out:
> +       return ret;
> +}
> +
> +static int ax88772_hw_reset(struct usbnet *dev, int in_pm)
> +{
> +       struct asix_data *data = (struct asix_data *)&dev->data;
>         int ret, embd_phy;
>         u16 rx_ctl;
>
> -       ret = asix_write_gpio(dev,
> -                       AX_GPIO_RSE | AX_GPIO_GPO_2 | AX_GPIO_GPO2EN, 5);
> +       ret = asix_write_gpio(dev, AX_GPIO_RSE | AX_GPIO_GPO_2 |
> +                             AX_GPIO_GPO2EN, 5, in_pm);
>         if (ret < 0)
>                 goto out;
>
> -       embd_phy = ((asix_get_phy_addr(dev) & 0x1f) == 0x10 ? 1 : 0);
> +       embd_phy = ((dev->mii.phy_id & 0x1f) == 0x10 ? 1 : 0);
>
> -       ret = asix_write_cmd(dev, AX_CMD_SW_PHY_SELECT, embd_phy, 0, 0, NULL);
> +       ret = asix_write_cmd(dev, AX_CMD_SW_PHY_SELECT, embd_phy,
> +                            0, 0, NULL, in_pm);
>         if (ret < 0) {
>                 netdev_dbg(dev->net, "Select PHY #1 failed: %d\n", ret);
>                 goto out;
>         }
>
> -       ret = asix_sw_reset(dev, AX_SWRESET_IPPD | AX_SWRESET_PRL);
> -       if (ret < 0)
> -               goto out;
> +       if (embd_phy) {
> +               ret = asix_sw_reset(dev, AX_SWRESET_IPPD, in_pm);
> +               if (ret < 0)
> +                       goto out;
>
> -       msleep(150);
> +               usleep_range(10000, 11000);
>
> -       ret = asix_sw_reset(dev, AX_SWRESET_CLEAR);
> -       if (ret < 0)
> -               goto out;
> +               ret = asix_sw_reset(dev, AX_SWRESET_CLEAR, in_pm);
> +               if (ret < 0)
> +                       goto out;
>
> -       msleep(150);
> +               msleep(60);
>
> -       if (embd_phy) {
> -               ret = asix_sw_reset(dev, AX_SWRESET_IPRL);
> +               ret = asix_sw_reset(dev, AX_SWRESET_IPRL | AX_SWRESET_PRL,
> +                                   in_pm);
>                 if (ret < 0)
>                         goto out;
>         } else {
> -               ret = asix_sw_reset(dev, AX_SWRESET_PRTE);
> +               ret = asix_sw_reset(dev, AX_SWRESET_IPPD | AX_SWRESET_PRL,
> +                                   in_pm);
>                 if (ret < 0)
>                         goto out;
>         }
>
>         msleep(150);
> -       rx_ctl = asix_read_rx_ctl(dev);
> -       netdev_dbg(dev->net, "RX_CTL is 0x%04x after software reset\n", rx_ctl);
> -       ret = asix_write_rx_ctl(dev, 0x0000);
> +
> +       if (in_pm && (!asix_mdio_read_nopm(dev->net, dev->mii.phy_id,
> +                                          MII_PHYSID1))){
> +               ret = -EIO;
> +               goto out;
> +       }
> +
> +       ret = asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL, in_pm);
> +       if (ret < 0)
> +               goto out;
> +
> +       ret = asix_write_medium_mode(dev, AX88772_MEDIUM_DEFAULT, in_pm);
>         if (ret < 0)
>                 goto out;
>
> -       rx_ctl = asix_read_rx_ctl(dev);
> -       netdev_dbg(dev->net, "RX_CTL is 0x%04x setting to 0x0000\n", rx_ctl);
> +       ret = asix_write_cmd(dev, AX_CMD_WRITE_IPG0,
> +                            AX88772_IPG0_DEFAULT | AX88772_IPG1_DEFAULT,
> +                            AX88772_IPG2_DEFAULT, 0, NULL, in_pm);
> +       if (ret < 0) {
> +               netdev_dbg(dev->net, "Write IPG,IPG1,IPG2 failed: %d\n", ret);
> +               goto out;
> +       }
>
> -       ret = asix_sw_reset(dev, AX_SWRESET_PRL);
> +       /* Rewrite MAC address */
> +       ether_addr_copy(data->mac_addr, dev->net->dev_addr);
> +       ret = asix_write_cmd(dev, AX_CMD_WRITE_NODE_ID, 0, 0,
> +                            ETH_ALEN, data->mac_addr, in_pm);
>         if (ret < 0)
>                 goto out;
>
> -       msleep(150);
> +       /* Set RX_CTL to default values with 2k buffer, and enable cactus */
> +       ret = asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL, in_pm);
> +       if (ret < 0)
> +               goto out;
> +
> +       rx_ctl = asix_read_rx_ctl(dev, in_pm);
> +       netdev_dbg(dev->net, "RX_CTL is 0x%04x after all initializations\n",
> +                  rx_ctl);
> +
> +       rx_ctl = asix_read_medium_status(dev, in_pm);
> +       netdev_dbg(dev->net,
> +                  "Medium Status is 0x%04x after all initializations\n",
> +                  rx_ctl);
> +
> +       return 0;
> +
> +out:
> +       return ret;
> +}
> +
> +static int ax88772a_hw_reset(struct usbnet *dev, int in_pm)
> +{
> +       struct asix_data *data = (struct asix_data *)&dev->data;
> +       int ret, embd_phy;
> +       u16 rx_ctl;
> +       u8 chipcode = 0;
>
> -       ret = asix_sw_reset(dev, AX_SWRESET_IPRL | AX_SWRESET_PRL);
> +       ret = asix_write_gpio(dev, AX_GPIO_RSE, 5, in_pm);
>         if (ret < 0)
>                 goto out;
>
> -       msleep(150);
> +       embd_phy = ((dev->mii.phy_id & 0x1f) == 0x10 ? 1 : 0);
>
> -       asix_mdio_write(dev->net, dev->mii.phy_id, MII_BMCR, BMCR_RESET);
> -       asix_mdio_write(dev->net, dev->mii.phy_id, MII_ADVERTISE,
> -                       ADVERTISE_ALL | ADVERTISE_CSMA);
> -       mii_nway_restart(&dev->mii);
> +       ret = asix_write_cmd(dev, AX_CMD_SW_PHY_SELECT, embd_phy |
> +                            AX_PHYSEL_SSEN, 0, 0, NULL, in_pm);
> +       if (ret < 0) {
> +               netdev_dbg(dev->net, "Select PHY #1 failed: %d\n", ret);
> +               goto out;
> +       }
> +       usleep_range(10000, 11000);
>
> -       ret = asix_write_medium_mode(dev, AX88772_MEDIUM_DEFAULT);
> +       ret = asix_sw_reset(dev, AX_SWRESET_IPPD | AX_SWRESET_IPRL, in_pm);
>         if (ret < 0)
>                 goto out;
>
> +       usleep_range(10000, 11000);
> +
> +       ret = asix_sw_reset(dev, AX_SWRESET_IPRL, in_pm);
> +       if (ret < 0)
> +               goto out;
> +
> +       msleep(160);
> +
> +       ret = asix_sw_reset(dev, AX_SWRESET_CLEAR, in_pm);
> +       if (ret < 0)
> +               goto out;
> +
> +       ret = asix_sw_reset(dev, AX_SWRESET_IPRL, in_pm);
> +       if (ret < 0)
> +               goto out;
> +
> +       msleep(200);
> +
> +       if (in_pm && (!asix_mdio_read_nopm(dev->net, dev->mii.phy_id,
> +                                          MII_PHYSID1))) {
> +               ret = -1;
> +               goto out;
> +       }
> +
> +       ret = asix_read_cmd(dev, AX_CMD_STATMNGSTS_REG, 0,
> +                           0, 1, &chipcode, in_pm);
> +       if (ret < 0)
> +               goto out;
> +
> +       if ((chipcode & AX_CHIPCODE_MASK) == AX_AX88772B_CHIPCODE) {
> +               ret = asix_write_cmd(dev, AX_QCTCTRL, 0x8000, 0x8001,
> +                                    0, NULL, in_pm);
> +               if (ret < 0) {
> +                       netdev_dbg(dev->net, "Write BQ setting failed: %d\n",
> +                                  ret);
> +                       goto out;
> +               }
> +       }
> +
>         ret = asix_write_cmd(dev, AX_CMD_WRITE_IPG0,
>                                 AX88772_IPG0_DEFAULT | AX88772_IPG1_DEFAULT,
> -                               AX88772_IPG2_DEFAULT, 0, NULL);
> +                               AX88772_IPG2_DEFAULT, 0, NULL, in_pm);
>         if (ret < 0) {
>                 netdev_dbg(dev->net, "Write IPG,IPG1,IPG2 failed: %d\n", ret);
>                 goto out;
> @@ -378,20 +493,29 @@ static int ax88772_reset(struct usbnet *dev)
>         /* Rewrite MAC address */
>         memcpy(data->mac_addr, dev->net->dev_addr, ETH_ALEN);
>         ret = asix_write_cmd(dev, AX_CMD_WRITE_NODE_ID, 0, 0, ETH_ALEN,
> -                                                       data->mac_addr);
> +                                                       data->mac_addr, in_pm);
> +       if (ret < 0)
> +               goto out;
> +
> +       /* Set RX_CTL to default values with 2k buffer, and enable cactus */
> +       ret = asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL, in_pm);
>         if (ret < 0)
>                 goto out;
>
> +       ret = asix_write_medium_mode(dev, AX88772_MEDIUM_DEFAULT, in_pm);
> +       if (ret < 0)
> +               return ret;
> +
>         /* Set RX_CTL to default values with 2k buffer, and enable cactus */
> -       ret = asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL);
> +       ret = asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL, in_pm);
>         if (ret < 0)
>                 goto out;
>
> -       rx_ctl = asix_read_rx_ctl(dev);
> +       rx_ctl = asix_read_rx_ctl(dev, in_pm);
>         netdev_dbg(dev->net, "RX_CTL is 0x%04x after all initializations\n",
>                    rx_ctl);
>
> -       rx_ctl = asix_read_medium_status(dev);
> +       rx_ctl = asix_read_medium_status(dev, in_pm);
>         netdev_dbg(dev->net,
>                    "Medium Status is 0x%04x after all initializations\n",
>                    rx_ctl);
> @@ -400,7 +524,6 @@ static int ax88772_reset(struct usbnet *dev)
>
>  out:
>         return ret;
> -
>  }
>
>  static const struct net_device_ops ax88772_netdev_ops = {
> @@ -415,11 +538,87 @@ static const struct net_device_ops ax88772_netdev_ops = {
>         .ndo_set_rx_mode        = asix_set_multicast,
>  };
>
> +static void ax88772_suspend(struct usbnet *dev)
> +{
> +       struct asix_common_private *priv = dev->driver_priv;
> +
> +       /* Preserve BMCR for restoring */
> +       priv->presvd_phy_bmcr =
> +               asix_mdio_read_nopm(dev->net, dev->mii.phy_id, MII_BMCR);
> +
> +       /* Preserve ANAR for restoring */
> +       priv->presvd_phy_advertise =
> +               asix_mdio_read_nopm(dev->net, dev->mii.phy_id, MII_ADVERTISE);
> +}
> +
> +static int asix_suspend(struct usb_interface *intf, pm_message_t message)
> +{
> +       struct usbnet *dev = usb_get_intfdata(intf);
> +       struct asix_common_private *priv = dev->driver_priv;
> +
> +       if (priv->suspend)
> +               priv->suspend(dev);
> +
> +       return usbnet_suspend(intf, message);
> +}
> +
> +static void ax88772_restore_phy(struct usbnet *dev)
> +{
> +       struct asix_common_private *priv = dev->driver_priv;
> +
> +       if (priv->presvd_phy_advertise) {
> +               /* Restore Advertisement control reg */
> +               asix_mdio_write_nopm(dev->net, dev->mii.phy_id, MII_ADVERTISE,
> +                                    priv->presvd_phy_advertise);
> +
> +               /* Restore BMCR */
> +               asix_mdio_write_nopm(dev->net, dev->mii.phy_id, MII_BMCR,
> +                                    priv->presvd_phy_bmcr);
> +
> +               priv->presvd_phy_advertise = 0;
> +               priv->presvd_phy_bmcr = 0;
> +       }
> +}
> +
> +static void ax88772_resume(struct usbnet *dev)
> +{
> +       int i;
> +
> +       for (i = 0; i < 3; i++)
> +               if (!ax88772_hw_reset(dev, 1))
> +                       break;
> +       ax88772_restore_phy(dev);
> +}
> +
> +static void ax88772a_resume(struct usbnet *dev)
> +{
> +       int i;
> +
> +       for (i = 0; i < 3; i++) {
> +               if (!ax88772a_hw_reset(dev, 1))
> +                       break;
> +       }
> +
> +       ax88772_restore_phy(dev);
> +}
> +
> +static int asix_resume(struct usb_interface *intf)
> +{
> +       struct usbnet *dev = usb_get_intfdata(intf);
> +       struct asix_common_private *priv = dev->driver_priv;
> +
> +       if (priv->resume)
> +               priv->resume(dev);
> +
> +       return usbnet_resume(intf);
> +}
> +
>  static int ax88772_bind(struct usbnet *dev, struct usb_interface *intf)
>  {
> -       int ret, embd_phy, i;
> -       u8 buf[ETH_ALEN];
> +       int ret, i;
> +       u8 buf[ETH_ALEN], chipcode = 0;
>         u32 phyid;
> +       struct asix_common_private *priv;
>
>         usbnet_get_endpoints(dev,intf);
>
> @@ -427,13 +626,13 @@ static int ax88772_bind(struct usbnet *dev, struct usb_interface *intf)
>         if (dev->driver_info->data & FLAG_EEPROM_MAC) {
>                 for (i = 0; i < (ETH_ALEN >> 1); i++) {
>                         ret = asix_read_cmd(dev, AX_CMD_READ_EEPROM, 0x04 + i,
> -                                       0, 2, buf + i * 2);
> +                                           0, 2, buf + i * 2, 0);
>                         if (ret < 0)
>                                 break;
>                 }
>         } else {
>                 ret = asix_read_cmd(dev, AX_CMD_READ_NODE_ID,
> -                               0, 0, ETH_ALEN, buf);
> +                               0, 0, ETH_ALEN, buf, 0);
>         }
>
>         if (ret < 0) {
> @@ -456,16 +655,11 @@ static int ax88772_bind(struct usbnet *dev, struct usb_interface *intf)
>         dev->net->needed_headroom = 4; /* cf asix_tx_fixup() */
>         dev->net->needed_tailroom = 4; /* cf asix_tx_fixup() */
>
> -       embd_phy = ((dev->mii.phy_id & 0x1f) == 0x10 ? 1 : 0);
> +       asix_read_cmd(dev, AX_CMD_STATMNGSTS_REG, 0, 0, 1, &chipcode, 0);
> +       chipcode &= AX_CHIPCODE_MASK;
>
> -       /* Reset the PHY to normal operation mode */
> -       ret = asix_write_cmd(dev, AX_CMD_SW_PHY_SELECT, embd_phy, 0, 0, NULL);
> -       if (ret < 0) {
> -               netdev_dbg(dev->net, "Select PHY #1 failed: %d\n", ret);
> -               return ret;
> -       }
> -
> -       ax88772_reset(dev);
> +       (chipcode == AX_AX88772_CHIPCODE) ? ax88772_hw_reset(dev, 0) :
> +                                           ax88772a_hw_reset(dev, 0);
>
>         /* Read PHYID register *AFTER* the PHY was reset properly */
>         phyid = asix_get_phyid(dev);
> @@ -482,6 +676,18 @@ static int ax88772_bind(struct usbnet *dev, struct usb_interface *intf)
>         if (!dev->driver_priv)
>                 return -ENOMEM;
>
> +       priv = dev->driver_priv;
> +
> +       priv->presvd_phy_bmcr = 0;
> +       priv->presvd_phy_advertise = 0;
> +       if (chipcode == AX_AX88772_CHIPCODE) {
> +               priv->resume = ax88772_resume;
> +               priv->suspend = ax88772_suspend;
> +       } else {
> +               priv->resume = ax88772a_resume;
> +               priv->suspend = ax88772_suspend;
> +       }
> +
>         return 0;
>  }
>
> @@ -593,12 +799,12 @@ static int ax88178_reset(struct usbnet *dev)
>         int gpio0 = 0;
>         u32 phyid;
>
> -       asix_read_cmd(dev, AX_CMD_READ_GPIOS, 0, 0, 1, &status);
> +       asix_read_cmd(dev, AX_CMD_READ_GPIOS, 0, 0, 1, &status, 0);
>         netdev_dbg(dev->net, "GPIO Status: 0x%04x\n", status);
>
> -       asix_write_cmd(dev, AX_CMD_WRITE_ENABLE, 0, 0, 0, NULL);
> -       asix_read_cmd(dev, AX_CMD_READ_EEPROM, 0x0017, 0, 2, &eeprom);
> -       asix_write_cmd(dev, AX_CMD_WRITE_DISABLE, 0, 0, 0, NULL);
> +       asix_write_cmd(dev, AX_CMD_WRITE_ENABLE, 0, 0, 0, NULL, 0);
> +       asix_read_cmd(dev, AX_CMD_READ_EEPROM, 0x0017, 0, 2, &eeprom, 0);
> +       asix_write_cmd(dev, AX_CMD_WRITE_DISABLE, 0, 0, 0, NULL, 0);
>
>         netdev_dbg(dev->net, "EEPROM index 0x17 is 0x%04x\n", eeprom);
>
> @@ -614,15 +820,16 @@ static int ax88178_reset(struct usbnet *dev)
>         netdev_dbg(dev->net, "GPIO0: %d, PhyMode: %d\n", gpio0, data->phymode);
>
>         /* Power up external GigaPHY through AX88178 GPIO pin */
> -       asix_write_gpio(dev, AX_GPIO_RSE | AX_GPIO_GPO_1 | AX_GPIO_GPO1EN, 40);
> +       asix_write_gpio(dev, AX_GPIO_RSE | AX_GPIO_GPO_1 |
> +                       AX_GPIO_GPO1EN, 40, 0);
>         if ((le16_to_cpu(eeprom) >> 8) != 1) {
> -               asix_write_gpio(dev, 0x003c, 30);
> -               asix_write_gpio(dev, 0x001c, 300);
> -               asix_write_gpio(dev, 0x003c, 30);
> +               asix_write_gpio(dev, 0x003c, 30, 0);
> +               asix_write_gpio(dev, 0x001c, 300, 0);
> +               asix_write_gpio(dev, 0x003c, 30, 0);
>         } else {
>                 netdev_dbg(dev->net, "gpio phymode == 1 path\n");
> -               asix_write_gpio(dev, AX_GPIO_GPO1EN, 30);
> -               asix_write_gpio(dev, AX_GPIO_GPO1EN | AX_GPIO_GPO_1, 30);
> +               asix_write_gpio(dev, AX_GPIO_GPO1EN, 30, 0);
> +               asix_write_gpio(dev, AX_GPIO_GPO1EN | AX_GPIO_GPO_1, 30, 0);
>         }
>
>         /* Read PHYID register *AFTER* powering up PHY */
> @@ -630,15 +837,15 @@ static int ax88178_reset(struct usbnet *dev)
>         netdev_dbg(dev->net, "PHYID=0x%08x\n", phyid);
>
>         /* Set AX88178 to enable MII/GMII/RGMII interface for external PHY */
> -       asix_write_cmd(dev, AX_CMD_SW_PHY_SELECT, 0, 0, 0, NULL);
> +       asix_write_cmd(dev, AX_CMD_SW_PHY_SELECT, 0, 0, 0, NULL, 0);
>
> -       asix_sw_reset(dev, 0);
> +       asix_sw_reset(dev, 0, 0);
>         msleep(150);
>
> -       asix_sw_reset(dev, AX_SWRESET_PRL | AX_SWRESET_IPPD);
> +       asix_sw_reset(dev, AX_SWRESET_PRL | AX_SWRESET_IPPD, 0);
>         msleep(150);
>
> -       asix_write_rx_ctl(dev, 0);
> +       asix_write_rx_ctl(dev, 0, 0);
>
>         if (data->phymode == PHY_MODE_MARVELL) {
>                 marvell_phy_init(dev);
> @@ -655,18 +862,18 @@ static int ax88178_reset(struct usbnet *dev)
>
>         mii_nway_restart(&dev->mii);
>
> -       ret = asix_write_medium_mode(dev, AX88178_MEDIUM_DEFAULT);
> +       ret = asix_write_medium_mode(dev, AX88178_MEDIUM_DEFAULT, 0);
>         if (ret < 0)
>                 return ret;
>
>         /* Rewrite MAC address */
>         memcpy(data->mac_addr, dev->net->dev_addr, ETH_ALEN);
>         ret = asix_write_cmd(dev, AX_CMD_WRITE_NODE_ID, 0, 0, ETH_ALEN,
> -                                                       data->mac_addr);
> +                                                       data->mac_addr, 0);
>         if (ret < 0)
>                 return ret;
>
> -       ret = asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL);
> +       ret = asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL, 0);
>         if (ret < 0)
>                 return ret;
>
> @@ -704,7 +911,7 @@ static int ax88178_link_reset(struct usbnet *dev)
>         netdev_dbg(dev->net, "ax88178_link_reset() speed: %u duplex: %d setting mode to 0x%04x\n",
>                    speed, ecmd.duplex, mode);
>
> -       asix_write_medium_mode(dev, mode);
> +       asix_write_medium_mode(dev, mode, 0);
>
>         if (data->phymode == PHY_MODE_MARVELL && data->ledmode)
>                 marvell_led_status(dev, speed);
> @@ -733,15 +940,15 @@ static void ax88178_set_mfb(struct usbnet *dev)
>                 mfb = AX_RX_CTL_MFB_16384;
>         }
>
> -       rxctl = asix_read_rx_ctl(dev);
> -       asix_write_rx_ctl(dev, (rxctl & ~AX_RX_CTL_MFB_16384) | mfb);
> +       rxctl = asix_read_rx_ctl(dev, 0);
> +       asix_write_rx_ctl(dev, (rxctl & ~AX_RX_CTL_MFB_16384) | mfb, 0);
>
> -       medium = asix_read_medium_status(dev);
> +       medium = asix_read_medium_status(dev, 0);
>         if (dev->net->mtu > 1500)
>                 medium |= AX_MEDIUM_JFE;
>         else
>                 medium &= ~AX_MEDIUM_JFE;
> -       asix_write_medium_mode(dev, medium);
> +       asix_write_medium_mode(dev, medium, 0);
>
>         if (dev->rx_urb_size > old_rx_urb_size)
>                 usbnet_unlink_rx_urbs(dev);
> @@ -790,7 +997,7 @@ static int ax88178_bind(struct usbnet *dev, struct usb_interface *intf)
>         usbnet_get_endpoints(dev,intf);
>
>         /* Get the MAC address */
> -       ret = asix_read_cmd(dev, AX_CMD_READ_NODE_ID, 0, 0, ETH_ALEN, buf);
> +       ret = asix_read_cmd(dev, AX_CMD_READ_NODE_ID, 0, 0, ETH_ALEN, buf, 0);
>         if (ret < 0) {
>                 netdev_dbg(dev->net, "Failed to read MAC address: %d\n", ret);
>                 return ret;
> @@ -811,10 +1018,10 @@ static int ax88178_bind(struct usbnet *dev, struct usb_interface *intf)
>         dev->net->ethtool_ops = &ax88178_ethtool_ops;
>
>         /* Blink LEDS so users know driver saw dongle */
> -       asix_sw_reset(dev, 0);
> +       asix_sw_reset(dev, 0, 0);
>         msleep(150);
>
> -       asix_sw_reset(dev, AX_SWRESET_PRL | AX_SWRESET_IPPD);
> +       asix_sw_reset(dev, AX_SWRESET_PRL | AX_SWRESET_IPPD, 0);
>         msleep(150);
>
>         /* Asix framing packs multiple eth frames into a 2K usb bulk transfer */
> @@ -877,7 +1084,7 @@ static const struct driver_info ax88772_info = {
>         .unbind = ax88772_unbind,
>         .status = asix_status,
>         .link_reset = ax88772_link_reset,
> -       .reset = ax88772_link_reset,
> +       .reset = ax88772_reset,
>         .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_LINK_INTR | FLAG_MULTI_PACKET,
>         .rx_fixup = asix_rx_fixup_common,
>         .tx_fixup = asix_tx_fixup,
> @@ -1005,7 +1212,7 @@ static const struct usb_device_id products [] = {
>  }, {
>         // Lenovo U2L100P 10/100
>         USB_DEVICE (0x17ef, 0x7203),
> -       .driver_info = (unsigned long) &ax88772_info,
> +       .driver_info = (unsigned long)&ax88772b_info,
>  }, {
>         // ASIX AX88772B 10/100
>         USB_DEVICE (0x0b95, 0x772b),
> @@ -1073,7 +1280,7 @@ static const struct usb_device_id products [] = {
>  }, {
>         // Asus USB Ethernet Adapter
>         USB_DEVICE (0x0b95, 0x7e2b),
> -       .driver_info = (unsigned long) &ax88772_info,
> +       .driver_info = (unsigned long)&ax88772b_info,
>  }, {
>         /* ASIX 88172a demo board */
>         USB_DEVICE(0x0b95, 0x172a),
> @@ -1095,8 +1302,8 @@ static struct usb_driver asix_driver = {
>         .name =         DRIVER_NAME,
>         .id_table =     products,
>         .probe =        usbnet_probe,
> -       .suspend =      usbnet_suspend,
> -       .resume =       usbnet_resume,
> +       .suspend =      asix_suspend,
> +       .resume =       asix_resume,
>         .disconnect =   usbnet_disconnect,
>         .supports_autosuspend = 1,
>         .disable_hub_initiated_lpm = 1,
> diff --git a/drivers/net/usb/ax88172a.c b/drivers/net/usb/ax88172a.c
> index 5f18fcb..c2394f1 100644
> --- a/drivers/net/usb/ax88172a.c
> +++ b/drivers/net/usb/ax88172a.c
> @@ -81,7 +81,7 @@ static void ax88172a_adjust_link(struct net_device *netdev)
>         }
>
>         if (mode != priv->oldmode) {
> -               asix_write_medium_mode(dev, mode);
> +               asix_write_medium_mode(dev, mode, 0);
>                 priv->oldmode = mode;
>                 netdev_dbg(netdev, "speed %u duplex %d, setting mode to 0x%04x\n",
>                            phydev->speed, phydev->duplex, mode);
> @@ -205,18 +205,19 @@ static int ax88172a_reset_phy(struct usbnet *dev, int embd_phy)
>  {
>         int ret;
>
> -       ret = asix_sw_reset(dev, AX_SWRESET_IPPD);
> +       ret = asix_sw_reset(dev, AX_SWRESET_IPPD, 0);
>         if (ret < 0)
>                 goto err;
>
>         msleep(150);
> -       ret = asix_sw_reset(dev, AX_SWRESET_CLEAR);
> +       ret = asix_sw_reset(dev, AX_SWRESET_CLEAR, 0);
>         if (ret < 0)
>                 goto err;
>
>         msleep(150);
>
> -       ret = asix_sw_reset(dev, embd_phy ? AX_SWRESET_IPRL : AX_SWRESET_IPPD);
> +       ret = asix_sw_reset(dev, embd_phy ? AX_SWRESET_IPRL : AX_SWRESET_IPPD,
> +                           0);
>         if (ret < 0)
>                 goto err;
>
> @@ -242,7 +243,7 @@ static int ax88172a_bind(struct usbnet *dev, struct usb_interface *intf)
>         dev->driver_priv = priv;
>
>         /* Get the MAC address */
> -       ret = asix_read_cmd(dev, AX_CMD_READ_NODE_ID, 0, 0, ETH_ALEN, buf);
> +       ret = asix_read_cmd(dev, AX_CMD_READ_NODE_ID, 0, 0, ETH_ALEN, buf, 0);
>         if (ret < 0) {
>                 netdev_err(dev->net, "Failed to read MAC address: %d\n", ret);
>                 goto free;
> @@ -253,7 +254,7 @@ static int ax88172a_bind(struct usbnet *dev, struct usb_interface *intf)
>         dev->net->ethtool_ops = &ax88172a_ethtool_ops;
>
>         /* are we using the internal or the external phy? */
> -       ret = asix_read_cmd(dev, AX_CMD_SW_PHY_STATUS, 0, 0, 1, buf);
> +       ret = asix_read_cmd(dev, AX_CMD_SW_PHY_STATUS, 0, 0, 1, buf, 0);
>         if (ret < 0) {
>                 netdev_err(dev->net, "Failed to read software interface selection register: %d\n",
>                            ret);
> @@ -332,20 +333,20 @@ static int ax88172a_reset(struct usbnet *dev)
>         ax88172a_reset_phy(dev, priv->use_embdphy);
>
>         msleep(150);
> -       rx_ctl = asix_read_rx_ctl(dev);
> +       rx_ctl = asix_read_rx_ctl(dev, 0);
>         netdev_dbg(dev->net, "RX_CTL is 0x%04x after software reset\n", rx_ctl);
> -       ret = asix_write_rx_ctl(dev, 0x0000);
> +       ret = asix_write_rx_ctl(dev, 0x0000, 0);
>         if (ret < 0)
>                 goto out;
>
> -       rx_ctl = asix_read_rx_ctl(dev);
> +       rx_ctl = asix_read_rx_ctl(dev, 0);
>         netdev_dbg(dev->net, "RX_CTL is 0x%04x setting to 0x0000\n", rx_ctl);
>
>         msleep(150);
>
>         ret = asix_write_cmd(dev, AX_CMD_WRITE_IPG0,
>                              AX88772_IPG0_DEFAULT | AX88772_IPG1_DEFAULT,
> -                            AX88772_IPG2_DEFAULT, 0, NULL);
> +                            AX88772_IPG2_DEFAULT, 0, NULL, 0);
>         if (ret < 0) {
>                 netdev_err(dev->net, "Write IPG,IPG1,IPG2 failed: %d\n", ret);
>                 goto out;
> @@ -354,20 +355,20 @@ static int ax88172a_reset(struct usbnet *dev)
>         /* Rewrite MAC address */
>         memcpy(data->mac_addr, dev->net->dev_addr, ETH_ALEN);
>         ret = asix_write_cmd(dev, AX_CMD_WRITE_NODE_ID, 0, 0, ETH_ALEN,
> -                            data->mac_addr);
> +                            data->mac_addr, 0);
>         if (ret < 0)
>                 goto out;
>
>         /* Set RX_CTL to default values with 2k buffer, and enable cactus */
> -       ret = asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL);
> +       ret = asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL, 0);
>         if (ret < 0)
>                 goto out;
>
> -       rx_ctl = asix_read_rx_ctl(dev);
> +       rx_ctl = asix_read_rx_ctl(dev, 0);
>         netdev_dbg(dev->net, "RX_CTL is 0x%04x after all initializations\n",
>                    rx_ctl);
>
> -       rx_ctl = asix_read_medium_status(dev);
> +       rx_ctl = asix_read_medium_status(dev, 0);
>         netdev_dbg(dev->net, "Medium Status is 0x%04x after all initializations\n",
>                    rx_ctl);
>
> --
> 2.7.4
>

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH 2/3] net: asix: Avoid looping when the device is disconnected
  2016-07-25 17:40 ` [PATCH 2/3] net: asix: Avoid looping when the device is disconnected robert.foss
@ 2016-07-25 21:37   ` Grant Grundler
  0 siblings, 0 replies; 9+ messages in thread
From: Grant Grundler @ 2016-07-25 21:37 UTC (permalink / raw)
  To: robert.foss
  Cc: David Miller, Grant Grundler, Mark_Craske, Dean_Jenkins,
	Vincent Palatin, ivecera, linux, WK TSAI, changchias, linux-usb,
	netdev, LKML, enric.balletbo, tomeu.vizoso

On Mon, Jul 25, 2016 at 10:40 AM,  <robert.foss@collabora.com> wrote:
> From: Vincent Palatin <vpalatin@chromium.org>
>
> Check the answers from the USB stack and avoid re-sending multiple times
> the request if the device has disappeared.
>
> Signed-off-by: Vincent Palatin <vpalatin@chromium.org>

The original chromium.org code review:
    https://chromium-review.googlesource.com/255931

resulted in some additional commit message meta-data that would be
nice to include in the kernel.org commit message:
    Reviewed-by: Julius Werner <jwerner@chromium.org>
    Reviewed-by: Douglas Anderson <dianders@chromium.org>
    Tested-by: Douglas Anderson <dianders@chromium.org>

And yes, patch LGTM too. :)

cheers,
grant

> ---
>  drivers/net/usb/asix_common.c  | 56 +++++++++++++++++++++++++++++++++---------
>  drivers/net/usb/asix_devices.c |  2 ++
>  2 files changed, 46 insertions(+), 12 deletions(-)
>
> diff --git a/drivers/net/usb/asix_common.c b/drivers/net/usb/asix_common.c
> index f0ccf76..0339560 100644
> --- a/drivers/net/usb/asix_common.c
> +++ b/drivers/net/usb/asix_common.c
> @@ -428,13 +428,21 @@ int asix_mdio_read(struct net_device *netdev, int phy_id, int loc)
>         __le16 res;
>         u8 smsr;
>         int i = 0;
> +       int ret;
>
>         mutex_lock(&dev->phy_mutex);
>         do {
> -               asix_set_sw_mii(dev, 0);
> +               ret = asix_set_sw_mii(dev, 0);
> +               if (ret == -ENODEV)
> +                       break;
>                 usleep_range(1000, 1100);
> -               asix_read_cmd(dev, AX_CMD_STATMNGSTS_REG, 0, 0, 1, &smsr, 0);
> -       } while (!(smsr & AX_HOST_EN) && (i++ < 30));
> +               ret = asix_read_cmd(dev, AX_CMD_STATMNGSTS_REG,
> +                                   0, 0, 1, &smsr, 0);
> +       } while (!(smsr & AX_HOST_EN) && (i++ < 30) && (ret != -ENODEV));
> +       if (ret == -ENODEV) {
> +               mutex_unlock(&dev->phy_mutex);
> +               return ret;
> +       }
>
>         asix_read_cmd(dev, AX_CMD_READ_MII_REG, phy_id,
>                                 (__u16)loc, 2, &res, 0);
> @@ -453,16 +461,24 @@ void asix_mdio_write(struct net_device *netdev, int phy_id, int loc, int val)
>         __le16 res = cpu_to_le16(val);
>         u8 smsr;
>         int i = 0;
> +       int ret;
>
>         netdev_dbg(dev->net, "asix_mdio_write() phy_id=0x%02x, loc=0x%02x, val=0x%04x\n",
>                         phy_id, loc, val);
>
>         mutex_lock(&dev->phy_mutex);
>         do {
> -               asix_set_sw_mii(dev, 0);
> +               ret = asix_set_sw_mii(dev, 0);
> +               if (ret == -ENODEV)
> +                       break;
>                 usleep_range(1000, 1100);
> -               asix_read_cmd(dev, AX_CMD_STATMNGSTS_REG, 0, 0, 1, &smsr, 0);
> -       } while (!(smsr & AX_HOST_EN) && (i++ < 30));
> +               ret = asix_read_cmd(dev, AX_CMD_STATMNGSTS_REG,
> +                                   0, 0, 1, &smsr, 0);
> +       } while (!(smsr & AX_HOST_EN) && (i++ < 30) && (ret != -ENODEV));
> +       if (ret == -ENODEV) {
> +               mutex_unlock(&dev->phy_mutex);
> +               return;
> +       }
>
>         asix_write_cmd(dev, AX_CMD_WRITE_MII_REG, phy_id,
>                        (__u16)loc, 2, &res, 0);
> @@ -476,13 +492,21 @@ int asix_mdio_read_nopm(struct net_device *netdev, int phy_id, int loc)
>         __le16 res;
>         u8 smsr;
>         int i = 0;
> +       int ret;
>
>         mutex_lock(&dev->phy_mutex);
>         do {
> -               asix_set_sw_mii(dev, 1);
> +               ret = asix_set_sw_mii(dev, 1);
> +               if (ret == -ENODEV)
> +                       break;
>                 usleep_range(1000, 1100);
> -               asix_read_cmd(dev, AX_CMD_STATMNGSTS_REG, 0, 0, 1, &smsr, 1);
> -       } while (!(smsr & AX_HOST_EN) && (i++ < 30));
> +               ret = asix_read_cmd(dev, AX_CMD_STATMNGSTS_REG,
> +                                   0, 0, 1, &smsr, 1);
> +       } while (!(smsr & AX_HOST_EN) && (i++ < 30) && (ret != -ENODEV));
> +       if (ret == -ENODEV) {
> +               mutex_unlock(&dev->phy_mutex);
> +               return ret;
> +       }
>
>         asix_read_cmd(dev, AX_CMD_READ_MII_REG, phy_id,
>                       (__u16)loc, 2, &res, 1);
> @@ -502,16 +526,24 @@ asix_mdio_write_nopm(struct net_device *netdev, int phy_id, int loc, int val)
>         __le16 res = cpu_to_le16(val);
>         u8 smsr;
>         int i = 0;
> +       int ret;
>
>         netdev_dbg(dev->net, "asix_mdio_write() phy_id=0x%02x, loc=0x%02x, val=0x%04x\n",
>                         phy_id, loc, val);
>
>         mutex_lock(&dev->phy_mutex);
>         do {
> -               asix_set_sw_mii(dev, 1);
> +               ret = asix_set_sw_mii(dev, 1);
> +               if (ret == -ENODEV)
> +                       break;
>                 usleep_range(1000, 1100);
> -               asix_read_cmd(dev, AX_CMD_STATMNGSTS_REG, 0, 0, 1, &smsr, 1);
> -       } while (!(smsr & AX_HOST_EN) && (i++ < 30));
> +               ret = asix_read_cmd(dev, AX_CMD_STATMNGSTS_REG,
> +                                   0, 0, 1, &smsr, 1);
> +       } while (!(smsr & AX_HOST_EN) && (i++ < 30) && (ret != -ENODEV));
> +       if (ret == -ENODEV) {
> +               mutex_unlock(&dev->phy_mutex);
> +               return;
> +       }
>
>         asix_write_cmd(dev, AX_CMD_WRITE_MII_REG, phy_id,
>                        (__u16)loc, 2, &res, 1);
> diff --git a/drivers/net/usb/asix_devices.c b/drivers/net/usb/asix_devices.c
> index aaa4290..ebeb730 100644
> --- a/drivers/net/usb/asix_devices.c
> +++ b/drivers/net/usb/asix_devices.c
> @@ -79,6 +79,8 @@ static u32 asix_get_phyid(struct usbnet *dev)
>         /* Poll for the rare case the FW or phy isn't ready yet.  */
>         for (i = 0; i < 100; i++) {
>                 phy_reg = asix_mdio_read(dev->net, dev->mii.phy_id, MII_PHYSID1);
> +               if (phy_reg < 0)
> +                       return 0;
>                 if (phy_reg != 0 && phy_reg != 0xFFFF)
>                         break;
>                 mdelay(1);
> --
> 2.7.4
>

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH 3/3] net: asix: Fix AX88772x resume failures
  2016-07-25 17:40 ` [PATCH 3/3] net: asix: Fix AX88772x resume failures robert.foss
@ 2016-07-25 21:45   ` Grant Grundler
  0 siblings, 0 replies; 9+ messages in thread
From: Grant Grundler @ 2016-07-25 21:45 UTC (permalink / raw)
  To: robert.foss
  Cc: David Miller, Grant Grundler, Mark_Craske, Dean_Jenkins,
	Vincent Palatin, ivecera, linux, WK TSAI, changchias, linux-usb,
	netdev, LKML, enric.balletbo, tomeu.vizoso

Robert,
This patch content LGTM but isn't including the correct "meta data"
regarding it's origin.

On Mon, Jul 25, 2016 at 10:40 AM,  <robert.foss@collabora.com> wrote:
> From: WK Tsai <wk.tsai@nvidia.com>

WK Tsai is not the author of this patch.
I believe Allan Chou <allan@asix.com.tw> is the author.

> The change fixes AX88772x resume failure by
> - Restore incorrect AX88772A PHY registers when resetting
> - Need to stop MAC operation when suspending
> - Need to restart MII when restoring PHY
>
> Signed-off-by: WK Tsai <wk.tsai@nvidia.com>

This patch was applied to four different chromium.org kernel branches:
   https://chromium-review.googlesource.com/#/q/Fix+AX88772x+resume+failures

And has substantial meta data regarding author and reviewers:
  (summarized from https://chromium-review.googlesource.com/#/c/314520/)

Signed-off-by: Allan Chou <allan@asix.com.tw>
Signed-off-by: WK Tsai <wk.tsai@nvidia.com>
Tested-by: Grant Grundler <grundler@chromium.org>
Reviewed-by: Wang-Kai Tsai <gis555t@gmail.com>
Reviewed-by: Mark Kuo <mkuo@nvidia.com>
Reviewed-by: Grant Grundler <grundler@chromium.org>
Reviewed-by: Allan Chou <allanchou1357@gmail.com>
Reviewed-by: Vincent Palatin <vpalatin@chromium.org>

cheers,
grant

> ---
>  drivers/net/usb/asix_devices.c | 47 +++++++++++++++++++++++++++++++++++++++++-
>  1 file changed, 46 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/net/usb/asix_devices.c b/drivers/net/usb/asix_devices.c
> index ebeb730..083dc2e 100644
> --- a/drivers/net/usb/asix_devices.c
> +++ b/drivers/net/usb/asix_devices.c
> @@ -35,6 +35,15 @@
>
>  #define        PHY_MODE_RTL8211CL      0x000C
>
> +#define AX88772A_PHY14H                0x14
> +#define AX88772A_PHY14H_DEFAULT 0x442C
> +
> +#define AX88772A_PHY15H                0x15
> +#define AX88772A_PHY15H_DEFAULT 0x03C8
> +
> +#define AX88772A_PHY16H                0x16
> +#define AX88772A_PHY16H_DEFAULT 0x4044
> +
>  struct ax88172_int_data {
>         __le16 res1;
>         u8 link;
> @@ -424,7 +433,7 @@ static int ax88772a_hw_reset(struct usbnet *dev, int in_pm)
>  {
>         struct asix_data *data = (struct asix_data *)&dev->data;
>         int ret, embd_phy;
> -       u16 rx_ctl;
> +       u16 rx_ctl, phy14h, phy15h, phy16h;
>         u8 chipcode = 0;
>
>         ret = asix_write_gpio(dev, AX_GPIO_RSE, 5, in_pm);
> @@ -482,6 +491,32 @@ static int ax88772a_hw_reset(struct usbnet *dev, int in_pm)
>                                    ret);
>                         goto out;
>                 }
> +       } else if ((chipcode & AX_CHIPCODE_MASK) == AX_AX88772A_CHIPCODE) {
> +               /* Check if the PHY registers have default settings */
> +               phy14h = asix_mdio_read_nopm(dev->net, dev->mii.phy_id,
> +                                            AX88772A_PHY14H);
> +               phy15h = asix_mdio_read_nopm(dev->net, dev->mii.phy_id,
> +                                            AX88772A_PHY15H);
> +               phy16h = asix_mdio_read_nopm(dev->net, dev->mii.phy_id,
> +                                            AX88772A_PHY16H);
> +
> +               netdev_dbg(dev->net,
> +                          "772a_hw_reset: MR20=0x%x MR21=0x%x MR22=0x%x\n",
> +                          phy14h, phy15h, phy16h);
> +
> +               /* Restore PHY registers default setting if not */
> +               if (phy14h != AX88772A_PHY14H_DEFAULT)
> +                       asix_mdio_write_nopm(dev->net, dev->mii.phy_id,
> +                                            AX88772A_PHY14H,
> +                                            AX88772A_PHY14H_DEFAULT);
> +               if (phy15h != AX88772A_PHY15H_DEFAULT)
> +                       asix_mdio_write_nopm(dev->net, dev->mii.phy_id,
> +                                            AX88772A_PHY15H,
> +                                            AX88772A_PHY15H_DEFAULT);
> +               if (phy16h != AX88772A_PHY16H_DEFAULT)
> +                       asix_mdio_write_nopm(dev->net, dev->mii.phy_id,
> +                                            AX88772A_PHY16H,
> +                                            AX88772A_PHY16H_DEFAULT);
>         }
>
>         ret = asix_write_cmd(dev, AX_CMD_WRITE_IPG0,
> @@ -543,6 +578,15 @@ static const struct net_device_ops ax88772_netdev_ops = {
>  static void ax88772_suspend(struct usbnet *dev)
>  {
>         struct asix_common_private *priv = dev->driver_priv;
> +       u16 medium;
> +
> +       /* Stop MAC operation */
> +       medium = asix_read_medium_status(dev, 0);
> +       medium &= ~AX_MEDIUM_RE;
> +       asix_write_medium_mode(dev, medium, 0);
> +
> +       netdev_dbg(dev->net, "ax88772_suspend: medium=0x%04x\n",
> +                  asix_read_medium_status(dev, 0));
>
>         /* Preserve BMCR for restoring */
>         priv->presvd_phy_bmcr =
> @@ -577,6 +621,7 @@ static void ax88772_restore_phy(struct usbnet *dev)
>                 asix_mdio_write_nopm(dev->net, dev->mii.phy_id, MII_BMCR,
>                                      priv->presvd_phy_bmcr);
>
> +               mii_nway_restart(&dev->mii);
>                 priv->presvd_phy_advertise = 0;
>                 priv->presvd_phy_bmcr = 0;
>         }
> --
> 2.7.4
>

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH 1/3] net: asix: Add in_pm parameter
  2016-07-25 17:40 [PATCH 1/3] net: asix: Add in_pm parameter robert.foss
                   ` (2 preceding siblings ...)
  2016-07-25 21:26 ` [PATCH 1/3] net: asix: Add in_pm parameter Grant Grundler
@ 2016-07-26  4:45 ` David Miller
  3 siblings, 0 replies; 9+ messages in thread
From: David Miller @ 2016-07-26  4:45 UTC (permalink / raw)
  To: robert.foss
  Cc: grundler, Mark_Craske, Dean_Jenkins, vpalatin, ivecera, linux,
	wk.tsai, changchias, linux-usb, netdev, linux-kernel,
	enric.balletbo, tomeu.vizoso


Please correct the problems Grant Grundler mentioned in all of these
patches, and resubmit this entire series freshly.

Also, please include a proper "[PATCH 0/3] ..." introduction posting
for the series which explains what this series is about, how it
implements what it is doing, and why it is doing things that way.

Thanks.

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH 1/3] net: asix: Add in_pm parameter
  2016-07-25 21:26 ` [PATCH 1/3] net: asix: Add in_pm parameter Grant Grundler
@ 2016-07-26 21:14   ` Robert Foss
  2016-07-26 21:54     ` Grant Grundler
  0 siblings, 1 reply; 9+ messages in thread
From: Robert Foss @ 2016-07-26 21:14 UTC (permalink / raw)
  To: Grant Grundler
  Cc: David Miller, Mark_Craske, Dean_Jenkins, Vincent Palatin,
	ivecera, linux, WK TSAI, changchias, linux-usb, netdev, LKML,
	enric.balletbo, tomeu.vizoso

>> From: Grant Grundler <grundler@chromium.org>
>
> For the record, I believe I am not the author of these patches.
>
> I believe the original author is
>     Signed-off-by: Freddy Xin <freddy@asix.com.tw>
>
> as recorded in the following code reviews (and testing) that I was
> responsible for:
>     https://chromium-review.googlesource.com/#/q/owner:%22Grant+Grundler%22+status:merged+asix+in_pm
>
> And I would certainly be happy to see this code go upstream and
> expected ASIX would submit this change upstream.
Thanks for the feedback (for this patch and the other ones)!
I'm preparing a v2 and will submit it withing a day or two.

>
>> In order to R/W registers in suspend/resume functions, in_pm flags are
>> added to some functions to determine whether the nopm version of usb
>> functions is called.
>
> FTR, current  drivers/net/usb/ax88179_178a.c uses "in_pm" as well.
Ah! Would you like that to be noted in the commit message?
>
>> Save BMCR and ANAR PHY registers in suspend function and restore them
>> in resume function.
>>
>> Reset HW in resume function to ensure the PHY works correctly.
>>
>> Signed-off-by: Grant Grundler <grundler@chromium.org>
>
> BTW, I have two additional changes for AX88772x support sitting in my
> "needs more work" queue (for quite a while already):
>    https://chromium-review.googlesource.com/#/c/229620/
>    "asix: autoneg will set WRITE_MEDIUM reg"
>
>    https://chromium-review.googlesource.com/#/c/231162/
>    "net: asix: see 802.3 spec for phy reset"
>
> I would certainly approve if _anyone_ picked these up, tested them,
> and then submitted them to netdev.
Unfortunately I am without appropriate hardware at the moment.

>
> cheers,
> grant
>
>> ---
>>  drivers/net/usb/asix.h         |  40 +++--
>>  drivers/net/usb/asix_common.c  | 180 +++++++++++++++-----
>>  drivers/net/usb/asix_devices.c | 373 ++++++++++++++++++++++++++++++++---------
>>  drivers/net/usb/ax88172a.c     |  29 ++--
>>  4 files changed, 472 insertions(+), 150 deletions(-)
>>
>> diff --git a/drivers/net/usb/asix.h b/drivers/net/usb/asix.h
>> index a2d3ea6..d109242 100644
>> --- a/drivers/net/usb/asix.h
>> +++ b/drivers/net/usb/asix.h
>> @@ -46,6 +46,7 @@
>>  #define AX_CMD_SET_SW_MII              0x06
>>  #define AX_CMD_READ_MII_REG            0x07
>>  #define AX_CMD_WRITE_MII_REG           0x08
>> +#define AX_CMD_STATMNGSTS_REG          0x09
>>  #define AX_CMD_SET_HW_MII              0x0a
>>  #define AX_CMD_READ_EEPROM             0x0b
>>  #define AX_CMD_WRITE_EEPROM            0x0c
>> @@ -71,6 +72,17 @@
>>  #define AX_CMD_SW_RESET                        0x20
>>  #define AX_CMD_SW_PHY_STATUS           0x21
>>  #define AX_CMD_SW_PHY_SELECT           0x22
>> +#define AX_QCTCTRL                     0x2A
>> +
>> +#define AX_CHIPCODE_MASK               0x70
>> +#define AX_AX88772_CHIPCODE            0x00
>> +#define AX_AX88772A_CHIPCODE           0x10
>> +#define AX_AX88772B_CHIPCODE           0x20
>> +#define AX_HOST_EN                     0x01
>> +
>> +#define AX_PHYSEL_PSEL                 0x01
>> +#define AX_PHYSEL_SSMII                        0
>> +#define AX_PHYSEL_SSEN                 0x10
>>
>>  #define AX_PHY_SELECT_MASK             (BIT(3) | BIT(2))
>>  #define AX_PHY_SELECT_INTERNAL         0
>> @@ -173,6 +185,10 @@ struct asix_rx_fixup_info {
>>  };
>>
>>  struct asix_common_private {
>> +       void (*resume)(struct usbnet *dev);
>> +       void (*suspend)(struct usbnet *dev);
>> +       u16 presvd_phy_advertise;
>> +       u16 presvd_phy_bmcr;
>>         struct asix_rx_fixup_info rx_fixup_info;
>>  };
>>
>> @@ -182,10 +198,10 @@ extern const struct driver_info ax88172a_info;
>>  #define FLAG_EEPROM_MAC                (1UL << 0)  /* init device MAC from eeprom */
>>
>>  int asix_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,
>> -                 u16 size, void *data);
>> +                 u16 size, void *data, int in_pm);
>>
>>  int asix_write_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,
>> -                  u16 size, void *data);
>> +                  u16 size, void *data, int in_pm);
>>
>>  void asix_write_cmd_async(struct usbnet *dev, u8 cmd, u16 value,
>>                           u16 index, u16 size, void *data);
>> @@ -197,27 +213,31 @@ int asix_rx_fixup_common(struct usbnet *dev, struct sk_buff *skb);
>>  struct sk_buff *asix_tx_fixup(struct usbnet *dev, struct sk_buff *skb,
>>                               gfp_t flags);
>>
>> -int asix_set_sw_mii(struct usbnet *dev);
>> -int asix_set_hw_mii(struct usbnet *dev);
>> +int asix_set_sw_mii(struct usbnet *dev, int in_pm);
>> +int asix_set_hw_mii(struct usbnet *dev, int in_pm);
>>
>>  int asix_read_phy_addr(struct usbnet *dev, int internal);
>>  int asix_get_phy_addr(struct usbnet *dev);
>>
>> -int asix_sw_reset(struct usbnet *dev, u8 flags);
>> +int asix_sw_reset(struct usbnet *dev, u8 flags, int in_pm);
>>
>> -u16 asix_read_rx_ctl(struct usbnet *dev);
>> -int asix_write_rx_ctl(struct usbnet *dev, u16 mode);
>> +u16 asix_read_rx_ctl(struct usbnet *dev, int in_pm);
>> +int asix_write_rx_ctl(struct usbnet *dev, u16 mode, int in_pm);
>>
>> -u16 asix_read_medium_status(struct usbnet *dev);
>> -int asix_write_medium_mode(struct usbnet *dev, u16 mode);
>> +u16 asix_read_medium_status(struct usbnet *dev, int in_pm);
>> +int asix_write_medium_mode(struct usbnet *dev, u16 mode, int in_pm);
>>
>> -int asix_write_gpio(struct usbnet *dev, u16 value, int sleep);
>> +int asix_write_gpio(struct usbnet *dev, u16 value, int sleep, int in_pm);
>>
>>  void asix_set_multicast(struct net_device *net);
>>
>>  int asix_mdio_read(struct net_device *netdev, int phy_id, int loc);
>>  void asix_mdio_write(struct net_device *netdev, int phy_id, int loc, int val);
>>
>> +int asix_mdio_read_nopm(struct net_device *netdev, int phy_id, int loc);
>> +void asix_mdio_write_nopm(struct net_device *netdev, int phy_id, int loc,
>> +                         int val);
>> +
>>  void asix_get_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo);
>>  int asix_set_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo);
>>
>> diff --git a/drivers/net/usb/asix_common.c b/drivers/net/usb/asix_common.c
>> index bd9acff..f0ccf76 100644
>> --- a/drivers/net/usb/asix_common.c
>> +++ b/drivers/net/usb/asix_common.c
>> @@ -22,24 +22,49 @@
>>  #include "asix.h"
>>
>>  int asix_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,
>> -                 u16 size, void *data)
>> +                 u16 size, void *data, int in_pm)
>>  {
>>         int ret;
>> -       ret = usbnet_read_cmd(dev, cmd,
>> -                              USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
>> -                              value, index, data, size);
>> +       int (*fn)(struct usbnet *, u8, u8, u16, u16, void *, u16);
>> +
>> +       BUG_ON(!dev);
>> +
>> +       if (!in_pm)
>> +               fn = usbnet_read_cmd;
>> +       else
>> +               fn = usbnet_read_cmd_nopm;
>> +
>> +       ret = fn(dev, cmd, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
>> +                value, index, data, size);
>> +
>> +       if (unlikely(ret < 0))
>> +               netdev_warn(dev->net, "Failed to read reg index 0x%04x: %d\n",
>> +                           index, ret);
>>
>> -       if (ret != size && ret >= 0)
>> -               return -EINVAL;
>>         return ret;
>>  }
>>
>>  int asix_write_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,
>> -                  u16 size, void *data)
>> +                  u16 size, void *data, int in_pm)
>>  {
>> -       return usbnet_write_cmd(dev, cmd,
>> -                               USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
>> -                               value, index, data, size);
>> +       int ret;
>> +       int (*fn)(struct usbnet *, u8, u8, u16, u16, const void *, u16);
>> +
>> +       BUG_ON(!dev);
>> +
>> +       if (!in_pm)
>> +               fn = usbnet_write_cmd;
>> +       else
>> +               fn = usbnet_write_cmd_nopm;
>> +
>> +       ret = fn(dev, cmd, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
>> +                value, index, data, size);
>> +
>> +       if (unlikely(ret < 0))
>> +               netdev_warn(dev->net, "Failed to write reg index 0x%04x: %d\n",
>> +                           index, ret);
>> +
>> +       return ret;
>>  }
>>
>>  void asix_write_cmd_async(struct usbnet *dev, u8 cmd, u16 value, u16 index,
>> @@ -225,19 +250,20 @@ struct sk_buff *asix_tx_fixup(struct usbnet *dev, struct sk_buff *skb,
>>         return skb;
>>  }
>>
>> -int asix_set_sw_mii(struct usbnet *dev)
>> +int asix_set_sw_mii(struct usbnet *dev, int in_pm)
>>  {
>>         int ret;
>> -       ret = asix_write_cmd(dev, AX_CMD_SET_SW_MII, 0x0000, 0, 0, NULL);
>> +       ret = asix_write_cmd(dev, AX_CMD_SET_SW_MII, 0x0000, 0, 0, NULL, in_pm);
>> +
>>         if (ret < 0)
>>                 netdev_err(dev->net, "Failed to enable software MII access\n");
>>         return ret;
>>  }
>>
>> -int asix_set_hw_mii(struct usbnet *dev)
>> +int asix_set_hw_mii(struct usbnet *dev, int in_pm)
>>  {
>>         int ret;
>> -       ret = asix_write_cmd(dev, AX_CMD_SET_HW_MII, 0x0000, 0, 0, NULL);
>> +       ret = asix_write_cmd(dev, AX_CMD_SET_HW_MII, 0x0000, 0, 0, NULL, in_pm);
>>         if (ret < 0)
>>                 netdev_err(dev->net, "Failed to enable hardware MII access\n");
>>         return ret;
>> @@ -247,7 +273,7 @@ int asix_read_phy_addr(struct usbnet *dev, int internal)
>>  {
>>         int offset = (internal ? 1 : 0);
>>         u8 buf[2];
>> -       int ret = asix_read_cmd(dev, AX_CMD_READ_PHY_ID, 0, 0, 2, buf);
>> +       int ret = asix_read_cmd(dev, AX_CMD_READ_PHY_ID, 0, 0, 2, buf, 0);
>>
>>         netdev_dbg(dev->net, "asix_get_phy_addr()\n");
>>
>> @@ -270,21 +296,21 @@ int asix_get_phy_addr(struct usbnet *dev)
>>  }
>>
>>
>> -int asix_sw_reset(struct usbnet *dev, u8 flags)
>> +int asix_sw_reset(struct usbnet *dev, u8 flags, int in_pm)
>>  {
>>         int ret;
>>
>> -        ret = asix_write_cmd(dev, AX_CMD_SW_RESET, flags, 0, 0, NULL);
>> +       ret = asix_write_cmd(dev, AX_CMD_SW_RESET, flags, 0, 0, NULL, in_pm);
>>         if (ret < 0)
>>                 netdev_err(dev->net, "Failed to send software reset: %02x\n", ret);
>>
>>         return ret;
>>  }
>>
>> -u16 asix_read_rx_ctl(struct usbnet *dev)
>> +u16 asix_read_rx_ctl(struct usbnet *dev, int in_pm)
>>  {
>>         __le16 v;
>> -       int ret = asix_read_cmd(dev, AX_CMD_READ_RX_CTL, 0, 0, 2, &v);
>> +       int ret = asix_read_cmd(dev, AX_CMD_READ_RX_CTL, 0, 0, 2, &v, in_pm);
>>
>>         if (ret < 0) {
>>                 netdev_err(dev->net, "Error reading RX_CTL register: %02x\n", ret);
>> @@ -295,12 +321,12 @@ out:
>>         return ret;
>>  }
>>
>> -int asix_write_rx_ctl(struct usbnet *dev, u16 mode)
>> +int asix_write_rx_ctl(struct usbnet *dev, u16 mode, int in_pm)
>>  {
>>         int ret;
>>
>>         netdev_dbg(dev->net, "asix_write_rx_ctl() - mode = 0x%04x\n", mode);
>> -       ret = asix_write_cmd(dev, AX_CMD_WRITE_RX_CTL, mode, 0, 0, NULL);
>> +       ret = asix_write_cmd(dev, AX_CMD_WRITE_RX_CTL, mode, 0, 0, NULL, in_pm);
>>         if (ret < 0)
>>                 netdev_err(dev->net, "Failed to write RX_CTL mode to 0x%04x: %02x\n",
>>                            mode, ret);
>> @@ -308,10 +334,11 @@ int asix_write_rx_ctl(struct usbnet *dev, u16 mode)
>>         return ret;
>>  }
>>
>> -u16 asix_read_medium_status(struct usbnet *dev)
>> +u16 asix_read_medium_status(struct usbnet *dev, int in_pm)
>>  {
>>         __le16 v;
>> -       int ret = asix_read_cmd(dev, AX_CMD_READ_MEDIUM_STATUS, 0, 0, 2, &v);
>> +       int ret = asix_read_cmd(dev, AX_CMD_READ_MEDIUM_STATUS,
>> +                               0, 0, 2, &v, in_pm);
>>
>>         if (ret < 0) {
>>                 netdev_err(dev->net, "Error reading Medium Status register: %02x\n",
>> @@ -323,12 +350,13 @@ u16 asix_read_medium_status(struct usbnet *dev)
>>
>>  }
>>
>> -int asix_write_medium_mode(struct usbnet *dev, u16 mode)
>> +int asix_write_medium_mode(struct usbnet *dev, u16 mode, int in_pm)
>>  {
>>         int ret;
>>
>>         netdev_dbg(dev->net, "asix_write_medium_mode() - mode = 0x%04x\n", mode);
>> -       ret = asix_write_cmd(dev, AX_CMD_WRITE_MEDIUM_MODE, mode, 0, 0, NULL);
>> +       ret = asix_write_cmd(dev, AX_CMD_WRITE_MEDIUM_MODE,
>> +                            mode, 0, 0, NULL, in_pm);
>>         if (ret < 0)
>>                 netdev_err(dev->net, "Failed to write Medium Mode mode to 0x%04x: %02x\n",
>>                            mode, ret);
>> @@ -336,12 +364,12 @@ int asix_write_medium_mode(struct usbnet *dev, u16 mode)
>>         return ret;
>>  }
>>
>> -int asix_write_gpio(struct usbnet *dev, u16 value, int sleep)
>> +int asix_write_gpio(struct usbnet *dev, u16 value, int sleep, int in_pm)
>>  {
>>         int ret;
>>
>>         netdev_dbg(dev->net, "asix_write_gpio() - value = 0x%04x\n", value);
>> -       ret = asix_write_cmd(dev, AX_CMD_WRITE_GPIOS, value, 0, 0, NULL);
>> +       ret = asix_write_cmd(dev, AX_CMD_WRITE_GPIOS, value, 0, 0, NULL, in_pm);
>>         if (ret < 0)
>>                 netdev_err(dev->net, "Failed to write GPIO value 0x%04x: %02x\n",
>>                            value, ret);
>> @@ -398,16 +426,23 @@ int asix_mdio_read(struct net_device *netdev, int phy_id, int loc)
>>  {
>>         struct usbnet *dev = netdev_priv(netdev);
>>         __le16 res;
>> +       u8 smsr;
>> +       int i = 0;
>>
>>         mutex_lock(&dev->phy_mutex);
>> -       asix_set_sw_mii(dev);
>> +       do {
>> +               asix_set_sw_mii(dev, 0);
>> +               usleep_range(1000, 1100);
>> +               asix_read_cmd(dev, AX_CMD_STATMNGSTS_REG, 0, 0, 1, &smsr, 0);
>> +       } while (!(smsr & AX_HOST_EN) && (i++ < 30));
>> +
>>         asix_read_cmd(dev, AX_CMD_READ_MII_REG, phy_id,
>> -                               (__u16)loc, 2, &res);
>> -       asix_set_hw_mii(dev);
>> +                               (__u16)loc, 2, &res, 0);
>> +       asix_set_hw_mii(dev, 0);
>>         mutex_unlock(&dev->phy_mutex);
>>
>>         netdev_dbg(dev->net, "asix_mdio_read() phy_id=0x%02x, loc=0x%02x, returns=0x%04x\n",
>> -                  phy_id, loc, le16_to_cpu(res));
>> +                       phy_id, loc, le16_to_cpu(res));
>>
>>         return le16_to_cpu(res);
>>  }
>> @@ -416,13 +451,71 @@ void asix_mdio_write(struct net_device *netdev, int phy_id, int loc, int val)
>>  {
>>         struct usbnet *dev = netdev_priv(netdev);
>>         __le16 res = cpu_to_le16(val);
>> +       u8 smsr;
>> +       int i = 0;
>>
>>         netdev_dbg(dev->net, "asix_mdio_write() phy_id=0x%02x, loc=0x%02x, val=0x%04x\n",
>> -                  phy_id, loc, val);
>> +                       phy_id, loc, val);
>> +
>> +       mutex_lock(&dev->phy_mutex);
>> +       do {
>> +               asix_set_sw_mii(dev, 0);
>> +               usleep_range(1000, 1100);
>> +               asix_read_cmd(dev, AX_CMD_STATMNGSTS_REG, 0, 0, 1, &smsr, 0);
>> +       } while (!(smsr & AX_HOST_EN) && (i++ < 30));
>> +
>> +       asix_write_cmd(dev, AX_CMD_WRITE_MII_REG, phy_id,
>> +                      (__u16)loc, 2, &res, 0);
>> +       asix_set_hw_mii(dev, 0);
>> +       mutex_unlock(&dev->phy_mutex);
>> +}
>> +
>> +int asix_mdio_read_nopm(struct net_device *netdev, int phy_id, int loc)
>> +{
>> +       struct usbnet *dev = netdev_priv(netdev);
>> +       __le16 res;
>> +       u8 smsr;
>> +       int i = 0;
>> +
>> +       mutex_lock(&dev->phy_mutex);
>> +       do {
>> +               asix_set_sw_mii(dev, 1);
>> +               usleep_range(1000, 1100);
>> +               asix_read_cmd(dev, AX_CMD_STATMNGSTS_REG, 0, 0, 1, &smsr, 1);
>> +       } while (!(smsr & AX_HOST_EN) && (i++ < 30));
>> +
>> +       asix_read_cmd(dev, AX_CMD_READ_MII_REG, phy_id,
>> +                     (__u16)loc, 2, &res, 1);
>> +       asix_set_hw_mii(dev, 1);
>> +       mutex_unlock(&dev->phy_mutex);
>> +
>> +       netdev_dbg(dev->net, "asix_mdio_read_nopm() phy_id=0x%02x, loc=0x%02x, returns=0x%04x\n",
>> +                       phy_id, loc, le16_to_cpu(res));
>> +
>> +       return le16_to_cpu(res);
>> +}
>> +
>> +void
>> +asix_mdio_write_nopm(struct net_device *netdev, int phy_id, int loc, int val)
>> +{
>> +       struct usbnet *dev = netdev_priv(netdev);
>> +       __le16 res = cpu_to_le16(val);
>> +       u8 smsr;
>> +       int i = 0;
>> +
>> +       netdev_dbg(dev->net, "asix_mdio_write() phy_id=0x%02x, loc=0x%02x, val=0x%04x\n",
>> +                       phy_id, loc, val);
>> +
>>         mutex_lock(&dev->phy_mutex);
>> -       asix_set_sw_mii(dev);
>> -       asix_write_cmd(dev, AX_CMD_WRITE_MII_REG, phy_id, (__u16)loc, 2, &res);
>> -       asix_set_hw_mii(dev);
>> +       do {
>> +               asix_set_sw_mii(dev, 1);
>> +               usleep_range(1000, 1100);
>> +               asix_read_cmd(dev, AX_CMD_STATMNGSTS_REG, 0, 0, 1, &smsr, 1);
>> +       } while (!(smsr & AX_HOST_EN) && (i++ < 30));
>> +
>> +       asix_write_cmd(dev, AX_CMD_WRITE_MII_REG, phy_id,
>> +                      (__u16)loc, 2, &res, 1);
>> +       asix_set_hw_mii(dev, 1);
>>         mutex_unlock(&dev->phy_mutex);
>>  }
>>
>> @@ -431,7 +524,8 @@ void asix_get_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo)
>>         struct usbnet *dev = netdev_priv(net);
>>         u8 opt;
>>
>> -       if (asix_read_cmd(dev, AX_CMD_READ_MONITOR_MODE, 0, 0, 1, &opt) < 0) {
>> +       if (asix_read_cmd(dev, AX_CMD_READ_MONITOR_MODE,
>> +                         0, 0, 1, &opt, 0) < 0) {
>>                 wolinfo->supported = 0;
>>                 wolinfo->wolopts = 0;
>>                 return;
>> @@ -455,7 +549,7 @@ int asix_set_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo)
>>                 opt |= AX_MONITOR_MAGIC;
>>
>>         if (asix_write_cmd(dev, AX_CMD_WRITE_MONITOR_MODE,
>> -                             opt, 0, 0, NULL) < 0)
>> +                             opt, 0, 0, NULL, 0) < 0)
>>                 return -EINVAL;
>>
>>         return 0;
>> @@ -490,7 +584,7 @@ int asix_get_eeprom(struct net_device *net, struct ethtool_eeprom *eeprom,
>>         /* ax8817x returns 2 bytes from eeprom on read */
>>         for (i = first_word; i <= last_word; i++) {
>>                 if (asix_read_cmd(dev, AX_CMD_READ_EEPROM, i, 0, 2,
>> -                                 &(eeprom_buff[i - first_word])) < 0) {
>> +                                 &eeprom_buff[i - first_word], 0) < 0) {
>>                         kfree(eeprom_buff);
>>                         return -EIO;
>>                 }
>> @@ -531,7 +625,7 @@ int asix_set_eeprom(struct net_device *net, struct ethtool_eeprom *eeprom,
>>            the EEPROM */
>>         if (eeprom->offset & 1) {
>>                 ret = asix_read_cmd(dev, AX_CMD_READ_EEPROM, first_word, 0, 2,
>> -                                   &(eeprom_buff[0]));
>> +                                   &eeprom_buff[0], 0);
>>                 if (ret < 0) {
>>                         netdev_err(net, "Failed to read EEPROM at offset 0x%02x.\n", first_word);
>>                         goto free;
>> @@ -540,7 +634,7 @@ int asix_set_eeprom(struct net_device *net, struct ethtool_eeprom *eeprom,
>>
>>         if ((eeprom->offset + eeprom->len) & 1) {
>>                 ret = asix_read_cmd(dev, AX_CMD_READ_EEPROM, last_word, 0, 2,
>> -                                   &(eeprom_buff[last_word - first_word]));
>> +                                   &eeprom_buff[last_word - first_word], 0);
>>                 if (ret < 0) {
>>                         netdev_err(net, "Failed to read EEPROM at offset 0x%02x.\n", last_word);
>>                         goto free;
>> @@ -550,7 +644,7 @@ int asix_set_eeprom(struct net_device *net, struct ethtool_eeprom *eeprom,
>>         memcpy((u8 *)eeprom_buff + (eeprom->offset & 1), data, eeprom->len);
>>
>>         /* write data to EEPROM */
>> -       ret = asix_write_cmd(dev, AX_CMD_WRITE_ENABLE, 0x0000, 0, 0, NULL);
>> +       ret = asix_write_cmd(dev, AX_CMD_WRITE_ENABLE, 0x0000, 0, 0, NULL, 0);
>>         if (ret < 0) {
>>                 netdev_err(net, "Failed to enable EEPROM write\n");
>>                 goto free;
>> @@ -561,7 +655,7 @@ int asix_set_eeprom(struct net_device *net, struct ethtool_eeprom *eeprom,
>>                 netdev_dbg(net, "write to EEPROM at offset 0x%02x, data 0x%04x\n",
>>                            i, eeprom_buff[i - first_word]);
>>                 ret = asix_write_cmd(dev, AX_CMD_WRITE_EEPROM, i,
>> -                                    eeprom_buff[i - first_word], 0, NULL);
>> +                                    eeprom_buff[i - first_word], 0, NULL, 0);
>>                 if (ret < 0) {
>>                         netdev_err(net, "Failed to write EEPROM at offset 0x%02x.\n",
>>                                    i);
>> @@ -570,7 +664,7 @@ int asix_set_eeprom(struct net_device *net, struct ethtool_eeprom *eeprom,
>>                 msleep(20);
>>         }
>>
>> -       ret = asix_write_cmd(dev, AX_CMD_WRITE_DISABLE, 0x0000, 0, 0, NULL);
>> +       ret = asix_write_cmd(dev, AX_CMD_WRITE_DISABLE, 0x0000, 0, 0, NULL, 0);
>>         if (ret < 0) {
>>                 netdev_err(net, "Failed to disable EEPROM write\n");
>>                 goto free;
>> diff --git a/drivers/net/usb/asix_devices.c b/drivers/net/usb/asix_devices.c
>> index 5cabefc..aaa4290 100644
>> --- a/drivers/net/usb/asix_devices.c
>> +++ b/drivers/net/usb/asix_devices.c
>> @@ -184,7 +184,7 @@ static int ax88172_link_reset(struct usbnet *dev)
>>         netdev_dbg(dev->net, "ax88172_link_reset() speed: %u duplex: %d setting mode to 0x%04x\n",
>>                    ethtool_cmd_speed(&ecmd), ecmd.duplex, mode);
>>
>> -       asix_write_medium_mode(dev, mode);
>> +       asix_write_medium_mode(dev, mode, 0);
>>
>>         return 0;
>>  }
>> @@ -213,18 +213,19 @@ static int ax88172_bind(struct usbnet *dev, struct usb_interface *intf)
>>         /* Toggle the GPIOs in a manufacturer/model specific way */
>>         for (i = 2; i >= 0; i--) {
>>                 ret = asix_write_cmd(dev, AX_CMD_WRITE_GPIOS,
>> -                               (gpio_bits >> (i * 8)) & 0xff, 0, 0, NULL);
>> +                               (gpio_bits >> (i * 8)) & 0xff, 0, 0, NULL, 0);
>>                 if (ret < 0)
>>                         goto out;
>>                 msleep(5);
>>         }
>>
>> -       ret = asix_write_rx_ctl(dev, 0x80);
>> +       ret = asix_write_rx_ctl(dev, 0x80, 0);
>>         if (ret < 0)
>>                 goto out;
>>
>>         /* Get the MAC address */
>> -       ret = asix_read_cmd(dev, AX88172_CMD_READ_NODE_ID, 0, 0, ETH_ALEN, buf);
>> +       ret = asix_read_cmd(dev, AX88172_CMD_READ_NODE_ID,
>> +                           0, 0, ETH_ALEN, buf, 0);
>>         if (ret < 0) {
>>                 netdev_dbg(dev->net, "read AX_CMD_READ_NODE_ID failed: %d\n",
>>                            ret);
>> @@ -290,7 +291,7 @@ static int ax88772_link_reset(struct usbnet *dev)
>>         netdev_dbg(dev->net, "ax88772_link_reset() speed: %u duplex: %d setting mode to 0x%04x\n",
>>                    ethtool_cmd_speed(&ecmd), ecmd.duplex, mode);
>>
>> -       asix_write_medium_mode(dev, mode);
>> +       asix_write_medium_mode(dev, mode, 0);
>>
>>         return 0;
>>  }
>> @@ -298,78 +299,192 @@ static int ax88772_link_reset(struct usbnet *dev)
>>  static int ax88772_reset(struct usbnet *dev)
>>  {
>>         struct asix_data *data = (struct asix_data *)&dev->data;
>> +       int ret;
>> +
>> +       /* Rewrite MAC address */
>> +       ether_addr_copy(data->mac_addr, dev->net->dev_addr);
>> +       ret = asix_write_cmd(dev, AX_CMD_WRITE_NODE_ID, 0, 0,
>> +                            ETH_ALEN, data->mac_addr, 0);
>> +       if (ret < 0)
>> +               goto out;
>> +
>> +       /* Set RX_CTL to default values with 2k buffer, and enable cactus */
>> +       ret = asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL, 0);
>> +       if (ret < 0)
>> +               goto out;
>> +
>> +       asix_write_medium_mode(dev, AX88772_MEDIUM_DEFAULT, 0);
>> +       if (ret < 0)
>> +               goto out;
>> +
>> +       return 0;
>> +
>> +out:
>> +       return ret;
>> +}
>> +
>> +static int ax88772_hw_reset(struct usbnet *dev, int in_pm)
>> +{
>> +       struct asix_data *data = (struct asix_data *)&dev->data;
>>         int ret, embd_phy;
>>         u16 rx_ctl;
>>
>> -       ret = asix_write_gpio(dev,
>> -                       AX_GPIO_RSE | AX_GPIO_GPO_2 | AX_GPIO_GPO2EN, 5);
>> +       ret = asix_write_gpio(dev, AX_GPIO_RSE | AX_GPIO_GPO_2 |
>> +                             AX_GPIO_GPO2EN, 5, in_pm);
>>         if (ret < 0)
>>                 goto out;
>>
>> -       embd_phy = ((asix_get_phy_addr(dev) & 0x1f) == 0x10 ? 1 : 0);
>> +       embd_phy = ((dev->mii.phy_id & 0x1f) == 0x10 ? 1 : 0);
>>
>> -       ret = asix_write_cmd(dev, AX_CMD_SW_PHY_SELECT, embd_phy, 0, 0, NULL);
>> +       ret = asix_write_cmd(dev, AX_CMD_SW_PHY_SELECT, embd_phy,
>> +                            0, 0, NULL, in_pm);
>>         if (ret < 0) {
>>                 netdev_dbg(dev->net, "Select PHY #1 failed: %d\n", ret);
>>                 goto out;
>>         }
>>
>> -       ret = asix_sw_reset(dev, AX_SWRESET_IPPD | AX_SWRESET_PRL);
>> -       if (ret < 0)
>> -               goto out;
>> +       if (embd_phy) {
>> +               ret = asix_sw_reset(dev, AX_SWRESET_IPPD, in_pm);
>> +               if (ret < 0)
>> +                       goto out;
>>
>> -       msleep(150);
>> +               usleep_range(10000, 11000);
>>
>> -       ret = asix_sw_reset(dev, AX_SWRESET_CLEAR);
>> -       if (ret < 0)
>> -               goto out;
>> +               ret = asix_sw_reset(dev, AX_SWRESET_CLEAR, in_pm);
>> +               if (ret < 0)
>> +                       goto out;
>>
>> -       msleep(150);
>> +               msleep(60);
>>
>> -       if (embd_phy) {
>> -               ret = asix_sw_reset(dev, AX_SWRESET_IPRL);
>> +               ret = asix_sw_reset(dev, AX_SWRESET_IPRL | AX_SWRESET_PRL,
>> +                                   in_pm);
>>                 if (ret < 0)
>>                         goto out;
>>         } else {
>> -               ret = asix_sw_reset(dev, AX_SWRESET_PRTE);
>> +               ret = asix_sw_reset(dev, AX_SWRESET_IPPD | AX_SWRESET_PRL,
>> +                                   in_pm);
>>                 if (ret < 0)
>>                         goto out;
>>         }
>>
>>         msleep(150);
>> -       rx_ctl = asix_read_rx_ctl(dev);
>> -       netdev_dbg(dev->net, "RX_CTL is 0x%04x after software reset\n", rx_ctl);
>> -       ret = asix_write_rx_ctl(dev, 0x0000);
>> +
>> +       if (in_pm && (!asix_mdio_read_nopm(dev->net, dev->mii.phy_id,
>> +                                          MII_PHYSID1))){
>> +               ret = -EIO;
>> +               goto out;
>> +       }
>> +
>> +       ret = asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL, in_pm);
>> +       if (ret < 0)
>> +               goto out;
>> +
>> +       ret = asix_write_medium_mode(dev, AX88772_MEDIUM_DEFAULT, in_pm);
>>         if (ret < 0)
>>                 goto out;
>>
>> -       rx_ctl = asix_read_rx_ctl(dev);
>> -       netdev_dbg(dev->net, "RX_CTL is 0x%04x setting to 0x0000\n", rx_ctl);
>> +       ret = asix_write_cmd(dev, AX_CMD_WRITE_IPG0,
>> +                            AX88772_IPG0_DEFAULT | AX88772_IPG1_DEFAULT,
>> +                            AX88772_IPG2_DEFAULT, 0, NULL, in_pm);
>> +       if (ret < 0) {
>> +               netdev_dbg(dev->net, "Write IPG,IPG1,IPG2 failed: %d\n", ret);
>> +               goto out;
>> +       }
>>
>> -       ret = asix_sw_reset(dev, AX_SWRESET_PRL);
>> +       /* Rewrite MAC address */
>> +       ether_addr_copy(data->mac_addr, dev->net->dev_addr);
>> +       ret = asix_write_cmd(dev, AX_CMD_WRITE_NODE_ID, 0, 0,
>> +                            ETH_ALEN, data->mac_addr, in_pm);
>>         if (ret < 0)
>>                 goto out;
>>
>> -       msleep(150);
>> +       /* Set RX_CTL to default values with 2k buffer, and enable cactus */
>> +       ret = asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL, in_pm);
>> +       if (ret < 0)
>> +               goto out;
>> +
>> +       rx_ctl = asix_read_rx_ctl(dev, in_pm);
>> +       netdev_dbg(dev->net, "RX_CTL is 0x%04x after all initializations\n",
>> +                  rx_ctl);
>> +
>> +       rx_ctl = asix_read_medium_status(dev, in_pm);
>> +       netdev_dbg(dev->net,
>> +                  "Medium Status is 0x%04x after all initializations\n",
>> +                  rx_ctl);
>> +
>> +       return 0;
>> +
>> +out:
>> +       return ret;
>> +}
>> +
>> +static int ax88772a_hw_reset(struct usbnet *dev, int in_pm)
>> +{
>> +       struct asix_data *data = (struct asix_data *)&dev->data;
>> +       int ret, embd_phy;
>> +       u16 rx_ctl;
>> +       u8 chipcode = 0;
>>
>> -       ret = asix_sw_reset(dev, AX_SWRESET_IPRL | AX_SWRESET_PRL);
>> +       ret = asix_write_gpio(dev, AX_GPIO_RSE, 5, in_pm);
>>         if (ret < 0)
>>                 goto out;
>>
>> -       msleep(150);
>> +       embd_phy = ((dev->mii.phy_id & 0x1f) == 0x10 ? 1 : 0);
>>
>> -       asix_mdio_write(dev->net, dev->mii.phy_id, MII_BMCR, BMCR_RESET);
>> -       asix_mdio_write(dev->net, dev->mii.phy_id, MII_ADVERTISE,
>> -                       ADVERTISE_ALL | ADVERTISE_CSMA);
>> -       mii_nway_restart(&dev->mii);
>> +       ret = asix_write_cmd(dev, AX_CMD_SW_PHY_SELECT, embd_phy |
>> +                            AX_PHYSEL_SSEN, 0, 0, NULL, in_pm);
>> +       if (ret < 0) {
>> +               netdev_dbg(dev->net, "Select PHY #1 failed: %d\n", ret);
>> +               goto out;
>> +       }
>> +       usleep_range(10000, 11000);
>>
>> -       ret = asix_write_medium_mode(dev, AX88772_MEDIUM_DEFAULT);
>> +       ret = asix_sw_reset(dev, AX_SWRESET_IPPD | AX_SWRESET_IPRL, in_pm);
>>         if (ret < 0)
>>                 goto out;
>>
>> +       usleep_range(10000, 11000);
>> +
>> +       ret = asix_sw_reset(dev, AX_SWRESET_IPRL, in_pm);
>> +       if (ret < 0)
>> +               goto out;
>> +
>> +       msleep(160);
>> +
>> +       ret = asix_sw_reset(dev, AX_SWRESET_CLEAR, in_pm);
>> +       if (ret < 0)
>> +               goto out;
>> +
>> +       ret = asix_sw_reset(dev, AX_SWRESET_IPRL, in_pm);
>> +       if (ret < 0)
>> +               goto out;
>> +
>> +       msleep(200);
>> +
>> +       if (in_pm && (!asix_mdio_read_nopm(dev->net, dev->mii.phy_id,
>> +                                          MII_PHYSID1))) {
>> +               ret = -1;
>> +               goto out;
>> +       }
>> +
>> +       ret = asix_read_cmd(dev, AX_CMD_STATMNGSTS_REG, 0,
>> +                           0, 1, &chipcode, in_pm);
>> +       if (ret < 0)
>> +               goto out;
>> +
>> +       if ((chipcode & AX_CHIPCODE_MASK) == AX_AX88772B_CHIPCODE) {
>> +               ret = asix_write_cmd(dev, AX_QCTCTRL, 0x8000, 0x8001,
>> +                                    0, NULL, in_pm);
>> +               if (ret < 0) {
>> +                       netdev_dbg(dev->net, "Write BQ setting failed: %d\n",
>> +                                  ret);
>> +                       goto out;
>> +               }
>> +       }
>> +
>>         ret = asix_write_cmd(dev, AX_CMD_WRITE_IPG0,
>>                                 AX88772_IPG0_DEFAULT | AX88772_IPG1_DEFAULT,
>> -                               AX88772_IPG2_DEFAULT, 0, NULL);
>> +                               AX88772_IPG2_DEFAULT, 0, NULL, in_pm);
>>         if (ret < 0) {
>>                 netdev_dbg(dev->net, "Write IPG,IPG1,IPG2 failed: %d\n", ret);
>>                 goto out;
>> @@ -378,20 +493,29 @@ static int ax88772_reset(struct usbnet *dev)
>>         /* Rewrite MAC address */
>>         memcpy(data->mac_addr, dev->net->dev_addr, ETH_ALEN);
>>         ret = asix_write_cmd(dev, AX_CMD_WRITE_NODE_ID, 0, 0, ETH_ALEN,
>> -                                                       data->mac_addr);
>> +                                                       data->mac_addr, in_pm);
>> +       if (ret < 0)
>> +               goto out;
>> +
>> +       /* Set RX_CTL to default values with 2k buffer, and enable cactus */
>> +       ret = asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL, in_pm);
>>         if (ret < 0)
>>                 goto out;
>>
>> +       ret = asix_write_medium_mode(dev, AX88772_MEDIUM_DEFAULT, in_pm);
>> +       if (ret < 0)
>> +               return ret;
>> +
>>         /* Set RX_CTL to default values with 2k buffer, and enable cactus */
>> -       ret = asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL);
>> +       ret = asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL, in_pm);
>>         if (ret < 0)
>>                 goto out;
>>
>> -       rx_ctl = asix_read_rx_ctl(dev);
>> +       rx_ctl = asix_read_rx_ctl(dev, in_pm);
>>         netdev_dbg(dev->net, "RX_CTL is 0x%04x after all initializations\n",
>>                    rx_ctl);
>>
>> -       rx_ctl = asix_read_medium_status(dev);
>> +       rx_ctl = asix_read_medium_status(dev, in_pm);
>>         netdev_dbg(dev->net,
>>                    "Medium Status is 0x%04x after all initializations\n",
>>                    rx_ctl);
>> @@ -400,7 +524,6 @@ static int ax88772_reset(struct usbnet *dev)
>>
>>  out:
>>         return ret;
>> -
>>  }
>>
>>  static const struct net_device_ops ax88772_netdev_ops = {
>> @@ -415,11 +538,87 @@ static const struct net_device_ops ax88772_netdev_ops = {
>>         .ndo_set_rx_mode        = asix_set_multicast,
>>  };
>>
>> +static void ax88772_suspend(struct usbnet *dev)
>> +{
>> +       struct asix_common_private *priv = dev->driver_priv;
>> +
>> +       /* Preserve BMCR for restoring */
>> +       priv->presvd_phy_bmcr =
>> +               asix_mdio_read_nopm(dev->net, dev->mii.phy_id, MII_BMCR);
>> +
>> +       /* Preserve ANAR for restoring */
>> +       priv->presvd_phy_advertise =
>> +               asix_mdio_read_nopm(dev->net, dev->mii.phy_id, MII_ADVERTISE);
>> +}
>> +
>> +static int asix_suspend(struct usb_interface *intf, pm_message_t message)
>> +{
>> +       struct usbnet *dev = usb_get_intfdata(intf);
>> +       struct asix_common_private *priv = dev->driver_priv;
>> +
>> +       if (priv->suspend)
>> +               priv->suspend(dev);
>> +
>> +       return usbnet_suspend(intf, message);
>> +}
>> +
>> +static void ax88772_restore_phy(struct usbnet *dev)
>> +{
>> +       struct asix_common_private *priv = dev->driver_priv;
>> +
>> +       if (priv->presvd_phy_advertise) {
>> +               /* Restore Advertisement control reg */
>> +               asix_mdio_write_nopm(dev->net, dev->mii.phy_id, MII_ADVERTISE,
>> +                                    priv->presvd_phy_advertise);
>> +
>> +               /* Restore BMCR */
>> +               asix_mdio_write_nopm(dev->net, dev->mii.phy_id, MII_BMCR,
>> +                                    priv->presvd_phy_bmcr);
>> +
>> +               priv->presvd_phy_advertise = 0;
>> +               priv->presvd_phy_bmcr = 0;
>> +       }
>> +}
>> +
>> +static void ax88772_resume(struct usbnet *dev)
>> +{
>> +       int i;
>> +
>> +       for (i = 0; i < 3; i++)
>> +               if (!ax88772_hw_reset(dev, 1))
>> +                       break;
>> +       ax88772_restore_phy(dev);
>> +}
>> +
>> +static void ax88772a_resume(struct usbnet *dev)
>> +{
>> +       int i;
>> +
>> +       for (i = 0; i < 3; i++) {
>> +               if (!ax88772a_hw_reset(dev, 1))
>> +                       break;
>> +       }
>> +
>> +       ax88772_restore_phy(dev);
>> +}
>> +
>> +static int asix_resume(struct usb_interface *intf)
>> +{
>> +       struct usbnet *dev = usb_get_intfdata(intf);
>> +       struct asix_common_private *priv = dev->driver_priv;
>> +
>> +       if (priv->resume)
>> +               priv->resume(dev);
>> +
>> +       return usbnet_resume(intf);
>> +}
>> +
>>  static int ax88772_bind(struct usbnet *dev, struct usb_interface *intf)
>>  {
>> -       int ret, embd_phy, i;
>> -       u8 buf[ETH_ALEN];
>> +       int ret, i;
>> +       u8 buf[ETH_ALEN], chipcode = 0;
>>         u32 phyid;
>> +       struct asix_common_private *priv;
>>
>>         usbnet_get_endpoints(dev,intf);
>>
>> @@ -427,13 +626,13 @@ static int ax88772_bind(struct usbnet *dev, struct usb_interface *intf)
>>         if (dev->driver_info->data & FLAG_EEPROM_MAC) {
>>                 for (i = 0; i < (ETH_ALEN >> 1); i++) {
>>                         ret = asix_read_cmd(dev, AX_CMD_READ_EEPROM, 0x04 + i,
>> -                                       0, 2, buf + i * 2);
>> +                                           0, 2, buf + i * 2, 0);
>>                         if (ret < 0)
>>                                 break;
>>                 }
>>         } else {
>>                 ret = asix_read_cmd(dev, AX_CMD_READ_NODE_ID,
>> -                               0, 0, ETH_ALEN, buf);
>> +                               0, 0, ETH_ALEN, buf, 0);
>>         }
>>
>>         if (ret < 0) {
>> @@ -456,16 +655,11 @@ static int ax88772_bind(struct usbnet *dev, struct usb_interface *intf)
>>         dev->net->needed_headroom = 4; /* cf asix_tx_fixup() */
>>         dev->net->needed_tailroom = 4; /* cf asix_tx_fixup() */
>>
>> -       embd_phy = ((dev->mii.phy_id & 0x1f) == 0x10 ? 1 : 0);
>> +       asix_read_cmd(dev, AX_CMD_STATMNGSTS_REG, 0, 0, 1, &chipcode, 0);
>> +       chipcode &= AX_CHIPCODE_MASK;
>>
>> -       /* Reset the PHY to normal operation mode */
>> -       ret = asix_write_cmd(dev, AX_CMD_SW_PHY_SELECT, embd_phy, 0, 0, NULL);
>> -       if (ret < 0) {
>> -               netdev_dbg(dev->net, "Select PHY #1 failed: %d\n", ret);
>> -               return ret;
>> -       }
>> -
>> -       ax88772_reset(dev);
>> +       (chipcode == AX_AX88772_CHIPCODE) ? ax88772_hw_reset(dev, 0) :
>> +                                           ax88772a_hw_reset(dev, 0);
>>
>>         /* Read PHYID register *AFTER* the PHY was reset properly */
>>         phyid = asix_get_phyid(dev);
>> @@ -482,6 +676,18 @@ static int ax88772_bind(struct usbnet *dev, struct usb_interface *intf)
>>         if (!dev->driver_priv)
>>                 return -ENOMEM;
>>
>> +       priv = dev->driver_priv;
>> +
>> +       priv->presvd_phy_bmcr = 0;
>> +       priv->presvd_phy_advertise = 0;
>> +       if (chipcode == AX_AX88772_CHIPCODE) {
>> +               priv->resume = ax88772_resume;
>> +               priv->suspend = ax88772_suspend;
>> +       } else {
>> +               priv->resume = ax88772a_resume;
>> +               priv->suspend = ax88772_suspend;
>> +       }
>> +
>>         return 0;
>>  }
>>
>> @@ -593,12 +799,12 @@ static int ax88178_reset(struct usbnet *dev)
>>         int gpio0 = 0;
>>         u32 phyid;
>>
>> -       asix_read_cmd(dev, AX_CMD_READ_GPIOS, 0, 0, 1, &status);
>> +       asix_read_cmd(dev, AX_CMD_READ_GPIOS, 0, 0, 1, &status, 0);
>>         netdev_dbg(dev->net, "GPIO Status: 0x%04x\n", status);
>>
>> -       asix_write_cmd(dev, AX_CMD_WRITE_ENABLE, 0, 0, 0, NULL);
>> -       asix_read_cmd(dev, AX_CMD_READ_EEPROM, 0x0017, 0, 2, &eeprom);
>> -       asix_write_cmd(dev, AX_CMD_WRITE_DISABLE, 0, 0, 0, NULL);
>> +       asix_write_cmd(dev, AX_CMD_WRITE_ENABLE, 0, 0, 0, NULL, 0);
>> +       asix_read_cmd(dev, AX_CMD_READ_EEPROM, 0x0017, 0, 2, &eeprom, 0);
>> +       asix_write_cmd(dev, AX_CMD_WRITE_DISABLE, 0, 0, 0, NULL, 0);
>>
>>         netdev_dbg(dev->net, "EEPROM index 0x17 is 0x%04x\n", eeprom);
>>
>> @@ -614,15 +820,16 @@ static int ax88178_reset(struct usbnet *dev)
>>         netdev_dbg(dev->net, "GPIO0: %d, PhyMode: %d\n", gpio0, data->phymode);
>>
>>         /* Power up external GigaPHY through AX88178 GPIO pin */
>> -       asix_write_gpio(dev, AX_GPIO_RSE | AX_GPIO_GPO_1 | AX_GPIO_GPO1EN, 40);
>> +       asix_write_gpio(dev, AX_GPIO_RSE | AX_GPIO_GPO_1 |
>> +                       AX_GPIO_GPO1EN, 40, 0);
>>         if ((le16_to_cpu(eeprom) >> 8) != 1) {
>> -               asix_write_gpio(dev, 0x003c, 30);
>> -               asix_write_gpio(dev, 0x001c, 300);
>> -               asix_write_gpio(dev, 0x003c, 30);
>> +               asix_write_gpio(dev, 0x003c, 30, 0);
>> +               asix_write_gpio(dev, 0x001c, 300, 0);
>> +               asix_write_gpio(dev, 0x003c, 30, 0);
>>         } else {
>>                 netdev_dbg(dev->net, "gpio phymode == 1 path\n");
>> -               asix_write_gpio(dev, AX_GPIO_GPO1EN, 30);
>> -               asix_write_gpio(dev, AX_GPIO_GPO1EN | AX_GPIO_GPO_1, 30);
>> +               asix_write_gpio(dev, AX_GPIO_GPO1EN, 30, 0);
>> +               asix_write_gpio(dev, AX_GPIO_GPO1EN | AX_GPIO_GPO_1, 30, 0);
>>         }
>>
>>         /* Read PHYID register *AFTER* powering up PHY */
>> @@ -630,15 +837,15 @@ static int ax88178_reset(struct usbnet *dev)
>>         netdev_dbg(dev->net, "PHYID=0x%08x\n", phyid);
>>
>>         /* Set AX88178 to enable MII/GMII/RGMII interface for external PHY */
>> -       asix_write_cmd(dev, AX_CMD_SW_PHY_SELECT, 0, 0, 0, NULL);
>> +       asix_write_cmd(dev, AX_CMD_SW_PHY_SELECT, 0, 0, 0, NULL, 0);
>>
>> -       asix_sw_reset(dev, 0);
>> +       asix_sw_reset(dev, 0, 0);
>>         msleep(150);
>>
>> -       asix_sw_reset(dev, AX_SWRESET_PRL | AX_SWRESET_IPPD);
>> +       asix_sw_reset(dev, AX_SWRESET_PRL | AX_SWRESET_IPPD, 0);
>>         msleep(150);
>>
>> -       asix_write_rx_ctl(dev, 0);
>> +       asix_write_rx_ctl(dev, 0, 0);
>>
>>         if (data->phymode == PHY_MODE_MARVELL) {
>>                 marvell_phy_init(dev);
>> @@ -655,18 +862,18 @@ static int ax88178_reset(struct usbnet *dev)
>>
>>         mii_nway_restart(&dev->mii);
>>
>> -       ret = asix_write_medium_mode(dev, AX88178_MEDIUM_DEFAULT);
>> +       ret = asix_write_medium_mode(dev, AX88178_MEDIUM_DEFAULT, 0);
>>         if (ret < 0)
>>                 return ret;
>>
>>         /* Rewrite MAC address */
>>         memcpy(data->mac_addr, dev->net->dev_addr, ETH_ALEN);
>>         ret = asix_write_cmd(dev, AX_CMD_WRITE_NODE_ID, 0, 0, ETH_ALEN,
>> -                                                       data->mac_addr);
>> +                                                       data->mac_addr, 0);
>>         if (ret < 0)
>>                 return ret;
>>
>> -       ret = asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL);
>> +       ret = asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL, 0);
>>         if (ret < 0)
>>                 return ret;
>>
>> @@ -704,7 +911,7 @@ static int ax88178_link_reset(struct usbnet *dev)
>>         netdev_dbg(dev->net, "ax88178_link_reset() speed: %u duplex: %d setting mode to 0x%04x\n",
>>                    speed, ecmd.duplex, mode);
>>
>> -       asix_write_medium_mode(dev, mode);
>> +       asix_write_medium_mode(dev, mode, 0);
>>
>>         if (data->phymode == PHY_MODE_MARVELL && data->ledmode)
>>                 marvell_led_status(dev, speed);
>> @@ -733,15 +940,15 @@ static void ax88178_set_mfb(struct usbnet *dev)
>>                 mfb = AX_RX_CTL_MFB_16384;
>>         }
>>
>> -       rxctl = asix_read_rx_ctl(dev);
>> -       asix_write_rx_ctl(dev, (rxctl & ~AX_RX_CTL_MFB_16384) | mfb);
>> +       rxctl = asix_read_rx_ctl(dev, 0);
>> +       asix_write_rx_ctl(dev, (rxctl & ~AX_RX_CTL_MFB_16384) | mfb, 0);
>>
>> -       medium = asix_read_medium_status(dev);
>> +       medium = asix_read_medium_status(dev, 0);
>>         if (dev->net->mtu > 1500)
>>                 medium |= AX_MEDIUM_JFE;
>>         else
>>                 medium &= ~AX_MEDIUM_JFE;
>> -       asix_write_medium_mode(dev, medium);
>> +       asix_write_medium_mode(dev, medium, 0);
>>
>>         if (dev->rx_urb_size > old_rx_urb_size)
>>                 usbnet_unlink_rx_urbs(dev);
>> @@ -790,7 +997,7 @@ static int ax88178_bind(struct usbnet *dev, struct usb_interface *intf)
>>         usbnet_get_endpoints(dev,intf);
>>
>>         /* Get the MAC address */
>> -       ret = asix_read_cmd(dev, AX_CMD_READ_NODE_ID, 0, 0, ETH_ALEN, buf);
>> +       ret = asix_read_cmd(dev, AX_CMD_READ_NODE_ID, 0, 0, ETH_ALEN, buf, 0);
>>         if (ret < 0) {
>>                 netdev_dbg(dev->net, "Failed to read MAC address: %d\n", ret);
>>                 return ret;
>> @@ -811,10 +1018,10 @@ static int ax88178_bind(struct usbnet *dev, struct usb_interface *intf)
>>         dev->net->ethtool_ops = &ax88178_ethtool_ops;
>>
>>         /* Blink LEDS so users know driver saw dongle */
>> -       asix_sw_reset(dev, 0);
>> +       asix_sw_reset(dev, 0, 0);
>>         msleep(150);
>>
>> -       asix_sw_reset(dev, AX_SWRESET_PRL | AX_SWRESET_IPPD);
>> +       asix_sw_reset(dev, AX_SWRESET_PRL | AX_SWRESET_IPPD, 0);
>>         msleep(150);
>>
>>         /* Asix framing packs multiple eth frames into a 2K usb bulk transfer */
>> @@ -877,7 +1084,7 @@ static const struct driver_info ax88772_info = {
>>         .unbind = ax88772_unbind,
>>         .status = asix_status,
>>         .link_reset = ax88772_link_reset,
>> -       .reset = ax88772_link_reset,
>> +       .reset = ax88772_reset,
>>         .flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_LINK_INTR | FLAG_MULTI_PACKET,
>>         .rx_fixup = asix_rx_fixup_common,
>>         .tx_fixup = asix_tx_fixup,
>> @@ -1005,7 +1212,7 @@ static const struct usb_device_id products [] = {
>>  }, {
>>         // Lenovo U2L100P 10/100
>>         USB_DEVICE (0x17ef, 0x7203),
>> -       .driver_info = (unsigned long) &ax88772_info,
>> +       .driver_info = (unsigned long)&ax88772b_info,
>>  }, {
>>         // ASIX AX88772B 10/100
>>         USB_DEVICE (0x0b95, 0x772b),
>> @@ -1073,7 +1280,7 @@ static const struct usb_device_id products [] = {
>>  }, {
>>         // Asus USB Ethernet Adapter
>>         USB_DEVICE (0x0b95, 0x7e2b),
>> -       .driver_info = (unsigned long) &ax88772_info,
>> +       .driver_info = (unsigned long)&ax88772b_info,
>>  }, {
>>         /* ASIX 88172a demo board */
>>         USB_DEVICE(0x0b95, 0x172a),
>> @@ -1095,8 +1302,8 @@ static struct usb_driver asix_driver = {
>>         .name =         DRIVER_NAME,
>>         .id_table =     products,
>>         .probe =        usbnet_probe,
>> -       .suspend =      usbnet_suspend,
>> -       .resume =       usbnet_resume,
>> +       .suspend =      asix_suspend,
>> +       .resume =       asix_resume,
>>         .disconnect =   usbnet_disconnect,
>>         .supports_autosuspend = 1,
>>         .disable_hub_initiated_lpm = 1,
>> diff --git a/drivers/net/usb/ax88172a.c b/drivers/net/usb/ax88172a.c
>> index 5f18fcb..c2394f1 100644
>> --- a/drivers/net/usb/ax88172a.c
>> +++ b/drivers/net/usb/ax88172a.c
>> @@ -81,7 +81,7 @@ static void ax88172a_adjust_link(struct net_device *netdev)
>>         }
>>
>>         if (mode != priv->oldmode) {
>> -               asix_write_medium_mode(dev, mode);
>> +               asix_write_medium_mode(dev, mode, 0);
>>                 priv->oldmode = mode;
>>                 netdev_dbg(netdev, "speed %u duplex %d, setting mode to 0x%04x\n",
>>                            phydev->speed, phydev->duplex, mode);
>> @@ -205,18 +205,19 @@ static int ax88172a_reset_phy(struct usbnet *dev, int embd_phy)
>>  {
>>         int ret;
>>
>> -       ret = asix_sw_reset(dev, AX_SWRESET_IPPD);
>> +       ret = asix_sw_reset(dev, AX_SWRESET_IPPD, 0);
>>         if (ret < 0)
>>                 goto err;
>>
>>         msleep(150);
>> -       ret = asix_sw_reset(dev, AX_SWRESET_CLEAR);
>> +       ret = asix_sw_reset(dev, AX_SWRESET_CLEAR, 0);
>>         if (ret < 0)
>>                 goto err;
>>
>>         msleep(150);
>>
>> -       ret = asix_sw_reset(dev, embd_phy ? AX_SWRESET_IPRL : AX_SWRESET_IPPD);
>> +       ret = asix_sw_reset(dev, embd_phy ? AX_SWRESET_IPRL : AX_SWRESET_IPPD,
>> +                           0);
>>         if (ret < 0)
>>                 goto err;
>>
>> @@ -242,7 +243,7 @@ static int ax88172a_bind(struct usbnet *dev, struct usb_interface *intf)
>>         dev->driver_priv = priv;
>>
>>         /* Get the MAC address */
>> -       ret = asix_read_cmd(dev, AX_CMD_READ_NODE_ID, 0, 0, ETH_ALEN, buf);
>> +       ret = asix_read_cmd(dev, AX_CMD_READ_NODE_ID, 0, 0, ETH_ALEN, buf, 0);
>>         if (ret < 0) {
>>                 netdev_err(dev->net, "Failed to read MAC address: %d\n", ret);
>>                 goto free;
>> @@ -253,7 +254,7 @@ static int ax88172a_bind(struct usbnet *dev, struct usb_interface *intf)
>>         dev->net->ethtool_ops = &ax88172a_ethtool_ops;
>>
>>         /* are we using the internal or the external phy? */
>> -       ret = asix_read_cmd(dev, AX_CMD_SW_PHY_STATUS, 0, 0, 1, buf);
>> +       ret = asix_read_cmd(dev, AX_CMD_SW_PHY_STATUS, 0, 0, 1, buf, 0);
>>         if (ret < 0) {
>>                 netdev_err(dev->net, "Failed to read software interface selection register: %d\n",
>>                            ret);
>> @@ -332,20 +333,20 @@ static int ax88172a_reset(struct usbnet *dev)
>>         ax88172a_reset_phy(dev, priv->use_embdphy);
>>
>>         msleep(150);
>> -       rx_ctl = asix_read_rx_ctl(dev);
>> +       rx_ctl = asix_read_rx_ctl(dev, 0);
>>         netdev_dbg(dev->net, "RX_CTL is 0x%04x after software reset\n", rx_ctl);
>> -       ret = asix_write_rx_ctl(dev, 0x0000);
>> +       ret = asix_write_rx_ctl(dev, 0x0000, 0);
>>         if (ret < 0)
>>                 goto out;
>>
>> -       rx_ctl = asix_read_rx_ctl(dev);
>> +       rx_ctl = asix_read_rx_ctl(dev, 0);
>>         netdev_dbg(dev->net, "RX_CTL is 0x%04x setting to 0x0000\n", rx_ctl);
>>
>>         msleep(150);
>>
>>         ret = asix_write_cmd(dev, AX_CMD_WRITE_IPG0,
>>                              AX88772_IPG0_DEFAULT | AX88772_IPG1_DEFAULT,
>> -                            AX88772_IPG2_DEFAULT, 0, NULL);
>> +                            AX88772_IPG2_DEFAULT, 0, NULL, 0);
>>         if (ret < 0) {
>>                 netdev_err(dev->net, "Write IPG,IPG1,IPG2 failed: %d\n", ret);
>>                 goto out;
>> @@ -354,20 +355,20 @@ static int ax88172a_reset(struct usbnet *dev)
>>         /* Rewrite MAC address */
>>         memcpy(data->mac_addr, dev->net->dev_addr, ETH_ALEN);
>>         ret = asix_write_cmd(dev, AX_CMD_WRITE_NODE_ID, 0, 0, ETH_ALEN,
>> -                            data->mac_addr);
>> +                            data->mac_addr, 0);
>>         if (ret < 0)
>>                 goto out;
>>
>>         /* Set RX_CTL to default values with 2k buffer, and enable cactus */
>> -       ret = asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL);
>> +       ret = asix_write_rx_ctl(dev, AX_DEFAULT_RX_CTL, 0);
>>         if (ret < 0)
>>                 goto out;
>>
>> -       rx_ctl = asix_read_rx_ctl(dev);
>> +       rx_ctl = asix_read_rx_ctl(dev, 0);
>>         netdev_dbg(dev->net, "RX_CTL is 0x%04x after all initializations\n",
>>                    rx_ctl);
>>
>> -       rx_ctl = asix_read_medium_status(dev);
>> +       rx_ctl = asix_read_medium_status(dev, 0);
>>         netdev_dbg(dev->net, "Medium Status is 0x%04x after all initializations\n",
>>                    rx_ctl);
>>
>> --
>> 2.7.4
>>

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH 1/3] net: asix: Add in_pm parameter
  2016-07-26 21:14   ` Robert Foss
@ 2016-07-26 21:54     ` Grant Grundler
  0 siblings, 0 replies; 9+ messages in thread
From: Grant Grundler @ 2016-07-26 21:54 UTC (permalink / raw)
  To: Robert Foss
  Cc: Grant Grundler, David Miller, Mark_Craske, Dean_Jenkins,
	Vincent Palatin, ivecera, linux, WK TSAI, Chia-Sheng Chang,
	linux-usb, netdev, LKML, enric.balletbo, Tomeu Vizoso

On Tue, Jul 26, 2016 at 2:14 PM, Robert Foss <robert.foss@collabora.com> wrote:
...
> Thanks for the feedback (for this patch and the other ones)!
> I'm preparing a v2 and will submit it withing a day or two.

Excellent! very welcome and thanks again for picking this up.

...
>> FTR, current  drivers/net/usb/ax88179_178a.c uses "in_pm" as well.
>
> Ah! Would you like that to be noted in the commit message?

No - I generally do not muck with commit messages that I didn't write.
It's part of the "finger print" of a patch (so one can trace origin
across distro's and kernel versions).

My comment was intended for reviewers and could be included in the
"comments after the commit message". (ie below the "---" marker in the
patch email).

...
>> BTW, I have two additional changes for AX88772x support sitting in my
>> "needs more work" queue (for quite a while already):
>>    https://chromium-review.googlesource.com/#/c/229620/
>>    "asix: autoneg will set WRITE_MEDIUM reg"
>>
>>    https://chromium-review.googlesource.com/#/c/231162/
>>    "net: asix: see 802.3 spec for phy reset"
>>
>> I would certainly approve if _anyone_ picked these up, tested them,
>> and then submitted them to netdev.
>
> Unfortunately I am without appropriate hardware at the moment.

That's a risky thing to point out for two reasons:

1) I can send you more HW than you could possibly ever use. :)

2) Reviewers of this patch series are already wondering how you
verified the AX88772x patches work on current kernel.org (or
netdev-next) branches.

Please email me with your shipping address (OFFLIST!) and I'll try to
send you some Asix HW to test with.  I'm located in "Silicon Valley"
(USA) and can easily ship in the US. Other countries is a bit harder
and in some cases not possible.

cheers,
grant

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2016-07-26 21:54 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-07-25 17:40 [PATCH 1/3] net: asix: Add in_pm parameter robert.foss
2016-07-25 17:40 ` [PATCH 2/3] net: asix: Avoid looping when the device is disconnected robert.foss
2016-07-25 21:37   ` Grant Grundler
2016-07-25 17:40 ` [PATCH 3/3] net: asix: Fix AX88772x resume failures robert.foss
2016-07-25 21:45   ` Grant Grundler
2016-07-25 21:26 ` [PATCH 1/3] net: asix: Add in_pm parameter Grant Grundler
2016-07-26 21:14   ` Robert Foss
2016-07-26 21:54     ` Grant Grundler
2016-07-26  4:45 ` David Miller

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).