linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH, resubmit] ax88179_178a: ASIX AX88179_178A USB 3.0/2.0 to gigabit ethernet adapter driver
@ 2013-02-07 13:39 Freddy Xin
  2013-02-07 20:02 ` Ben Hutchings
  0 siblings, 1 reply; 27+ messages in thread
From: Freddy Xin @ 2013-02-07 13:39 UTC (permalink / raw)
  To: netdev; +Cc: davem, linux-usb, linux-kernel, Freddy Xin

From: Freddy Xin <freddy@asix.com.tw>

This is a resubmission.
Added "const" to ethtool_ops structure and fixed the coding style of AX88179_BULKIN_SIZE array.
Fixed the issue that the default MTU is not 1500.
Added ax88179_change_mtu function and enabled the hardware jumbo frame function to support an
MTU higher than 1500.
Fixed indentation and empty line coding style errors.
The _nopm version usb functions were added to access register in suspend and resume functions.
Serveral variables allocted dynamically were removed and replaced by stack variables.
ax88179_get_eeprom were modified from asix_get_eeprom in asix_common.

This patch adds a driver for ASIX's AX88179 family of USB 3.0/2.0
to gigabit ethernet adapters. It's based on the AX88xxx driver but
the usb commands used to access registers for AX88179 are completely different.
This driver had been verified on x86 system with AX88179/AX88178A and
Sitcomm LN-032 USB dongles.

Signed-off-by: Freddy Xin <freddy@asix.com.tw>
---
 drivers/net/usb/Kconfig        |   18 +
 drivers/net/usb/Makefile       |    1 +
 drivers/net/usb/ax88179_178a.c | 1399 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 1418 insertions(+)
 create mode 100644 drivers/net/usb/ax88179_178a.c

diff --git a/drivers/net/usb/Kconfig b/drivers/net/usb/Kconfig
index ef97621..75af401 100644
--- a/drivers/net/usb/Kconfig
+++ b/drivers/net/usb/Kconfig
@@ -158,6 +158,24 @@ config USB_NET_AX8817X
 	  This driver creates an interface named "ethX", where X depends on
 	  what other networking devices you have in use.
 
+config USB_NET_AX88179_178A
+	tristate "ASIX AX88179/178A USB 3.0/2.0 to Gigabit Ethernet"
+	depends on USB_USBNET
+	select CRC32
+	select PHYLIB
+	default y
+	help
+	  This option adds support for ASIX AX88179 based USB 3.0/2.0
+	  to Gigabit Ethernet adapters.
+
+ 	  This driver should work with at least the following devices:
+	    * ASIX AX88179
+	    * ASIX AX88178A
+	    * Sitcomm LN-032
+
+	  This driver creates an interface named "ethX", where X depends on
+	  what other networking devices you have in use.
+
 config USB_NET_CDCETHER
 	tristate "CDC Ethernet support (smart devices such as cable modems)"
 	depends on USB_USBNET
diff --git a/drivers/net/usb/Makefile b/drivers/net/usb/Makefile
index 4786913..119b06c 100644
--- a/drivers/net/usb/Makefile
+++ b/drivers/net/usb/Makefile
@@ -9,6 +9,7 @@ obj-$(CONFIG_USB_RTL8150)	+= rtl8150.o
 obj-$(CONFIG_USB_HSO)		+= hso.o
 obj-$(CONFIG_USB_NET_AX8817X)	+= asix.o
 asix-y := asix_devices.o asix_common.o ax88172a.o
+obj-$(CONFIG_USB_NET_AX88179_178A)      += ax88179_178a.o
 obj-$(CONFIG_USB_NET_CDCETHER)	+= cdc_ether.o
 obj-$(CONFIG_USB_NET_CDC_EEM)	+= cdc_eem.o
 obj-$(CONFIG_USB_NET_DM9601)	+= dm9601.o
diff --git a/drivers/net/usb/ax88179_178a.c b/drivers/net/usb/ax88179_178a.c
new file mode 100644
index 0000000..545151a
--- /dev/null
+++ b/drivers/net/usb/ax88179_178a.c
@@ -0,0 +1,1399 @@
+/*
+ * ASIX AX88179/178A USB 3.0/2.0 to Gigabit Ethernet Devices
+ *
+ * Copyright (C) 2011-2013 ASIX
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/etherdevice.h>
+#include <linux/mii.h>
+#include <linux/usb.h>
+#include <linux/crc32.h>
+#include <linux/usb/usbnet.h>
+
+#define AX88179_PHY_ID				0x03
+#define AX_EEPROM_LEN				0x100
+#define AX88179_EEPROM_MAGIC			0x17900b95
+#define AX_MCAST_FLTSIZE			8
+#define AX_MAX_MCAST				64
+#define AX_INT_PPLS_LINK			BIT(0)
+#define AX_RXHDR_L4_TYPE_UDP			1
+#define AX_RXHDR_L4_TYPE_TCP			4
+#define AX_ACCESS_MAC				0x01
+#define AX_ACCESS_PHY				0x02
+#define AX_ACCESS_EEPROM			0x04
+#define AX_ACCESS_EFUS				0x05
+#define AX_PAUSE_WATERLVL_HIGH			0x54
+#define AX_PAUSE_WATERLVL_LOW			0x55
+
+#define PHYSICAL_LINK_STATUS			0x02
+	#define	AX_USB_SS		0x04
+	#define	AX_USB_HS		0x02
+
+#define GENERAL_STATUS				0x03
+/* Check AX88179 version. UA1:Bit2 = 0,  UA2:Bit2 = 1 */
+	#define	AX_SECLD		0x04
+
+#define AX_SROM_ADDR				0x07
+#define AX_SROM_CMD				0x0a
+	#define EEP_RD			0x04
+	#define EEP_BUSY		0x10
+
+#define AX_SROM_DATA_LOW			0x08
+#define AX_SROM_DATA_HIGH			0x09
+
+#define AX_RX_CTL				0x0b
+	#define AX_RX_CTL_DROPCRCERR	0x0100
+	#define AX_RX_CTL_IPE		0x0200
+	#define AX_RX_CTL_START		0x0080
+	#define AX_RX_CTL_AP		0x0020
+	#define AX_RX_CTL_AM		0x0010
+	#define AX_RX_CTL_AB		0x0008
+	#define AX_RX_CTL_AMALL		0x0002
+	#define AX_RX_CTL_PRO		0x0001
+	#define AX_RX_CTL_STOP		0x0000
+
+#define AX_NODE_ID				0x10
+#define AX_MULFLTARY				0x16
+
+#define AX_MEDIUM_STATUS_MODE			0x22
+	#define AX_MEDIUM_GIGAMODE	0x01
+	#define AX_MEDIUM_FULL_DUPLEX	0x02
+	#define AX_MEDIUM_ALWAYS_ONE	0x04
+	#define AX_MEDIUM_EN_125MHZ	0x08
+	#define AX_MEDIUM_RXFLOW_CTRLEN	0x10
+	#define AX_MEDIUM_TXFLOW_CTRLEN	0x20
+	#define AX_MEDIUM_RECEIVE_EN	0x100
+	#define AX_MEDIUM_PS		0x200
+	#define AX_MEDIUM_JUMBO_EN	0x8040
+
+#define AX_MONITOR_MOD				0x24
+	#define AX_MONITOR_MODE_RWLC	0x02
+	#define AX_MONITOR_MODE_RWMP	0x04
+	#define AX_MONITOR_MODE_PMEPOL	0x20
+	#define AX_MONITOR_MODE_PMETYPE	0x40
+
+#define AX_GPIO_CTRL				0x25
+	#define AX_GPIO_CTRL_GPIO3EN	0x80
+	#define AX_GPIO_CTRL_GPIO2EN	0x40
+	#define AX_GPIO_CTRL_GPIO1EN	0x20
+
+#define AX_PHYPWR_RSTCTL			0x26
+	#define AX_PHYPWR_RSTCTL_BZ	0x0010
+	#define AX_PHYPWR_RSTCTL_IPRL	0x0020
+	#define AX_PHYPWR_RSTCTL_AT	0x1000
+
+#define AX_RX_BULKIN_QCTRL			0x2e
+#define AX_CLK_SELECT				0x33
+	#define AX_CLK_SELECT_BCS	0x01
+	#define AX_CLK_SELECT_ACS	0x02
+	#define AX_CLK_SELECT_ULR	0x08
+
+#define AX_RXCOE_CTL				0x34
+	#define AX_RXCOE_IP		0x01
+	#define AX_RXCOE_TCP		0x02
+	#define AX_RXCOE_UDP		0x04
+	#define AX_RXCOE_TCPV6		0x20
+	#define AX_RXCOE_UDPV6		0x40
+
+#define AX_TXCOE_CTL				0x35
+	#define AX_TXCOE_IP		0x01
+	#define AX_TXCOE_TCP		0x02
+	#define AX_TXCOE_UDP		0x04
+	#define AX_TXCOE_TCPV6		0x20
+	#define AX_TXCOE_UDPV6		0x40
+
+#define AX_LEDCTRL				0x73
+
+#define GMII_PHY_PHYSR				0x11
+	#define GMII_PHY_PHYSR_SMASK	0xc000
+	#define GMII_PHY_PHYSR_GIGA	0x8000
+	#define GMII_PHY_PHYSR_100	0x4000
+	#define GMII_PHY_PHYSR_FULL	0x2000
+	#define GMII_PHY_PHYSR_LINK	0x400
+
+#define GMII_LED_ACT				0x1a
+	#define GMII_LED_ACTIVE_MASK	0xff8fd
+	#define GMII_LED0_ACTIVE	BIT(4)
+	#define GMII_LED1_ACTIVE	BIT(5)
+	#define GMII_LED2_ACTIVE	BIT(6)
+
+#define GMII_LED_LINK				0x1c
+	#define GMII_LED_LINK_MASK	0xf888
+	#define GMII_LED0_LINK_10	BIT(0)
+	#define GMII_LED0_LINK_100	BIT(1)
+	#define GMII_LED0_LINK_1000	BIT(2)
+	#define GMII_LED1_LINK_10	BIT(4)
+	#define GMII_LED1_LINK_100	BIT(5)
+	#define GMII_LED1_LINK_1000	BIT(6)
+	#define GMII_LED2_LINK_10	BIT(8)
+	#define GMII_LED2_LINK_100	BIT(9)
+	#define GMII_LED2_LINK_1000	BIT(10)
+	#define	LED0_ACTIVE		BIT(0)
+	#define	LED0_LINK_10		BIT(1)
+	#define	LED0_LINK_100		BIT(2)
+	#define	LED0_LINK_1000		BIT(3)
+	#define	LED0_FD			BIT(4)
+	#define LED0_USB3_MASK		0x001f
+	#define	LED1_ACTIVE		BIT(5)
+	#define	LED1_LINK_10		BIT(6)
+	#define	LED1_LINK_100		BIT(7)
+	#define	LED1_LINK_1000		BIT(8)
+	#define	LED1_FD			BIT(9)
+	#define LED1_USB3_MASK		0x03e0
+	#define	LED2_ACTIVE		BIT(10)
+	#define	LED2_LINK_1000		BIT(13)
+	#define	LED2_LINK_100		BIT(12)
+	#define	LED2_LINK_10		BIT(11)
+	#define	LED2_FD			BIT(14)
+	#define	LED_VALID		BIT(15)
+	#define LED2_USB3_MASK		0x7c00
+
+#define GMII_PHYPAGE				0x1e
+#define GMII_PHY_PAGE_SELECT			0x1f
+	#define GMII_PHY_PGSEL_EXT	0x0007
+	#define GMII_PHY_PGSEL_PAGE0	0X0000
+
+struct ax88179_data {
+	u16 rxctl;
+};
+
+struct ax88179_int_data {
+	u16 res1;
+	u8 link;
+	u16 res2;
+	u8 status;
+	u16 res3;
+} __packed;
+
+static const struct {
+	unsigned char ctrl, timer_l, timer_h, size, ifg;
+} AX88179_BULKIN_SIZE[] =	{
+	{7, 0x4f, 0,	0x12, 0xff},
+	{7, 0xf0, 1,	0x15, 0xff},
+	{7, 0xae, 7,	0x18, 0xff},
+	{7, 0xcc, 0x4c, 0x18, 8},
+};
+
+struct ax88179_rx_pkt_header {
+	u8	l4_csum_err:1,
+		l3_csum_err:1,
+		l4_type:3,
+		l3_type:2,
+		ce:1;
+
+	u8	vlan_ind:3,
+		rx_ok:1,
+		pri:3,
+		bmc:1;
+
+	u16	len:13,
+		crc:1,
+		mii:1,
+		drop:1;
+} __packed;
+
+static int __ax88179_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,
+			      u16 size, void *data, int in_pm)
+{
+	int ret;
+	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);
+
+	return ret;
+}
+
+static int __ax88179_write_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,
+			       u16 size, void *data, int in_pm)
+{
+	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;
+}
+
+static void ax88179_write_cmd_async(struct usbnet *dev, u8 cmd, u16 value,
+				    u16 index, u16 size, void *data)
+{
+	usbnet_write_cmd_async(dev, cmd, USB_DIR_OUT | USB_TYPE_VENDOR |
+			       USB_RECIP_DEVICE, value, index, data, size);
+}
+
+static int ax88179_read_cmd_nopm(struct usbnet *dev, u8 cmd, u16 value,
+				 u16 index, u16 size, void *data)
+{
+	return __ax88179_read_cmd(dev, cmd, value, index, size, data, 1);
+}
+
+static int ax88179_write_cmd_nopm(struct usbnet *dev, u8 cmd, u16 value,
+				  u16 index, u16 size, void *data)
+{
+	return __ax88179_write_cmd(dev, cmd, value, index, size, data, 1);
+}
+
+static int ax88179_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,
+			    u16 size, void *data)
+{
+	return __ax88179_read_cmd(dev, cmd, value, index, size, data, 0);
+}
+
+static int ax88179_write_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,
+			     u16 size, void *data)
+{
+	return __ax88179_write_cmd(dev, cmd, value, index, size, data, 0);
+}
+
+static void ax88179_status(struct usbnet *dev, struct urb *urb)
+{
+	struct ax88179_int_data *event;
+	int link;
+
+	if (urb->actual_length < 8)
+		return;
+
+	event = urb->transfer_buffer;
+	link = event->link & AX_INT_PPLS_LINK;
+	if (netif_carrier_ok(dev->net) != link) {
+		if (link)
+			usbnet_defer_kevent(dev, EVENT_LINK_RESET);
+		else
+			netif_carrier_off(dev->net);
+		netdev_info(dev->net, "ax88179 - Link status is: %d\n", link);
+	}
+}
+
+static int ax88179_mdio_read(struct net_device *netdev, int phy_id, int loc)
+{
+	struct usbnet *dev = netdev_priv(netdev);
+	__u16 res;
+
+	ax88179_read_cmd(dev, AX_ACCESS_PHY, phy_id, (__u16)loc, 2, &res);
+	return le16_to_cpu(res & 0xffff);
+}
+
+static void ax88179_mdio_write(struct net_device *netdev, int phy_id, int loc,
+			       int val)
+{
+	struct usbnet *dev = netdev_priv(netdev);
+	__le16 res;
+
+	res = cpu_to_le16(val);
+	ax88179_write_cmd(dev, AX_ACCESS_PHY, phy_id, (__u16)loc, 2, &res);
+}
+
+static int ax88179_suspend(struct usb_interface *intf, pm_message_t message)
+{
+	struct usbnet *dev = usb_get_intfdata(intf);
+	u16 tmp16;
+
+	usbnet_suspend(intf, message);
+
+	/* Disable RX path */
+	ax88179_read_cmd_nopm(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,
+			      2, 2, &tmp16);
+	tmp16 &= ~AX_MEDIUM_RECEIVE_EN;
+	ax88179_write_cmd_nopm(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,
+			       2, 2, &tmp16);
+
+	/* Force bulk-in zero length */
+	ax88179_read_cmd_nopm(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL,
+			      2, 2, &tmp16);
+
+	tmp16 |= AX_PHYPWR_RSTCTL_BZ | AX_PHYPWR_RSTCTL_IPRL;
+	ax88179_write_cmd_nopm(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL,
+			       2, 2, &tmp16);
+
+	/* change clock */
+	tmp16 = 0;
+	ax88179_write_cmd_nopm(dev, AX_ACCESS_MAC, AX_CLK_SELECT, 1, 1, &tmp16);
+
+	/* Configure RX control register => stop operation */
+	tmp16 = AX_RX_CTL_STOP;
+	ax88179_write_cmd_nopm(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2, &tmp16);
+
+	return 0;
+}
+
+/* This function is used to enable the autodetach function. */
+/* This function is determined by offset 0x43 of EEPROM */
+static int ax88179_auto_detach(struct usbnet *dev, int in_pm)
+{
+	u16 tmp;
+
+	if (__ax88179_read_cmd(dev, AX_ACCESS_EEPROM, 0x43, 1, 2,
+			       &tmp, in_pm) < 0)
+		return 0;
+
+	if ((tmp == 0xFFFF) || (!(tmp & 0x0100)))
+		return 0;
+
+	/* Enable Auto Detach bit */
+	tmp = 0;
+	__ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_CLK_SELECT,
+			   1, 1, &tmp, in_pm);
+	tmp |= AX_CLK_SELECT_ULR;
+	__ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_CLK_SELECT,
+			    1, 1, &tmp, in_pm);
+
+	__ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL,
+			   2, 2, &tmp, in_pm);
+	tmp |= AX_PHYPWR_RSTCTL_AT;
+	__ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL,
+			    2, 2, &tmp, in_pm);
+
+	return 0;
+}
+
+static int ax88179_resume(struct usb_interface *intf)
+{
+	struct usbnet *dev = usb_get_intfdata(intf);
+	u16 tmp16;
+
+	netif_carrier_off(dev->net);
+
+	/* Power up ethernet PHY */
+	tmp16 = 0;
+	ax88179_write_cmd_nopm(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL,
+			       2, 2, &tmp16);
+	udelay(1000);
+
+	tmp16 = AX_PHYPWR_RSTCTL_IPRL;
+	ax88179_write_cmd_nopm(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL,
+			       2, 2, &tmp16);
+	msleep(200);
+
+	/* Ethernet PHY Auto Detach*/
+	ax88179_auto_detach(dev, 1);
+
+	/* enable clock */
+	ax88179_read_cmd_nopm(dev, AX_ACCESS_MAC,  AX_CLK_SELECT, 1, 1, &tmp16);
+	tmp16 |= AX_CLK_SELECT_ACS | AX_CLK_SELECT_BCS;
+	ax88179_write_cmd_nopm(dev, AX_ACCESS_MAC, AX_CLK_SELECT, 1, 1, &tmp16);
+	msleep(100);
+
+	/* Configure RX control register => start operation */
+	tmp16 = AX_RX_CTL_DROPCRCERR | AX_RX_CTL_IPE | AX_RX_CTL_START |
+		AX_RX_CTL_AP | AX_RX_CTL_AMALL | AX_RX_CTL_AB;
+	ax88179_write_cmd_nopm(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2, &tmp16);
+
+	return usbnet_resume(intf);
+}
+
+static void
+ax88179_get_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo)
+{
+	struct usbnet *dev = netdev_priv(net);
+	u8 opt;
+
+	if (ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_MONITOR_MOD,
+			     1, 1, &opt) < 0) {
+		wolinfo->supported = 0;
+		wolinfo->wolopts = 0;
+		return;
+	}
+
+	wolinfo->supported = WAKE_PHY | WAKE_MAGIC;
+	wolinfo->wolopts = 0;
+	if (opt & AX_MONITOR_MODE_RWLC)
+		wolinfo->wolopts |= WAKE_PHY;
+	if (opt & AX_MONITOR_MODE_RWMP)
+		wolinfo->wolopts |= WAKE_MAGIC;
+}
+
+static int
+ax88179_set_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo)
+{
+	struct usbnet *dev = netdev_priv(net);
+	u8 opt = 0;
+
+	if (wolinfo->wolopts & WAKE_PHY)
+		opt |= AX_MONITOR_MODE_RWLC;
+	if (wolinfo->wolopts & WAKE_MAGIC)
+		opt |= AX_MONITOR_MODE_RWMP;
+
+	if (ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MONITOR_MOD,
+			      1, 1, &opt) < 0)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int ax88179_get_eeprom_len(struct net_device *net)
+{
+	return AX_EEPROM_LEN;
+}
+
+static int
+ax88179_get_eeprom(struct net_device *net, struct ethtool_eeprom *eeprom,
+		   u8 *data)
+{
+	struct usbnet *dev = netdev_priv(net);
+	u16 *eeprom_buff;
+	int first_word, last_word;
+	int i;
+
+	if (eeprom->len == 0)
+		return -EINVAL;
+
+	eeprom->magic = AX88179_EEPROM_MAGIC;
+
+	first_word = eeprom->offset >> 1;
+	last_word = (eeprom->offset + eeprom->len - 1) >> 1;
+	eeprom_buff = kmalloc(sizeof(u16) * (last_word - first_word + 1),
+			      GFP_KERNEL);
+	if (!eeprom_buff)
+		return -ENOMEM;
+
+	/* ax88179/178A returns 2 bytes from eeprom on read */
+	for (i = first_word; i <= last_word; i++) {
+		if (ax88179_read_cmd(dev, AX_ACCESS_EEPROM, i, 1, 2,
+				     &(eeprom_buff[i - first_word])) < 0) {
+			kfree(eeprom_buff);
+			return -EIO;
+		}
+	}
+
+	memcpy(data, (u8 *)eeprom_buff + (eeprom->offset & 1), eeprom->len);
+	kfree(eeprom_buff);
+	return 0;
+}
+
+static int ax88179_get_settings(struct net_device *net, struct ethtool_cmd *cmd)
+{
+	struct usbnet *dev = netdev_priv(net);
+	return mii_ethtool_gset(&dev->mii, cmd);
+}
+
+static int ax88179_set_settings(struct net_device *net, struct ethtool_cmd *cmd)
+{
+	struct usbnet *dev = netdev_priv(net);
+	return mii_ethtool_sset(&dev->mii, cmd);
+}
+
+
+static int ax88179_ioctl(struct net_device *net, struct ifreq *rq, int cmd)
+{
+	struct usbnet *dev = netdev_priv(net);
+	return generic_mii_ioctl(&dev->mii, if_mii(rq), cmd, NULL);
+}
+
+static const struct ethtool_ops ax88179_ethtool_ops = {
+	.get_link		= ethtool_op_get_link,
+	.get_msglevel		= usbnet_get_msglevel,
+	.set_msglevel		= usbnet_set_msglevel,
+	.get_wol		= ax88179_get_wol,
+	.set_wol		= ax88179_set_wol,
+	.get_eeprom_len		= ax88179_get_eeprom_len,
+	.get_eeprom		= ax88179_get_eeprom,
+	.get_settings		= ax88179_get_settings,
+	.set_settings		= ax88179_set_settings,
+	.nway_reset		= usbnet_nway_reset,
+};
+
+static void ax88179_set_multicast(struct net_device *net)
+{
+	struct usbnet *dev = netdev_priv(net);
+	struct ax88179_data *data = (struct ax88179_data *)dev->data;
+	u8 *m_filter = ((u8 *)dev->data) + 12;
+
+	data->rxctl = (AX_RX_CTL_START | AX_RX_CTL_AB | AX_RX_CTL_IPE);
+
+	if (net->flags & IFF_PROMISC) {
+		data->rxctl |= AX_RX_CTL_PRO;
+	} else if (net->flags & IFF_ALLMULTI ||
+		   netdev_mc_count(net) > AX_MAX_MCAST) {
+		data->rxctl |= AX_RX_CTL_AMALL;
+	} else if (netdev_mc_empty(net)) {
+		/* just broadcast and directed */
+	} else {
+		/* We use the 20 byte dev->data for our 8 byte filter buffer
+		 * to avoid allocating memory that is tricky to free later
+		 */
+		u32 crc_bits;
+		struct netdev_hw_addr *ha;
+
+		memset(m_filter, 0, AX_MCAST_FLTSIZE);
+
+		netdev_for_each_mc_addr(ha, net) {
+			crc_bits = ether_crc(ETH_ALEN, ha->addr) >> 26;
+			*(m_filter + (crc_bits >> 3)) |= (1 << (crc_bits & 7));
+		}
+
+		ax88179_write_cmd_async(dev, AX_ACCESS_MAC, AX_MULFLTARY,
+					AX_MCAST_FLTSIZE, AX_MCAST_FLTSIZE,
+					m_filter);
+
+		data->rxctl |= AX_RX_CTL_AM;
+	}
+
+	ax88179_write_cmd_async(dev, AX_ACCESS_MAC, AX_RX_CTL,
+				2, 2, &data->rxctl);
+}
+
+static int
+ax88179_set_features(struct net_device *net, netdev_features_t features)
+{
+	u8 tmp;
+	struct usbnet *dev = netdev_priv(net);
+	netdev_features_t changed = net->features ^ features;
+
+	if (changed & NETIF_F_TSO)
+		net->features ^= NETIF_F_TSO;
+
+	if (changed & NETIF_F_SG)
+		net->features ^= NETIF_F_SG;
+
+	if (changed & NETIF_F_IP_CSUM) {
+		ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, 1, 1, &tmp);
+		tmp ^= AX_TXCOE_TCP | AX_TXCOE_UDP;
+		ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, 1, 1, &tmp);
+
+		net->features ^= NETIF_F_IP_CSUM;
+	}
+
+	if (changed & NETIF_F_IPV6_CSUM) {
+		ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, 1, 1, &tmp);
+		tmp ^= AX_TXCOE_TCPV6 | AX_TXCOE_UDPV6;
+		ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, 1, 1, &tmp);
+
+		net->features ^= NETIF_F_IPV6_CSUM;
+	}
+
+	if (changed & NETIF_F_RXCSUM) {
+		ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_RXCOE_CTL, 1, 1, &tmp);
+		tmp ^= AX_RXCOE_IP | AX_RXCOE_TCP | AX_RXCOE_UDP |
+		       AX_RXCOE_TCPV6 | AX_RXCOE_UDPV6;
+		ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RXCOE_CTL, 1, 1, &tmp);
+
+		net->features ^= NETIF_F_RXCSUM;
+	}
+
+	return 0;
+}
+
+static int ax88179_change_mtu(struct net_device *net, int new_mtu)
+{
+	struct usbnet *dev = netdev_priv(net);
+	u16 tmp16;
+
+	if (new_mtu <= 0 || new_mtu > 4088)
+		return -EINVAL;
+
+	net->mtu = new_mtu;
+	dev->hard_mtu = net->mtu + net->hard_header_len;
+
+	if (net->mtu > 1500) {
+		ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,
+				 2, 2, &tmp16);
+		tmp16 |= AX_MEDIUM_JUMBO_EN;
+		ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,
+				  2, 2, &tmp16);
+	} else {
+		ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,
+				 2, 2, &tmp16);
+		tmp16 &= ~AX_MEDIUM_JUMBO_EN;
+		ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,
+				  2, 2, &tmp16);
+	}
+
+	return 0;
+}
+
+static int ax88179_set_mac_addr(struct net_device *net, void *p)
+{
+	struct usbnet *dev = netdev_priv(net);
+	struct sockaddr *addr = p;
+
+	if (netif_running(net))
+		return -EBUSY;
+	if (!is_valid_ether_addr(addr->sa_data))
+		return -EADDRNOTAVAIL;
+
+	memcpy(net->dev_addr, addr->sa_data, ETH_ALEN);
+
+	/* Set the MAC address */
+	return ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_NODE_ID, ETH_ALEN,
+				 ETH_ALEN, net->dev_addr);
+}
+
+static const struct net_device_ops ax88179_netdev_ops = {
+	.ndo_open		= usbnet_open,
+	.ndo_stop		= usbnet_stop,
+	.ndo_start_xmit		= usbnet_start_xmit,
+	.ndo_tx_timeout		= usbnet_tx_timeout,
+	.ndo_change_mtu		= ax88179_change_mtu,
+	.ndo_set_mac_address	= ax88179_set_mac_addr,
+	.ndo_validate_addr	= eth_validate_addr,
+	.ndo_do_ioctl		= ax88179_ioctl,
+	.ndo_set_rx_mode	= ax88179_set_multicast,
+	.ndo_set_features	= ax88179_set_features,
+};
+
+static int ax88179_check_eeprom(struct usbnet *dev)
+{
+	u8 i;
+	u8 buf;
+	u8 eeprom[20];
+	u16 csum, delay = HZ / 10;
+	unsigned long jtimeout;
+
+	/* Read EEPROM content */
+	for (i = 0; i < 6; i++) {
+		buf = i;
+		if (ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_SROM_ADDR,
+				      1, 1, &buf) < 0)
+			return -EINVAL;
+
+		buf = EEP_RD;
+		if (ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_SROM_CMD,
+				      1, 1, &buf) < 0)
+			return -EINVAL;
+
+		jtimeout = jiffies + delay;
+		do {
+			ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_SROM_CMD,
+					 1, 1, &buf);
+
+			if (time_after(jiffies, jtimeout))
+				return -EINVAL;
+
+		} while (buf & EEP_BUSY);
+
+		ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_SROM_DATA_LOW,
+				 2, 2, &eeprom[i * 2]);
+		if ((i == 0) && (eeprom[0] == 0xFF))
+			return -EINVAL;
+	}
+
+	csum = eeprom[6] + eeprom[7] + eeprom[8] + eeprom[9];
+	csum = (csum >> 8) + (csum & 0xff);
+	if ((csum + eeprom[10]) != 0xff)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int ax88179_check_efuse(struct usbnet *dev, u16 *ledmode)
+{
+	u8	i;
+	u8	efuse[64];
+	u16	csum = 0;
+
+	if (ax88179_read_cmd(dev, AX_ACCESS_EFUS, 0, 64, 64, efuse) < 0)
+		return -EINVAL;
+
+	if (*efuse == 0xFF)
+		return -EINVAL;
+
+	for (i = 0; i < 64; i++)
+		csum = csum + efuse[i];
+
+	while (csum > 255)
+		csum = (csum & 0x00FF) + ((csum >> 8) & 0x00FF);
+
+	if (csum != 0xFF)
+		return -EINVAL;
+
+	*ledmode = (efuse[51] << 8) | efuse[52];
+
+	return 0;
+}
+
+static int ax88179_convert_old_led(struct usbnet *dev, u16 *ledvalue)
+{
+	u16 led;
+
+	/* Loaded the old eFuse LED Mode */
+	if (ax88179_read_cmd(dev, AX_ACCESS_EEPROM, 0x3C, 1, 2, &led) < 0)
+		return -EINVAL;
+
+	led >>= 8;
+	switch (led) {
+	case 0xFF:
+		led = LED0_ACTIVE | LED1_LINK_10 | LED1_LINK_100 |
+		      LED1_LINK_1000 | LED2_ACTIVE | LED2_LINK_10 |
+		      LED2_LINK_100 | LED2_LINK_1000 | LED_VALID;
+		break;
+	case 0xFE:
+		led = LED0_ACTIVE | LED1_LINK_1000 | LED2_LINK_100 | LED_VALID;
+		break;
+	case 0xFD:
+		led = LED0_ACTIVE | LED1_LINK_1000 | LED2_LINK_100 |
+		      LED2_LINK_10 | LED_VALID;
+		break;
+	case 0xFC:
+		led = LED0_ACTIVE | LED1_ACTIVE | LED1_LINK_1000 | LED2_ACTIVE |
+		      LED2_LINK_100 | LED2_LINK_10 | LED_VALID;
+		break;
+	default:
+		led = LED0_ACTIVE | LED1_LINK_10 | LED1_LINK_100 |
+		      LED1_LINK_1000 | LED2_ACTIVE | LED2_LINK_10 |
+		      LED2_LINK_100 | LED2_LINK_1000 | LED_VALID;
+		break;
+	}
+
+	*ledvalue = led;
+
+	return 0;
+}
+
+static int ax88179_led_setting(struct usbnet *dev)
+{
+	u8 ledfd, value = 0;
+	u16 tmp, ledact, ledlink, ledvalue = 0, delay = HZ / 10;
+	unsigned long jtimeout;
+
+	/* Check AX88179 version. UA1 or UA2*/
+	ax88179_read_cmd(dev, AX_ACCESS_MAC, GENERAL_STATUS, 1, 1, &value);
+
+	if (!(value & AX_SECLD)) {	/* UA1 */
+		value = AX_GPIO_CTRL_GPIO3EN | AX_GPIO_CTRL_GPIO2EN |
+			AX_GPIO_CTRL_GPIO1EN;
+		if (ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_GPIO_CTRL,
+				      1, 1, &value) < 0)
+			return -EINVAL;
+	}
+
+	/* Check EEPROM */
+	if (!ax88179_check_eeprom(dev)) {
+		value = 0x42;
+		if (ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_SROM_ADDR,
+				      1, 1, &value) < 0)
+			return -EINVAL;
+
+		value = EEP_RD;
+		if (ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_SROM_CMD,
+				      1, 1, &value) < 0)
+			return -EINVAL;
+
+		jtimeout = jiffies + delay;
+		do {
+			ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_SROM_CMD,
+					 1, 1, &value);
+
+			if (time_after(jiffies, jtimeout))
+				return -EINVAL;
+
+		} while (value & EEP_BUSY);
+
+		ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_SROM_DATA_HIGH,
+				 1, 1, &value);
+		ledvalue = (value << 8);
+
+		ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_SROM_DATA_LOW,
+				 1, 1, &value);
+		ledvalue |= value;
+
+		/* load internal ROM for defaule setting */
+		if ((ledvalue == 0xFFFF) || ((ledvalue & LED_VALID) == 0))
+			ax88179_convert_old_led(dev, &ledvalue);
+
+	} else if (!ax88179_check_efuse(dev, &ledvalue)) {
+		if ((ledvalue == 0xFFFF) || ((ledvalue & LED_VALID) == 0))
+			ax88179_convert_old_led(dev, &ledvalue);
+	} else {
+		ax88179_convert_old_led(dev, &ledvalue);
+	}
+
+	tmp = GMII_PHY_PGSEL_EXT;
+	ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
+			  GMII_PHY_PAGE_SELECT, 2, &tmp);
+
+	tmp = 0x2c;
+	ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
+			  GMII_PHYPAGE, 2, &tmp);
+
+	ax88179_read_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, GMII_LED_ACT,
+			 2, &ledact);
+
+	ax88179_read_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
+			 GMII_LED_LINK, 2, &ledlink);
+
+	ledact &= GMII_LED_ACTIVE_MASK;
+	ledlink &= GMII_LED_LINK_MASK;
+
+	if (ledvalue & LED0_ACTIVE)
+		ledact |= GMII_LED0_ACTIVE;
+
+	if (ledvalue & LED1_ACTIVE)
+		ledact |= GMII_LED1_ACTIVE;
+
+	if (ledvalue & LED2_ACTIVE)
+		ledact |= GMII_LED2_ACTIVE;
+
+	if (ledvalue & LED0_LINK_10)
+		ledlink |= GMII_LED0_LINK_10;
+
+	if (ledvalue & LED1_LINK_10)
+		ledlink |= GMII_LED1_LINK_10;
+
+	if (ledvalue & LED2_LINK_10)
+		ledlink |= GMII_LED2_LINK_10;
+
+	if (ledvalue & LED0_LINK_100)
+		ledlink |= GMII_LED0_LINK_100;
+
+	if (ledvalue & LED1_LINK_100)
+		ledlink |= GMII_LED1_LINK_100;
+
+	if (ledvalue & LED2_LINK_100)
+		ledlink |= GMII_LED2_LINK_100;
+
+	if (ledvalue & LED0_LINK_1000)
+		ledlink |= GMII_LED0_LINK_1000;
+
+	if (ledvalue & LED1_LINK_1000)
+		ledlink |= GMII_LED1_LINK_1000;
+
+	if (ledvalue & LED2_LINK_1000)
+		ledlink |= GMII_LED2_LINK_1000;
+
+	tmp = ledact;
+	ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, GMII_LED_ACT,
+			  2, &tmp);
+
+	tmp = ledlink;
+	ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, GMII_LED_LINK,
+			  2, &tmp);
+
+	tmp = GMII_PHY_PGSEL_PAGE0;
+	ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
+			  GMII_PHY_PAGE_SELECT, 2, &tmp);
+
+	/* LED full duplex setting */
+	ledfd = 0;
+	if (ledvalue & LED0_FD)
+		ledfd |= 0x01;
+	else if ((ledvalue & LED0_USB3_MASK) == 0)
+		ledfd |= 0x02;
+
+	if (ledvalue & LED1_FD)
+		ledfd |= 0x04;
+	else if ((ledvalue & LED1_USB3_MASK) == 0)
+		ledfd |= 0x08;
+
+	if (ledvalue & LED2_FD)
+		ledfd |= 0x10;
+	else if ((ledvalue & LED2_USB3_MASK) == 0)
+		ledfd |= 0x20;
+
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_LEDCTRL, 1, 1, &ledfd);
+
+	return 0;
+}
+
+static int ax88179_bind(struct usbnet *dev, struct usb_interface *intf)
+{
+	u8 buf[5];
+	u16 *tmp16;
+	u8 *tmp;
+	struct ax88179_data *ax179_data = (struct ax88179_data *)dev->data;
+
+	usbnet_get_endpoints(dev, intf);
+
+	tmp16 = (u16 *)buf;
+	tmp = (u8 *)buf;
+
+	memset(ax179_data, 0, sizeof(*ax179_data));
+
+	/* Power up ethernet PHY */
+	*tmp16 = 0;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, tmp16);
+	*tmp16 = AX_PHYPWR_RSTCTL_IPRL;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, tmp16);
+	msleep(200);
+
+	*tmp = AX_CLK_SELECT_ACS | AX_CLK_SELECT_BCS;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_CLK_SELECT, 1, 1, tmp);
+	msleep(100);
+
+	ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_NODE_ID, ETH_ALEN,
+			 ETH_ALEN, dev->net->dev_addr);
+	memcpy(dev->net->perm_addr, dev->net->dev_addr, ETH_ALEN);
+
+	/* RX bulk configuration */
+	memcpy(tmp, &AX88179_BULKIN_SIZE[0], 5);
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_BULKIN_QCTRL, 5, 5, tmp);
+
+	dev->rx_urb_size = 1024 * 20;
+
+	*tmp = 0x34;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PAUSE_WATERLVL_LOW, 1, 1, tmp);
+
+	*tmp = 0x52;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PAUSE_WATERLVL_HIGH,
+			  1, 1, tmp);
+
+	dev->net->netdev_ops = &ax88179_netdev_ops;
+	dev->net->ethtool_ops = &ax88179_ethtool_ops;
+	dev->net->needed_headroom = 8;
+
+	/* Initialize MII structure */
+	dev->mii.dev = dev->net;
+	dev->mii.mdio_read = ax88179_mdio_read;
+	dev->mii.mdio_write = ax88179_mdio_write;
+	dev->mii.phy_id_mask = 0xff;
+	dev->mii.reg_num_mask = 0xff;
+	dev->mii.phy_id = 0x03;
+	dev->mii.supports_gmii = 1;
+
+	dev->net->features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
+			      NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_TSO;
+
+	dev->net->hw_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
+				 NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_TSO;
+
+	/* Enable checksum offload */
+	*tmp = AX_RXCOE_IP | AX_RXCOE_TCP | AX_RXCOE_UDP |
+	       AX_RXCOE_TCPV6 | AX_RXCOE_UDPV6;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RXCOE_CTL, 1, 1, tmp);
+
+	*tmp = AX_TXCOE_IP | AX_TXCOE_TCP | AX_TXCOE_UDP |
+	       AX_TXCOE_TCPV6 | AX_TXCOE_UDPV6;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, 1, 1, tmp);
+
+	/* Configure RX control register => start operation */
+	*tmp16 = AX_RX_CTL_DROPCRCERR | AX_RX_CTL_IPE | AX_RX_CTL_START |
+		 AX_RX_CTL_AP | AX_RX_CTL_AMALL | AX_RX_CTL_AB;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, 0x0b, 2, 2, tmp16);
+
+	*tmp = AX_MONITOR_MODE_PMETYPE | AX_MONITOR_MODE_PMEPOL |
+	       AX_MONITOR_MODE_RWMP;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MONITOR_MOD, 1, 1, tmp);
+
+	/* Configure default medium type => giga */
+	*tmp16 = AX_MEDIUM_RECEIVE_EN | AX_MEDIUM_TXFLOW_CTRLEN |
+		 AX_MEDIUM_RXFLOW_CTRLEN | AX_MEDIUM_ALWAYS_ONE |
+		 AX_MEDIUM_FULL_DUPLEX | AX_MEDIUM_GIGAMODE;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,
+			  2, 2, tmp16);
+
+	ax88179_led_setting(dev);
+
+	/* Restart autoneg */
+	mii_nway_restart(&dev->mii);
+
+	netif_carrier_off(dev->net);
+
+	return 0;
+}
+
+static void ax88179_unbind(struct usbnet *dev, struct usb_interface *intf)
+{
+	u16 tmp16;
+
+	/* Configure RX control register => stop operation */
+	tmp16 = AX_RX_CTL_STOP;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2, &tmp16);
+
+	tmp16 = 0;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_CLK_SELECT, 1, 1, &tmp16);
+
+	/* Power down ethernet PHY */
+	tmp16 = 0;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, &tmp16);
+}
+
+static void
+ax88179_rx_checksum(struct sk_buff *skb, struct ax88179_rx_pkt_header *pkt_hdr)
+{
+	skb->ip_summed = CHECKSUM_NONE;
+
+	/* checksum error bit is set */
+	if (pkt_hdr->l3_csum_err || pkt_hdr->l4_csum_err)
+		return;
+
+	/* It must be a TCP or UDP packet with a valid checksum */
+	if ((pkt_hdr->l4_type == AX_RXHDR_L4_TYPE_TCP) ||
+	    (pkt_hdr->l4_type == AX_RXHDR_L4_TYPE_UDP))
+		skb->ip_summed = CHECKSUM_UNNECESSARY;
+}
+
+static int ax88179_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
+{
+	struct sk_buff *ax_skb;
+	int pkt_cnt;
+	u32 rx_hdr;
+	u16 hdr_off;
+	struct ax88179_rx_pkt_header *pkt_hdr;
+
+	skb_trim(skb, skb->len - 4);
+	memcpy(&rx_hdr, skb_tail_pointer(skb), sizeof(rx_hdr));
+	le32_to_cpus(&rx_hdr);
+
+	pkt_cnt = (u16)rx_hdr;
+	hdr_off = (u16)(rx_hdr >> 16);
+	pkt_hdr = (struct ax88179_rx_pkt_header *)(skb->data + hdr_off);
+
+
+	while (pkt_cnt--) {
+		u16 pkt_len;
+
+		le32_to_cpus((u32 *)pkt_hdr);
+		pkt_len = pkt_hdr->len;
+
+		/* Check CRC or runt packet */
+		if (pkt_hdr->crc || pkt_hdr->drop) {
+			skb_pull(skb, (pkt_len + 7) & 0xFFF8);
+			pkt_hdr++;
+			continue;
+		}
+
+		if (pkt_cnt == 0) {
+			/* Skip IP alignment psudo header */
+			skb_pull(skb, 2);
+			skb->len = pkt_len;
+			skb_set_tail_pointer(skb, pkt_len);
+			skb->truesize = pkt_len + sizeof(struct sk_buff);
+			ax88179_rx_checksum(skb, pkt_hdr);
+			return 1;
+		}
+
+		ax_skb = skb_clone(skb, GFP_ATOMIC);
+		if (ax_skb) {
+			ax_skb->len = pkt_len;
+			ax_skb->data = skb->data + 2;
+			skb_set_tail_pointer(ax_skb, pkt_len);
+			ax_skb->truesize = pkt_len + sizeof(struct sk_buff);
+			ax88179_rx_checksum(ax_skb, pkt_hdr);
+			usbnet_skb_return(dev, ax_skb);
+		} else {
+			return 0;
+		}
+
+		skb_pull(skb, (pkt_len + 7) & 0xFFF8);
+		pkt_hdr++;
+	}
+	return 1;
+}
+
+static struct sk_buff *
+ax88179_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags)
+{
+	u32 tx_hdr1, tx_hdr2;
+	int frame_size = dev->maxpacket;
+	int mss = skb_shinfo(skb)->gso_size;
+	int headroom;
+	int tailroom;
+
+	tx_hdr1 = skb->len;
+	tx_hdr2 = mss;
+	if (((skb->len + 8) % frame_size) == 0)
+		tx_hdr2 |= 0x80008000;	/* Enable padding */
+
+	skb_linearize(skb);
+	headroom = skb_headroom(skb);
+	tailroom = skb_tailroom(skb);
+
+	if (!skb_header_cloned(skb) &&
+	    !skb_cloned(skb) &&
+	    (headroom + tailroom) >= 8) {
+		if (headroom < 8) {
+			skb->data = memmove(skb->head + 8, skb->data, skb->len);
+			skb_set_tail_pointer(skb, skb->len);
+		}
+	} else {
+		struct sk_buff *skb2;
+
+		skb2 = skb_copy_expand(skb, 8, 0, flags);
+		dev_kfree_skb_any(skb);
+		skb = skb2;
+		if (!skb)
+			return NULL;
+	}
+
+	skb_push(skb, 4);
+	cpu_to_le32s(&tx_hdr2);
+	skb_copy_to_linear_data(skb, &tx_hdr2, 4);
+
+	skb_push(skb, 4);
+	cpu_to_le32s(&tx_hdr1);
+	skb_copy_to_linear_data(skb, &tx_hdr1, 4);
+
+	return skb;
+}
+
+static int ax88179_link_reset(struct usbnet *dev)
+{
+	struct ax88179_data *ax179_data = (struct ax88179_data *)dev->data;
+	u8 tmp[5], link_sts;
+	u16 mode, tmp16, delay = HZ / 10;
+	u32 tmp32 = 0x40000000;
+	unsigned long jtimeout;
+
+	jtimeout = jiffies + delay;
+	while (tmp32 & 0x40000000) {
+		mode = 0;
+		ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2, &mode);
+		ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2,
+				  &ax179_data->rxctl);
+
+		/*link up, check the usb device control TX FIFO full or empty*/
+		ax88179_read_cmd(dev, 0x81, 0x8c, 0, 4, &tmp32);
+
+		if (time_after(jiffies, jtimeout))
+			return 0;
+	}
+
+	mode = AX_MEDIUM_RECEIVE_EN | AX_MEDIUM_TXFLOW_CTRLEN |
+	       AX_MEDIUM_RXFLOW_CTRLEN | AX_MEDIUM_ALWAYS_ONE;
+
+	ax88179_read_cmd(dev, AX_ACCESS_MAC, PHYSICAL_LINK_STATUS,
+			 1, 1, &link_sts);
+
+	ax88179_read_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, GMII_PHY_PHYSR,
+			 2, &tmp16);
+
+	if (!(tmp16 & GMII_PHY_PHYSR_LINK)) {
+		return 0;
+	} else if (GMII_PHY_PHYSR_GIGA == (tmp16 & GMII_PHY_PHYSR_SMASK)) {
+		mode |= AX_MEDIUM_GIGAMODE | AX_MEDIUM_EN_125MHZ;
+		if (dev->net->mtu > 1500)
+			mode |= AX_MEDIUM_JUMBO_EN;
+
+		if (link_sts & AX_USB_SS)
+			memcpy(tmp, &AX88179_BULKIN_SIZE[0], 5);
+		else if (link_sts & AX_USB_HS)
+			memcpy(tmp, &AX88179_BULKIN_SIZE[1], 5);
+		else
+			memcpy(tmp, &AX88179_BULKIN_SIZE[3], 5);
+	} else if (GMII_PHY_PHYSR_100 == (tmp16 & GMII_PHY_PHYSR_SMASK)) {
+		mode |= AX_MEDIUM_PS;
+
+		if (link_sts & (AX_USB_SS | AX_USB_HS))
+			memcpy(tmp, &AX88179_BULKIN_SIZE[2], 5);
+		else
+			memcpy(tmp, &AX88179_BULKIN_SIZE[3], 5);
+	} else {
+		memcpy(tmp, &AX88179_BULKIN_SIZE[3], 5);
+	}
+
+	/* RX bulk configuration */
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_BULKIN_QCTRL, 5, 5, tmp);
+
+	dev->rx_urb_size = (1024 * (tmp[3] + 2));
+
+	if (tmp16 & GMII_PHY_PHYSR_FULL)
+		mode |= AX_MEDIUM_FULL_DUPLEX;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,
+			  2, 2, &mode);
+
+	netif_carrier_on(dev->net);
+
+	return 0;
+}
+
+static int ax88179_reset(struct usbnet *dev)
+{
+	u8 buf[5];
+	u16 *tmp16;
+	u8 *tmp;
+
+	tmp16 = (u16 *)buf;
+	tmp = (u8 *)buf;
+
+	/* Power up ethernet PHY */
+	*tmp16 = 0;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, tmp16);
+
+	*tmp16 = AX_PHYPWR_RSTCTL_IPRL;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, tmp16);
+	msleep(200);
+
+	*tmp = AX_CLK_SELECT_ACS | AX_CLK_SELECT_BCS;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_CLK_SELECT, 1, 1, tmp);
+	msleep(100);
+
+	/* Ethernet PHY Auto Detach*/
+	ax88179_auto_detach(dev, 0);
+
+	ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_NODE_ID, ETH_ALEN, ETH_ALEN,
+			 dev->net->dev_addr);
+	memcpy(dev->net->perm_addr, dev->net->dev_addr, ETH_ALEN);
+
+	/* RX bulk configuration */
+	memcpy(tmp, &AX88179_BULKIN_SIZE[0], 5);
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_BULKIN_QCTRL, 5, 5, tmp);
+
+	dev->rx_urb_size = 1024 * 20;
+
+	*tmp = 0x34;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PAUSE_WATERLVL_LOW, 1, 1, tmp);
+
+	*tmp = 0x52;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PAUSE_WATERLVL_HIGH,
+			  1, 1, tmp);
+
+	dev->net->features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
+			      NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_TSO;
+
+	dev->net->hw_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
+				 NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_TSO;
+
+	/* Enable checksum offload */
+	*tmp = AX_RXCOE_IP | AX_RXCOE_TCP | AX_RXCOE_UDP |
+	       AX_RXCOE_TCPV6 | AX_RXCOE_UDPV6;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RXCOE_CTL, 1, 1, tmp);
+
+	*tmp = AX_TXCOE_IP | AX_TXCOE_TCP | AX_TXCOE_UDP |
+	       AX_TXCOE_TCPV6 | AX_TXCOE_UDPV6;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, 1, 1, tmp);
+
+	/* Configure RX control register => start operation */
+	*tmp16 = AX_RX_CTL_DROPCRCERR | AX_RX_CTL_IPE | AX_RX_CTL_START |
+		 AX_RX_CTL_AP | AX_RX_CTL_AMALL | AX_RX_CTL_AB;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, 0x0b, 2, 2, tmp16);
+
+	*tmp = AX_MONITOR_MODE_PMETYPE | AX_MONITOR_MODE_PMEPOL |
+	       AX_MONITOR_MODE_RWMP;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MONITOR_MOD, 1, 1, tmp);
+
+	/* Configure default medium type => giga */
+	*tmp16 = AX_MEDIUM_RECEIVE_EN | AX_MEDIUM_TXFLOW_CTRLEN |
+		 AX_MEDIUM_RXFLOW_CTRLEN | AX_MEDIUM_ALWAYS_ONE |
+		 AX_MEDIUM_FULL_DUPLEX | AX_MEDIUM_GIGAMODE;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,
+			  2, 2, tmp16);
+
+	ax88179_led_setting(dev);
+
+	/* Restart autoneg */
+	mii_nway_restart(&dev->mii);
+
+	netif_carrier_off(dev->net);
+
+	return 0;
+}
+
+static int ax88179_stop(struct usbnet *dev)
+{
+	u16 tmp16;
+
+	ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,
+			 2, 2, &tmp16);
+	tmp16 &= ~AX_MEDIUM_RECEIVE_EN;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,
+			  2, 2, &tmp16);
+
+	return 0;
+}
+
+static const struct driver_info ax88179_info = {
+	.description = "ASIX AX88179 USB 3.0 Gigibit Ethernet",
+	.bind = ax88179_bind,
+	.unbind = ax88179_unbind,
+	.status = ax88179_status,
+	.link_reset = ax88179_link_reset,
+	.reset = ax88179_reset,
+	.stop = ax88179_stop,
+	.flags = FLAG_ETHER | FLAG_FRAMING_AX,
+	.rx_fixup = ax88179_rx_fixup,
+	.tx_fixup = ax88179_tx_fixup,
+};
+
+static const struct driver_info ax88178a_info = {
+	.description = "ASIX AX88178A USB 2.0 Gigibit Ethernet",
+	.bind = ax88179_bind,
+	.unbind = ax88179_unbind,
+	.status = ax88179_status,
+	.link_reset = ax88179_link_reset,
+	.reset = ax88179_reset,
+	.stop = ax88179_stop,
+	.flags = FLAG_ETHER | FLAG_FRAMING_AX,
+	.rx_fixup = ax88179_rx_fixup,
+	.tx_fixup = ax88179_tx_fixup,
+};
+
+static const struct driver_info sitecom_info = {
+	.description = "Sitecom USB 3.0 to Gigabit Adapter",
+	.bind = ax88179_bind,
+	.unbind = ax88179_unbind,
+	.status = ax88179_status,
+	.link_reset = ax88179_link_reset,
+	.reset = ax88179_reset,
+	.stop = ax88179_stop,
+	.flags = FLAG_ETHER | FLAG_FRAMING_AX,
+	.rx_fixup = ax88179_rx_fixup,
+	.tx_fixup = ax88179_tx_fixup,
+};
+
+static const struct usb_device_id products[] = {
+{
+	/* ASIX AX88179 10/100/1000 */
+	USB_DEVICE(0x0b95, 0x1790),
+	.driver_info = (unsigned long)&ax88179_info,
+}, {
+	/* ASIX AX88178A 10/100/1000 */
+	USB_DEVICE(0x0b95, 0x178a),
+	.driver_info = (unsigned long)&ax88178a_info,
+}, {
+	/* Sitecom USB 3.0 to Gigabit Adapter */
+	USB_DEVICE(0x0df6, 0x0072),
+	.driver_info = (unsigned long) &sitecom_info,
+},
+	{ },
+};
+MODULE_DEVICE_TABLE(usb, products);
+
+static struct usb_driver asix_driver = {
+	.name =		"ax88179_178a",
+	.id_table =	products,
+	.probe =	usbnet_probe,
+	.suspend =	ax88179_suspend,
+	.resume =	ax88179_resume,
+	.disconnect =	usbnet_disconnect,
+};
+
+static int __init asix_init(void)
+{
+	return usb_register(&asix_driver);
+}
+module_init(asix_init);
+
+static void __exit asix_exit(void)
+{
+	usb_deregister(&asix_driver);
+}
+module_exit(asix_exit);
+
+MODULE_DESCRIPTION("ASIX AX88179/178A based USB 3.0/2.0 Gigabit Ethernet Devices");
+MODULE_LICENSE("GPL");
-- 
1.7.9.5


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

* Re: [PATCH, resubmit] ax88179_178a: ASIX AX88179_178A USB 3.0/2.0 to gigabit ethernet adapter driver
  2013-02-07 13:39 [PATCH, resubmit] ax88179_178a: ASIX AX88179_178A USB 3.0/2.0 to gigabit ethernet adapter driver Freddy Xin
@ 2013-02-07 20:02 ` Ben Hutchings
  2013-02-07 20:05   ` David Miller
                     ` (2 more replies)
  0 siblings, 3 replies; 27+ messages in thread
From: Ben Hutchings @ 2013-02-07 20:02 UTC (permalink / raw)
  To: Freddy Xin; +Cc: netdev, davem, linux-usb, linux-kernel

On Thu, 2013-02-07 at 21:39 +0800, Freddy Xin wrote:
> From: Freddy Xin <freddy@asix.com.tw>
> 
> This is a resubmission.
> Added "const" to ethtool_ops structure and fixed the coding style of AX88179_BULKIN_SIZE array.
> Fixed the issue that the default MTU is not 1500.
> Added ax88179_change_mtu function and enabled the hardware jumbo frame function to support an
> MTU higher than 1500.
> Fixed indentation and empty line coding style errors.
> The _nopm version usb functions were added to access register in suspend and resume functions.
> Serveral variables allocted dynamically were removed and replaced by stack variables.
> ax88179_get_eeprom were modified from asix_get_eeprom in asix_common.
> 
> This patch adds a driver for ASIX's AX88179 family of USB 3.0/2.0
> to gigabit ethernet adapters. It's based on the AX88xxx driver but
> the usb commands used to access registers for AX88179 are completely different.
> This driver had been verified on x86 system with AX88179/AX88178A and
> Sitcomm LN-032 USB dongles.
> 
> Signed-off-by: Freddy Xin <freddy@asix.com.tw>
[...]
> --- /dev/null
> +++ b/drivers/net/usb/ax88179_178a.c
[...]
> +struct ax88179_data {
> +       u16 rxctl;
> +};

Should also be declared __packed, as on some architectures it may be
padded to 4 bytes.

rxctl is presumably required to be little-endian (__le16)?

> +struct ax88179_int_data {
> +       u16 res1;
> +       u8 link;
> +       u16 res2;
> +       u8 status;
> +       u16 res3;
> +} __packed;

Same here, the u16 fields are presumably little-endian?

[...]
> +struct ax88179_rx_pkt_header {
> +	u8	l4_csum_err:1,
> +		l3_csum_err:1,
> +		l4_type:3,
> +		l3_type:2,
> +		ce:1;
> +
> +	u8	vlan_ind:3,
> +		rx_ok:1,
> +		pri:3,
> +		bmc:1;
> +
> +	u16	len:13,
> +		crc:1,
> +		mii:1,
> +		drop:1;
> +} __packed;

This won't work on both big-endian systems (assuming this works on x86).
You apparently try to convert the structure in-place in
ax88179_rx_fixup() by calling le32_to_cpus(); that may work if you
define all the bitfields to be part of a u32 but it won't work with the
current definition.

[...]
> +static int
> +ax88179_set_features(struct net_device *net, netdev_features_t features)
> +{
> +	u8 tmp;
> +	struct usbnet *dev = netdev_priv(net);
> +	netdev_features_t changed = net->features ^ features;
> +
> +	if (changed & NETIF_F_TSO)
> +		net->features ^= NETIF_F_TSO;
> +
> +	if (changed & NETIF_F_SG)
> +		net->features ^= NETIF_F_SG;

Don't change net->features; the caller will do that for you.

> +       if (changed & NETIF_F_IP_CSUM) {
> +               ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, 1, 1, &tmp);
> +               tmp ^= AX_TXCOE_TCP | AX_TXCOE_UDP;
> +               ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, 1, 1, &tmp);
> +
> +               net->features ^= NETIF_F_IP_CSUM;
> +       }
[...]

Isn't tmp going to be in little-endian byte order, so this doesn't work
correctly on a big-endian system?

There are a lot of reads and writes of 16-bit registers using
ax88179_{read,write}_cmd(); maybe you should add
ax88179_{read,write}_le16() to handle this specific case.

Ben.

-- 
Ben Hutchings, Staff Engineer, Solarflare
Not speaking for my employer; that's the marketing department's job.
They asked us to note that Solarflare product names are trademarked.


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

* Re: [PATCH, resubmit] ax88179_178a: ASIX AX88179_178A USB 3.0/2.0 to gigabit ethernet adapter driver
  2013-02-07 20:02 ` Ben Hutchings
@ 2013-02-07 20:05   ` David Miller
  2013-02-08  3:41   ` Freddy
  2013-02-08 10:23   ` David Laight
  2 siblings, 0 replies; 27+ messages in thread
From: David Miller @ 2013-02-07 20:05 UTC (permalink / raw)
  To: bhutchings; +Cc: freddy, netdev, linux-usb, linux-kernel

From: Ben Hutchings <bhutchings@solarflare.com>
Date: Thu, 7 Feb 2013 20:02:55 +0000

> On Thu, 2013-02-07 at 21:39 +0800, Freddy Xin wrote:
>> From: Freddy Xin <freddy@asix.com.tw>
>> 
>> This is a resubmission.
>> Added "const" to ethtool_ops structure and fixed the coding style of AX88179_BULKIN_SIZE array.
>> Fixed the issue that the default MTU is not 1500.
>> Added ax88179_change_mtu function and enabled the hardware jumbo frame function to support an
>> MTU higher than 1500.
>> Fixed indentation and empty line coding style errors.
>> The _nopm version usb functions were added to access register in suspend and resume functions.
>> Serveral variables allocted dynamically were removed and replaced by stack variables.
>> ax88179_get_eeprom were modified from asix_get_eeprom in asix_common.
>> 
>> This patch adds a driver for ASIX's AX88179 family of USB 3.0/2.0
>> to gigabit ethernet adapters. It's based on the AX88xxx driver but
>> the usb commands used to access registers for AX88179 are completely different.
>> This driver had been verified on x86 system with AX88179/AX88178A and
>> Sitcomm LN-032 USB dongles.
>> 
>> Signed-off-by: Freddy Xin <freddy@asix.com.tw>
> [...]
>> --- /dev/null
>> +++ b/drivers/net/usb/ax88179_178a.c
> [...]
>> +struct ax88179_data {
>> +       u16 rxctl;
>> +};
> 
> Should also be declared __packed, as on some architectures it may be
> padded to 4 bytes.

Packed is evil and should be avoid everywhere that it is possible to
do so.  It's going to use byte loads on this u16 on RISC cpus simply
because __packed makes it so that the compiler cannot assume data
alignment any longer.

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

* Re: [PATCH, resubmit] ax88179_178a: ASIX AX88179_178A USB 3.0/2.0 to gigabit ethernet adapter driver
  2013-02-07 20:02 ` Ben Hutchings
  2013-02-07 20:05   ` David Miller
@ 2013-02-08  3:41   ` Freddy
  2013-02-08 10:23   ` David Laight
  2 siblings, 0 replies; 27+ messages in thread
From: Freddy @ 2013-02-08  3:41 UTC (permalink / raw)
  To: Ben Hutchings; +Cc: netdev, davem, linux-usb, linux-kernel

On 02/08/2013 04:02 AM, Ben Hutchings wrote:
> On Thu, 2013-02-07 at 21:39 +0800, Freddy Xin wrote:
>> From: Freddy Xin <freddy@asix.com.tw>
>>
>> This is a resubmission.
>> Added "const" to ethtool_ops structure and fixed the coding style of AX88179_BULKIN_SIZE array.
>> Fixed the issue that the default MTU is not 1500.
>> Added ax88179_change_mtu function and enabled the hardware jumbo frame function to support an
>> MTU higher than 1500.
>> Fixed indentation and empty line coding style errors.
>> The _nopm version usb functions were added to access register in suspend and resume functions.
>> Serveral variables allocted dynamically were removed and replaced by stack variables.
>> ax88179_get_eeprom were modified from asix_get_eeprom in asix_common.
>>
>> This patch adds a driver for ASIX's AX88179 family of USB 3.0/2.0
>> to gigabit ethernet adapters. It's based on the AX88xxx driver but
>> the usb commands used to access registers for AX88179 are completely different.
>> This driver had been verified on x86 system with AX88179/AX88178A and
>> Sitcomm LN-032 USB dongles.
>>
>> Signed-off-by: Freddy Xin <freddy@asix.com.tw>
> [...]
>> --- /dev/null
>> +++ b/drivers/net/usb/ax88179_178a.c
> [...]
>> +struct ax88179_data {
>> +       u16 rxctl;
>> +};
> Should also be declared __packed, as on some architectures it may be
> padded to 4 bytes.
>
> rxctl is presumably required to be little-endian (__le16)?
>
>> +struct ax88179_int_data {
>> +       u16 res1;
>> +       u8 link;
>> +       u16 res2;
>> +       u8 status;
>> +       u16 res3;
>> +} __packed;
> Same here, the u16 fields are presumably little-endian?
>
> [...]
>> +struct ax88179_rx_pkt_header {
>> +	u8	l4_csum_err:1,
>> +		l3_csum_err:1,
>> +		l4_type:3,
>> +		l3_type:2,
>> +		ce:1;
>> +
>> +	u8	vlan_ind:3,
>> +		rx_ok:1,
>> +		pri:3,
>> +		bmc:1;
>> +
>> +	u16	len:13,
>> +		crc:1,
>> +		mii:1,
>> +		drop:1;
>> +} __packed;
> This won't work on both big-endian systems (assuming this works on x86).
> You apparently try to convert the structure in-place in
> ax88179_rx_fixup() by calling le32_to_cpus(); that may work if you
> define all the bitfields to be part of a u32 but it won't work with the
> current definition.
>
> [...]
>> +static int
>> +ax88179_set_features(struct net_device *net, netdev_features_t features)
>> +{
>> +	u8 tmp;
>> +	struct usbnet *dev = netdev_priv(net);
>> +	netdev_features_t changed = net->features ^ features;
>> +
>> +	if (changed & NETIF_F_TSO)
>> +		net->features ^= NETIF_F_TSO;
>> +
>> +	if (changed & NETIF_F_SG)
>> +		net->features ^= NETIF_F_SG;
> Don't change net->features; the caller will do that for you.
>
>> +       if (changed & NETIF_F_IP_CSUM) {
>> +               ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, 1, 1, &tmp);
>> +               tmp ^= AX_TXCOE_TCP | AX_TXCOE_UDP;
>> +               ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, 1, 1, &tmp);
>> +
>> +               net->features ^= NETIF_F_IP_CSUM;
>> +       }
> [...]
>
> Isn't tmp going to be in little-endian byte order, so this doesn't work
> correctly on a big-endian system?
>
> There are a lot of reads and writes of 16-bit registers using
> ax88179_{read,write}_cmd(); maybe you should add
> ax88179_{read,write}_le16() to handle this specific case.
>
> Ben.
>
Thank you, Ben. I will fix bugs and test it on a big-endian system.

Freddy

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

* RE: [PATCH, resubmit] ax88179_178a: ASIX AX88179_178A USB 3.0/2.0 to gigabit ethernet adapter driver
  2013-02-07 20:02 ` Ben Hutchings
  2013-02-07 20:05   ` David Miller
  2013-02-08  3:41   ` Freddy
@ 2013-02-08 10:23   ` David Laight
  2013-02-08 19:23     ` David Miller
  2 siblings, 1 reply; 27+ messages in thread
From: David Laight @ 2013-02-08 10:23 UTC (permalink / raw)
  To: Ben Hutchings, Freddy Xin; +Cc: netdev, davem, linux-usb, linux-kernel

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset="utf-8", Size: 1087 bytes --]

> > +struct ax88179_rx_pkt_header {
> > +	u8	l4_csum_err:1,
> > +		l3_csum_err:1,
> > +		l4_type:3,
> > +		l3_type:2,
> > +		ce:1;
> > +
> > +	u8	vlan_ind:3,
> > +		rx_ok:1,
> > +		pri:3,
> > +		bmc:1;
> > +
> > +	u16	len:13,
> > +		crc:1,
> > +		mii:1,
> > +		drop:1;
> > +} __packed;
> 
> This won't work on both big-endian systems (assuming this works on x86).
> You apparently try to convert the structure in-place in
> ax88179_rx_fixup() by calling le32_to_cpus(); that may work if you
> define all the bitfields to be part of a u32 but it won't work with the
> current definition.

Trying to use bit fields to map hardware registers (etc) isn't a
good idea at all. The C standard says absolutely nothing about
the order in which the bits are allocated to the field names.
(There are at least 4 possible orederings.)

It is much better to define constants for the bit values and
explicitly mask them as required.

	David

ÿôèº{.nÇ+‰·Ÿ®‰­†+%ŠËÿ±éݶ\x17¥Šwÿº{.nÇ+‰·¥Š{±þG«éÿŠ{ayº\x1dʇڙë,j\a­¢f£¢·hšïêÿ‘êçz_è®\x03(­éšŽŠÝ¢j"ú\x1a¶^[m§ÿÿ¾\a«þG«éÿ¢¸?™¨è­Ú&£ø§~á¶iO•æ¬z·švØ^\x14\x04\x1a¶^[m§ÿÿÃ\fÿ¶ìÿ¢¸?–I¥

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

* Re: [PATCH, resubmit] ax88179_178a: ASIX AX88179_178A USB 3.0/2.0 to gigabit ethernet adapter driver
  2013-02-08 10:23   ` David Laight
@ 2013-02-08 19:23     ` David Miller
  0 siblings, 0 replies; 27+ messages in thread
From: David Miller @ 2013-02-08 19:23 UTC (permalink / raw)
  To: David.Laight; +Cc: bhutchings, freddy, netdev, linux-usb, linux-kernel

From: "David Laight" <David.Laight@ACULAB.COM>
Date: Fri, 8 Feb 2013 10:23:08 -0000

> It is much better to define constants for the bit values and
> explicitly mask them as required.

Yes, __be32/__le32 along with bit define macros is the only reasonable
way to do this kind of stuff.

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

* RE: [PATCH, resubmit] ax88179_178a: ASIX AX88179_178A USB 3.0/2.0 to gigabit ethernet adapter driver
  2013-02-03 14:21 Daniel J Blueman
@ 2013-12-04 12:48 ` Freddy
  0 siblings, 0 replies; 27+ messages in thread
From: Freddy @ 2013-12-04 12:48 UTC (permalink / raw)
  To: 'Daniel J Blueman'; +Cc: 'Linux Kernel'



-----Original Message-----
From: Daniel J Blueman [mailto:daniel@quora.org] 
Sent: Sunday, February 03, 2013 10:22 PM
To: Freddy Xin
Cc: Linux Kernel
Subject: Re: [PATCH, resubmit] ax88179_178a: ASIX AX88179_178A USB 3.0/2.0
to gigabit ethernet adapter driver

> Hi Freddy,

> Michael, could you give me more information about how do you test this
driver?
> I have tried to reproduce the issue by using "ifconfig ethX mtu 1500", but
I didn't confront the same issue.
> Thank you in advance for your help.

> I found the same by just starting with 'ifconfig eth0 1500' and testing as
high as 4000; pinging another host with a large payload of size mtu-40
starts failing; after ~30s, I see the transmit time out trace [1].

> Of course, a default MTU size of 1500 is essential to avoid fragmentation
issues, so should be fixed too. Jumbo frames support is essential these days
too.

Hi Daniel,

The default MTU size will be change to 1500 and the HW jumbo frame function
will be enabled in the next submission of the driver.
Thanks for your help.

Freddy


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

* Re: [PATCH resubmit] ax88179_178a: ASIX AX88179_178A USB 3.0/2.0 to gigabit ethernet adapter driver
  2013-03-02 10:41 [PATCH " Freddy Xin
@ 2013-03-03  6:44 ` David Miller
  0 siblings, 0 replies; 27+ messages in thread
From: David Miller @ 2013-03-03  6:44 UTC (permalink / raw)
  To: freddy; +Cc: netdev, linux-usb, linux-kernel, louis

From: Freddy Xin <freddy@asix.com.tw>
Date: Sat,  2 Mar 2013 18:41:11 +0800

> From: Freddy Xin <freddy@asix.com.tw>
> 
> This is a resubmission.
> Added kfree() in ax88179_get_eeprom to prevent memory leakage.
> Modified "__le16 rxctl" to "u16 rxctl" in "struct ax88179_data" and removed pointless casts.
> Removed asix_init and asix_exit functions and added "module_usb_driver(ax88179_178a_driver)".
> Fixed endianness issue on big endian systems and verified this driver on iBook G4.
> Removed steps that change net->features in ax88179_set_features function.
> Added "const" to ethtool_ops structure and fixed the coding style of AX88179_BULKIN_SIZE array.
> Fixed the issue that the default MTU is not 1500.
> Added ax88179_change_mtu function and enabled the hardware jumbo frame function to support an
> MTU higher than 1500.
> Fixed indentation and empty line coding style errors.
> The _nopm version usb functions were added to access register in suspend and resume functions.
> Serveral variables allocted dynamically were removed and replaced by stack variables.
> ax88179_get_eeprom were modified from asix_get_eeprom in asix_common.
> 
> This patch adds a driver for ASIX's AX88179 family of USB 3.0/2.0
> to gigabit ethernet adapters. It's based on the AX88xxx driver but
> the usb commands used to access registers for AX88179 are completely different.
> This driver had been verified on x86 system with AX88179/AX88178A and
> Sitcomm LN-032 USB dongles.
> 
> Signed-off-by: Freddy Xin <freddy@asix.com.tw>

Applied.

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

* [PATCH resubmit] ax88179_178a: ASIX AX88179_178A USB 3.0/2.0 to gigabit ethernet adapter driver
@ 2013-03-02 10:41 Freddy Xin
  2013-03-03  6:44 ` David Miller
  0 siblings, 1 reply; 27+ messages in thread
From: Freddy Xin @ 2013-03-02 10:41 UTC (permalink / raw)
  To: netdev; +Cc: linux-usb, linux-kernel, louis, Freddy Xin

From: Freddy Xin <freddy@asix.com.tw>

This is a resubmission.
Added kfree() in ax88179_get_eeprom to prevent memory leakage.
Modified "__le16 rxctl" to "u16 rxctl" in "struct ax88179_data" and removed pointless casts.
Removed asix_init and asix_exit functions and added "module_usb_driver(ax88179_178a_driver)".
Fixed endianness issue on big endian systems and verified this driver on iBook G4.
Removed steps that change net->features in ax88179_set_features function.
Added "const" to ethtool_ops structure and fixed the coding style of AX88179_BULKIN_SIZE array.
Fixed the issue that the default MTU is not 1500.
Added ax88179_change_mtu function and enabled the hardware jumbo frame function to support an
MTU higher than 1500.
Fixed indentation and empty line coding style errors.
The _nopm version usb functions were added to access register in suspend and resume functions.
Serveral variables allocted dynamically were removed and replaced by stack variables.
ax88179_get_eeprom were modified from asix_get_eeprom in asix_common.

This patch adds a driver for ASIX's AX88179 family of USB 3.0/2.0
to gigabit ethernet adapters. It's based on the AX88xxx driver but
the usb commands used to access registers for AX88179 are completely different.
This driver had been verified on x86 system with AX88179/AX88178A and
Sitcomm LN-032 USB dongles.

Signed-off-by: Freddy Xin <freddy@asix.com.tw>
---
 drivers/net/usb/Kconfig        |   18 +
 drivers/net/usb/Makefile       |    1 +
 drivers/net/usb/ax88179_178a.c | 1448 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 1467 insertions(+)
 create mode 100644 drivers/net/usb/ax88179_178a.c

diff --git a/drivers/net/usb/Kconfig b/drivers/net/usb/Kconfig
index da92ed3..21e084d 100644
--- a/drivers/net/usb/Kconfig
+++ b/drivers/net/usb/Kconfig
@@ -156,6 +156,24 @@ config USB_NET_AX8817X
 	  This driver creates an interface named "ethX", where X depends on
 	  what other networking devices you have in use.
 
+config USB_NET_AX88179_178A
+	tristate "ASIX AX88179/178A USB 3.0/2.0 to Gigabit Ethernet"
+	depends on USB_USBNET
+	select CRC32
+	select PHYLIB
+	default y
+	help
+	  This option adds support for ASIX AX88179 based USB 3.0/2.0
+	  to Gigabit Ethernet adapters.
+
+ 	  This driver should work with at least the following devices:
+	    * ASIX AX88179
+	    * ASIX AX88178A
+	    * Sitcomm LN-032
+
+	  This driver creates an interface named "ethX", where X depends on
+	  what other networking devices you have in use.
+
 config USB_NET_CDCETHER
 	tristate "CDC Ethernet support (smart devices such as cable modems)"
 	depends on USB_USBNET
diff --git a/drivers/net/usb/Makefile b/drivers/net/usb/Makefile
index 4786913..119b06c 100644
--- a/drivers/net/usb/Makefile
+++ b/drivers/net/usb/Makefile
@@ -9,6 +9,7 @@ obj-$(CONFIG_USB_RTL8150)	+= rtl8150.o
 obj-$(CONFIG_USB_HSO)		+= hso.o
 obj-$(CONFIG_USB_NET_AX8817X)	+= asix.o
 asix-y := asix_devices.o asix_common.o ax88172a.o
+obj-$(CONFIG_USB_NET_AX88179_178A)      += ax88179_178a.o
 obj-$(CONFIG_USB_NET_CDCETHER)	+= cdc_ether.o
 obj-$(CONFIG_USB_NET_CDC_EEM)	+= cdc_eem.o
 obj-$(CONFIG_USB_NET_DM9601)	+= dm9601.o
diff --git a/drivers/net/usb/ax88179_178a.c b/drivers/net/usb/ax88179_178a.c
new file mode 100644
index 0000000..71c27d8
--- /dev/null
+++ b/drivers/net/usb/ax88179_178a.c
@@ -0,0 +1,1448 @@
+/*
+ * ASIX AX88179/178A USB 3.0/2.0 to Gigabit Ethernet Devices
+ *
+ * Copyright (C) 2011-2013 ASIX
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/etherdevice.h>
+#include <linux/mii.h>
+#include <linux/usb.h>
+#include <linux/crc32.h>
+#include <linux/usb/usbnet.h>
+
+#define AX88179_PHY_ID				0x03
+#define AX_EEPROM_LEN				0x100
+#define AX88179_EEPROM_MAGIC			0x17900b95
+#define AX_MCAST_FLTSIZE			8
+#define AX_MAX_MCAST				64
+#define AX_INT_PPLS_LINK			((u32)BIT(16))
+#define AX_RXHDR_L4_TYPE_MASK			0x1c
+#define AX_RXHDR_L4_TYPE_UDP			4
+#define AX_RXHDR_L4_TYPE_TCP			16
+#define AX_RXHDR_L3CSUM_ERR			2
+#define AX_RXHDR_L4CSUM_ERR			1
+#define AX_RXHDR_CRC_ERR			((u32)BIT(31))
+#define AX_RXHDR_DROP_ERR			((u32)BIT(30))
+#define AX_ACCESS_MAC				0x01
+#define AX_ACCESS_PHY				0x02
+#define AX_ACCESS_EEPROM			0x04
+#define AX_ACCESS_EFUS				0x05
+#define AX_PAUSE_WATERLVL_HIGH			0x54
+#define AX_PAUSE_WATERLVL_LOW			0x55
+
+#define PHYSICAL_LINK_STATUS			0x02
+	#define	AX_USB_SS		0x04
+	#define	AX_USB_HS		0x02
+
+#define GENERAL_STATUS				0x03
+/* Check AX88179 version. UA1:Bit2 = 0,  UA2:Bit2 = 1 */
+	#define	AX_SECLD		0x04
+
+#define AX_SROM_ADDR				0x07
+#define AX_SROM_CMD				0x0a
+	#define EEP_RD			0x04
+	#define EEP_BUSY		0x10
+
+#define AX_SROM_DATA_LOW			0x08
+#define AX_SROM_DATA_HIGH			0x09
+
+#define AX_RX_CTL				0x0b
+	#define AX_RX_CTL_DROPCRCERR	0x0100
+	#define AX_RX_CTL_IPE		0x0200
+	#define AX_RX_CTL_START		0x0080
+	#define AX_RX_CTL_AP		0x0020
+	#define AX_RX_CTL_AM		0x0010
+	#define AX_RX_CTL_AB		0x0008
+	#define AX_RX_CTL_AMALL		0x0002
+	#define AX_RX_CTL_PRO		0x0001
+	#define AX_RX_CTL_STOP		0x0000
+
+#define AX_NODE_ID				0x10
+#define AX_MULFLTARY				0x16
+
+#define AX_MEDIUM_STATUS_MODE			0x22
+	#define AX_MEDIUM_GIGAMODE	0x01
+	#define AX_MEDIUM_FULL_DUPLEX	0x02
+	#define AX_MEDIUM_ALWAYS_ONE	0x04
+	#define AX_MEDIUM_EN_125MHZ	0x08
+	#define AX_MEDIUM_RXFLOW_CTRLEN	0x10
+	#define AX_MEDIUM_TXFLOW_CTRLEN	0x20
+	#define AX_MEDIUM_RECEIVE_EN	0x100
+	#define AX_MEDIUM_PS		0x200
+	#define AX_MEDIUM_JUMBO_EN	0x8040
+
+#define AX_MONITOR_MOD				0x24
+	#define AX_MONITOR_MODE_RWLC	0x02
+	#define AX_MONITOR_MODE_RWMP	0x04
+	#define AX_MONITOR_MODE_PMEPOL	0x20
+	#define AX_MONITOR_MODE_PMETYPE	0x40
+
+#define AX_GPIO_CTRL				0x25
+	#define AX_GPIO_CTRL_GPIO3EN	0x80
+	#define AX_GPIO_CTRL_GPIO2EN	0x40
+	#define AX_GPIO_CTRL_GPIO1EN	0x20
+
+#define AX_PHYPWR_RSTCTL			0x26
+	#define AX_PHYPWR_RSTCTL_BZ	0x0010
+	#define AX_PHYPWR_RSTCTL_IPRL	0x0020
+	#define AX_PHYPWR_RSTCTL_AT	0x1000
+
+#define AX_RX_BULKIN_QCTRL			0x2e
+#define AX_CLK_SELECT				0x33
+	#define AX_CLK_SELECT_BCS	0x01
+	#define AX_CLK_SELECT_ACS	0x02
+	#define AX_CLK_SELECT_ULR	0x08
+
+#define AX_RXCOE_CTL				0x34
+	#define AX_RXCOE_IP		0x01
+	#define AX_RXCOE_TCP		0x02
+	#define AX_RXCOE_UDP		0x04
+	#define AX_RXCOE_TCPV6		0x20
+	#define AX_RXCOE_UDPV6		0x40
+
+#define AX_TXCOE_CTL				0x35
+	#define AX_TXCOE_IP		0x01
+	#define AX_TXCOE_TCP		0x02
+	#define AX_TXCOE_UDP		0x04
+	#define AX_TXCOE_TCPV6		0x20
+	#define AX_TXCOE_UDPV6		0x40
+
+#define AX_LEDCTRL				0x73
+
+#define GMII_PHY_PHYSR				0x11
+	#define GMII_PHY_PHYSR_SMASK	0xc000
+	#define GMII_PHY_PHYSR_GIGA	0x8000
+	#define GMII_PHY_PHYSR_100	0x4000
+	#define GMII_PHY_PHYSR_FULL	0x2000
+	#define GMII_PHY_PHYSR_LINK	0x400
+
+#define GMII_LED_ACT				0x1a
+	#define	GMII_LED_ACTIVE_MASK	0xff8f
+	#define	GMII_LED0_ACTIVE	BIT(4)
+	#define	GMII_LED1_ACTIVE	BIT(5)
+	#define	GMII_LED2_ACTIVE	BIT(6)
+
+#define GMII_LED_LINK				0x1c
+	#define	GMII_LED_LINK_MASK	0xf888
+	#define	GMII_LED0_LINK_10	BIT(0)
+	#define	GMII_LED0_LINK_100	BIT(1)
+	#define	GMII_LED0_LINK_1000	BIT(2)
+	#define	GMII_LED1_LINK_10	BIT(4)
+	#define	GMII_LED1_LINK_100	BIT(5)
+	#define	GMII_LED1_LINK_1000	BIT(6)
+	#define	GMII_LED2_LINK_10	BIT(8)
+	#define	GMII_LED2_LINK_100	BIT(9)
+	#define	GMII_LED2_LINK_1000	BIT(10)
+	#define	LED0_ACTIVE		BIT(0)
+	#define	LED0_LINK_10		BIT(1)
+	#define	LED0_LINK_100		BIT(2)
+	#define	LED0_LINK_1000		BIT(3)
+	#define	LED0_FD			BIT(4)
+	#define	LED0_USB3_MASK		0x001f
+	#define	LED1_ACTIVE		BIT(5)
+	#define	LED1_LINK_10		BIT(6)
+	#define	LED1_LINK_100		BIT(7)
+	#define	LED1_LINK_1000		BIT(8)
+	#define	LED1_FD			BIT(9)
+	#define	LED1_USB3_MASK		0x03e0
+	#define	LED2_ACTIVE		BIT(10)
+	#define	LED2_LINK_1000		BIT(13)
+	#define	LED2_LINK_100		BIT(12)
+	#define	LED2_LINK_10		BIT(11)
+	#define	LED2_FD			BIT(14)
+	#define	LED_VALID		BIT(15)
+	#define	LED2_USB3_MASK		0x7c00
+
+#define GMII_PHYPAGE				0x1e
+#define GMII_PHY_PAGE_SELECT			0x1f
+	#define GMII_PHY_PGSEL_EXT	0x0007
+	#define GMII_PHY_PGSEL_PAGE0	0x0000
+
+struct ax88179_data {
+	u16 rxctl;
+	u16 reserved;
+};
+
+struct ax88179_int_data {
+	__le32 intdata1;
+	__le32 intdata2;
+};
+
+static const struct {
+	unsigned char ctrl, timer_l, timer_h, size, ifg;
+} AX88179_BULKIN_SIZE[] =	{
+	{7, 0x4f, 0,	0x12, 0xff},
+	{7, 0x20, 3,	0x16, 0xff},
+	{7, 0xae, 7,	0x18, 0xff},
+	{7, 0xcc, 0x4c, 0x18, 8},
+};
+
+static int __ax88179_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,
+			      u16 size, void *data, int in_pm)
+{
+	int ret;
+	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);
+
+	return ret;
+}
+
+static int __ax88179_write_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,
+			       u16 size, void *data, int in_pm)
+{
+	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;
+}
+
+static void ax88179_write_cmd_async(struct usbnet *dev, u8 cmd, u16 value,
+				    u16 index, u16 size, void *data)
+{
+	u16 buf;
+
+	if (2 == size) {
+		buf = *((u16 *)data);
+		cpu_to_le16s(&buf);
+		usbnet_write_cmd_async(dev, cmd, USB_DIR_OUT | USB_TYPE_VENDOR |
+				       USB_RECIP_DEVICE, value, index, &buf,
+				       size);
+	} else {
+		usbnet_write_cmd_async(dev, cmd, USB_DIR_OUT | USB_TYPE_VENDOR |
+				       USB_RECIP_DEVICE, value, index, data,
+				       size);
+	}
+}
+
+static int ax88179_read_cmd_nopm(struct usbnet *dev, u8 cmd, u16 value,
+				 u16 index, u16 size, void *data)
+{
+	int ret;
+
+	if (2 == size) {
+		u16 buf;
+		ret = __ax88179_read_cmd(dev, cmd, value, index, size, &buf, 1);
+		le16_to_cpus(&buf);
+		*((u16 *)data) = buf;
+	} else if (4 == size) {
+		u32 buf;
+		ret = __ax88179_read_cmd(dev, cmd, value, index, size, &buf, 1);
+		le32_to_cpus(&buf);
+		*((u32 *)data) = buf;
+	} else {
+		ret = __ax88179_read_cmd(dev, cmd, value, index, size, data, 1);
+	}
+
+	return ret;
+}
+
+static int ax88179_write_cmd_nopm(struct usbnet *dev, u8 cmd, u16 value,
+				  u16 index, u16 size, void *data)
+{
+	int ret;
+
+	if (2 == size) {
+		u16 buf;
+		buf = *((u16 *)data);
+		cpu_to_le16s(&buf);
+		ret = __ax88179_write_cmd(dev, cmd, value, index,
+					  size, &buf, 1);
+	} else {
+		ret = __ax88179_write_cmd(dev, cmd, value, index,
+					  size, data, 1);
+	}
+
+	return ret;
+}
+
+static int ax88179_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,
+			    u16 size, void *data)
+{
+	int ret;
+
+	if (2 == size) {
+		u16 buf;
+		ret = __ax88179_read_cmd(dev, cmd, value, index, size, &buf, 0);
+		le16_to_cpus(&buf);
+		*((u16 *)data) = buf;
+	} else if (4 == size) {
+		u32 buf;
+		ret = __ax88179_read_cmd(dev, cmd, value, index, size, &buf, 0);
+		le32_to_cpus(&buf);
+		*((u32 *)data) = buf;
+	} else {
+		ret = __ax88179_read_cmd(dev, cmd, value, index, size, data, 0);
+	}
+
+	return ret;
+}
+
+static int ax88179_write_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,
+			     u16 size, void *data)
+{
+	int ret;
+
+	if (2 == size) {
+		u16 buf;
+		buf = *((u16 *)data);
+		cpu_to_le16s(&buf);
+		ret = __ax88179_write_cmd(dev, cmd, value, index,
+					  size, &buf, 0);
+	} else {
+		ret = __ax88179_write_cmd(dev, cmd, value, index,
+					  size, data, 0);
+	}
+
+	return ret;
+}
+
+static void ax88179_status(struct usbnet *dev, struct urb *urb)
+{
+	struct ax88179_int_data *event;
+	u32 link;
+
+	if (urb->actual_length < 8)
+		return;
+
+	event = urb->transfer_buffer;
+	le32_to_cpus((void *)&event->intdata1);
+
+	link = (((__force u32)event->intdata1) & AX_INT_PPLS_LINK) >> 16;
+
+	if (netif_carrier_ok(dev->net) != link) {
+		if (link)
+			usbnet_defer_kevent(dev, EVENT_LINK_RESET);
+		else
+			netif_carrier_off(dev->net);
+
+		netdev_info(dev->net, "ax88179 - Link status is: %d\n", link);
+	}
+}
+
+static int ax88179_mdio_read(struct net_device *netdev, int phy_id, int loc)
+{
+	struct usbnet *dev = netdev_priv(netdev);
+	u16 res;
+
+	ax88179_read_cmd(dev, AX_ACCESS_PHY, phy_id, (__u16)loc, 2, &res);
+	return res;
+}
+
+static void ax88179_mdio_write(struct net_device *netdev, int phy_id, int loc,
+			       int val)
+{
+	struct usbnet *dev = netdev_priv(netdev);
+	u16 res = (u16) val;
+
+	ax88179_write_cmd(dev, AX_ACCESS_PHY, phy_id, (__u16)loc, 2, &res);
+}
+
+static int ax88179_suspend(struct usb_interface *intf, pm_message_t message)
+{
+	struct usbnet *dev = usb_get_intfdata(intf);
+	u16 tmp16;
+	u8 tmp8;
+
+	usbnet_suspend(intf, message);
+
+	/* Disable RX path */
+	ax88179_read_cmd_nopm(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,
+			      2, 2, &tmp16);
+	tmp16 &= ~AX_MEDIUM_RECEIVE_EN;
+	ax88179_write_cmd_nopm(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,
+			       2, 2, &tmp16);
+
+	/* Force bulk-in zero length */
+	ax88179_read_cmd_nopm(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL,
+			      2, 2, &tmp16);
+
+	tmp16 |= AX_PHYPWR_RSTCTL_BZ | AX_PHYPWR_RSTCTL_IPRL;
+	ax88179_write_cmd_nopm(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL,
+			       2, 2, &tmp16);
+
+	/* change clock */
+	tmp8 = 0;
+	ax88179_write_cmd_nopm(dev, AX_ACCESS_MAC, AX_CLK_SELECT, 1, 1, &tmp8);
+
+	/* Configure RX control register => stop operation */
+	tmp16 = AX_RX_CTL_STOP;
+	ax88179_write_cmd_nopm(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2, &tmp16);
+
+	return 0;
+}
+
+/* This function is used to enable the autodetach function. */
+/* This function is determined by offset 0x43 of EEPROM */
+static int ax88179_auto_detach(struct usbnet *dev, int in_pm)
+{
+	u16 tmp16;
+	u8 tmp8;
+	int (*fnr)(struct usbnet *, u8, u16, u16, u16, void *);
+	int (*fnw)(struct usbnet *, u8, u16, u16, u16, void *);
+
+	if (!in_pm) {
+		fnr = ax88179_read_cmd;
+		fnw = ax88179_write_cmd;
+	} else {
+		fnr = ax88179_read_cmd_nopm;
+		fnw = ax88179_write_cmd_nopm;
+	}
+
+	if (fnr(dev, AX_ACCESS_EEPROM, 0x43, 1, 2, &tmp16) < 0)
+		return 0;
+
+	if ((tmp16 == 0xFFFF) || (!(tmp16 & 0x0100)))
+		return 0;
+
+	/* Enable Auto Detach bit */
+	tmp8 = 0;
+	fnr(dev, AX_ACCESS_MAC, AX_CLK_SELECT, 1, 1, &tmp8);
+	tmp8 |= AX_CLK_SELECT_ULR;
+	fnw(dev, AX_ACCESS_MAC, AX_CLK_SELECT, 1, 1, &tmp8);
+
+	fnr(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, &tmp16);
+	tmp16 |= AX_PHYPWR_RSTCTL_AT;
+	fnw(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, &tmp16);
+
+	return 0;
+}
+
+static int ax88179_resume(struct usb_interface *intf)
+{
+	struct usbnet *dev = usb_get_intfdata(intf);
+	u16 tmp16;
+	u8 tmp8;
+
+	netif_carrier_off(dev->net);
+
+	/* Power up ethernet PHY */
+	tmp16 = 0;
+	ax88179_write_cmd_nopm(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL,
+			       2, 2, &tmp16);
+	udelay(1000);
+
+	tmp16 = AX_PHYPWR_RSTCTL_IPRL;
+	ax88179_write_cmd_nopm(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL,
+			       2, 2, &tmp16);
+	msleep(200);
+
+	/* Ethernet PHY Auto Detach*/
+	ax88179_auto_detach(dev, 1);
+
+	/* Enable clock */
+	ax88179_read_cmd_nopm(dev, AX_ACCESS_MAC,  AX_CLK_SELECT, 1, 1, &tmp8);
+	tmp8 |= AX_CLK_SELECT_ACS | AX_CLK_SELECT_BCS;
+	ax88179_write_cmd_nopm(dev, AX_ACCESS_MAC, AX_CLK_SELECT, 1, 1, &tmp8);
+	msleep(100);
+
+	/* Configure RX control register => start operation */
+	tmp16 = AX_RX_CTL_DROPCRCERR | AX_RX_CTL_IPE | AX_RX_CTL_START |
+		AX_RX_CTL_AP | AX_RX_CTL_AMALL | AX_RX_CTL_AB;
+	ax88179_write_cmd_nopm(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2, &tmp16);
+
+	return usbnet_resume(intf);
+}
+
+static void
+ax88179_get_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo)
+{
+	struct usbnet *dev = netdev_priv(net);
+	u8 opt;
+
+	if (ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_MONITOR_MOD,
+			     1, 1, &opt) < 0) {
+		wolinfo->supported = 0;
+		wolinfo->wolopts = 0;
+		return;
+	}
+
+	wolinfo->supported = WAKE_PHY | WAKE_MAGIC;
+	wolinfo->wolopts = 0;
+	if (opt & AX_MONITOR_MODE_RWLC)
+		wolinfo->wolopts |= WAKE_PHY;
+	if (opt & AX_MONITOR_MODE_RWMP)
+		wolinfo->wolopts |= WAKE_MAGIC;
+}
+
+static int
+ax88179_set_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo)
+{
+	struct usbnet *dev = netdev_priv(net);
+	u8 opt = 0;
+
+	if (wolinfo->wolopts & WAKE_PHY)
+		opt |= AX_MONITOR_MODE_RWLC;
+	if (wolinfo->wolopts & WAKE_MAGIC)
+		opt |= AX_MONITOR_MODE_RWMP;
+
+	if (ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MONITOR_MOD,
+			      1, 1, &opt) < 0)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int ax88179_get_eeprom_len(struct net_device *net)
+{
+	return AX_EEPROM_LEN;
+}
+
+static int
+ax88179_get_eeprom(struct net_device *net, struct ethtool_eeprom *eeprom,
+		   u8 *data)
+{
+	struct usbnet *dev = netdev_priv(net);
+	u16 *eeprom_buff;
+	int first_word, last_word;
+	int i, ret;
+
+	if (eeprom->len == 0)
+		return -EINVAL;
+
+	eeprom->magic = AX88179_EEPROM_MAGIC;
+
+	first_word = eeprom->offset >> 1;
+	last_word = (eeprom->offset + eeprom->len - 1) >> 1;
+	eeprom_buff = kmalloc(sizeof(u16) * (last_word - first_word + 1),
+			      GFP_KERNEL);
+	if (!eeprom_buff)
+		return -ENOMEM;
+
+	/* ax88179/178A returns 2 bytes from eeprom on read */
+	for (i = first_word; i <= last_word; i++) {
+		ret = __ax88179_read_cmd(dev, AX_ACCESS_EEPROM, i, 1, 2,
+					 &eeprom_buff[i - first_word],
+					 0);
+		if (ret < 0) {
+			kfree(eeprom_buff);
+			return -EIO;
+		}
+	}
+
+	memcpy(data, (u8 *)eeprom_buff + (eeprom->offset & 1), eeprom->len);
+	kfree(eeprom_buff);
+	return 0;
+}
+
+static int ax88179_get_settings(struct net_device *net, struct ethtool_cmd *cmd)
+{
+	struct usbnet *dev = netdev_priv(net);
+	return mii_ethtool_gset(&dev->mii, cmd);
+}
+
+static int ax88179_set_settings(struct net_device *net, struct ethtool_cmd *cmd)
+{
+	struct usbnet *dev = netdev_priv(net);
+	return mii_ethtool_sset(&dev->mii, cmd);
+}
+
+
+static int ax88179_ioctl(struct net_device *net, struct ifreq *rq, int cmd)
+{
+	struct usbnet *dev = netdev_priv(net);
+	return generic_mii_ioctl(&dev->mii, if_mii(rq), cmd, NULL);
+}
+
+static const struct ethtool_ops ax88179_ethtool_ops = {
+	.get_link		= ethtool_op_get_link,
+	.get_msglevel		= usbnet_get_msglevel,
+	.set_msglevel		= usbnet_set_msglevel,
+	.get_wol		= ax88179_get_wol,
+	.set_wol		= ax88179_set_wol,
+	.get_eeprom_len		= ax88179_get_eeprom_len,
+	.get_eeprom		= ax88179_get_eeprom,
+	.get_settings		= ax88179_get_settings,
+	.set_settings		= ax88179_set_settings,
+	.nway_reset		= usbnet_nway_reset,
+};
+
+static void ax88179_set_multicast(struct net_device *net)
+{
+	struct usbnet *dev = netdev_priv(net);
+	struct ax88179_data *data = (struct ax88179_data *)dev->data;
+	u8 *m_filter = ((u8 *)dev->data) + 12;
+
+	data->rxctl = (AX_RX_CTL_START | AX_RX_CTL_AB | AX_RX_CTL_IPE);
+
+	if (net->flags & IFF_PROMISC) {
+		data->rxctl |= AX_RX_CTL_PRO;
+	} else if (net->flags & IFF_ALLMULTI ||
+		   netdev_mc_count(net) > AX_MAX_MCAST) {
+		data->rxctl |= AX_RX_CTL_AMALL;
+	} else if (netdev_mc_empty(net)) {
+		/* just broadcast and directed */
+	} else {
+		/* We use the 20 byte dev->data for our 8 byte filter buffer
+		 * to avoid allocating memory that is tricky to free later
+		 */
+		u32 crc_bits;
+		struct netdev_hw_addr *ha;
+
+		memset(m_filter, 0, AX_MCAST_FLTSIZE);
+
+		netdev_for_each_mc_addr(ha, net) {
+			crc_bits = ether_crc(ETH_ALEN, ha->addr) >> 26;
+			*(m_filter + (crc_bits >> 3)) |= (1 << (crc_bits & 7));
+		}
+
+		ax88179_write_cmd_async(dev, AX_ACCESS_MAC, AX_MULFLTARY,
+					AX_MCAST_FLTSIZE, AX_MCAST_FLTSIZE,
+					m_filter);
+
+		data->rxctl |= AX_RX_CTL_AM;
+	}
+
+	ax88179_write_cmd_async(dev, AX_ACCESS_MAC, AX_RX_CTL,
+				2, 2, &data->rxctl);
+}
+
+static int
+ax88179_set_features(struct net_device *net, netdev_features_t features)
+{
+	u8 tmp;
+	struct usbnet *dev = netdev_priv(net);
+	netdev_features_t changed = net->features ^ features;
+
+	if (changed & NETIF_F_IP_CSUM) {
+		ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, 1, 1, &tmp);
+		tmp ^= AX_TXCOE_TCP | AX_TXCOE_UDP;
+		ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, 1, 1, &tmp);
+	}
+
+	if (changed & NETIF_F_IPV6_CSUM) {
+		ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, 1, 1, &tmp);
+		tmp ^= AX_TXCOE_TCPV6 | AX_TXCOE_UDPV6;
+		ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, 1, 1, &tmp);
+	}
+
+	if (changed & NETIF_F_RXCSUM) {
+		ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_RXCOE_CTL, 1, 1, &tmp);
+		tmp ^= AX_RXCOE_IP | AX_RXCOE_TCP | AX_RXCOE_UDP |
+		       AX_RXCOE_TCPV6 | AX_RXCOE_UDPV6;
+		ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RXCOE_CTL, 1, 1, &tmp);
+	}
+
+	return 0;
+}
+
+static int ax88179_change_mtu(struct net_device *net, int new_mtu)
+{
+	struct usbnet *dev = netdev_priv(net);
+	u16 tmp16;
+
+	if (new_mtu <= 0 || new_mtu > 4088)
+		return -EINVAL;
+
+	net->mtu = new_mtu;
+	dev->hard_mtu = net->mtu + net->hard_header_len;
+
+	if (net->mtu > 1500) {
+		ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,
+				 2, 2, &tmp16);
+		tmp16 |= AX_MEDIUM_JUMBO_EN;
+		ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,
+				  2, 2, &tmp16);
+	} else {
+		ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,
+				 2, 2, &tmp16);
+		tmp16 &= ~AX_MEDIUM_JUMBO_EN;
+		ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,
+				  2, 2, &tmp16);
+	}
+
+	return 0;
+}
+
+static int ax88179_set_mac_addr(struct net_device *net, void *p)
+{
+	struct usbnet *dev = netdev_priv(net);
+	struct sockaddr *addr = p;
+
+	if (netif_running(net))
+		return -EBUSY;
+	if (!is_valid_ether_addr(addr->sa_data))
+		return -EADDRNOTAVAIL;
+
+	memcpy(net->dev_addr, addr->sa_data, ETH_ALEN);
+
+	/* Set the MAC address */
+	return ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_NODE_ID, ETH_ALEN,
+				 ETH_ALEN, net->dev_addr);
+}
+
+static const struct net_device_ops ax88179_netdev_ops = {
+	.ndo_open		= usbnet_open,
+	.ndo_stop		= usbnet_stop,
+	.ndo_start_xmit		= usbnet_start_xmit,
+	.ndo_tx_timeout		= usbnet_tx_timeout,
+	.ndo_change_mtu		= ax88179_change_mtu,
+	.ndo_set_mac_address	= ax88179_set_mac_addr,
+	.ndo_validate_addr	= eth_validate_addr,
+	.ndo_do_ioctl		= ax88179_ioctl,
+	.ndo_set_rx_mode	= ax88179_set_multicast,
+	.ndo_set_features	= ax88179_set_features,
+};
+
+static int ax88179_check_eeprom(struct usbnet *dev)
+{
+	u8 i, buf, eeprom[20];
+	u16 csum, delay = HZ / 10;
+	unsigned long jtimeout;
+
+	/* Read EEPROM content */
+	for (i = 0; i < 6; i++) {
+		buf = i;
+		if (ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_SROM_ADDR,
+				      1, 1, &buf) < 0)
+			return -EINVAL;
+
+		buf = EEP_RD;
+		if (ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_SROM_CMD,
+				      1, 1, &buf) < 0)
+			return -EINVAL;
+
+		jtimeout = jiffies + delay;
+		do {
+			ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_SROM_CMD,
+					 1, 1, &buf);
+
+			if (time_after(jiffies, jtimeout))
+				return -EINVAL;
+
+		} while (buf & EEP_BUSY);
+
+		__ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_SROM_DATA_LOW,
+				   2, 2, &eeprom[i * 2], 0);
+
+		if ((i == 0) && (eeprom[0] == 0xFF))
+			return -EINVAL;
+	}
+
+	csum = eeprom[6] + eeprom[7] + eeprom[8] + eeprom[9];
+	csum = (csum >> 8) + (csum & 0xff);
+	if ((csum + eeprom[10]) != 0xff)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int ax88179_check_efuse(struct usbnet *dev, u16 *ledmode)
+{
+	u8	i;
+	u8	efuse[64];
+	u16	csum = 0;
+
+	if (ax88179_read_cmd(dev, AX_ACCESS_EFUS, 0, 64, 64, efuse) < 0)
+		return -EINVAL;
+
+	if (*efuse == 0xFF)
+		return -EINVAL;
+
+	for (i = 0; i < 64; i++)
+		csum = csum + efuse[i];
+
+	while (csum > 255)
+		csum = (csum & 0x00FF) + ((csum >> 8) & 0x00FF);
+
+	if (csum != 0xFF)
+		return -EINVAL;
+
+	*ledmode = (efuse[51] << 8) | efuse[52];
+
+	return 0;
+}
+
+static int ax88179_convert_old_led(struct usbnet *dev, u16 *ledvalue)
+{
+	u16 led;
+
+	/* Loaded the old eFuse LED Mode */
+	if (ax88179_read_cmd(dev, AX_ACCESS_EEPROM, 0x3C, 1, 2, &led) < 0)
+		return -EINVAL;
+
+	led >>= 8;
+	switch (led) {
+	case 0xFF:
+		led = LED0_ACTIVE | LED1_LINK_10 | LED1_LINK_100 |
+		      LED1_LINK_1000 | LED2_ACTIVE | LED2_LINK_10 |
+		      LED2_LINK_100 | LED2_LINK_1000 | LED_VALID;
+		break;
+	case 0xFE:
+		led = LED0_ACTIVE | LED1_LINK_1000 | LED2_LINK_100 | LED_VALID;
+		break;
+	case 0xFD:
+		led = LED0_ACTIVE | LED1_LINK_1000 | LED2_LINK_100 |
+		      LED2_LINK_10 | LED_VALID;
+		break;
+	case 0xFC:
+		led = LED0_ACTIVE | LED1_ACTIVE | LED1_LINK_1000 | LED2_ACTIVE |
+		      LED2_LINK_100 | LED2_LINK_10 | LED_VALID;
+		break;
+	default:
+		led = LED0_ACTIVE | LED1_LINK_10 | LED1_LINK_100 |
+		      LED1_LINK_1000 | LED2_ACTIVE | LED2_LINK_10 |
+		      LED2_LINK_100 | LED2_LINK_1000 | LED_VALID;
+		break;
+	}
+
+	*ledvalue = led;
+
+	return 0;
+}
+
+static int ax88179_led_setting(struct usbnet *dev)
+{
+	u8 ledfd, value = 0;
+	u16 tmp, ledact, ledlink, ledvalue = 0, delay = HZ / 10;
+	unsigned long jtimeout;
+
+	/* Check AX88179 version. UA1 or UA2*/
+	ax88179_read_cmd(dev, AX_ACCESS_MAC, GENERAL_STATUS, 1, 1, &value);
+
+	if (!(value & AX_SECLD)) {	/* UA1 */
+		value = AX_GPIO_CTRL_GPIO3EN | AX_GPIO_CTRL_GPIO2EN |
+			AX_GPIO_CTRL_GPIO1EN;
+		if (ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_GPIO_CTRL,
+				      1, 1, &value) < 0)
+			return -EINVAL;
+	}
+
+	/* Check EEPROM */
+	if (!ax88179_check_eeprom(dev)) {
+		value = 0x42;
+		if (ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_SROM_ADDR,
+				      1, 1, &value) < 0)
+			return -EINVAL;
+
+		value = EEP_RD;
+		if (ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_SROM_CMD,
+				      1, 1, &value) < 0)
+			return -EINVAL;
+
+		jtimeout = jiffies + delay;
+		do {
+			ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_SROM_CMD,
+					 1, 1, &value);
+
+			if (time_after(jiffies, jtimeout))
+				return -EINVAL;
+
+		} while (value & EEP_BUSY);
+
+		ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_SROM_DATA_HIGH,
+				 1, 1, &value);
+		ledvalue = (value << 8);
+
+		ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_SROM_DATA_LOW,
+				 1, 1, &value);
+		ledvalue |= value;
+
+		/* load internal ROM for defaule setting */
+		if ((ledvalue == 0xFFFF) || ((ledvalue & LED_VALID) == 0))
+			ax88179_convert_old_led(dev, &ledvalue);
+
+	} else if (!ax88179_check_efuse(dev, &ledvalue)) {
+		if ((ledvalue == 0xFFFF) || ((ledvalue & LED_VALID) == 0))
+			ax88179_convert_old_led(dev, &ledvalue);
+	} else {
+		ax88179_convert_old_led(dev, &ledvalue);
+	}
+
+	tmp = GMII_PHY_PGSEL_EXT;
+	ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
+			  GMII_PHY_PAGE_SELECT, 2, &tmp);
+
+	tmp = 0x2c;
+	ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
+			  GMII_PHYPAGE, 2, &tmp);
+
+	ax88179_read_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
+			 GMII_LED_ACT, 2, &ledact);
+
+	ax88179_read_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
+			 GMII_LED_LINK, 2, &ledlink);
+
+	ledact &= GMII_LED_ACTIVE_MASK;
+	ledlink &= GMII_LED_LINK_MASK;
+
+	if (ledvalue & LED0_ACTIVE)
+		ledact |= GMII_LED0_ACTIVE;
+
+	if (ledvalue & LED1_ACTIVE)
+		ledact |= GMII_LED1_ACTIVE;
+
+	if (ledvalue & LED2_ACTIVE)
+		ledact |= GMII_LED2_ACTIVE;
+
+	if (ledvalue & LED0_LINK_10)
+		ledlink |= GMII_LED0_LINK_10;
+
+	if (ledvalue & LED1_LINK_10)
+		ledlink |= GMII_LED1_LINK_10;
+
+	if (ledvalue & LED2_LINK_10)
+		ledlink |= GMII_LED2_LINK_10;
+
+	if (ledvalue & LED0_LINK_100)
+		ledlink |= GMII_LED0_LINK_100;
+
+	if (ledvalue & LED1_LINK_100)
+		ledlink |= GMII_LED1_LINK_100;
+
+	if (ledvalue & LED2_LINK_100)
+		ledlink |= GMII_LED2_LINK_100;
+
+	if (ledvalue & LED0_LINK_1000)
+		ledlink |= GMII_LED0_LINK_1000;
+
+	if (ledvalue & LED1_LINK_1000)
+		ledlink |= GMII_LED1_LINK_1000;
+
+	if (ledvalue & LED2_LINK_1000)
+		ledlink |= GMII_LED2_LINK_1000;
+
+	tmp = ledact;
+	ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
+			  GMII_LED_ACT, 2, &tmp);
+
+	tmp = ledlink;
+	ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
+			  GMII_LED_LINK, 2, &tmp);
+
+	tmp = GMII_PHY_PGSEL_PAGE0;
+	ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
+			  GMII_PHY_PAGE_SELECT, 2, &tmp);
+
+	/* LED full duplex setting */
+	ledfd = 0;
+	if (ledvalue & LED0_FD)
+		ledfd |= 0x01;
+	else if ((ledvalue & LED0_USB3_MASK) == 0)
+		ledfd |= 0x02;
+
+	if (ledvalue & LED1_FD)
+		ledfd |= 0x04;
+	else if ((ledvalue & LED1_USB3_MASK) == 0)
+		ledfd |= 0x08;
+
+	if (ledvalue & LED2_FD)
+		ledfd |= 0x10;
+	else if ((ledvalue & LED2_USB3_MASK) == 0)
+		ledfd |= 0x20;
+
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_LEDCTRL, 1, 1, &ledfd);
+
+	return 0;
+}
+
+static int ax88179_bind(struct usbnet *dev, struct usb_interface *intf)
+{
+	u8 buf[5];
+	u16 *tmp16;
+	u8 *tmp;
+	struct ax88179_data *ax179_data = (struct ax88179_data *)dev->data;
+
+	usbnet_get_endpoints(dev, intf);
+
+	tmp16 = (u16 *)buf;
+	tmp = (u8 *)buf;
+
+	memset(ax179_data, 0, sizeof(*ax179_data));
+
+	/* Power up ethernet PHY */
+	*tmp16 = 0;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, tmp16);
+	*tmp16 = AX_PHYPWR_RSTCTL_IPRL;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, tmp16);
+	msleep(200);
+
+	*tmp = AX_CLK_SELECT_ACS | AX_CLK_SELECT_BCS;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_CLK_SELECT, 1, 1, tmp);
+	msleep(100);
+
+	ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_NODE_ID, ETH_ALEN,
+			 ETH_ALEN, dev->net->dev_addr);
+	memcpy(dev->net->perm_addr, dev->net->dev_addr, ETH_ALEN);
+
+	/* RX bulk configuration */
+	memcpy(tmp, &AX88179_BULKIN_SIZE[0], 5);
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_BULKIN_QCTRL, 5, 5, tmp);
+
+	dev->rx_urb_size = 1024 * 20;
+
+	*tmp = 0x34;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PAUSE_WATERLVL_LOW, 1, 1, tmp);
+
+	*tmp = 0x52;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PAUSE_WATERLVL_HIGH,
+			  1, 1, tmp);
+
+	dev->net->netdev_ops = &ax88179_netdev_ops;
+	dev->net->ethtool_ops = &ax88179_ethtool_ops;
+	dev->net->needed_headroom = 8;
+
+	/* Initialize MII structure */
+	dev->mii.dev = dev->net;
+	dev->mii.mdio_read = ax88179_mdio_read;
+	dev->mii.mdio_write = ax88179_mdio_write;
+	dev->mii.phy_id_mask = 0xff;
+	dev->mii.reg_num_mask = 0xff;
+	dev->mii.phy_id = 0x03;
+	dev->mii.supports_gmii = 1;
+
+	dev->net->features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
+			      NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_TSO;
+
+	dev->net->hw_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
+				 NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_TSO;
+
+	/* Enable checksum offload */
+	*tmp = AX_RXCOE_IP | AX_RXCOE_TCP | AX_RXCOE_UDP |
+	       AX_RXCOE_TCPV6 | AX_RXCOE_UDPV6;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RXCOE_CTL, 1, 1, tmp);
+
+	*tmp = AX_TXCOE_IP | AX_TXCOE_TCP | AX_TXCOE_UDP |
+	       AX_TXCOE_TCPV6 | AX_TXCOE_UDPV6;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, 1, 1, tmp);
+
+	/* Configure RX control register => start operation */
+	*tmp16 = AX_RX_CTL_DROPCRCERR | AX_RX_CTL_IPE | AX_RX_CTL_START |
+		 AX_RX_CTL_AP | AX_RX_CTL_AMALL | AX_RX_CTL_AB;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2, tmp16);
+
+	*tmp = AX_MONITOR_MODE_PMETYPE | AX_MONITOR_MODE_PMEPOL |
+	       AX_MONITOR_MODE_RWMP;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MONITOR_MOD, 1, 1, tmp);
+
+	/* Configure default medium type => giga */
+	*tmp16 = AX_MEDIUM_RECEIVE_EN | AX_MEDIUM_TXFLOW_CTRLEN |
+		 AX_MEDIUM_RXFLOW_CTRLEN | AX_MEDIUM_ALWAYS_ONE |
+		 AX_MEDIUM_FULL_DUPLEX | AX_MEDIUM_GIGAMODE;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,
+			  2, 2, tmp16);
+
+	ax88179_led_setting(dev);
+
+	/* Restart autoneg */
+	mii_nway_restart(&dev->mii);
+
+	netif_carrier_off(dev->net);
+
+	return 0;
+}
+
+static void ax88179_unbind(struct usbnet *dev, struct usb_interface *intf)
+{
+	u16 tmp16;
+
+	/* Configure RX control register => stop operation */
+	tmp16 = AX_RX_CTL_STOP;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2, &tmp16);
+
+	tmp16 = 0;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_CLK_SELECT, 1, 1, &tmp16);
+
+	/* Power down ethernet PHY */
+	tmp16 = 0;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, &tmp16);
+}
+
+static void
+ax88179_rx_checksum(struct sk_buff *skb, u32 *pkt_hdr)
+{
+	skb->ip_summed = CHECKSUM_NONE;
+
+	/* checksum error bit is set */
+	if ((*pkt_hdr & AX_RXHDR_L3CSUM_ERR) ||
+	    (*pkt_hdr & AX_RXHDR_L4CSUM_ERR))
+		return;
+
+	/* It must be a TCP or UDP packet with a valid checksum */
+	if (((*pkt_hdr & AX_RXHDR_L4_TYPE_MASK) == AX_RXHDR_L4_TYPE_TCP) ||
+	    ((*pkt_hdr & AX_RXHDR_L4_TYPE_MASK) == AX_RXHDR_L4_TYPE_UDP))
+		skb->ip_summed = CHECKSUM_UNNECESSARY;
+}
+
+static int ax88179_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
+{
+	struct sk_buff *ax_skb;
+	int pkt_cnt;
+	u32 rx_hdr;
+	u16 hdr_off;
+	u32 *pkt_hdr;
+
+	skb_trim(skb, skb->len - 4);
+	memcpy(&rx_hdr, skb_tail_pointer(skb), 4);
+	le32_to_cpus(&rx_hdr);
+
+	pkt_cnt = (u16)rx_hdr;
+	hdr_off = (u16)(rx_hdr >> 16);
+	pkt_hdr = (u32 *)(skb->data + hdr_off);
+
+	while (pkt_cnt--) {
+		u16 pkt_len;
+
+		le32_to_cpus(pkt_hdr);
+		pkt_len = (*pkt_hdr >> 16) & 0x1fff;
+
+		/* Check CRC or runt packet */
+		if ((*pkt_hdr & AX_RXHDR_CRC_ERR) ||
+		    (*pkt_hdr & AX_RXHDR_DROP_ERR)) {
+			skb_pull(skb, (pkt_len + 7) & 0xFFF8);
+			pkt_hdr++;
+			continue;
+		}
+
+		if (pkt_cnt == 0) {
+			/* Skip IP alignment psudo header */
+			skb_pull(skb, 2);
+			skb->len = pkt_len;
+			skb_set_tail_pointer(skb, pkt_len);
+			skb->truesize = pkt_len + sizeof(struct sk_buff);
+			ax88179_rx_checksum(skb, pkt_hdr);
+			return 1;
+		}
+
+		ax_skb = skb_clone(skb, GFP_ATOMIC);
+		if (ax_skb) {
+			ax_skb->len = pkt_len;
+			ax_skb->data = skb->data + 2;
+			skb_set_tail_pointer(ax_skb, pkt_len);
+			ax_skb->truesize = pkt_len + sizeof(struct sk_buff);
+			ax88179_rx_checksum(ax_skb, pkt_hdr);
+			usbnet_skb_return(dev, ax_skb);
+		} else {
+			return 0;
+		}
+
+		skb_pull(skb, (pkt_len + 7) & 0xFFF8);
+		pkt_hdr++;
+	}
+	return 1;
+}
+
+static struct sk_buff *
+ax88179_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags)
+{
+	u32 tx_hdr1, tx_hdr2;
+	int frame_size = dev->maxpacket;
+	int mss = skb_shinfo(skb)->gso_size;
+	int headroom;
+	int tailroom;
+
+	tx_hdr1 = skb->len;
+	tx_hdr2 = mss;
+	if (((skb->len + 8) % frame_size) == 0)
+		tx_hdr2 |= 0x80008000;	/* Enable padding */
+
+	skb_linearize(skb);
+	headroom = skb_headroom(skb);
+	tailroom = skb_tailroom(skb);
+
+	if (!skb_header_cloned(skb) &&
+	    !skb_cloned(skb) &&
+	    (headroom + tailroom) >= 8) {
+		if (headroom < 8) {
+			skb->data = memmove(skb->head + 8, skb->data, skb->len);
+			skb_set_tail_pointer(skb, skb->len);
+		}
+	} else {
+		struct sk_buff *skb2;
+
+		skb2 = skb_copy_expand(skb, 8, 0, flags);
+		dev_kfree_skb_any(skb);
+		skb = skb2;
+		if (!skb)
+			return NULL;
+	}
+
+	skb_push(skb, 4);
+	cpu_to_le32s(&tx_hdr2);
+	skb_copy_to_linear_data(skb, &tx_hdr2, 4);
+
+	skb_push(skb, 4);
+	cpu_to_le32s(&tx_hdr1);
+	skb_copy_to_linear_data(skb, &tx_hdr1, 4);
+
+	return skb;
+}
+
+static int ax88179_link_reset(struct usbnet *dev)
+{
+	struct ax88179_data *ax179_data = (struct ax88179_data *)dev->data;
+	u8 tmp[5], link_sts;
+	u16 mode, tmp16, delay = HZ / 10;
+	u32 tmp32 = 0x40000000;
+	unsigned long jtimeout;
+
+	jtimeout = jiffies + delay;
+	while (tmp32 & 0x40000000) {
+		mode = 0;
+		ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2, &mode);
+		ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2,
+				  &ax179_data->rxctl);
+
+		/*link up, check the usb device control TX FIFO full or empty*/
+		ax88179_read_cmd(dev, 0x81, 0x8c, 0, 4, &tmp32);
+
+		if (time_after(jiffies, jtimeout))
+			return 0;
+	}
+
+	mode = AX_MEDIUM_RECEIVE_EN | AX_MEDIUM_TXFLOW_CTRLEN |
+	       AX_MEDIUM_RXFLOW_CTRLEN | AX_MEDIUM_ALWAYS_ONE;
+
+	ax88179_read_cmd(dev, AX_ACCESS_MAC, PHYSICAL_LINK_STATUS,
+			 1, 1, &link_sts);
+
+	ax88179_read_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
+			 GMII_PHY_PHYSR, 2, &tmp16);
+
+	if (!(tmp16 & GMII_PHY_PHYSR_LINK)) {
+		return 0;
+	} else if (GMII_PHY_PHYSR_GIGA == (tmp16 & GMII_PHY_PHYSR_SMASK)) {
+		mode |= AX_MEDIUM_GIGAMODE | AX_MEDIUM_EN_125MHZ;
+		if (dev->net->mtu > 1500)
+			mode |= AX_MEDIUM_JUMBO_EN;
+
+		if (link_sts & AX_USB_SS)
+			memcpy(tmp, &AX88179_BULKIN_SIZE[0], 5);
+		else if (link_sts & AX_USB_HS)
+			memcpy(tmp, &AX88179_BULKIN_SIZE[1], 5);
+		else
+			memcpy(tmp, &AX88179_BULKIN_SIZE[3], 5);
+	} else if (GMII_PHY_PHYSR_100 == (tmp16 & GMII_PHY_PHYSR_SMASK)) {
+		mode |= AX_MEDIUM_PS;
+
+		if (link_sts & (AX_USB_SS | AX_USB_HS))
+			memcpy(tmp, &AX88179_BULKIN_SIZE[2], 5);
+		else
+			memcpy(tmp, &AX88179_BULKIN_SIZE[3], 5);
+	} else {
+		memcpy(tmp, &AX88179_BULKIN_SIZE[3], 5);
+	}
+
+	/* RX bulk configuration */
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_BULKIN_QCTRL, 5, 5, tmp);
+
+	dev->rx_urb_size = (1024 * (tmp[3] + 2));
+
+	if (tmp16 & GMII_PHY_PHYSR_FULL)
+		mode |= AX_MEDIUM_FULL_DUPLEX;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,
+			  2, 2, &mode);
+
+	netif_carrier_on(dev->net);
+
+	return 0;
+}
+
+static int ax88179_reset(struct usbnet *dev)
+{
+	u8 buf[5];
+	u16 *tmp16;
+	u8 *tmp;
+
+	tmp16 = (u16 *)buf;
+	tmp = (u8 *)buf;
+
+	/* Power up ethernet PHY */
+	*tmp16 = 0;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, tmp16);
+
+	*tmp16 = AX_PHYPWR_RSTCTL_IPRL;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, tmp16);
+	msleep(200);
+
+	*tmp = AX_CLK_SELECT_ACS | AX_CLK_SELECT_BCS;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_CLK_SELECT, 1, 1, tmp);
+	msleep(100);
+
+	/* Ethernet PHY Auto Detach*/
+	ax88179_auto_detach(dev, 0);
+
+	ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_NODE_ID, ETH_ALEN, ETH_ALEN,
+			 dev->net->dev_addr);
+	memcpy(dev->net->perm_addr, dev->net->dev_addr, ETH_ALEN);
+
+	/* RX bulk configuration */
+	memcpy(tmp, &AX88179_BULKIN_SIZE[0], 5);
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_BULKIN_QCTRL, 5, 5, tmp);
+
+	dev->rx_urb_size = 1024 * 20;
+
+	*tmp = 0x34;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PAUSE_WATERLVL_LOW, 1, 1, tmp);
+
+	*tmp = 0x52;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PAUSE_WATERLVL_HIGH,
+			  1, 1, tmp);
+
+	dev->net->features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
+			      NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_TSO;
+
+	dev->net->hw_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
+				 NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_TSO;
+
+	/* Enable checksum offload */
+	*tmp = AX_RXCOE_IP | AX_RXCOE_TCP | AX_RXCOE_UDP |
+	       AX_RXCOE_TCPV6 | AX_RXCOE_UDPV6;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RXCOE_CTL, 1, 1, tmp);
+
+	*tmp = AX_TXCOE_IP | AX_TXCOE_TCP | AX_TXCOE_UDP |
+	       AX_TXCOE_TCPV6 | AX_TXCOE_UDPV6;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, 1, 1, tmp);
+
+	/* Configure RX control register => start operation */
+	*tmp16 = AX_RX_CTL_DROPCRCERR | AX_RX_CTL_IPE | AX_RX_CTL_START |
+		 AX_RX_CTL_AP | AX_RX_CTL_AMALL | AX_RX_CTL_AB;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2, tmp16);
+
+	*tmp = AX_MONITOR_MODE_PMETYPE | AX_MONITOR_MODE_PMEPOL |
+	       AX_MONITOR_MODE_RWMP;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MONITOR_MOD, 1, 1, tmp);
+
+	/* Configure default medium type => giga */
+	*tmp16 = AX_MEDIUM_RECEIVE_EN | AX_MEDIUM_TXFLOW_CTRLEN |
+		 AX_MEDIUM_RXFLOW_CTRLEN | AX_MEDIUM_ALWAYS_ONE |
+		 AX_MEDIUM_FULL_DUPLEX | AX_MEDIUM_GIGAMODE;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,
+			  2, 2, tmp16);
+
+	ax88179_led_setting(dev);
+
+	/* Restart autoneg */
+	mii_nway_restart(&dev->mii);
+
+	netif_carrier_off(dev->net);
+
+	return 0;
+}
+
+static int ax88179_stop(struct usbnet *dev)
+{
+	u16 tmp16;
+
+	ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,
+			 2, 2, &tmp16);
+	tmp16 &= ~AX_MEDIUM_RECEIVE_EN;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,
+			  2, 2, &tmp16);
+
+	return 0;
+}
+
+static const struct driver_info ax88179_info = {
+	.description = "ASIX AX88179 USB 3.0 Gigibit Ethernet",
+	.bind = ax88179_bind,
+	.unbind = ax88179_unbind,
+	.status = ax88179_status,
+	.link_reset = ax88179_link_reset,
+	.reset = ax88179_reset,
+	.stop = ax88179_stop,
+	.flags = FLAG_ETHER | FLAG_FRAMING_AX,
+	.rx_fixup = ax88179_rx_fixup,
+	.tx_fixup = ax88179_tx_fixup,
+};
+
+static const struct driver_info ax88178a_info = {
+	.description = "ASIX AX88178A USB 2.0 Gigibit Ethernet",
+	.bind = ax88179_bind,
+	.unbind = ax88179_unbind,
+	.status = ax88179_status,
+	.link_reset = ax88179_link_reset,
+	.reset = ax88179_reset,
+	.stop = ax88179_stop,
+	.flags = FLAG_ETHER | FLAG_FRAMING_AX,
+	.rx_fixup = ax88179_rx_fixup,
+	.tx_fixup = ax88179_tx_fixup,
+};
+
+static const struct driver_info sitecom_info = {
+	.description = "Sitecom USB 3.0 to Gigabit Adapter",
+	.bind = ax88179_bind,
+	.unbind = ax88179_unbind,
+	.status = ax88179_status,
+	.link_reset = ax88179_link_reset,
+	.reset = ax88179_reset,
+	.stop = ax88179_stop,
+	.flags = FLAG_ETHER | FLAG_FRAMING_AX,
+	.rx_fixup = ax88179_rx_fixup,
+	.tx_fixup = ax88179_tx_fixup,
+};
+
+static const struct usb_device_id products[] = {
+{
+	/* ASIX AX88179 10/100/1000 */
+	USB_DEVICE(0x0b95, 0x1790),
+	.driver_info = (unsigned long)&ax88179_info,
+}, {
+	/* ASIX AX88178A 10/100/1000 */
+	USB_DEVICE(0x0b95, 0x178a),
+	.driver_info = (unsigned long)&ax88178a_info,
+}, {
+	/* Sitecom USB 3.0 to Gigabit Adapter */
+	USB_DEVICE(0x0df6, 0x0072),
+	.driver_info = (unsigned long) &sitecom_info,
+},
+	{ },
+};
+MODULE_DEVICE_TABLE(usb, products);
+
+static struct usb_driver ax88179_178a_driver = {
+	.name =		"ax88179_178a",
+	.id_table =	products,
+	.probe =	usbnet_probe,
+	.suspend =	ax88179_suspend,
+	.resume =	ax88179_resume,
+	.disconnect =	usbnet_disconnect,
+	.supports_autosuspend = 1,
+	.disable_hub_initiated_lpm = 1,
+};
+
+module_usb_driver(ax88179_178a_driver);
+
+MODULE_DESCRIPTION("ASIX AX88179/178A based USB 3.0/2.0 Gigabit Ethernet Devices");
+MODULE_LICENSE("GPL");
-- 
1.7.9.5


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

* Re: [PATCH resubmit] ax88179_178a: ASIX AX88179_178A USB 3.0/2.0 to gigabit ethernet adapter driver
  2013-02-27 12:01 Freddy Xin
@ 2013-03-01 21:15 ` David Miller
  0 siblings, 0 replies; 27+ messages in thread
From: David Miller @ 2013-03-01 21:15 UTC (permalink / raw)
  To: freddy; +Cc: netdev, linux-usb, linux-kernel, louis

From: Freddy Xin <freddy@asix.com.tw>
Date: Wed, 27 Feb 2013 20:01:58 +0800

> +	eeprom_buff = kmalloc(sizeof(u16) * (last_word - first_word + 1),
> +			      GFP_KERNEL);
> +	if (!eeprom_buff)
> +		return -ENOMEM;
> +
> +	/* ax88179/178A returns 2 bytes from eeprom on read */
> +	for (i = first_word; i <= last_word; i++) {
> +		ret = __ax88179_read_cmd(dev, AX_ACCESS_EEPROM, i, 1, 2,
> +					 &eeprom_buff[i - first_word],
> +					 0);
> +		if (ret < 0)
> +			return -EIO;

In this -EIO error path we will leak eeprom_buff, you must free it in
all possible exit cases of this function after the allocation succeeds.

> +	data->rxctl = (__force __le16)(AX_RX_CTL_START | AX_RX_CTL_AB |
> +				       AX_RX_CTL_IPE);

You're not storying a __le16 in data->rxctl, you're storing the bits
in cpu endianness.  So make it a plain u16 instead and get rid of
these pointless casts.

> +static int __init asix_init(void)
> +{
> +	return usb_register(&asix_driver);
> +}
> +module_init(asix_init);
> +
> +static void __exit asix_exit(void)
> +{
> +	usb_deregister(&asix_driver);
> +}
> +module_exit(asix_exit);

You can replace all of this with a simple:

module_usb_driver(asix_driver);

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

* [PATCH resubmit] ax88179_178a: ASIX AX88179_178A USB 3.0/2.0 to gigabit ethernet adapter driver
@ 2013-02-27 12:01 Freddy Xin
  2013-03-01 21:15 ` David Miller
  0 siblings, 1 reply; 27+ messages in thread
From: Freddy Xin @ 2013-02-27 12:01 UTC (permalink / raw)
  To: netdev; +Cc: davem, linux-usb, linux-kernel, louis, Freddy Xin

From: Freddy Xin <freddy@asix.com.tw>

This is a resubmission.
Fixed endianness issue on big endian systems and verified this driver on iBook G4.
Removed steps that change net->features in ax88179_set_features function.
Added "const" to ethtool_ops structure and fixed the coding style of AX88179_BULKIN_SIZE array.
Fixed the issue that the default MTU is not 1500.
Added ax88179_change_mtu function and enabled the hardware jumbo frame function to support an
MTU higher than 1500.
Fixed indentation and empty line coding style errors.
The _nopm version usb functions were added to access register in suspend and resume functions.
Serveral variables allocted dynamically were removed and replaced by stack variables.
ax88179_get_eeprom were modified from asix_get_eeprom in asix_common.

This patch adds a driver for ASIX's AX88179 family of USB 3.0/2.0
to gigabit ethernet adapters. It's based on the AX88xxx driver but
the usb commands used to access registers for AX88179 are completely different.
This driver had been verified on x86 system with AX88179/AX88178A and
Sitcomm LN-032 USB dongles.

Signed-off-by: Freddy Xin <freddy@asix.com.tw>
---
 drivers/net/usb/Kconfig        |   18 +
 drivers/net/usb/Makefile       |    1 +
 drivers/net/usb/ax88179_178a.c | 1455 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 1474 insertions(+)
 create mode 100644 drivers/net/usb/ax88179_178a.c

diff --git a/drivers/net/usb/Kconfig b/drivers/net/usb/Kconfig
index da92ed3..21e084d 100644
--- a/drivers/net/usb/Kconfig
+++ b/drivers/net/usb/Kconfig
@@ -156,6 +156,24 @@ config USB_NET_AX8817X
 	  This driver creates an interface named "ethX", where X depends on
 	  what other networking devices you have in use.
 
+config USB_NET_AX88179_178A
+	tristate "ASIX AX88179/178A USB 3.0/2.0 to Gigabit Ethernet"
+	depends on USB_USBNET
+	select CRC32
+	select PHYLIB
+	default y
+	help
+	  This option adds support for ASIX AX88179 based USB 3.0/2.0
+	  to Gigabit Ethernet adapters.
+
+ 	  This driver should work with at least the following devices:
+	    * ASIX AX88179
+	    * ASIX AX88178A
+	    * Sitcomm LN-032
+
+	  This driver creates an interface named "ethX", where X depends on
+	  what other networking devices you have in use.
+
 config USB_NET_CDCETHER
 	tristate "CDC Ethernet support (smart devices such as cable modems)"
 	depends on USB_USBNET
diff --git a/drivers/net/usb/Makefile b/drivers/net/usb/Makefile
index 4786913..119b06c 100644
--- a/drivers/net/usb/Makefile
+++ b/drivers/net/usb/Makefile
@@ -9,6 +9,7 @@ obj-$(CONFIG_USB_RTL8150)	+= rtl8150.o
 obj-$(CONFIG_USB_HSO)		+= hso.o
 obj-$(CONFIG_USB_NET_AX8817X)	+= asix.o
 asix-y := asix_devices.o asix_common.o ax88172a.o
+obj-$(CONFIG_USB_NET_AX88179_178A)      += ax88179_178a.o
 obj-$(CONFIG_USB_NET_CDCETHER)	+= cdc_ether.o
 obj-$(CONFIG_USB_NET_CDC_EEM)	+= cdc_eem.o
 obj-$(CONFIG_USB_NET_DM9601)	+= dm9601.o
diff --git a/drivers/net/usb/ax88179_178a.c b/drivers/net/usb/ax88179_178a.c
new file mode 100644
index 0000000..ed1ab2f
--- /dev/null
+++ b/drivers/net/usb/ax88179_178a.c
@@ -0,0 +1,1455 @@
+/*
+ * ASIX AX88179/178A USB 3.0/2.0 to Gigabit Ethernet Devices
+ *
+ * Copyright (C) 2011-2013 ASIX
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/etherdevice.h>
+#include <linux/mii.h>
+#include <linux/usb.h>
+#include <linux/crc32.h>
+#include <linux/usb/usbnet.h>
+
+#define AX88179_PHY_ID				0x03
+#define AX_EEPROM_LEN				0x100
+#define AX88179_EEPROM_MAGIC			0x17900b95
+#define AX_MCAST_FLTSIZE			8
+#define AX_MAX_MCAST				64
+#define AX_INT_PPLS_LINK			((u32)BIT(16))
+#define AX_RXHDR_L4_TYPE_MASK			0x1c
+#define AX_RXHDR_L4_TYPE_UDP			4
+#define AX_RXHDR_L4_TYPE_TCP			16
+#define AX_RXHDR_L3CSUM_ERR			2
+#define AX_RXHDR_L4CSUM_ERR			1
+#define AX_RXHDR_CRC_ERR			((u32)BIT(31))
+#define AX_RXHDR_DROP_ERR			((u32)BIT(30))
+#define AX_ACCESS_MAC				0x01
+#define AX_ACCESS_PHY				0x02
+#define AX_ACCESS_EEPROM			0x04
+#define AX_ACCESS_EFUS				0x05
+#define AX_PAUSE_WATERLVL_HIGH			0x54
+#define AX_PAUSE_WATERLVL_LOW			0x55
+
+#define PHYSICAL_LINK_STATUS			0x02
+	#define	AX_USB_SS		0x04
+	#define	AX_USB_HS		0x02
+
+#define GENERAL_STATUS				0x03
+/* Check AX88179 version. UA1:Bit2 = 0,  UA2:Bit2 = 1 */
+	#define	AX_SECLD		0x04
+
+#define AX_SROM_ADDR				0x07
+#define AX_SROM_CMD				0x0a
+	#define EEP_RD			0x04
+	#define EEP_BUSY		0x10
+
+#define AX_SROM_DATA_LOW			0x08
+#define AX_SROM_DATA_HIGH			0x09
+
+#define AX_RX_CTL				0x0b
+	#define AX_RX_CTL_DROPCRCERR	0x0100
+	#define AX_RX_CTL_IPE		0x0200
+	#define AX_RX_CTL_START		0x0080
+	#define AX_RX_CTL_AP		0x0020
+	#define AX_RX_CTL_AM		0x0010
+	#define AX_RX_CTL_AB		0x0008
+	#define AX_RX_CTL_AMALL		0x0002
+	#define AX_RX_CTL_PRO		0x0001
+	#define AX_RX_CTL_STOP		0x0000
+
+#define AX_NODE_ID				0x10
+#define AX_MULFLTARY				0x16
+
+#define AX_MEDIUM_STATUS_MODE			0x22
+	#define AX_MEDIUM_GIGAMODE	0x01
+	#define AX_MEDIUM_FULL_DUPLEX	0x02
+	#define AX_MEDIUM_ALWAYS_ONE	0x04
+	#define AX_MEDIUM_EN_125MHZ	0x08
+	#define AX_MEDIUM_RXFLOW_CTRLEN	0x10
+	#define AX_MEDIUM_TXFLOW_CTRLEN	0x20
+	#define AX_MEDIUM_RECEIVE_EN	0x100
+	#define AX_MEDIUM_PS		0x200
+	#define AX_MEDIUM_JUMBO_EN	0x8040
+
+#define AX_MONITOR_MOD				0x24
+	#define AX_MONITOR_MODE_RWLC	0x02
+	#define AX_MONITOR_MODE_RWMP	0x04
+	#define AX_MONITOR_MODE_PMEPOL	0x20
+	#define AX_MONITOR_MODE_PMETYPE	0x40
+
+#define AX_GPIO_CTRL				0x25
+	#define AX_GPIO_CTRL_GPIO3EN	0x80
+	#define AX_GPIO_CTRL_GPIO2EN	0x40
+	#define AX_GPIO_CTRL_GPIO1EN	0x20
+
+#define AX_PHYPWR_RSTCTL			0x26
+	#define AX_PHYPWR_RSTCTL_BZ	0x0010
+	#define AX_PHYPWR_RSTCTL_IPRL	0x0020
+	#define AX_PHYPWR_RSTCTL_AT	0x1000
+
+#define AX_RX_BULKIN_QCTRL			0x2e
+#define AX_CLK_SELECT				0x33
+	#define AX_CLK_SELECT_BCS	0x01
+	#define AX_CLK_SELECT_ACS	0x02
+	#define AX_CLK_SELECT_ULR	0x08
+
+#define AX_RXCOE_CTL				0x34
+	#define AX_RXCOE_IP		0x01
+	#define AX_RXCOE_TCP		0x02
+	#define AX_RXCOE_UDP		0x04
+	#define AX_RXCOE_TCPV6		0x20
+	#define AX_RXCOE_UDPV6		0x40
+
+#define AX_TXCOE_CTL				0x35
+	#define AX_TXCOE_IP		0x01
+	#define AX_TXCOE_TCP		0x02
+	#define AX_TXCOE_UDP		0x04
+	#define AX_TXCOE_TCPV6		0x20
+	#define AX_TXCOE_UDPV6		0x40
+
+#define AX_LEDCTRL				0x73
+
+#define GMII_PHY_PHYSR				0x11
+	#define GMII_PHY_PHYSR_SMASK	0xc000
+	#define GMII_PHY_PHYSR_GIGA	0x8000
+	#define GMII_PHY_PHYSR_100	0x4000
+	#define GMII_PHY_PHYSR_FULL	0x2000
+	#define GMII_PHY_PHYSR_LINK	0x400
+
+#define GMII_LED_ACT				0x1a
+	#define GMII_LED_ACTIVE_MASK	0xff8f
+	#define GMII_LED0_ACTIVE	BIT(4)
+	#define GMII_LED1_ACTIVE	BIT(5)
+	#define GMII_LED2_ACTIVE	BIT(6)
+
+#define GMII_LED_LINK				0x1c
+	#define GMII_LED_LINK_MASK	0xf888
+	#define GMII_LED0_LINK_10	BIT(0)
+	#define GMII_LED0_LINK_100	BIT(1)
+	#define GMII_LED0_LINK_1000	BIT(2)
+	#define GMII_LED1_LINK_10	BIT(4)
+	#define GMII_LED1_LINK_100	BIT(5)
+	#define GMII_LED1_LINK_1000	BIT(6)
+	#define GMII_LED2_LINK_10	BIT(8)
+	#define GMII_LED2_LINK_100	BIT(9)
+	#define GMII_LED2_LINK_1000	BIT(10)
+	#define	LED0_ACTIVE		BIT(0)
+	#define	LED0_LINK_10		BIT(1)
+	#define	LED0_LINK_100		BIT(2)
+	#define	LED0_LINK_1000		BIT(3)
+	#define	LED0_FD			BIT(4)
+	#define LED0_USB3_MASK		0x001f
+	#define	LED1_ACTIVE		BIT(5)
+	#define	LED1_LINK_10		BIT(6)
+	#define	LED1_LINK_100		BIT(7)
+	#define	LED1_LINK_1000		BIT(8)
+	#define	LED1_FD			BIT(9)
+	#define LED1_USB3_MASK		0x03e0
+	#define	LED2_ACTIVE		BIT(10)
+	#define	LED2_LINK_1000		BIT(13)
+	#define	LED2_LINK_100		BIT(12)
+	#define	LED2_LINK_10		BIT(11)
+	#define	LED2_FD			BIT(14)
+	#define	LED_VALID		BIT(15)
+	#define LED2_USB3_MASK		0x7c00
+
+#define GMII_PHYPAGE				0x1e
+#define GMII_PHY_PAGE_SELECT			0x1f
+	#define GMII_PHY_PGSEL_EXT	0x0007
+	#define GMII_PHY_PGSEL_PAGE0	0x0000
+
+struct ax88179_data {
+	__le16 rxctl;
+	__le16 reserved;
+};
+
+struct ax88179_int_data {
+	__le32 intdata1;
+	__le32 intdata2;
+};
+
+static const struct {
+	unsigned char ctrl, timer_l, timer_h, size, ifg;
+} AX88179_BULKIN_SIZE[] =	{
+	{7, 0x4f, 0,	0x12, 0xff},
+	{7, 0x20, 3,	0x16, 0xff},
+	{7, 0xae, 7,	0x18, 0xff},
+	{7, 0xcc, 0x4c, 0x18, 8},
+};
+
+static int __ax88179_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,
+			      u16 size, void *data, int in_pm)
+{
+	int ret;
+	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);
+
+	return ret;
+}
+
+static int __ax88179_write_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,
+			       u16 size, void *data, int in_pm)
+{
+	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;
+}
+
+static void ax88179_write_cmd_async(struct usbnet *dev, u8 cmd, u16 value,
+				    u16 index, u16 size, void *data)
+{
+	u16 buf;
+
+	if (2 == size) {
+		buf = *((u16 *)data);
+		cpu_to_le16s(&buf);
+		usbnet_write_cmd_async(dev, cmd, USB_DIR_OUT | USB_TYPE_VENDOR |
+				       USB_RECIP_DEVICE, value, index, &buf,
+				       size);
+	} else {
+		usbnet_write_cmd_async(dev, cmd, USB_DIR_OUT | USB_TYPE_VENDOR |
+				       USB_RECIP_DEVICE, value, index, data,
+				       size);
+	}
+}
+
+static int ax88179_read_cmd_nopm(struct usbnet *dev, u8 cmd, u16 value,
+				 u16 index, u16 size, void *data)
+{
+	int ret;
+
+	if (2 == size) {
+		u16 buf;
+		ret = __ax88179_read_cmd(dev, cmd, value, index, size, &buf, 1);
+		le16_to_cpus(&buf);
+		*((u16 *)data) = buf;
+	} else if (4 == size) {
+		u32 buf;
+		ret = __ax88179_read_cmd(dev, cmd, value, index, size, &buf, 1);
+		le32_to_cpus(&buf);
+		*((u32 *)data) = buf;
+	} else {
+		ret = __ax88179_read_cmd(dev, cmd, value, index, size, data, 1);
+	}
+
+	return ret;
+}
+
+static int ax88179_write_cmd_nopm(struct usbnet *dev, u8 cmd, u16 value,
+				  u16 index, u16 size, void *data)
+{
+	int ret;
+
+	if (2 == size) {
+		u16 buf;
+		buf = *((u16 *)data);
+		cpu_to_le16s(&buf);
+		ret = __ax88179_write_cmd(dev, cmd, value, index,
+					  size, &buf, 1);
+	} else {
+		ret = __ax88179_write_cmd(dev, cmd, value, index,
+					  size, data, 1);
+	}
+
+	return ret;
+}
+
+static int ax88179_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,
+			    u16 size, void *data)
+{
+	int ret;
+
+	if (2 == size) {
+		u16 buf;
+		ret = __ax88179_read_cmd(dev, cmd, value, index, size, &buf, 0);
+		le16_to_cpus(&buf);
+		*((u16 *)data) = buf;
+	} else if (4 == size) {
+		u32 buf;
+		ret = __ax88179_read_cmd(dev, cmd, value, index, size, &buf, 0);
+		le32_to_cpus(&buf);
+		*((u32 *)data) = buf;
+	} else {
+		ret = __ax88179_read_cmd(dev, cmd, value, index, size, data, 0);
+	}
+
+	return ret;
+}
+
+static int ax88179_write_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,
+			     u16 size, void *data)
+{
+	int ret;
+
+	if (2 == size) {
+		u16 buf;
+		buf = *((u16 *)data);
+		cpu_to_le16s(&buf);
+		ret = __ax88179_write_cmd(dev, cmd, value, index,
+					  size, &buf, 0);
+	} else {
+		ret = __ax88179_write_cmd(dev, cmd, value, index,
+					  size, data, 0);
+	}
+
+	return ret;
+}
+
+static void ax88179_status(struct usbnet *dev, struct urb *urb)
+{
+	struct ax88179_int_data *event;
+	u32 link;
+
+	if (urb->actual_length < 8)
+		return;
+
+	event = urb->transfer_buffer;
+	le32_to_cpus((void *)&event->intdata1);
+
+	link = (((__force u32)event->intdata1) & AX_INT_PPLS_LINK) >> 16;
+
+	if (netif_carrier_ok(dev->net) != link) {
+		if (link)
+			usbnet_defer_kevent(dev, EVENT_LINK_RESET);
+		else
+			netif_carrier_off(dev->net);
+
+		netdev_info(dev->net, "ax88179 - Link status is: %d\n", link);
+	}
+}
+
+static int ax88179_mdio_read(struct net_device *netdev, int phy_id, int loc)
+{
+	struct usbnet *dev = netdev_priv(netdev);
+	u16 res;
+
+	ax88179_read_cmd(dev, AX_ACCESS_PHY, phy_id, (__u16)loc, 2, &res);
+	return res;
+}
+
+static void ax88179_mdio_write(struct net_device *netdev, int phy_id, int loc,
+			       int val)
+{
+	struct usbnet *dev = netdev_priv(netdev);
+	u16 res = (u16) val;
+
+	ax88179_write_cmd(dev, AX_ACCESS_PHY, phy_id, (__u16)loc, 2, &res);
+}
+
+static int ax88179_suspend(struct usb_interface *intf, pm_message_t message)
+{
+	struct usbnet *dev = usb_get_intfdata(intf);
+	u16 tmp16;
+	u8 tmp8;
+
+	usbnet_suspend(intf, message);
+
+	/* Disable RX path */
+	ax88179_read_cmd_nopm(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,
+			      2, 2, &tmp16);
+	tmp16 &= ~AX_MEDIUM_RECEIVE_EN;
+	ax88179_write_cmd_nopm(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,
+			       2, 2, &tmp16);
+
+	/* Force bulk-in zero length */
+	ax88179_read_cmd_nopm(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL,
+			      2, 2, &tmp16);
+
+	tmp16 |= AX_PHYPWR_RSTCTL_BZ | AX_PHYPWR_RSTCTL_IPRL;
+	ax88179_write_cmd_nopm(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL,
+			       2, 2, &tmp16);
+
+	/* change clock */
+	tmp8 = 0;
+	ax88179_write_cmd_nopm(dev, AX_ACCESS_MAC, AX_CLK_SELECT, 1, 1, &tmp8);
+
+	/* Configure RX control register => stop operation */
+	tmp16 = AX_RX_CTL_STOP;
+	ax88179_write_cmd_nopm(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2, &tmp16);
+
+	return 0;
+}
+
+/* This function is used to enable the autodetach function. */
+/* This function is determined by offset 0x43 of EEPROM */
+static int ax88179_auto_detach(struct usbnet *dev, int in_pm)
+{
+	u16 tmp16;
+	u8 tmp8;
+	int (*fnr)(struct usbnet *, u8, u16, u16, u16, void *);
+	int (*fnw)(struct usbnet *, u8, u16, u16, u16, void *);
+
+	if (!in_pm) {
+		fnr = ax88179_read_cmd;
+		fnw = ax88179_write_cmd;
+	} else {
+		fnr = ax88179_read_cmd_nopm;
+		fnw = ax88179_write_cmd_nopm;
+	}
+
+	if (fnr(dev, AX_ACCESS_EEPROM, 0x43, 1, 2, &tmp16) < 0)
+		return 0;
+
+	if ((tmp16 == 0xFFFF) || (!(tmp16 & 0x0100)))
+		return 0;
+
+	/* Enable Auto Detach bit */
+	tmp8 = 0;
+	fnr(dev, AX_ACCESS_MAC, AX_CLK_SELECT, 1, 1, &tmp8);
+	tmp8 |= AX_CLK_SELECT_ULR;
+	fnw(dev, AX_ACCESS_MAC, AX_CLK_SELECT, 1, 1, &tmp8);
+
+	fnr(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, &tmp16);
+	tmp16 |= AX_PHYPWR_RSTCTL_AT;
+	fnw(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, &tmp16);
+
+	return 0;
+}
+
+static int ax88179_resume(struct usb_interface *intf)
+{
+	struct usbnet *dev = usb_get_intfdata(intf);
+	u16 tmp16;
+	u8 tmp8;
+
+	netif_carrier_off(dev->net);
+
+	/* Power up ethernet PHY */
+	tmp16 = 0;
+	ax88179_write_cmd_nopm(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL,
+			       2, 2, &tmp16);
+	udelay(1000);
+
+	tmp16 = AX_PHYPWR_RSTCTL_IPRL;
+	ax88179_write_cmd_nopm(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL,
+			       2, 2, &tmp16);
+	msleep(200);
+
+	/* Ethernet PHY Auto Detach*/
+	ax88179_auto_detach(dev, 1);
+
+	/* Enable clock */
+	ax88179_read_cmd_nopm(dev, AX_ACCESS_MAC,  AX_CLK_SELECT, 1, 1, &tmp8);
+	tmp8 |= AX_CLK_SELECT_ACS | AX_CLK_SELECT_BCS;
+	ax88179_write_cmd_nopm(dev, AX_ACCESS_MAC, AX_CLK_SELECT, 1, 1, &tmp8);
+	msleep(100);
+
+	/* Configure RX control register => start operation */
+	tmp16 = AX_RX_CTL_DROPCRCERR | AX_RX_CTL_IPE | AX_RX_CTL_START |
+		AX_RX_CTL_AP | AX_RX_CTL_AMALL | AX_RX_CTL_AB;
+	ax88179_write_cmd_nopm(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2, &tmp16);
+
+	return usbnet_resume(intf);
+}
+
+static void
+ax88179_get_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo)
+{
+	struct usbnet *dev = netdev_priv(net);
+	u8 opt;
+
+	if (ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_MONITOR_MOD,
+			     1, 1, &opt) < 0) {
+		wolinfo->supported = 0;
+		wolinfo->wolopts = 0;
+		return;
+	}
+
+	wolinfo->supported = WAKE_PHY | WAKE_MAGIC;
+	wolinfo->wolopts = 0;
+	if (opt & AX_MONITOR_MODE_RWLC)
+		wolinfo->wolopts |= WAKE_PHY;
+	if (opt & AX_MONITOR_MODE_RWMP)
+		wolinfo->wolopts |= WAKE_MAGIC;
+}
+
+static int
+ax88179_set_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo)
+{
+	struct usbnet *dev = netdev_priv(net);
+	u8 opt = 0;
+
+	if (wolinfo->wolopts & WAKE_PHY)
+		opt |= AX_MONITOR_MODE_RWLC;
+	if (wolinfo->wolopts & WAKE_MAGIC)
+		opt |= AX_MONITOR_MODE_RWMP;
+
+	if (ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MONITOR_MOD,
+			      1, 1, &opt) < 0)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int ax88179_get_eeprom_len(struct net_device *net)
+{
+	return AX_EEPROM_LEN;
+}
+
+static int
+ax88179_get_eeprom(struct net_device *net, struct ethtool_eeprom *eeprom,
+		   u8 *data)
+{
+	struct usbnet *dev = netdev_priv(net);
+	u16 *eeprom_buff;
+	int first_word, last_word;
+	int i, ret;
+
+	if (eeprom->len == 0)
+		return -EINVAL;
+
+	eeprom->magic = AX88179_EEPROM_MAGIC;
+
+	first_word = eeprom->offset >> 1;
+	last_word = (eeprom->offset + eeprom->len - 1) >> 1;
+	eeprom_buff = kmalloc(sizeof(u16) * (last_word - first_word + 1),
+			      GFP_KERNEL);
+	if (!eeprom_buff)
+		return -ENOMEM;
+
+	/* ax88179/178A returns 2 bytes from eeprom on read */
+	for (i = first_word; i <= last_word; i++) {
+		ret = __ax88179_read_cmd(dev, AX_ACCESS_EEPROM, i, 1, 2,
+					 &eeprom_buff[i - first_word],
+					 0);
+		if (ret < 0)
+			return -EIO;
+	}
+
+	memcpy(data, (u8 *)eeprom_buff + (eeprom->offset & 1), eeprom->len);
+	kfree(eeprom_buff);
+	return 0;
+}
+
+static int ax88179_get_settings(struct net_device *net, struct ethtool_cmd *cmd)
+{
+	struct usbnet *dev = netdev_priv(net);
+	return mii_ethtool_gset(&dev->mii, cmd);
+}
+
+static int ax88179_set_settings(struct net_device *net, struct ethtool_cmd *cmd)
+{
+	struct usbnet *dev = netdev_priv(net);
+	return mii_ethtool_sset(&dev->mii, cmd);
+}
+
+
+static int ax88179_ioctl(struct net_device *net, struct ifreq *rq, int cmd)
+{
+	struct usbnet *dev = netdev_priv(net);
+	return generic_mii_ioctl(&dev->mii, if_mii(rq), cmd, NULL);
+}
+
+static const struct ethtool_ops ax88179_ethtool_ops = {
+	.get_link		= ethtool_op_get_link,
+	.get_msglevel		= usbnet_get_msglevel,
+	.set_msglevel		= usbnet_set_msglevel,
+	.get_wol		= ax88179_get_wol,
+	.set_wol		= ax88179_set_wol,
+	.get_eeprom_len		= ax88179_get_eeprom_len,
+	.get_eeprom		= ax88179_get_eeprom,
+	.get_settings		= ax88179_get_settings,
+	.set_settings		= ax88179_set_settings,
+	.nway_reset		= usbnet_nway_reset,
+};
+
+static void ax88179_set_multicast(struct net_device *net)
+{
+	struct usbnet *dev = netdev_priv(net);
+	struct ax88179_data *data = (struct ax88179_data *)dev->data;
+	u8 *m_filter = ((u8 *)dev->data) + 12;
+
+	data->rxctl = (__force __le16)(AX_RX_CTL_START | AX_RX_CTL_AB |
+				       AX_RX_CTL_IPE);
+
+	if (net->flags & IFF_PROMISC) {
+		data->rxctl |= (__force __le16)AX_RX_CTL_PRO;
+	} else if (net->flags & IFF_ALLMULTI ||
+		   netdev_mc_count(net) > AX_MAX_MCAST) {
+		data->rxctl |= (__force __le16)AX_RX_CTL_AMALL;
+	} else if (netdev_mc_empty(net)) {
+		/* just broadcast and directed */
+	} else {
+		/* We use the 20 byte dev->data for our 8 byte filter buffer
+		 * to avoid allocating memory that is tricky to free later
+		 */
+		u32 crc_bits;
+		struct netdev_hw_addr *ha;
+
+		memset(m_filter, 0, AX_MCAST_FLTSIZE);
+
+		netdev_for_each_mc_addr(ha, net) {
+			crc_bits = ether_crc(ETH_ALEN, ha->addr) >> 26;
+			*(m_filter + (crc_bits >> 3)) |= (1 << (crc_bits & 7));
+		}
+
+		ax88179_write_cmd_async(dev, AX_ACCESS_MAC, AX_MULFLTARY,
+					AX_MCAST_FLTSIZE, AX_MCAST_FLTSIZE,
+					m_filter);
+
+		data->rxctl |= (__force __le16)AX_RX_CTL_AM;
+	}
+
+	ax88179_write_cmd_async(dev, AX_ACCESS_MAC, AX_RX_CTL,
+				2, 2, &data->rxctl);
+}
+
+static int
+ax88179_set_features(struct net_device *net, netdev_features_t features)
+{
+	u8 tmp;
+	struct usbnet *dev = netdev_priv(net);
+	netdev_features_t changed = net->features ^ features;
+
+	if (changed & NETIF_F_IP_CSUM) {
+		ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, 1, 1, &tmp);
+		tmp ^= AX_TXCOE_TCP | AX_TXCOE_UDP;
+		ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, 1, 1, &tmp);
+	}
+
+	if (changed & NETIF_F_IPV6_CSUM) {
+		ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, 1, 1, &tmp);
+		tmp ^= AX_TXCOE_TCPV6 | AX_TXCOE_UDPV6;
+		ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, 1, 1, &tmp);
+	}
+
+	if (changed & NETIF_F_RXCSUM) {
+		ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_RXCOE_CTL, 1, 1, &tmp);
+		tmp ^= AX_RXCOE_IP | AX_RXCOE_TCP | AX_RXCOE_UDP |
+		       AX_RXCOE_TCPV6 | AX_RXCOE_UDPV6;
+		ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RXCOE_CTL, 1, 1, &tmp);
+	}
+
+	return 0;
+}
+
+static int ax88179_change_mtu(struct net_device *net, int new_mtu)
+{
+	struct usbnet *dev = netdev_priv(net);
+	u16 tmp16;
+
+	if (new_mtu <= 0 || new_mtu > 4088)
+		return -EINVAL;
+
+	net->mtu = new_mtu;
+	dev->hard_mtu = net->mtu + net->hard_header_len;
+
+	if (net->mtu > 1500) {
+		ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,
+				 2, 2, &tmp16);
+		tmp16 |= AX_MEDIUM_JUMBO_EN;
+		ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,
+				  2, 2, &tmp16);
+	} else {
+		ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,
+				 2, 2, &tmp16);
+		tmp16 &= ~AX_MEDIUM_JUMBO_EN;
+		ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,
+				  2, 2, &tmp16);
+	}
+
+	return 0;
+}
+
+static int ax88179_set_mac_addr(struct net_device *net, void *p)
+{
+	struct usbnet *dev = netdev_priv(net);
+	struct sockaddr *addr = p;
+
+	if (netif_running(net))
+		return -EBUSY;
+	if (!is_valid_ether_addr(addr->sa_data))
+		return -EADDRNOTAVAIL;
+
+	memcpy(net->dev_addr, addr->sa_data, ETH_ALEN);
+
+	/* Set the MAC address */
+	return ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_NODE_ID, ETH_ALEN,
+				 ETH_ALEN, net->dev_addr);
+}
+
+static const struct net_device_ops ax88179_netdev_ops = {
+	.ndo_open		= usbnet_open,
+	.ndo_stop		= usbnet_stop,
+	.ndo_start_xmit		= usbnet_start_xmit,
+	.ndo_tx_timeout		= usbnet_tx_timeout,
+	.ndo_change_mtu		= ax88179_change_mtu,
+	.ndo_set_mac_address	= ax88179_set_mac_addr,
+	.ndo_validate_addr	= eth_validate_addr,
+	.ndo_do_ioctl		= ax88179_ioctl,
+	.ndo_set_rx_mode	= ax88179_set_multicast,
+	.ndo_set_features	= ax88179_set_features,
+};
+
+static int ax88179_check_eeprom(struct usbnet *dev)
+{
+	u8 i, buf, eeprom[20];
+	u16 csum, delay = HZ / 10;
+	unsigned long jtimeout;
+
+	/* Read EEPROM content */
+	for (i = 0; i < 6; i++) {
+		buf = i;
+		if (ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_SROM_ADDR,
+				      1, 1, &buf) < 0)
+			return -EINVAL;
+
+		buf = EEP_RD;
+		if (ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_SROM_CMD,
+				      1, 1, &buf) < 0)
+			return -EINVAL;
+
+		jtimeout = jiffies + delay;
+		do {
+			ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_SROM_CMD,
+					 1, 1, &buf);
+
+			if (time_after(jiffies, jtimeout))
+				return -EINVAL;
+
+		} while (buf & EEP_BUSY);
+
+		__ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_SROM_DATA_LOW,
+				   2, 2, &eeprom[i * 2], 0);
+
+		if ((i == 0) && (eeprom[0] == 0xFF))
+			return -EINVAL;
+	}
+
+	csum = eeprom[6] + eeprom[7] + eeprom[8] + eeprom[9];
+	csum = (csum >> 8) + (csum & 0xff);
+	if ((csum + eeprom[10]) != 0xff)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int ax88179_check_efuse(struct usbnet *dev, u16 *ledmode)
+{
+	u8	i;
+	u8	efuse[64];
+	u16	csum = 0;
+
+	if (ax88179_read_cmd(dev, AX_ACCESS_EFUS, 0, 64, 64, efuse) < 0)
+		return -EINVAL;
+
+	if (*efuse == 0xFF)
+		return -EINVAL;
+
+	for (i = 0; i < 64; i++)
+		csum = csum + efuse[i];
+
+	while (csum > 255)
+		csum = (csum & 0x00FF) + ((csum >> 8) & 0x00FF);
+
+	if (csum != 0xFF)
+		return -EINVAL;
+
+	*ledmode = (efuse[51] << 8) | efuse[52];
+
+	return 0;
+}
+
+static int ax88179_convert_old_led(struct usbnet *dev, u16 *ledvalue)
+{
+	u16 led;
+
+	/* Loaded the old eFuse LED Mode */
+	if (ax88179_read_cmd(dev, AX_ACCESS_EEPROM, 0x3C, 1, 2, &led) < 0)
+		return -EINVAL;
+
+	led >>= 8;
+	switch (led) {
+	case 0xFF:
+		led = LED0_ACTIVE | LED1_LINK_10 | LED1_LINK_100 |
+		      LED1_LINK_1000 | LED2_ACTIVE | LED2_LINK_10 |
+		      LED2_LINK_100 | LED2_LINK_1000 | LED_VALID;
+		break;
+	case 0xFE:
+		led = LED0_ACTIVE | LED1_LINK_1000 | LED2_LINK_100 | LED_VALID;
+		break;
+	case 0xFD:
+		led = LED0_ACTIVE | LED1_LINK_1000 | LED2_LINK_100 |
+		      LED2_LINK_10 | LED_VALID;
+		break;
+	case 0xFC:
+		led = LED0_ACTIVE | LED1_ACTIVE | LED1_LINK_1000 | LED2_ACTIVE |
+		      LED2_LINK_100 | LED2_LINK_10 | LED_VALID;
+		break;
+	default:
+		led = LED0_ACTIVE | LED1_LINK_10 | LED1_LINK_100 |
+		      LED1_LINK_1000 | LED2_ACTIVE | LED2_LINK_10 |
+		      LED2_LINK_100 | LED2_LINK_1000 | LED_VALID;
+		break;
+	}
+
+	*ledvalue = led;
+
+	return 0;
+}
+
+static int ax88179_led_setting(struct usbnet *dev)
+{
+	u8 ledfd, value = 0;
+	u16 tmp, ledact, ledlink, ledvalue = 0, delay = HZ / 10;
+	unsigned long jtimeout;
+
+	/* Check AX88179 version. UA1 or UA2*/
+	ax88179_read_cmd(dev, AX_ACCESS_MAC, GENERAL_STATUS, 1, 1, &value);
+
+	if (!(value & AX_SECLD)) {	/* UA1 */
+		value = AX_GPIO_CTRL_GPIO3EN | AX_GPIO_CTRL_GPIO2EN |
+			AX_GPIO_CTRL_GPIO1EN;
+		if (ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_GPIO_CTRL,
+				      1, 1, &value) < 0)
+			return -EINVAL;
+	}
+
+	/* Check EEPROM */
+	if (!ax88179_check_eeprom(dev)) {
+		value = 0x42;
+		if (ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_SROM_ADDR,
+				      1, 1, &value) < 0)
+			return -EINVAL;
+
+		value = EEP_RD;
+		if (ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_SROM_CMD,
+				      1, 1, &value) < 0)
+			return -EINVAL;
+
+		jtimeout = jiffies + delay;
+		do {
+			ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_SROM_CMD,
+					 1, 1, &value);
+
+			if (time_after(jiffies, jtimeout))
+				return -EINVAL;
+
+		} while (value & EEP_BUSY);
+
+		ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_SROM_DATA_HIGH,
+				 1, 1, &value);
+		ledvalue = (value << 8);
+
+		ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_SROM_DATA_LOW,
+				 1, 1, &value);
+		ledvalue |= value;
+
+		/* load internal ROM for defaule setting */
+		if ((ledvalue == 0xFFFF) || ((ledvalue & LED_VALID) == 0))
+			ax88179_convert_old_led(dev, &ledvalue);
+
+	} else if (!ax88179_check_efuse(dev, &ledvalue)) {
+		if ((ledvalue == 0xFFFF) || ((ledvalue & LED_VALID) == 0))
+			ax88179_convert_old_led(dev, &ledvalue);
+	} else {
+		ax88179_convert_old_led(dev, &ledvalue);
+	}
+
+	tmp = GMII_PHY_PGSEL_EXT;
+	ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
+			  GMII_PHY_PAGE_SELECT, 2, &tmp);
+
+	tmp = 0x2c;
+	ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
+			  GMII_PHYPAGE, 2, &tmp);
+
+	ax88179_read_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
+			 GMII_LED_ACT, 2, &ledact);
+
+	ax88179_read_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
+			 GMII_LED_LINK, 2, &ledlink);
+
+	ledact &= GMII_LED_ACTIVE_MASK;
+	ledlink &= GMII_LED_LINK_MASK;
+
+	if (ledvalue & LED0_ACTIVE)
+		ledact |= GMII_LED0_ACTIVE;
+
+	if (ledvalue & LED1_ACTIVE)
+		ledact |= GMII_LED1_ACTIVE;
+
+	if (ledvalue & LED2_ACTIVE)
+		ledact |= GMII_LED2_ACTIVE;
+
+	if (ledvalue & LED0_LINK_10)
+		ledlink |= GMII_LED0_LINK_10;
+
+	if (ledvalue & LED1_LINK_10)
+		ledlink |= GMII_LED1_LINK_10;
+
+	if (ledvalue & LED2_LINK_10)
+		ledlink |= GMII_LED2_LINK_10;
+
+	if (ledvalue & LED0_LINK_100)
+		ledlink |= GMII_LED0_LINK_100;
+
+	if (ledvalue & LED1_LINK_100)
+		ledlink |= GMII_LED1_LINK_100;
+
+	if (ledvalue & LED2_LINK_100)
+		ledlink |= GMII_LED2_LINK_100;
+
+	if (ledvalue & LED0_LINK_1000)
+		ledlink |= GMII_LED0_LINK_1000;
+
+	if (ledvalue & LED1_LINK_1000)
+		ledlink |= GMII_LED1_LINK_1000;
+
+	if (ledvalue & LED2_LINK_1000)
+		ledlink |= GMII_LED2_LINK_1000;
+
+	tmp = ledact;
+	ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
+			  GMII_LED_ACT, 2, &tmp);
+
+	tmp = ledlink;
+	ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
+			  GMII_LED_LINK, 2, &tmp);
+
+	tmp = GMII_PHY_PGSEL_PAGE0;
+	ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
+			  GMII_PHY_PAGE_SELECT, 2, &tmp);
+
+	/* LED full duplex setting */
+	ledfd = 0;
+	if (ledvalue & LED0_FD)
+		ledfd |= 0x01;
+	else if ((ledvalue & LED0_USB3_MASK) == 0)
+		ledfd |= 0x02;
+
+	if (ledvalue & LED1_FD)
+		ledfd |= 0x04;
+	else if ((ledvalue & LED1_USB3_MASK) == 0)
+		ledfd |= 0x08;
+
+	if (ledvalue & LED2_FD)
+		ledfd |= 0x10;
+	else if ((ledvalue & LED2_USB3_MASK) == 0)
+		ledfd |= 0x20;
+
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_LEDCTRL, 1, 1, &ledfd);
+
+	return 0;
+}
+
+static int ax88179_bind(struct usbnet *dev, struct usb_interface *intf)
+{
+	u8 buf[5];
+	u16 *tmp16;
+	u8 *tmp;
+	struct ax88179_data *ax179_data = (struct ax88179_data *)dev->data;
+
+	usbnet_get_endpoints(dev, intf);
+
+	tmp16 = (u16 *)buf;
+	tmp = (u8 *)buf;
+
+	memset(ax179_data, 0, sizeof(*ax179_data));
+
+	/* Power up ethernet PHY */
+	*tmp16 = 0;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, tmp16);
+	*tmp16 = AX_PHYPWR_RSTCTL_IPRL;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, tmp16);
+	msleep(200);
+
+	*tmp = AX_CLK_SELECT_ACS | AX_CLK_SELECT_BCS;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_CLK_SELECT, 1, 1, tmp);
+	msleep(100);
+
+	ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_NODE_ID, ETH_ALEN,
+			 ETH_ALEN, dev->net->dev_addr);
+	memcpy(dev->net->perm_addr, dev->net->dev_addr, ETH_ALEN);
+
+	/* RX bulk configuration */
+	memcpy(tmp, &AX88179_BULKIN_SIZE[0], 5);
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_BULKIN_QCTRL, 5, 5, tmp);
+
+	dev->rx_urb_size = 1024 * 20;
+
+	*tmp = 0x34;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PAUSE_WATERLVL_LOW, 1, 1, tmp);
+
+	*tmp = 0x52;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PAUSE_WATERLVL_HIGH,
+			  1, 1, tmp);
+
+	dev->net->netdev_ops = &ax88179_netdev_ops;
+	dev->net->ethtool_ops = &ax88179_ethtool_ops;
+	dev->net->needed_headroom = 8;
+
+	/* Initialize MII structure */
+	dev->mii.dev = dev->net;
+	dev->mii.mdio_read = ax88179_mdio_read;
+	dev->mii.mdio_write = ax88179_mdio_write;
+	dev->mii.phy_id_mask = 0xff;
+	dev->mii.reg_num_mask = 0xff;
+	dev->mii.phy_id = 0x03;
+	dev->mii.supports_gmii = 1;
+
+	dev->net->features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
+			      NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_TSO;
+
+	dev->net->hw_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
+				 NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_TSO;
+
+	/* Enable checksum offload */
+	*tmp = AX_RXCOE_IP | AX_RXCOE_TCP | AX_RXCOE_UDP |
+	       AX_RXCOE_TCPV6 | AX_RXCOE_UDPV6;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RXCOE_CTL, 1, 1, tmp);
+
+	*tmp = AX_TXCOE_IP | AX_TXCOE_TCP | AX_TXCOE_UDP |
+	       AX_TXCOE_TCPV6 | AX_TXCOE_UDPV6;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, 1, 1, tmp);
+
+	/* Configure RX control register => start operation */
+	*tmp16 = AX_RX_CTL_DROPCRCERR | AX_RX_CTL_IPE | AX_RX_CTL_START |
+		 AX_RX_CTL_AP | AX_RX_CTL_AMALL | AX_RX_CTL_AB;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2, tmp16);
+
+	*tmp = AX_MONITOR_MODE_PMETYPE | AX_MONITOR_MODE_PMEPOL |
+	       AX_MONITOR_MODE_RWMP;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MONITOR_MOD, 1, 1, tmp);
+
+	/* Configure default medium type => giga */
+	*tmp16 = AX_MEDIUM_RECEIVE_EN | AX_MEDIUM_TXFLOW_CTRLEN |
+		 AX_MEDIUM_RXFLOW_CTRLEN | AX_MEDIUM_ALWAYS_ONE |
+		 AX_MEDIUM_FULL_DUPLEX | AX_MEDIUM_GIGAMODE;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,
+			  2, 2, tmp16);
+
+	ax88179_led_setting(dev);
+
+	/* Restart autoneg */
+	mii_nway_restart(&dev->mii);
+
+	netif_carrier_off(dev->net);
+
+	return 0;
+}
+
+static void ax88179_unbind(struct usbnet *dev, struct usb_interface *intf)
+{
+	u16 tmp16;
+
+	/* Configure RX control register => stop operation */
+	tmp16 = AX_RX_CTL_STOP;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2, &tmp16);
+
+	tmp16 = 0;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_CLK_SELECT, 1, 1, &tmp16);
+
+	/* Power down ethernet PHY */
+	tmp16 = 0;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, &tmp16);
+}
+
+static void
+ax88179_rx_checksum(struct sk_buff *skb, u32 *pkt_hdr)
+{
+	skb->ip_summed = CHECKSUM_NONE;
+
+	/* checksum error bit is set */
+	if ((*pkt_hdr & AX_RXHDR_L3CSUM_ERR) ||
+	    (*pkt_hdr & AX_RXHDR_L4CSUM_ERR))
+		return;
+
+	/* It must be a TCP or UDP packet with a valid checksum */
+	if (((*pkt_hdr & AX_RXHDR_L4_TYPE_MASK) == AX_RXHDR_L4_TYPE_TCP) ||
+	    ((*pkt_hdr & AX_RXHDR_L4_TYPE_MASK) == AX_RXHDR_L4_TYPE_UDP))
+		skb->ip_summed = CHECKSUM_UNNECESSARY;
+}
+
+static int ax88179_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
+{
+	struct sk_buff *ax_skb;
+	int pkt_cnt;
+	u32 rx_hdr;
+	u16 hdr_off;
+	u32 *pkt_hdr;
+
+	skb_trim(skb, skb->len - 4);
+	memcpy(&rx_hdr, skb_tail_pointer(skb), 4);
+	le32_to_cpus(&rx_hdr);
+
+	pkt_cnt = (u16)rx_hdr;
+	hdr_off = (u16)(rx_hdr >> 16);
+	pkt_hdr = (u32 *)(skb->data + hdr_off);
+
+	while (pkt_cnt--) {
+		u16 pkt_len;
+
+		le32_to_cpus(pkt_hdr);
+		pkt_len = (*pkt_hdr >> 16) & 0x1fff;
+
+		/* Check CRC or runt packet */
+		if ((*pkt_hdr & AX_RXHDR_CRC_ERR) ||
+		    (*pkt_hdr & AX_RXHDR_DROP_ERR)) {
+			skb_pull(skb, (pkt_len + 7) & 0xFFF8);
+			pkt_hdr++;
+			continue;
+		}
+
+		if (pkt_cnt == 0) {
+			/* Skip IP alignment psudo header */
+			skb_pull(skb, 2);
+			skb->len = pkt_len;
+			skb_set_tail_pointer(skb, pkt_len);
+			skb->truesize = pkt_len + sizeof(struct sk_buff);
+			ax88179_rx_checksum(skb, pkt_hdr);
+			return 1;
+		}
+
+		ax_skb = skb_clone(skb, GFP_ATOMIC);
+		if (ax_skb) {
+			ax_skb->len = pkt_len;
+			ax_skb->data = skb->data + 2;
+			skb_set_tail_pointer(ax_skb, pkt_len);
+			ax_skb->truesize = pkt_len + sizeof(struct sk_buff);
+			ax88179_rx_checksum(ax_skb, pkt_hdr);
+			usbnet_skb_return(dev, ax_skb);
+		} else {
+			return 0;
+		}
+
+		skb_pull(skb, (pkt_len + 7) & 0xFFF8);
+		pkt_hdr++;
+	}
+	return 1;
+}
+
+static struct sk_buff *
+ax88179_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags)
+{
+	u32 tx_hdr1, tx_hdr2;
+	int frame_size = dev->maxpacket;
+	int mss = skb_shinfo(skb)->gso_size;
+	int headroom;
+	int tailroom;
+
+	tx_hdr1 = skb->len;
+	tx_hdr2 = mss;
+	if (((skb->len + 8) % frame_size) == 0)
+		tx_hdr2 |= 0x80008000;	/* Enable padding */
+
+	skb_linearize(skb);
+	headroom = skb_headroom(skb);
+	tailroom = skb_tailroom(skb);
+
+	if (!skb_header_cloned(skb) &&
+	    !skb_cloned(skb) &&
+	    (headroom + tailroom) >= 8) {
+		if (headroom < 8) {
+			skb->data = memmove(skb->head + 8, skb->data, skb->len);
+			skb_set_tail_pointer(skb, skb->len);
+		}
+	} else {
+		struct sk_buff *skb2;
+
+		skb2 = skb_copy_expand(skb, 8, 0, flags);
+		dev_kfree_skb_any(skb);
+		skb = skb2;
+		if (!skb)
+			return NULL;
+	}
+
+	skb_push(skb, 4);
+	cpu_to_le32s(&tx_hdr2);
+	skb_copy_to_linear_data(skb, &tx_hdr2, 4);
+
+	skb_push(skb, 4);
+	cpu_to_le32s(&tx_hdr1);
+	skb_copy_to_linear_data(skb, &tx_hdr1, 4);
+
+	return skb;
+}
+
+static int ax88179_link_reset(struct usbnet *dev)
+{
+	struct ax88179_data *ax179_data = (struct ax88179_data *)dev->data;
+	u8 tmp[5], link_sts;
+	u16 mode, tmp16, delay = HZ / 10;
+	u32 tmp32 = 0x40000000;
+	unsigned long jtimeout;
+
+	jtimeout = jiffies + delay;
+	while (tmp32 & 0x40000000) {
+		mode = 0;
+		ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2, &mode);
+		ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2,
+				  &ax179_data->rxctl);
+
+		/*link up, check the usb device control TX FIFO full or empty*/
+		ax88179_read_cmd(dev, 0x81, 0x8c, 0, 4, &tmp32);
+
+		if (time_after(jiffies, jtimeout))
+			return 0;
+	}
+
+	mode = AX_MEDIUM_RECEIVE_EN | AX_MEDIUM_TXFLOW_CTRLEN |
+	       AX_MEDIUM_RXFLOW_CTRLEN | AX_MEDIUM_ALWAYS_ONE;
+
+	ax88179_read_cmd(dev, AX_ACCESS_MAC, PHYSICAL_LINK_STATUS,
+			 1, 1, &link_sts);
+
+	ax88179_read_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
+			 GMII_PHY_PHYSR, 2, &tmp16);
+
+	if (!(tmp16 & GMII_PHY_PHYSR_LINK)) {
+		return 0;
+	} else if (GMII_PHY_PHYSR_GIGA == (tmp16 & GMII_PHY_PHYSR_SMASK)) {
+		mode |= AX_MEDIUM_GIGAMODE | AX_MEDIUM_EN_125MHZ;
+		if (dev->net->mtu > 1500)
+			mode |= AX_MEDIUM_JUMBO_EN;
+
+		if (link_sts & AX_USB_SS)
+			memcpy(tmp, &AX88179_BULKIN_SIZE[0], 5);
+		else if (link_sts & AX_USB_HS)
+			memcpy(tmp, &AX88179_BULKIN_SIZE[1], 5);
+		else
+			memcpy(tmp, &AX88179_BULKIN_SIZE[3], 5);
+	} else if (GMII_PHY_PHYSR_100 == (tmp16 & GMII_PHY_PHYSR_SMASK)) {
+		mode |= AX_MEDIUM_PS;
+
+		if (link_sts & (AX_USB_SS | AX_USB_HS))
+			memcpy(tmp, &AX88179_BULKIN_SIZE[2], 5);
+		else
+			memcpy(tmp, &AX88179_BULKIN_SIZE[3], 5);
+	} else {
+		memcpy(tmp, &AX88179_BULKIN_SIZE[3], 5);
+	}
+
+	/* RX bulk configuration */
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_BULKIN_QCTRL, 5, 5, tmp);
+
+	dev->rx_urb_size = (1024 * (tmp[3] + 2));
+
+	if (tmp16 & GMII_PHY_PHYSR_FULL)
+		mode |= AX_MEDIUM_FULL_DUPLEX;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,
+			  2, 2, &mode);
+
+	netif_carrier_on(dev->net);
+
+	return 0;
+}
+
+static int ax88179_reset(struct usbnet *dev)
+{
+	u8 buf[5];
+	u16 *tmp16;
+	u8 *tmp;
+
+	tmp16 = (u16 *)buf;
+	tmp = (u8 *)buf;
+
+	/* Power up ethernet PHY */
+	*tmp16 = 0;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, tmp16);
+
+	*tmp16 = AX_PHYPWR_RSTCTL_IPRL;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, tmp16);
+	msleep(200);
+
+	*tmp = AX_CLK_SELECT_ACS | AX_CLK_SELECT_BCS;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_CLK_SELECT, 1, 1, tmp);
+	msleep(100);
+
+	/* Ethernet PHY Auto Detach*/
+	ax88179_auto_detach(dev, 0);
+
+	ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_NODE_ID, ETH_ALEN, ETH_ALEN,
+			 dev->net->dev_addr);
+	memcpy(dev->net->perm_addr, dev->net->dev_addr, ETH_ALEN);
+
+	/* RX bulk configuration */
+	memcpy(tmp, &AX88179_BULKIN_SIZE[0], 5);
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_BULKIN_QCTRL, 5, 5, tmp);
+
+	dev->rx_urb_size = 1024 * 20;
+
+	*tmp = 0x34;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PAUSE_WATERLVL_LOW, 1, 1, tmp);
+
+	*tmp = 0x52;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PAUSE_WATERLVL_HIGH,
+			  1, 1, tmp);
+
+	dev->net->features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
+			      NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_TSO;
+
+	dev->net->hw_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
+				 NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_TSO;
+
+	/* Enable checksum offload */
+	*tmp = AX_RXCOE_IP | AX_RXCOE_TCP | AX_RXCOE_UDP |
+	       AX_RXCOE_TCPV6 | AX_RXCOE_UDPV6;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RXCOE_CTL, 1, 1, tmp);
+
+	*tmp = AX_TXCOE_IP | AX_TXCOE_TCP | AX_TXCOE_UDP |
+	       AX_TXCOE_TCPV6 | AX_TXCOE_UDPV6;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, 1, 1, tmp);
+
+	/* Configure RX control register => start operation */
+	*tmp16 = AX_RX_CTL_DROPCRCERR | AX_RX_CTL_IPE | AX_RX_CTL_START |
+		 AX_RX_CTL_AP | AX_RX_CTL_AMALL | AX_RX_CTL_AB;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2, tmp16);
+
+	*tmp = AX_MONITOR_MODE_PMETYPE | AX_MONITOR_MODE_PMEPOL |
+	       AX_MONITOR_MODE_RWMP;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MONITOR_MOD, 1, 1, tmp);
+
+	/* Configure default medium type => giga */
+	*tmp16 = AX_MEDIUM_RECEIVE_EN | AX_MEDIUM_TXFLOW_CTRLEN |
+		 AX_MEDIUM_RXFLOW_CTRLEN | AX_MEDIUM_ALWAYS_ONE |
+		 AX_MEDIUM_FULL_DUPLEX | AX_MEDIUM_GIGAMODE;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,
+			  2, 2, tmp16);
+
+	ax88179_led_setting(dev);
+
+	/* Restart autoneg */
+	mii_nway_restart(&dev->mii);
+
+	netif_carrier_off(dev->net);
+
+	return 0;
+}
+
+static int ax88179_stop(struct usbnet *dev)
+{
+	u16 tmp16;
+
+	ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,
+			 2, 2, &tmp16);
+	tmp16 &= ~AX_MEDIUM_RECEIVE_EN;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,
+			  2, 2, &tmp16);
+
+	return 0;
+}
+
+static const struct driver_info ax88179_info = {
+	.description = "ASIX AX88179 USB 3.0 Gigibit Ethernet",
+	.bind = ax88179_bind,
+	.unbind = ax88179_unbind,
+	.status = ax88179_status,
+	.link_reset = ax88179_link_reset,
+	.reset = ax88179_reset,
+	.stop = ax88179_stop,
+	.flags = FLAG_ETHER | FLAG_FRAMING_AX,
+	.rx_fixup = ax88179_rx_fixup,
+	.tx_fixup = ax88179_tx_fixup,
+};
+
+static const struct driver_info ax88178a_info = {
+	.description = "ASIX AX88178A USB 2.0 Gigibit Ethernet",
+	.bind = ax88179_bind,
+	.unbind = ax88179_unbind,
+	.status = ax88179_status,
+	.link_reset = ax88179_link_reset,
+	.reset = ax88179_reset,
+	.stop = ax88179_stop,
+	.flags = FLAG_ETHER | FLAG_FRAMING_AX,
+	.rx_fixup = ax88179_rx_fixup,
+	.tx_fixup = ax88179_tx_fixup,
+};
+
+static const struct driver_info sitecom_info = {
+	.description = "Sitecom USB 3.0 to Gigabit Adapter",
+	.bind = ax88179_bind,
+	.unbind = ax88179_unbind,
+	.status = ax88179_status,
+	.link_reset = ax88179_link_reset,
+	.reset = ax88179_reset,
+	.stop = ax88179_stop,
+	.flags = FLAG_ETHER | FLAG_FRAMING_AX,
+	.rx_fixup = ax88179_rx_fixup,
+	.tx_fixup = ax88179_tx_fixup,
+};
+
+static const struct usb_device_id products[] = {
+{
+	/* ASIX AX88179 10/100/1000 */
+	USB_DEVICE(0x0b95, 0x1790),
+	.driver_info = (unsigned long)&ax88179_info,
+}, {
+	/* ASIX AX88178A 10/100/1000 */
+	USB_DEVICE(0x0b95, 0x178a),
+	.driver_info = (unsigned long)&ax88178a_info,
+}, {
+	/* Sitecom USB 3.0 to Gigabit Adapter */
+	USB_DEVICE(0x0df6, 0x0072),
+	.driver_info = (unsigned long) &sitecom_info,
+},
+	{ },
+};
+MODULE_DEVICE_TABLE(usb, products);
+
+static struct usb_driver asix_driver = {
+	.name =		"ax88179_178a",
+	.id_table =	products,
+	.probe =	usbnet_probe,
+	.suspend =	ax88179_suspend,
+	.resume =	ax88179_resume,
+	.disconnect =	usbnet_disconnect,
+};
+
+static int __init asix_init(void)
+{
+	return usb_register(&asix_driver);
+}
+module_init(asix_init);
+
+static void __exit asix_exit(void)
+{
+	usb_deregister(&asix_driver);
+}
+module_exit(asix_exit);
+
+MODULE_DESCRIPTION("ASIX AX88179/178A based USB 3.0/2.0 Gigabit Ethernet Devices");
+MODULE_LICENSE("GPL");
-- 
1.7.9.5


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

* Re: [PATCH, resubmit] ax88179_178a: ASIX AX88179_178A USB 3.0/2.0 to gigabit ethernet adapter driver
  2013-02-07  5:46 ` Stephen Hemminger
@ 2013-02-07  6:50   ` Freddy
  0 siblings, 0 replies; 27+ messages in thread
From: Freddy @ 2013-02-07  6:50 UTC (permalink / raw)
  To: Stephen Hemminger; +Cc: netdev, davem, linux-usb, linux-kernel

On 02/07/2013 01:46 PM, Stephen Hemminger wrote:
> On Thu,  7 Feb 2013 12:36:34 +0800
> Freddy Xin <freddy@asix.com.tw> wrote:
>
>> +static struct ethtool_ops ax88179_ethtool_ops = {
>> +	.get_link		= ethtool_op_get_link,
>> +	.get_msglevel		= usbnet_get_msglevel,
>> +	.set_msglevel		= usbnet_set_msglevel,
>> +	.get_wol		= ax88179_get_wol,
>> +	.set_wol		= ax88179_set_wol,
>> +	.get_eeprom_len		= ax88179_get_eeprom_len,
>> +	.get_eeprom		= ax88179_get_eeprom,
>> +	.get_settings		= ax88179_get_settings,
>> +	.set_settings		= ax88179_set_settings,
>> +	.nway_reset		= usbnet_nway_reset,
>> +};
>> +
> ethtool_ops should always be const
>
Thank you, Stephen. I will fix these errors.

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

* Re: [PATCH, resubmit] ax88179_178a: ASIX AX88179_178A USB 3.0/2.0 to gigabit ethernet adapter driver
  2013-02-07  5:45 ` Stephen Hemminger
@ 2013-02-07  6:45   ` Freddy
  0 siblings, 0 replies; 27+ messages in thread
From: Freddy @ 2013-02-07  6:45 UTC (permalink / raw)
  To: freddy; +Cc: linux-kernel

On 02/07/2013 01:45 PM, Stephen Hemminger wrote:
> On Thu,  7 Feb 2013 12:36:34 +0800
> Freddy Xin <freddy@asix.com.tw> wrote:
>
>> +struct {unsigned char ctrl, timer_l, timer_h, size, ifg; }
>> +AX88179_BULKIN_SIZE[] =	{
>> +	{7, 0x4f, 0,	0x12, 0xff},
>> +	{7, 0xf0, 1,	0x15, 0xff},
>> +	{7, 0xae, 7,	0x18, 0xff},
>> +	{7, 0xcc, 0x4c, 0x18, 8},
>> +};
>   Better to make it static, const, and add a couple
>   of line breaks.
>
>   static const struct {
>        unsigned char ctrl, timer_l, timer_h, size, ifg;
>   } AX88179_BULKIN_SIZE[] =	{
> 	{7, 0x4f, 0,	0x12, 0xff},
> 	{7, 0xf0, 1,	0x15, 0xff},
> 	{7, 0xae, 7,	0x18, 0xff},
> 	{7, 0xcc, 0x4c, 0x18, 8},
> };
Thank you, Stephen. I will fix these errors.
>


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

* Re: [PATCH, resubmit] ax88179_178a: ASIX AX88179_178A USB 3.0/2.0 to gigabit ethernet adapter driver
  2013-02-07  4:36 [PATCH, " Freddy Xin
  2013-02-07  5:45 ` Stephen Hemminger
@ 2013-02-07  5:46 ` Stephen Hemminger
  2013-02-07  6:50   ` Freddy
  1 sibling, 1 reply; 27+ messages in thread
From: Stephen Hemminger @ 2013-02-07  5:46 UTC (permalink / raw)
  To: Freddy Xin; +Cc: netdev, davem, linux-usb, linux-kernel

On Thu,  7 Feb 2013 12:36:34 +0800
Freddy Xin <freddy@asix.com.tw> wrote:

> +static struct ethtool_ops ax88179_ethtool_ops = {
> +	.get_link		= ethtool_op_get_link,
> +	.get_msglevel		= usbnet_get_msglevel,
> +	.set_msglevel		= usbnet_set_msglevel,
> +	.get_wol		= ax88179_get_wol,
> +	.set_wol		= ax88179_set_wol,
> +	.get_eeprom_len		= ax88179_get_eeprom_len,
> +	.get_eeprom		= ax88179_get_eeprom,
> +	.get_settings		= ax88179_get_settings,
> +	.set_settings		= ax88179_set_settings,
> +	.nway_reset		= usbnet_nway_reset,
> +};
> +

ethtool_ops should always be const

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

* Re: [PATCH, resubmit] ax88179_178a: ASIX AX88179_178A USB 3.0/2.0 to gigabit ethernet adapter driver
  2013-02-07  4:36 [PATCH, " Freddy Xin
@ 2013-02-07  5:45 ` Stephen Hemminger
  2013-02-07  6:45   ` Freddy
  2013-02-07  5:46 ` Stephen Hemminger
  1 sibling, 1 reply; 27+ messages in thread
From: Stephen Hemminger @ 2013-02-07  5:45 UTC (permalink / raw)
  To: Freddy Xin; +Cc: netdev, davem, linux-usb, linux-kernel

On Thu,  7 Feb 2013 12:36:34 +0800
Freddy Xin <freddy@asix.com.tw> wrote:

> +struct {unsigned char ctrl, timer_l, timer_h, size, ifg; }
> +AX88179_BULKIN_SIZE[] =	{
> +	{7, 0x4f, 0,	0x12, 0xff},
> +	{7, 0xf0, 1,	0x15, 0xff},
> +	{7, 0xae, 7,	0x18, 0xff},
> +	{7, 0xcc, 0x4c, 0x18, 8},
> +};

Better to make it static, const, and add a couple
of line breaks.

static const struct {
      unsigned char ctrl, timer_l, timer_h, size, ifg;
} AX88179_BULKIN_SIZE[] =	{
	{7, 0x4f, 0,	0x12, 0xff},
	{7, 0xf0, 1,	0x15, 0xff},
	{7, 0xae, 7,	0x18, 0xff},
	{7, 0xcc, 0x4c, 0x18, 8},
};

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

* [PATCH, resubmit] ax88179_178a: ASIX AX88179_178A USB 3.0/2.0 to gigabit ethernet adapter driver
@ 2013-02-07  4:36 Freddy Xin
  2013-02-07  5:45 ` Stephen Hemminger
  2013-02-07  5:46 ` Stephen Hemminger
  0 siblings, 2 replies; 27+ messages in thread
From: Freddy Xin @ 2013-02-07  4:36 UTC (permalink / raw)
  To: netdev; +Cc: davem, linux-usb, linux-kernel, Freddy Xin

From: Freddy Xin <freddy@asix.com.tw>

This is a resubmission.
Fixed the issue that the default MTU is not 1500.
Added ax88179_change_mtu function and enabled the hardware jumbo frame function to support an
MTU higher than 1500.
Fixed indentation and empty line coding style errors.
The _nopm version usb functions were added to access register in suspend and resume functions.
Serveral variables allocted dynamically were removed and replaced by stack variables.
ax88179_get_eeprom were modified from asix_get_eeprom in asix_common.

This patch adds a driver for ASIX's AX88179 family of USB 3.0/2.0
to gigabit ethernet adapters. It's based on the AX88xxx driver but
the usb commands used to access registers for AX88179 are completely different.
This driver had been verified on x86 system with AX88179/AX88178A and
Sitcomm LN-032 USB dongles.

Signed-off-by: Freddy Xin <freddy@asix.com.tw>
---
 drivers/net/usb/Kconfig        |   18 +
 drivers/net/usb/Makefile       |    1 +
 drivers/net/usb/ax88179_178a.c | 1398 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 1417 insertions(+)
 create mode 100644 drivers/net/usb/ax88179_178a.c

diff --git a/drivers/net/usb/Kconfig b/drivers/net/usb/Kconfig
index ef97621..75af401 100644
--- a/drivers/net/usb/Kconfig
+++ b/drivers/net/usb/Kconfig
@@ -158,6 +158,24 @@ config USB_NET_AX8817X
 	  This driver creates an interface named "ethX", where X depends on
 	  what other networking devices you have in use.
 
+config USB_NET_AX88179_178A
+	tristate "ASIX AX88179/178A USB 3.0/2.0 to Gigabit Ethernet"
+	depends on USB_USBNET
+	select CRC32
+	select PHYLIB
+	default y
+	help
+	  This option adds support for ASIX AX88179 based USB 3.0/2.0
+	  to Gigabit Ethernet adapters.
+
+ 	  This driver should work with at least the following devices:
+	    * ASIX AX88179
+	    * ASIX AX88178A
+	    * Sitcomm LN-032
+
+	  This driver creates an interface named "ethX", where X depends on
+	  what other networking devices you have in use.
+
 config USB_NET_CDCETHER
 	tristate "CDC Ethernet support (smart devices such as cable modems)"
 	depends on USB_USBNET
diff --git a/drivers/net/usb/Makefile b/drivers/net/usb/Makefile
index 4786913..119b06c 100644
--- a/drivers/net/usb/Makefile
+++ b/drivers/net/usb/Makefile
@@ -9,6 +9,7 @@ obj-$(CONFIG_USB_RTL8150)	+= rtl8150.o
 obj-$(CONFIG_USB_HSO)		+= hso.o
 obj-$(CONFIG_USB_NET_AX8817X)	+= asix.o
 asix-y := asix_devices.o asix_common.o ax88172a.o
+obj-$(CONFIG_USB_NET_AX88179_178A)      += ax88179_178a.o
 obj-$(CONFIG_USB_NET_CDCETHER)	+= cdc_ether.o
 obj-$(CONFIG_USB_NET_CDC_EEM)	+= cdc_eem.o
 obj-$(CONFIG_USB_NET_DM9601)	+= dm9601.o
diff --git a/drivers/net/usb/ax88179_178a.c b/drivers/net/usb/ax88179_178a.c
new file mode 100644
index 0000000..6862e47
--- /dev/null
+++ b/drivers/net/usb/ax88179_178a.c
@@ -0,0 +1,1398 @@
+/*
+ * ASIX AX88179/178A USB 3.0/2.0 to Gigabit Ethernet Devices
+ *
+ * Copyright (C) 2011-2013 ASIX
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#include <linux/module.h>
+#include <linux/etherdevice.h>
+#include <linux/mii.h>
+#include <linux/usb.h>
+#include <linux/crc32.h>
+#include <linux/usb/usbnet.h>
+
+#define AX88179_PHY_ID				0x03
+#define AX_EEPROM_LEN				0x100
+#define AX88179_EEPROM_MAGIC			0x17900b95
+#define AX_MCAST_FLTSIZE			8
+#define AX_MAX_MCAST				64
+#define AX_INT_PPLS_LINK			BIT(0)
+#define AX_RXHDR_L4_TYPE_UDP			1
+#define AX_RXHDR_L4_TYPE_TCP			4
+#define AX_ACCESS_MAC				0x01
+#define AX_ACCESS_PHY				0x02
+#define AX_ACCESS_EEPROM			0x04
+#define AX_ACCESS_EFUS				0x05
+#define AX_PAUSE_WATERLVL_HIGH			0x54
+#define AX_PAUSE_WATERLVL_LOW			0x55
+
+#define PHYSICAL_LINK_STATUS			0x02
+	#define	AX_USB_SS		0x04
+	#define	AX_USB_HS		0x02
+
+#define GENERAL_STATUS				0x03
+/* Check AX88179 version. UA1:Bit2 = 0,  UA2:Bit2 = 1 */
+	#define	AX_SECLD		0x04
+
+#define AX_SROM_ADDR				0x07
+#define AX_SROM_CMD				0x0a
+	#define EEP_RD			0x04
+	#define EEP_BUSY		0x10
+
+#define AX_SROM_DATA_LOW			0x08
+#define AX_SROM_DATA_HIGH			0x09
+
+#define AX_RX_CTL				0x0b
+	#define AX_RX_CTL_DROPCRCERR	0x0100
+	#define AX_RX_CTL_IPE		0x0200
+	#define AX_RX_CTL_START		0x0080
+	#define AX_RX_CTL_AP		0x0020
+	#define AX_RX_CTL_AM		0x0010
+	#define AX_RX_CTL_AB		0x0008
+	#define AX_RX_CTL_AMALL		0x0002
+	#define AX_RX_CTL_PRO		0x0001
+	#define AX_RX_CTL_STOP		0x0000
+
+#define AX_NODE_ID				0x10
+#define AX_MULFLTARY				0x16
+
+#define AX_MEDIUM_STATUS_MODE			0x22
+	#define AX_MEDIUM_GIGAMODE	0x01
+	#define AX_MEDIUM_FULL_DUPLEX	0x02
+	#define AX_MEDIUM_ALWAYS_ONE	0x04
+	#define AX_MEDIUM_EN_125MHZ	0x08
+	#define AX_MEDIUM_RXFLOW_CTRLEN	0x10
+	#define AX_MEDIUM_TXFLOW_CTRLEN	0x20
+	#define AX_MEDIUM_RECEIVE_EN	0x100
+	#define AX_MEDIUM_PS		0x200
+	#define AX_MEDIUM_JUMBO_EN	0x8040
+
+#define AX_MONITOR_MOD				0x24
+	#define AX_MONITOR_MODE_RWLC	0x02
+	#define AX_MONITOR_MODE_RWMP	0x04
+	#define AX_MONITOR_MODE_PMEPOL	0x20
+	#define AX_MONITOR_MODE_PMETYPE	0x40
+
+#define AX_GPIO_CTRL				0x25
+	#define AX_GPIO_CTRL_GPIO3EN	0x80
+	#define AX_GPIO_CTRL_GPIO2EN	0x40
+	#define AX_GPIO_CTRL_GPIO1EN	0x20
+
+#define AX_PHYPWR_RSTCTL			0x26
+	#define AX_PHYPWR_RSTCTL_BZ	0x0010
+	#define AX_PHYPWR_RSTCTL_IPRL	0x0020
+	#define AX_PHYPWR_RSTCTL_AT	0x1000
+
+#define AX_RX_BULKIN_QCTRL			0x2e
+#define AX_CLK_SELECT				0x33
+	#define AX_CLK_SELECT_BCS	0x01
+	#define AX_CLK_SELECT_ACS	0x02
+	#define AX_CLK_SELECT_ULR	0x08
+
+#define AX_RXCOE_CTL				0x34
+	#define AX_RXCOE_IP		0x01
+	#define AX_RXCOE_TCP		0x02
+	#define AX_RXCOE_UDP		0x04
+	#define AX_RXCOE_TCPV6		0x20
+	#define AX_RXCOE_UDPV6		0x40
+
+#define AX_TXCOE_CTL				0x35
+	#define AX_TXCOE_IP		0x01
+	#define AX_TXCOE_TCP		0x02
+	#define AX_TXCOE_UDP		0x04
+	#define AX_TXCOE_TCPV6		0x20
+	#define AX_TXCOE_UDPV6		0x40
+
+#define AX_LEDCTRL				0x73
+
+#define GMII_PHY_PHYSR				0x11
+	#define GMII_PHY_PHYSR_SMASK	0xc000
+	#define GMII_PHY_PHYSR_GIGA	0x8000
+	#define GMII_PHY_PHYSR_100	0x4000
+	#define GMII_PHY_PHYSR_FULL	0x2000
+	#define GMII_PHY_PHYSR_LINK	0x400
+
+#define GMII_LED_ACT				0x1a
+	#define GMII_LED_ACTIVE_MASK	0xff8fd
+	#define GMII_LED0_ACTIVE	BIT(4)
+	#define GMII_LED1_ACTIVE	BIT(5)
+	#define GMII_LED2_ACTIVE	BIT(6)
+
+#define GMII_LED_LINK				0x1c
+	#define GMII_LED_LINK_MASK	0xf888
+	#define GMII_LED0_LINK_10	BIT(0)
+	#define GMII_LED0_LINK_100	BIT(1)
+	#define GMII_LED0_LINK_1000	BIT(2)
+	#define GMII_LED1_LINK_10	BIT(4)
+	#define GMII_LED1_LINK_100	BIT(5)
+	#define GMII_LED1_LINK_1000	BIT(6)
+	#define GMII_LED2_LINK_10	BIT(8)
+	#define GMII_LED2_LINK_100	BIT(9)
+	#define GMII_LED2_LINK_1000	BIT(10)
+	#define	LED0_ACTIVE		BIT(0)
+	#define	LED0_LINK_10		BIT(1)
+	#define	LED0_LINK_100		BIT(2)
+	#define	LED0_LINK_1000		BIT(3)
+	#define	LED0_FD			BIT(4)
+	#define LED0_USB3_MASK		0x001f
+	#define	LED1_ACTIVE		BIT(5)
+	#define	LED1_LINK_10		BIT(6)
+	#define	LED1_LINK_100		BIT(7)
+	#define	LED1_LINK_1000		BIT(8)
+	#define	LED1_FD			BIT(9)
+	#define LED1_USB3_MASK		0x03e0
+	#define	LED2_ACTIVE		BIT(10)
+	#define	LED2_LINK_1000		BIT(13)
+	#define	LED2_LINK_100		BIT(12)
+	#define	LED2_LINK_10		BIT(11)
+	#define	LED2_FD			BIT(14)
+	#define	LED_VALID		BIT(15)
+	#define LED2_USB3_MASK		0x7c00
+
+#define GMII_PHYPAGE				0x1e
+#define GMII_PHY_PAGE_SELECT			0x1f
+	#define GMII_PHY_PGSEL_EXT	0x0007
+	#define GMII_PHY_PGSEL_PAGE0	0X0000
+
+struct ax88179_data {
+	u16 rxctl;
+};
+
+struct ax88179_int_data {
+	u16 res1;
+	u8 link;
+	u16 res2;
+	u8 status;
+	u16 res3;
+} __packed;
+
+struct {unsigned char ctrl, timer_l, timer_h, size, ifg; }
+AX88179_BULKIN_SIZE[] =	{
+	{7, 0x4f, 0,	0x12, 0xff},
+	{7, 0xf0, 1,	0x15, 0xff},
+	{7, 0xae, 7,	0x18, 0xff},
+	{7, 0xcc, 0x4c, 0x18, 8},
+};
+
+struct ax88179_rx_pkt_header {
+	u8	l4_csum_err:1,
+		l3_csum_err:1,
+		l4_type:3,
+		l3_type:2,
+		ce:1;
+
+	u8	vlan_ind:3,
+		rx_ok:1,
+		pri:3,
+		bmc:1;
+
+	u16	len:13,
+		crc:1,
+		mii:1,
+		drop:1;
+} __packed;
+
+static int __ax88179_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,
+			      u16 size, void *data, int in_pm)
+{
+	int ret;
+	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);
+
+	return ret;
+}
+
+static int __ax88179_write_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,
+			       u16 size, void *data, int in_pm)
+{
+	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;
+}
+
+static void ax88179_write_cmd_async(struct usbnet *dev, u8 cmd, u16 value,
+				    u16 index, u16 size, void *data)
+{
+	usbnet_write_cmd_async(dev, cmd, USB_DIR_OUT | USB_TYPE_VENDOR |
+			       USB_RECIP_DEVICE, value, index, data, size);
+}
+
+static int ax88179_read_cmd_nopm(struct usbnet *dev, u8 cmd, u16 value,
+				 u16 index, u16 size, void *data)
+{
+	return __ax88179_read_cmd(dev, cmd, value, index, size, data, 1);
+}
+
+static int ax88179_write_cmd_nopm(struct usbnet *dev, u8 cmd, u16 value,
+				  u16 index, u16 size, void *data)
+{
+	return __ax88179_write_cmd(dev, cmd, value, index, size, data, 1);
+}
+
+static int ax88179_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,
+			    u16 size, void *data)
+{
+	return __ax88179_read_cmd(dev, cmd, value, index, size, data, 0);
+}
+
+static int ax88179_write_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,
+			     u16 size, void *data)
+{
+	return __ax88179_write_cmd(dev, cmd, value, index, size, data, 0);
+}
+
+static void ax88179_status(struct usbnet *dev, struct urb *urb)
+{
+	struct ax88179_int_data *event;
+	int link;
+
+	if (urb->actual_length < 8)
+		return;
+
+	event = urb->transfer_buffer;
+	link = event->link & AX_INT_PPLS_LINK;
+	if (netif_carrier_ok(dev->net) != link) {
+		if (link)
+			usbnet_defer_kevent(dev, EVENT_LINK_RESET);
+		else
+			netif_carrier_off(dev->net);
+		netdev_info(dev->net, "ax88179 - Link status is: %d\n", link);
+	}
+}
+
+static int ax88179_mdio_read(struct net_device *netdev, int phy_id, int loc)
+{
+	struct usbnet *dev = netdev_priv(netdev);
+	__u16 res;
+
+	ax88179_read_cmd(dev, AX_ACCESS_PHY, phy_id, (__u16)loc, 2, &res);
+	return le16_to_cpu(res & 0xffff);
+}
+
+static void ax88179_mdio_write(struct net_device *netdev, int phy_id, int loc,
+			       int val)
+{
+	struct usbnet *dev = netdev_priv(netdev);
+	__le16 res;
+
+	res = cpu_to_le16(val);
+	ax88179_write_cmd(dev, AX_ACCESS_PHY, phy_id, (__u16)loc, 2, &res);
+}
+
+static int ax88179_suspend(struct usb_interface *intf, pm_message_t message)
+{
+	struct usbnet *dev = usb_get_intfdata(intf);
+	u16 tmp16;
+
+	usbnet_suspend(intf, message);
+
+	/* Disable RX path */
+	ax88179_read_cmd_nopm(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,
+			      2, 2, &tmp16);
+	tmp16 &= ~AX_MEDIUM_RECEIVE_EN;
+	ax88179_write_cmd_nopm(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,
+			       2, 2, &tmp16);
+
+	/* Force bulk-in zero length */
+	ax88179_read_cmd_nopm(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL,
+			      2, 2, &tmp16);
+
+	tmp16 |= AX_PHYPWR_RSTCTL_BZ | AX_PHYPWR_RSTCTL_IPRL;
+	ax88179_write_cmd_nopm(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL,
+			       2, 2, &tmp16);
+
+	/* change clock */
+	tmp16 = 0;
+	ax88179_write_cmd_nopm(dev, AX_ACCESS_MAC, AX_CLK_SELECT, 1, 1, &tmp16);
+
+	/* Configure RX control register => stop operation */
+	tmp16 = AX_RX_CTL_STOP;
+	ax88179_write_cmd_nopm(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2, &tmp16);
+
+	return 0;
+}
+
+/* This function is used to enable the autodetach function. */
+/* This function is determined by offset 0x43 of EEPROM */
+static int ax88179_auto_detach(struct usbnet *dev, int in_pm)
+{
+	u16 tmp;
+
+	if (__ax88179_read_cmd(dev, AX_ACCESS_EEPROM, 0x43, 1, 2,
+			       &tmp, in_pm) < 0)
+		return 0;
+
+	if ((tmp == 0xFFFF) || (!(tmp & 0x0100)))
+		return 0;
+
+	/* Enable Auto Detach bit */
+	tmp = 0;
+	__ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_CLK_SELECT,
+			   1, 1, &tmp, in_pm);
+	tmp |= AX_CLK_SELECT_ULR;
+	__ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_CLK_SELECT,
+			    1, 1, &tmp, in_pm);
+
+	__ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL,
+			   2, 2, &tmp, in_pm);
+	tmp |= AX_PHYPWR_RSTCTL_AT;
+	__ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL,
+			    2, 2, &tmp, in_pm);
+
+	return 0;
+}
+
+static int ax88179_resume(struct usb_interface *intf)
+{
+	struct usbnet *dev = usb_get_intfdata(intf);
+	u16 tmp16;
+
+	netif_carrier_off(dev->net);
+
+	/* Power up ethernet PHY */
+	tmp16 = 0;
+	ax88179_write_cmd_nopm(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL,
+			       2, 2, &tmp16);
+	udelay(1000);
+
+	tmp16 = AX_PHYPWR_RSTCTL_IPRL;
+	ax88179_write_cmd_nopm(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL,
+			       2, 2, &tmp16);
+	msleep(200);
+
+	/* Ethernet PHY Auto Detach*/
+	ax88179_auto_detach(dev, 1);
+
+	/* enable clock */
+	ax88179_read_cmd_nopm(dev, AX_ACCESS_MAC,  AX_CLK_SELECT, 1, 1, &tmp16);
+	tmp16 |= AX_CLK_SELECT_ACS | AX_CLK_SELECT_BCS;
+	ax88179_write_cmd_nopm(dev, AX_ACCESS_MAC, AX_CLK_SELECT, 1, 1, &tmp16);
+	msleep(100);
+
+	/* Configure RX control register => start operation */
+	tmp16 = AX_RX_CTL_DROPCRCERR | AX_RX_CTL_IPE | AX_RX_CTL_START |
+		AX_RX_CTL_AP | AX_RX_CTL_AMALL | AX_RX_CTL_AB;
+	ax88179_write_cmd_nopm(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2, &tmp16);
+
+	return usbnet_resume(intf);
+}
+
+static void
+ax88179_get_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo)
+{
+	struct usbnet *dev = netdev_priv(net);
+	u8 opt;
+
+	if (ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_MONITOR_MOD,
+			     1, 1, &opt) < 0) {
+		wolinfo->supported = 0;
+		wolinfo->wolopts = 0;
+		return;
+	}
+
+	wolinfo->supported = WAKE_PHY | WAKE_MAGIC;
+	wolinfo->wolopts = 0;
+	if (opt & AX_MONITOR_MODE_RWLC)
+		wolinfo->wolopts |= WAKE_PHY;
+	if (opt & AX_MONITOR_MODE_RWMP)
+		wolinfo->wolopts |= WAKE_MAGIC;
+}
+
+static int
+ax88179_set_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo)
+{
+	struct usbnet *dev = netdev_priv(net);
+	u8 opt = 0;
+
+	if (wolinfo->wolopts & WAKE_PHY)
+		opt |= AX_MONITOR_MODE_RWLC;
+	if (wolinfo->wolopts & WAKE_MAGIC)
+		opt |= AX_MONITOR_MODE_RWMP;
+
+	if (ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MONITOR_MOD,
+			      1, 1, &opt) < 0)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int ax88179_get_eeprom_len(struct net_device *net)
+{
+	return AX_EEPROM_LEN;
+}
+
+static int
+ax88179_get_eeprom(struct net_device *net, struct ethtool_eeprom *eeprom,
+		   u8 *data)
+{
+	struct usbnet *dev = netdev_priv(net);
+	u16 *eeprom_buff;
+	int first_word, last_word;
+	int i;
+
+	if (eeprom->len == 0)
+		return -EINVAL;
+
+	eeprom->magic = AX88179_EEPROM_MAGIC;
+
+	first_word = eeprom->offset >> 1;
+	last_word = (eeprom->offset + eeprom->len - 1) >> 1;
+	eeprom_buff = kmalloc(sizeof(u16) * (last_word - first_word + 1),
+			      GFP_KERNEL);
+	if (!eeprom_buff)
+		return -ENOMEM;
+
+	/* ax88179/178A returns 2 bytes from eeprom on read */
+	for (i = first_word; i <= last_word; i++) {
+		if (ax88179_read_cmd(dev, AX_ACCESS_EEPROM, i, 1, 2,
+				     &(eeprom_buff[i - first_word])) < 0) {
+			kfree(eeprom_buff);
+			return -EIO;
+		}
+	}
+
+	memcpy(data, (u8 *)eeprom_buff + (eeprom->offset & 1), eeprom->len);
+	kfree(eeprom_buff);
+	return 0;
+}
+
+static int ax88179_get_settings(struct net_device *net, struct ethtool_cmd *cmd)
+{
+	struct usbnet *dev = netdev_priv(net);
+	return mii_ethtool_gset(&dev->mii, cmd);
+}
+
+static int ax88179_set_settings(struct net_device *net, struct ethtool_cmd *cmd)
+{
+	struct usbnet *dev = netdev_priv(net);
+	return mii_ethtool_sset(&dev->mii, cmd);
+}
+
+
+static int ax88179_ioctl(struct net_device *net, struct ifreq *rq, int cmd)
+{
+	struct usbnet *dev = netdev_priv(net);
+	return generic_mii_ioctl(&dev->mii, if_mii(rq), cmd, NULL);
+}
+
+static struct ethtool_ops ax88179_ethtool_ops = {
+	.get_link		= ethtool_op_get_link,
+	.get_msglevel		= usbnet_get_msglevel,
+	.set_msglevel		= usbnet_set_msglevel,
+	.get_wol		= ax88179_get_wol,
+	.set_wol		= ax88179_set_wol,
+	.get_eeprom_len		= ax88179_get_eeprom_len,
+	.get_eeprom		= ax88179_get_eeprom,
+	.get_settings		= ax88179_get_settings,
+	.set_settings		= ax88179_set_settings,
+	.nway_reset		= usbnet_nway_reset,
+};
+
+static void ax88179_set_multicast(struct net_device *net)
+{
+	struct usbnet *dev = netdev_priv(net);
+	struct ax88179_data *data = (struct ax88179_data *)dev->data;
+	u8 *m_filter = ((u8 *)dev->data) + 12;
+
+	data->rxctl = (AX_RX_CTL_START | AX_RX_CTL_AB | AX_RX_CTL_IPE);
+
+	if (net->flags & IFF_PROMISC) {
+		data->rxctl |= AX_RX_CTL_PRO;
+	} else if (net->flags & IFF_ALLMULTI ||
+		   netdev_mc_count(net) > AX_MAX_MCAST) {
+		data->rxctl |= AX_RX_CTL_AMALL;
+	} else if (netdev_mc_empty(net)) {
+		/* just broadcast and directed */
+	} else {
+		/* We use the 20 byte dev->data for our 8 byte filter buffer
+		 * to avoid allocating memory that is tricky to free later
+		 */
+		u32 crc_bits;
+		struct netdev_hw_addr *ha;
+
+		memset(m_filter, 0, AX_MCAST_FLTSIZE);
+
+		netdev_for_each_mc_addr(ha, net) {
+			crc_bits = ether_crc(ETH_ALEN, ha->addr) >> 26;
+			*(m_filter + (crc_bits >> 3)) |= (1 << (crc_bits & 7));
+		}
+
+		ax88179_write_cmd_async(dev, AX_ACCESS_MAC, AX_MULFLTARY,
+					AX_MCAST_FLTSIZE, AX_MCAST_FLTSIZE,
+					m_filter);
+
+		data->rxctl |= AX_RX_CTL_AM;
+	}
+
+	ax88179_write_cmd_async(dev, AX_ACCESS_MAC, AX_RX_CTL,
+				2, 2, &data->rxctl);
+}
+
+static int
+ax88179_set_features(struct net_device *net, netdev_features_t features)
+{
+	u8 tmp;
+	struct usbnet *dev = netdev_priv(net);
+	netdev_features_t changed = net->features ^ features;
+
+	if (changed & NETIF_F_TSO)
+		net->features ^= NETIF_F_TSO;
+
+	if (changed & NETIF_F_SG)
+		net->features ^= NETIF_F_SG;
+
+	if (changed & NETIF_F_IP_CSUM) {
+		ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, 1, 1, &tmp);
+		tmp ^= AX_TXCOE_TCP | AX_TXCOE_UDP;
+		ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, 1, 1, &tmp);
+
+		net->features ^= NETIF_F_IP_CSUM;
+	}
+
+	if (changed & NETIF_F_IPV6_CSUM) {
+		ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, 1, 1, &tmp);
+		tmp ^= AX_TXCOE_TCPV6 | AX_TXCOE_UDPV6;
+		ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, 1, 1, &tmp);
+
+		net->features ^= NETIF_F_IPV6_CSUM;
+	}
+
+	if (changed & NETIF_F_RXCSUM) {
+		ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_RXCOE_CTL, 1, 1, &tmp);
+		tmp ^= AX_RXCOE_IP | AX_RXCOE_TCP | AX_RXCOE_UDP |
+		       AX_RXCOE_TCPV6 | AX_RXCOE_UDPV6;
+		ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RXCOE_CTL, 1, 1, &tmp);
+
+		net->features ^= NETIF_F_RXCSUM;
+	}
+
+	return 0;
+}
+
+static int ax88179_change_mtu(struct net_device *net, int new_mtu)
+{
+	struct usbnet *dev = netdev_priv(net);
+	u16 tmp16;
+
+	if (new_mtu <= 0 || new_mtu > 4088)
+		return -EINVAL;
+
+	net->mtu = new_mtu;
+	dev->hard_mtu = net->mtu + net->hard_header_len;
+
+	if (net->mtu > 1500) {
+		ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,
+				 2, 2, &tmp16);
+		tmp16 |= AX_MEDIUM_JUMBO_EN;
+		ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,
+				  2, 2, &tmp16);
+	} else {
+		ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,
+				 2, 2, &tmp16);
+		tmp16 &= ~AX_MEDIUM_JUMBO_EN;
+		ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,
+				  2, 2, &tmp16);
+	}
+
+	return 0;
+}
+
+static int ax88179_set_mac_addr(struct net_device *net, void *p)
+{
+	struct usbnet *dev = netdev_priv(net);
+	struct sockaddr *addr = p;
+
+	if (netif_running(net))
+		return -EBUSY;
+	if (!is_valid_ether_addr(addr->sa_data))
+		return -EADDRNOTAVAIL;
+
+	memcpy(net->dev_addr, addr->sa_data, ETH_ALEN);
+
+	/* Set the MAC address */
+	return ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_NODE_ID, ETH_ALEN,
+				 ETH_ALEN, net->dev_addr);
+}
+
+static const struct net_device_ops ax88179_netdev_ops = {
+	.ndo_open		= usbnet_open,
+	.ndo_stop		= usbnet_stop,
+	.ndo_start_xmit		= usbnet_start_xmit,
+	.ndo_tx_timeout		= usbnet_tx_timeout,
+	.ndo_change_mtu		= ax88179_change_mtu,
+	.ndo_set_mac_address	= ax88179_set_mac_addr,
+	.ndo_validate_addr	= eth_validate_addr,
+	.ndo_do_ioctl		= ax88179_ioctl,
+	.ndo_set_rx_mode	= ax88179_set_multicast,
+	.ndo_set_features	= ax88179_set_features,
+};
+
+static int ax88179_check_eeprom(struct usbnet *dev)
+{
+	u8 i;
+	u8 buf;
+	u8 eeprom[20];
+	u16 csum, delay = HZ / 10;
+	unsigned long jtimeout;
+
+	/* Read EEPROM content */
+	for (i = 0; i < 6; i++) {
+		buf = i;
+		if (ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_SROM_ADDR,
+				      1, 1, &buf) < 0)
+			return -EINVAL;
+
+		buf = EEP_RD;
+		if (ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_SROM_CMD,
+				      1, 1, &buf) < 0)
+			return -EINVAL;
+
+		jtimeout = jiffies + delay;
+		do {
+			ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_SROM_CMD,
+					 1, 1, &buf);
+
+			if (time_after(jiffies, jtimeout))
+				return -EINVAL;
+
+		} while (buf & EEP_BUSY);
+
+		ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_SROM_DATA_LOW,
+				 2, 2, &eeprom[i * 2]);
+		if ((i == 0) && (eeprom[0] == 0xFF))
+			return -EINVAL;
+	}
+
+	csum = eeprom[6] + eeprom[7] + eeprom[8] + eeprom[9];
+	csum = (csum >> 8) + (csum & 0xff);
+	if ((csum + eeprom[10]) != 0xff)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int ax88179_check_efuse(struct usbnet *dev, u16 *ledmode)
+{
+	u8	i;
+	u8	efuse[64];
+	u16	csum = 0;
+
+	if (ax88179_read_cmd(dev, AX_ACCESS_EFUS, 0, 64, 64, efuse) < 0)
+		return -EINVAL;
+
+	if (*efuse == 0xFF)
+		return -EINVAL;
+
+	for (i = 0; i < 64; i++)
+		csum = csum + efuse[i];
+
+	while (csum > 255)
+		csum = (csum & 0x00FF) + ((csum >> 8) & 0x00FF);
+
+	if (csum != 0xFF)
+		return -EINVAL;
+
+	*ledmode = (efuse[51] << 8) | efuse[52];
+
+	return 0;
+}
+
+static int ax88179_convert_old_led(struct usbnet *dev, u16 *ledvalue)
+{
+	u16 led;
+
+	/* Loaded the old eFuse LED Mode */
+	if (ax88179_read_cmd(dev, AX_ACCESS_EEPROM, 0x3C, 1, 2, &led) < 0)
+		return -EINVAL;
+
+	led >>= 8;
+	switch (led) {
+	case 0xFF:
+		led = LED0_ACTIVE | LED1_LINK_10 | LED1_LINK_100 |
+		      LED1_LINK_1000 | LED2_ACTIVE | LED2_LINK_10 |
+		      LED2_LINK_100 | LED2_LINK_1000 | LED_VALID;
+		break;
+	case 0xFE:
+		led = LED0_ACTIVE | LED1_LINK_1000 | LED2_LINK_100 | LED_VALID;
+		break;
+	case 0xFD:
+		led = LED0_ACTIVE | LED1_LINK_1000 | LED2_LINK_100 |
+		      LED2_LINK_10 | LED_VALID;
+		break;
+	case 0xFC:
+		led = LED0_ACTIVE | LED1_ACTIVE | LED1_LINK_1000 | LED2_ACTIVE |
+		      LED2_LINK_100 | LED2_LINK_10 | LED_VALID;
+		break;
+	default:
+		led = LED0_ACTIVE | LED1_LINK_10 | LED1_LINK_100 |
+		      LED1_LINK_1000 | LED2_ACTIVE | LED2_LINK_10 |
+		      LED2_LINK_100 | LED2_LINK_1000 | LED_VALID;
+		break;
+	}
+
+	*ledvalue = led;
+
+	return 0;
+}
+
+static int ax88179_led_setting(struct usbnet *dev)
+{
+	u8 ledfd, value = 0;
+	u16 tmp, ledact, ledlink, ledvalue = 0, delay = HZ / 10;
+	unsigned long jtimeout;
+
+	/* Check AX88179 version. UA1 or UA2*/
+	ax88179_read_cmd(dev, AX_ACCESS_MAC, GENERAL_STATUS, 1, 1, &value);
+
+	if (!(value & AX_SECLD)) {	/* UA1 */
+		value = AX_GPIO_CTRL_GPIO3EN | AX_GPIO_CTRL_GPIO2EN |
+			AX_GPIO_CTRL_GPIO1EN;
+		if (ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_GPIO_CTRL,
+				      1, 1, &value) < 0)
+			return -EINVAL;
+	}
+
+	/* Check EEPROM */
+	if (!ax88179_check_eeprom(dev)) {
+		value = 0x42;
+		if (ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_SROM_ADDR,
+				      1, 1, &value) < 0)
+			return -EINVAL;
+
+		value = EEP_RD;
+		if (ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_SROM_CMD,
+				      1, 1, &value) < 0)
+			return -EINVAL;
+
+		jtimeout = jiffies + delay;
+		do {
+			ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_SROM_CMD,
+					 1, 1, &value);
+
+			if (time_after(jiffies, jtimeout))
+				return -EINVAL;
+
+		} while (value & EEP_BUSY);
+
+		ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_SROM_DATA_HIGH,
+				 1, 1, &value);
+		ledvalue = (value << 8);
+
+		ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_SROM_DATA_LOW,
+				 1, 1, &value);
+		ledvalue |= value;
+
+		/* load internal ROM for defaule setting */
+		if ((ledvalue == 0xFFFF) || ((ledvalue & LED_VALID) == 0))
+			ax88179_convert_old_led(dev, &ledvalue);
+
+	} else if (!ax88179_check_efuse(dev, &ledvalue)) {
+		if ((ledvalue == 0xFFFF) || ((ledvalue & LED_VALID) == 0))
+			ax88179_convert_old_led(dev, &ledvalue);
+	} else {
+		ax88179_convert_old_led(dev, &ledvalue);
+	}
+
+	tmp = GMII_PHY_PGSEL_EXT;
+	ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
+			  GMII_PHY_PAGE_SELECT, 2, &tmp);
+
+	tmp = 0x2c;
+	ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
+			  GMII_PHYPAGE, 2, &tmp);
+
+	ax88179_read_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, GMII_LED_ACT,
+			 2, &ledact);
+
+	ax88179_read_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
+			 GMII_LED_LINK, 2, &ledlink);
+
+	ledact &= GMII_LED_ACTIVE_MASK;
+	ledlink &= GMII_LED_LINK_MASK;
+
+	if (ledvalue & LED0_ACTIVE)
+		ledact |= GMII_LED0_ACTIVE;
+
+	if (ledvalue & LED1_ACTIVE)
+		ledact |= GMII_LED1_ACTIVE;
+
+	if (ledvalue & LED2_ACTIVE)
+		ledact |= GMII_LED2_ACTIVE;
+
+	if (ledvalue & LED0_LINK_10)
+		ledlink |= GMII_LED0_LINK_10;
+
+	if (ledvalue & LED1_LINK_10)
+		ledlink |= GMII_LED1_LINK_10;
+
+	if (ledvalue & LED2_LINK_10)
+		ledlink |= GMII_LED2_LINK_10;
+
+	if (ledvalue & LED0_LINK_100)
+		ledlink |= GMII_LED0_LINK_100;
+
+	if (ledvalue & LED1_LINK_100)
+		ledlink |= GMII_LED1_LINK_100;
+
+	if (ledvalue & LED2_LINK_100)
+		ledlink |= GMII_LED2_LINK_100;
+
+	if (ledvalue & LED0_LINK_1000)
+		ledlink |= GMII_LED0_LINK_1000;
+
+	if (ledvalue & LED1_LINK_1000)
+		ledlink |= GMII_LED1_LINK_1000;
+
+	if (ledvalue & LED2_LINK_1000)
+		ledlink |= GMII_LED2_LINK_1000;
+
+	tmp = ledact;
+	ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, GMII_LED_ACT,
+			  2, &tmp);
+
+	tmp = ledlink;
+	ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, GMII_LED_LINK,
+			  2, &tmp);
+
+	tmp = GMII_PHY_PGSEL_PAGE0;
+	ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
+			  GMII_PHY_PAGE_SELECT, 2, &tmp);
+
+	/* LED full duplex setting */
+	ledfd = 0;
+	if (ledvalue & LED0_FD)
+		ledfd |= 0x01;
+	else if ((ledvalue & LED0_USB3_MASK) == 0)
+		ledfd |= 0x02;
+
+	if (ledvalue & LED1_FD)
+		ledfd |= 0x04;
+	else if ((ledvalue & LED1_USB3_MASK) == 0)
+		ledfd |= 0x08;
+
+	if (ledvalue & LED2_FD)
+		ledfd |= 0x10;
+	else if ((ledvalue & LED2_USB3_MASK) == 0)
+		ledfd |= 0x20;
+
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_LEDCTRL, 1, 1, &ledfd);
+
+	return 0;
+}
+
+static int ax88179_bind(struct usbnet *dev, struct usb_interface *intf)
+{
+	u8 buf[5];
+	u16 *tmp16;
+	u8 *tmp;
+	struct ax88179_data *ax179_data = (struct ax88179_data *)dev->data;
+
+	usbnet_get_endpoints(dev, intf);
+
+	tmp16 = (u16 *)buf;
+	tmp = (u8 *)buf;
+
+	memset(ax179_data, 0, sizeof(*ax179_data));
+
+	/* Power up ethernet PHY */
+	*tmp16 = 0;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, tmp16);
+	*tmp16 = AX_PHYPWR_RSTCTL_IPRL;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, tmp16);
+	msleep(200);
+
+	*tmp = AX_CLK_SELECT_ACS | AX_CLK_SELECT_BCS;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_CLK_SELECT, 1, 1, tmp);
+	msleep(100);
+
+	ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_NODE_ID, ETH_ALEN,
+			 ETH_ALEN, dev->net->dev_addr);
+	memcpy(dev->net->perm_addr, dev->net->dev_addr, ETH_ALEN);
+
+	/* RX bulk configuration */
+	memcpy(tmp, &AX88179_BULKIN_SIZE[0], 5);
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_BULKIN_QCTRL, 5, 5, tmp);
+
+	dev->rx_urb_size = 1024 * 20;
+
+	*tmp = 0x34;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PAUSE_WATERLVL_LOW, 1, 1, tmp);
+
+	*tmp = 0x52;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PAUSE_WATERLVL_HIGH,
+			  1, 1, tmp);
+
+	dev->net->netdev_ops = &ax88179_netdev_ops;
+	dev->net->ethtool_ops = &ax88179_ethtool_ops;
+	dev->net->needed_headroom = 8;
+
+	/* Initialize MII structure */
+	dev->mii.dev = dev->net;
+	dev->mii.mdio_read = ax88179_mdio_read;
+	dev->mii.mdio_write = ax88179_mdio_write;
+	dev->mii.phy_id_mask = 0xff;
+	dev->mii.reg_num_mask = 0xff;
+	dev->mii.phy_id = 0x03;
+	dev->mii.supports_gmii = 1;
+
+	dev->net->features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
+			      NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_TSO;
+
+	dev->net->hw_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
+				 NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_TSO;
+
+	/* Enable checksum offload */
+	*tmp = AX_RXCOE_IP | AX_RXCOE_TCP | AX_RXCOE_UDP |
+	       AX_RXCOE_TCPV6 | AX_RXCOE_UDPV6;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RXCOE_CTL, 1, 1, tmp);
+
+	*tmp = AX_TXCOE_IP | AX_TXCOE_TCP | AX_TXCOE_UDP |
+	       AX_TXCOE_TCPV6 | AX_TXCOE_UDPV6;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, 1, 1, tmp);
+
+	/* Configure RX control register => start operation */
+	*tmp16 = AX_RX_CTL_DROPCRCERR | AX_RX_CTL_IPE | AX_RX_CTL_START |
+		 AX_RX_CTL_AP | AX_RX_CTL_AMALL | AX_RX_CTL_AB;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, 0x0b, 2, 2, tmp16);
+
+	*tmp = AX_MONITOR_MODE_PMETYPE | AX_MONITOR_MODE_PMEPOL |
+	       AX_MONITOR_MODE_RWMP;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MONITOR_MOD, 1, 1, tmp);
+
+	/* Configure default medium type => giga */
+	*tmp16 = AX_MEDIUM_RECEIVE_EN | AX_MEDIUM_TXFLOW_CTRLEN |
+		 AX_MEDIUM_RXFLOW_CTRLEN | AX_MEDIUM_ALWAYS_ONE |
+		 AX_MEDIUM_FULL_DUPLEX | AX_MEDIUM_GIGAMODE;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,
+			  2, 2, tmp16);
+
+	ax88179_led_setting(dev);
+
+	/* Restart autoneg */
+	mii_nway_restart(&dev->mii);
+
+	netif_carrier_off(dev->net);
+
+	return 0;
+}
+
+static void ax88179_unbind(struct usbnet *dev, struct usb_interface *intf)
+{
+	u16 tmp16;
+
+	/* Configure RX control register => stop operation */
+	tmp16 = AX_RX_CTL_STOP;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2, &tmp16);
+
+	tmp16 = 0;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_CLK_SELECT, 1, 1, &tmp16);
+
+	/* Power down ethernet PHY */
+	tmp16 = 0;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, &tmp16);
+}
+
+static void
+ax88179_rx_checksum(struct sk_buff *skb, struct ax88179_rx_pkt_header *pkt_hdr)
+{
+	skb->ip_summed = CHECKSUM_NONE;
+
+	/* checksum error bit is set */
+	if (pkt_hdr->l3_csum_err || pkt_hdr->l4_csum_err)
+		return;
+
+	/* It must be a TCP or UDP packet with a valid checksum */
+	if ((pkt_hdr->l4_type == AX_RXHDR_L4_TYPE_TCP) ||
+	    (pkt_hdr->l4_type == AX_RXHDR_L4_TYPE_UDP))
+		skb->ip_summed = CHECKSUM_UNNECESSARY;
+}
+
+static int ax88179_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
+{
+	struct sk_buff *ax_skb;
+	int pkt_cnt;
+	u32 rx_hdr;
+	u16 hdr_off;
+	struct ax88179_rx_pkt_header *pkt_hdr;
+
+	skb_trim(skb, skb->len - 4);
+	memcpy(&rx_hdr, skb_tail_pointer(skb), sizeof(rx_hdr));
+	le32_to_cpus(&rx_hdr);
+
+	pkt_cnt = (u16)rx_hdr;
+	hdr_off = (u16)(rx_hdr >> 16);
+	pkt_hdr = (struct ax88179_rx_pkt_header *)(skb->data + hdr_off);
+
+
+	while (pkt_cnt--) {
+		u16 pkt_len;
+
+		le32_to_cpus((u32 *)pkt_hdr);
+		pkt_len = pkt_hdr->len;
+
+		/* Check CRC or runt packet */
+		if (pkt_hdr->crc || pkt_hdr->drop) {
+			skb_pull(skb, (pkt_len + 7) & 0xFFF8);
+			pkt_hdr++;
+			continue;
+		}
+
+		if (pkt_cnt == 0) {
+			/* Skip IP alignment psudo header */
+			skb_pull(skb, 2);
+			skb->len = pkt_len;
+			skb_set_tail_pointer(skb, pkt_len);
+			skb->truesize = pkt_len + sizeof(struct sk_buff);
+			ax88179_rx_checksum(skb, pkt_hdr);
+			return 1;
+		}
+
+		ax_skb = skb_clone(skb, GFP_ATOMIC);
+		if (ax_skb) {
+			ax_skb->len = pkt_len;
+			ax_skb->data = skb->data + 2;
+			skb_set_tail_pointer(ax_skb, pkt_len);
+			ax_skb->truesize = pkt_len + sizeof(struct sk_buff);
+			ax88179_rx_checksum(ax_skb, pkt_hdr);
+			usbnet_skb_return(dev, ax_skb);
+		} else {
+			return 0;
+		}
+
+		skb_pull(skb, (pkt_len + 7) & 0xFFF8);
+		pkt_hdr++;
+	}
+	return 1;
+}
+
+static struct sk_buff *
+ax88179_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags)
+{
+	u32 tx_hdr1, tx_hdr2;
+	int frame_size = dev->maxpacket;
+	int mss = skb_shinfo(skb)->gso_size;
+	int headroom;
+	int tailroom;
+
+	tx_hdr1 = skb->len;
+	tx_hdr2 = mss;
+	if (((skb->len + 8) % frame_size) == 0)
+		tx_hdr2 |= 0x80008000;	/* Enable padding */
+
+	skb_linearize(skb);
+	headroom = skb_headroom(skb);
+	tailroom = skb_tailroom(skb);
+
+	if (!skb_header_cloned(skb) &&
+	    !skb_cloned(skb) &&
+	    (headroom + tailroom) >= 8) {
+		if (headroom < 8) {
+			skb->data = memmove(skb->head + 8, skb->data, skb->len);
+			skb_set_tail_pointer(skb, skb->len);
+		}
+	} else {
+		struct sk_buff *skb2;
+
+		skb2 = skb_copy_expand(skb, 8, 0, flags);
+		dev_kfree_skb_any(skb);
+		skb = skb2;
+		if (!skb)
+			return NULL;
+	}
+
+	skb_push(skb, 4);
+	cpu_to_le32s(&tx_hdr2);
+	skb_copy_to_linear_data(skb, &tx_hdr2, 4);
+
+	skb_push(skb, 4);
+	cpu_to_le32s(&tx_hdr1);
+	skb_copy_to_linear_data(skb, &tx_hdr1, 4);
+
+	return skb;
+}
+
+static int ax88179_link_reset(struct usbnet *dev)
+{
+	struct ax88179_data *ax179_data = (struct ax88179_data *)dev->data;
+	u8 tmp[5], link_sts;
+	u16 mode, tmp16, delay = HZ / 10;
+	u32 tmp32 = 0x40000000;
+	unsigned long jtimeout;
+
+	jtimeout = jiffies + delay;
+	while (tmp32 & 0x40000000) {
+		mode = 0;
+		ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2, &mode);
+		ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2,
+				  &ax179_data->rxctl);
+
+		/*link up, check the usb device control TX FIFO full or empty*/
+		ax88179_read_cmd(dev, 0x81, 0x8c, 0, 4, &tmp32);
+
+		if (time_after(jiffies, jtimeout))
+			return 0;
+	}
+
+	mode = AX_MEDIUM_RECEIVE_EN | AX_MEDIUM_TXFLOW_CTRLEN |
+	       AX_MEDIUM_RXFLOW_CTRLEN | AX_MEDIUM_ALWAYS_ONE;
+
+	ax88179_read_cmd(dev, AX_ACCESS_MAC, PHYSICAL_LINK_STATUS,
+			 1, 1, &link_sts);
+
+	ax88179_read_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, GMII_PHY_PHYSR,
+			 2, &tmp16);
+
+	if (!(tmp16 & GMII_PHY_PHYSR_LINK)) {
+		return 0;
+	} else if (GMII_PHY_PHYSR_GIGA == (tmp16 & GMII_PHY_PHYSR_SMASK)) {
+		mode |= AX_MEDIUM_GIGAMODE | AX_MEDIUM_EN_125MHZ;
+		if (dev->net->mtu > 1500)
+			mode |= AX_MEDIUM_JUMBO_EN;
+
+		if (link_sts & AX_USB_SS)
+			memcpy(tmp, &AX88179_BULKIN_SIZE[0], 5);
+		else if (link_sts & AX_USB_HS)
+			memcpy(tmp, &AX88179_BULKIN_SIZE[1], 5);
+		else
+			memcpy(tmp, &AX88179_BULKIN_SIZE[3], 5);
+	} else if (GMII_PHY_PHYSR_100 == (tmp16 & GMII_PHY_PHYSR_SMASK)) {
+		mode |= AX_MEDIUM_PS;
+
+		if (link_sts & (AX_USB_SS | AX_USB_HS))
+			memcpy(tmp, &AX88179_BULKIN_SIZE[2], 5);
+		else
+			memcpy(tmp, &AX88179_BULKIN_SIZE[3], 5);
+	} else {
+		memcpy(tmp, &AX88179_BULKIN_SIZE[3], 5);
+	}
+
+	/* RX bulk configuration */
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_BULKIN_QCTRL, 5, 5, tmp);
+
+	dev->rx_urb_size = (1024 * (tmp[3] + 2));
+
+	if (tmp16 & GMII_PHY_PHYSR_FULL)
+		mode |= AX_MEDIUM_FULL_DUPLEX;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,
+			  2, 2, &mode);
+
+	netif_carrier_on(dev->net);
+
+	return 0;
+}
+
+static int ax88179_reset(struct usbnet *dev)
+{
+	u8 buf[5];
+	u16 *tmp16;
+	u8 *tmp;
+
+	tmp16 = (u16 *)buf;
+	tmp = (u8 *)buf;
+
+	/* Power up ethernet PHY */
+	*tmp16 = 0;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, tmp16);
+
+	*tmp16 = AX_PHYPWR_RSTCTL_IPRL;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, tmp16);
+	msleep(200);
+
+	*tmp = AX_CLK_SELECT_ACS | AX_CLK_SELECT_BCS;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_CLK_SELECT, 1, 1, tmp);
+	msleep(100);
+
+	/* Ethernet PHY Auto Detach*/
+	ax88179_auto_detach(dev, 0);
+
+	ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_NODE_ID, ETH_ALEN, ETH_ALEN,
+			 dev->net->dev_addr);
+	memcpy(dev->net->perm_addr, dev->net->dev_addr, ETH_ALEN);
+
+	/* RX bulk configuration */
+	memcpy(tmp, &AX88179_BULKIN_SIZE[0], 5);
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_BULKIN_QCTRL, 5, 5, tmp);
+
+	dev->rx_urb_size = 1024 * 20;
+
+	*tmp = 0x34;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PAUSE_WATERLVL_LOW, 1, 1, tmp);
+
+	*tmp = 0x52;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PAUSE_WATERLVL_HIGH,
+			  1, 1, tmp);
+
+	dev->net->features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
+			      NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_TSO;
+
+	dev->net->hw_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
+				 NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_TSO;
+
+	/* Enable checksum offload */
+	*tmp = AX_RXCOE_IP | AX_RXCOE_TCP | AX_RXCOE_UDP |
+	       AX_RXCOE_TCPV6 | AX_RXCOE_UDPV6;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RXCOE_CTL, 1, 1, tmp);
+
+	*tmp = AX_TXCOE_IP | AX_TXCOE_TCP | AX_TXCOE_UDP |
+	       AX_TXCOE_TCPV6 | AX_TXCOE_UDPV6;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, 1, 1, tmp);
+
+	/* Configure RX control register => start operation */
+	*tmp16 = AX_RX_CTL_DROPCRCERR | AX_RX_CTL_IPE | AX_RX_CTL_START |
+		 AX_RX_CTL_AP | AX_RX_CTL_AMALL | AX_RX_CTL_AB;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, 0x0b, 2, 2, tmp16);
+
+	*tmp = AX_MONITOR_MODE_PMETYPE | AX_MONITOR_MODE_PMEPOL |
+	       AX_MONITOR_MODE_RWMP;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MONITOR_MOD, 1, 1, tmp);
+
+	/* Configure default medium type => giga */
+	*tmp16 = AX_MEDIUM_RECEIVE_EN | AX_MEDIUM_TXFLOW_CTRLEN |
+		 AX_MEDIUM_RXFLOW_CTRLEN | AX_MEDIUM_ALWAYS_ONE |
+		 AX_MEDIUM_FULL_DUPLEX | AX_MEDIUM_GIGAMODE;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,
+			  2, 2, tmp16);
+
+	ax88179_led_setting(dev);
+
+	/* Restart autoneg */
+	mii_nway_restart(&dev->mii);
+
+	netif_carrier_off(dev->net);
+
+	return 0;
+}
+
+static int ax88179_stop(struct usbnet *dev)
+{
+	u16 tmp16;
+
+	ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,
+			 2, 2, &tmp16);
+	tmp16 &= ~AX_MEDIUM_RECEIVE_EN;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,
+			  2, 2, &tmp16);
+
+	return 0;
+}
+
+static const struct driver_info ax88179_info = {
+	.description = "ASIX AX88179 USB 3.0 Gigibit Ethernet",
+	.bind = ax88179_bind,
+	.unbind = ax88179_unbind,
+	.status = ax88179_status,
+	.link_reset = ax88179_link_reset,
+	.reset = ax88179_reset,
+	.stop = ax88179_stop,
+	.flags = FLAG_ETHER | FLAG_FRAMING_AX,
+	.rx_fixup = ax88179_rx_fixup,
+	.tx_fixup = ax88179_tx_fixup,
+};
+
+static const struct driver_info ax88178a_info = {
+	.description = "ASIX AX88178A USB 2.0 Gigibit Ethernet",
+	.bind = ax88179_bind,
+	.unbind = ax88179_unbind,
+	.status = ax88179_status,
+	.link_reset = ax88179_link_reset,
+	.reset = ax88179_reset,
+	.stop = ax88179_stop,
+	.flags = FLAG_ETHER | FLAG_FRAMING_AX,
+	.rx_fixup = ax88179_rx_fixup,
+	.tx_fixup = ax88179_tx_fixup,
+};
+
+static const struct driver_info sitecom_info = {
+	.description = "Sitecom USB 3.0 to Gigabit Adapter",
+	.bind = ax88179_bind,
+	.unbind = ax88179_unbind,
+	.status = ax88179_status,
+	.link_reset = ax88179_link_reset,
+	.reset = ax88179_reset,
+	.stop = ax88179_stop,
+	.flags = FLAG_ETHER | FLAG_FRAMING_AX,
+	.rx_fixup = ax88179_rx_fixup,
+	.tx_fixup = ax88179_tx_fixup,
+};
+
+static const struct usb_device_id products[] = {
+{
+	/* ASIX AX88179 10/100/1000 */
+	USB_DEVICE(0x0b95, 0x1790),
+	.driver_info = (unsigned long)&ax88179_info,
+}, {
+	/* ASIX AX88178A 10/100/1000 */
+	USB_DEVICE(0x0b95, 0x178a),
+	.driver_info = (unsigned long)&ax88178a_info,
+}, {
+	/* Sitecom USB 3.0 to Gigabit Adapter */
+	USB_DEVICE(0x0df6, 0x0072),
+	.driver_info = (unsigned long) &sitecom_info,
+},
+	{ },
+};
+MODULE_DEVICE_TABLE(usb, products);
+
+static struct usb_driver asix_driver = {
+	.name =		"ax88179_178a",
+	.id_table =	products,
+	.probe =	usbnet_probe,
+	.suspend =	ax88179_suspend,
+	.resume =	ax88179_resume,
+	.disconnect =	usbnet_disconnect,
+};
+
+static int __init asix_init(void)
+{
+	return usb_register(&asix_driver);
+}
+module_init(asix_init);
+
+static void __exit asix_exit(void)
+{
+	usb_deregister(&asix_driver);
+}
+module_exit(asix_exit);
+
+MODULE_DESCRIPTION("ASIX AX88179/178A based USB 3.0/2.0 Gigabit Ethernet Devices");
+MODULE_LICENSE("GPL");
-- 
1.7.9.5


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

* Re: [PATCH, resubmit] ax88179_178a: ASIX AX88179_178A USB 3.0/2.0 to gigabit ethernet adapter driver
@ 2013-02-03 14:21 Daniel J Blueman
  2013-12-04 12:48 ` Freddy
  0 siblings, 1 reply; 27+ messages in thread
From: Daniel J Blueman @ 2013-02-03 14:21 UTC (permalink / raw)
  To: Freddy Xin; +Cc: Linux Kernel

Hi Freddy,

> Michael, could you give me more information about how do you test this driver?
> I have tried to reproduce the issue by using "ifconfig ethX mtu 1500", but I didn't confront the same issue.
> Thank you in advance for your help.

I found the same by just starting with 'ifconfig eth0 1500' and
testing as high as 4000; pinging another host with a large payload of
size mtu-40 starts failing; after ~30s, I see the transmit time out
trace [1].

Of course, a default MTU size of 1500 is essential to avoid
fragmentation issues, so should be fixed too. Jumbo frames support is
essential these days too.

Thanks,
  Daniel

--- [1]

usb 4-1: new SuperSpeed USB device number 3 using xhci_hcd
ax88179_178a 4-1:1.0 eth0: register 'ax88179_178a' at
usb-0000:00:14.0-1, ASIX AX88179 USB 3.0 Gigibit Ethernet,
00:0a:cd:21:46:a7
ax88179_178a 4-1:1.0 eth2: ax88179 - Link status is: 1
------------[ cut here ]------------
WARNING: at net/sched/sch_generic.c:254 dev_watchdog+0x26b/0x280()
Hardware name: MacBookPro10,1
NETDEV WATCHDOG: eth2 (ax88179_178a): transmit queue 0 timed out
Modules linked in: fuse snd_hda_codec_hdmi snd_hda_codec_cirrus joydev
hid_apple bcm5974 coretemp kvm_intel kvm ghash_clmulni_intel b43 ssb
ax88179_178a usbnet mii uvcvideo videobuf2_core videobuf2_vmalloc
videobuf2_memops applesmc input_polldev microcode bcma bnep rfcomm
lpc_ich mfd_core snd_hda_intel snd_hda_codec snd_hwdep snd_pcm nouveau
apple_gmux snd_timer i915 ttm drm_kms_helper snd hwmon binfmt_misc
mxm_wmi snd_page_alloc video apple_bl nls_iso8859_1
Pid: 0, comm: swapper/0 Not tainted 3.8.0-rc6-expert+ #2
Call Trace:
 <IRQ> [<ffffffff81408d00>] ? dev_watchdog+0x250/0x280
 [<ffffffff8103f8ea>] warn_slowpath_common+0x7a/0xb0
 [<ffffffff8103f9c1>] warn_slowpath_fmt+0x41/0x50
 [<ffffffff81408d1b>] dev_watchdog+0x26b/0x280
 [<ffffffff81408ab0>] ? pfifo_fast_dequeue+0xe0/0xe0
 [<ffffffff8104db84>] call_timer_fn+0x74/0xf0
 [<ffffffff8104db10>] ? usleep_range+0x40/0x40
 [<ffffffff81408ab0>] ? pfifo_fast_dequeue+0xe0/0xe0
 [<ffffffff8104df0b>] run_timer_softirq+0x18b/0x220
 [<ffffffff81047ad2>] __do_softirq+0xc2/0x180
 [<ffffffff8108b3ff>] ? tick_program_event+0x1f/0x30
 [<ffffffff813b18d0>] ? read_measured_perf_ctrs+0x70/0x70
 [<ffffffff8154647c>] call_softirq+0x1c/0x30
 [<ffffffff810042ed>] do_softirq+0x7d/0xb0
 [<ffffffff81047d2e>] irq_exit+0x9e/0xc0
 [<ffffffff81022779>] smp_apic_timer_interrupt+0x69/0xa0
 [<ffffffff81545ddc>] apic_timer_interrupt+0x6c/0x80
 <EOI> [<ffffffff8104e884>] ? get_next_timer_interrupt+0x1c4/0x290
 [<ffffffff813b21a0>] ? cpuidle_wrap_enter+0x50/0x90
 [<ffffffff813b219c>] ? cpuidle_wrap_enter+0x4c/0x90
 [<ffffffff813b1900>] cpuidle_enter_tk+0x10/0x20
 [<ffffffff813b1f3c>] cpuidle_idle_call+0x7c/0x110
 [<ffffffff8100b7ba>] cpu_idle+0x7a/0xf0
 [<ffffffff8152b954>] rest_init+0x144/0x150
 [<ffffffff8152b810>] ? csum_partial_copy_generic+0x170/0x170
 [<ffffffff81ab49e6>] ? efi_free_boot_services+0x53/0x58
 [<ffffffff81aa3ad9>] start_kernel+0x359/0x366
 [<ffffffff81aa3580>] ? repair_env_string+0x5e/0x5e
 [<ffffffff81aa32a5>] x86_64_start_reservations+0x131/0x135
 [<ffffffff81aa3120>] ? early_idt_handlers+0x120/0x120
 [<ffffffff81aa337c>] x86_64_start_kernel+0xd3/0xd7
---[ end trace 58c12634a365560a ]---
-- 
Daniel J Blueman

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

* Re: [PATCH, resubmit] ax88179_178a: ASIX AX88179_178A USB 3.0/2.0 to gigabit ethernet adapter driver
  2013-01-28 13:36     ` Freddy
  2013-01-28 14:24       ` Bjørn Mork
@ 2013-01-28 16:42       ` Michael Leun
  1 sibling, 0 replies; 27+ messages in thread
From: Michael Leun @ 2013-01-28 16:42 UTC (permalink / raw)
  To: Freddy
  Cc: 'Bjørn Mork',
	netdev, linux-usb, linux-kernel, louis, davem, Support

On Mon, 28 Jan 2013 21:36:20 +0800
"Freddy" <freddy@asix.com.tw> wrote:

> > I would vote to not accept that driver for mainline as long as this 
> > issues are not fixed.
> 
> 
> Michael, could you give me more information about how do you test
> this driver? I have tried to reproduce the issue by using "ifconfig
> ethX mtu 1500", but I didn't confront the same issue. Thank you in
> advance for your help.

I'm very sorry, but this seems to be some mistake on my side. I would
have sworn that I had done this also with mtu 1500, but now it looks
like you have to set the mtu to at least 1519 to see the issue (likely
an typo and I actually had set it to 1600):

jill:/home/ml # ifconfig eth3 192.168.123.2/24
jill:/home/ml # ifconfig eth3
eth3      Link encap:Ethernet  Hardware Adresse 00:24:9B:05:5C:E3  
          inet Adresse:192.168.123.2  Bcast:192.168.123.255  Maske:255.255.255.0
          inet6 Adresse: fe80::224:9bff:fe05:5ce3/64 Gültigkeitsbereich:Verbindung
          UP BROADCAST RUNNING MULTICAST  MTU:1488  Metric:1
          RX packets:0 errors:0 dropped:0 overruns:0 frame:0
          TX packets:1 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 Sendewarteschlangenlänge:1000 
          RX bytes:0 (0.0 b)  TX bytes:98 (98.0 b)

jill:/home/ml # ping 192.168.123.1
PING 192.168.123.1 (192.168.123.1) 56(84) bytes of data.
64 bytes from 192.168.123.1: icmp_seq=1 ttl=64 time=1.41 ms
64 bytes from 192.168.123.1: icmp_seq=2 ttl=64 time=0.969 ms
64 bytes from 192.168.123.1: icmp_seq=3 ttl=64 time=1.01 ms
^C
--- 192.168.123.1 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2002ms
rtt min/avg/max/mdev = 0.969/1.131/1.412/0.199 ms
jill:/home/ml # ping -s 1491 192.168.123.1
PING 192.168.123.1 (192.168.123.1) 1491(1519) bytes of data.
1499 bytes from 192.168.123.1: icmp_seq=1 ttl=64 time=1.30 ms
1499 bytes from 192.168.123.1: icmp_seq=2 ttl=64 time=1.10 ms
1499 bytes from 192.168.123.1: icmp_seq=3 ttl=64 time=1.24 ms
^C
--- 192.168.123.1 ping statistics ---
3 packets transmitted, 3 received, 0% packet loss, time 2002ms
rtt min/avg/max/mdev = 1.109/1.218/1.306/0.086 ms
jill:/home/ml # ifconfig eth3 mtu 1519
jill:/home/ml # ping 192.168.123.1
PING 192.168.123.1 (192.168.123.1) 56(84) bytes of data.
64 bytes from 192.168.123.1: icmp_seq=1 ttl=64 time=1.11 ms
64 bytes from 192.168.123.1: icmp_seq=2 ttl=64 time=0.969 ms
^C
--- 192.168.123.1 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1001ms
rtt min/avg/max/mdev = 0.969/1.042/1.115/0.073 ms
jill:/home/ml # ping -s 1491 192.168.123.1
PING 192.168.123.1 (192.168.123.1) 1491(1519) bytes of data.
^C
--- 192.168.123.1 ping statistics ---
2 packets transmitted, 0 received, 100% packet loss, time 1000ms

jill:/home/ml # ping 192.168.123.1
PING 192.168.123.1 (192.168.123.1) 56(84) bytes of data.
^C
--- 192.168.123.1 ping statistics ---
6 packets transmitted, 0 received, 100% packet loss, time 4999ms

jill:/home/ml # 

-- 
MfG,

Michael Leun


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

* Re: [PATCH, resubmit] ax88179_178a: ASIX AX88179_178A USB 3.0/2.0 to gigabit ethernet adapter driver
  2013-01-28 13:36     ` Freddy
@ 2013-01-28 14:24       ` Bjørn Mork
  2013-01-28 16:42       ` Michael Leun
  1 sibling, 0 replies; 27+ messages in thread
From: Bjørn Mork @ 2013-01-28 14:24 UTC (permalink / raw)
  To: Freddy
  Cc: 'Michael Leun',
	netdev, linux-usb, linux-kernel, louis, davem, Support

"Freddy" <freddy@asix.com.tw> writes:

> Bjørn, I am trying to reproduce the issue mentioned by Michael and I
> have a question about submitting this driver.
>
> Should I merge this driver into asix_devices.c and asix_common.c even
> through the usb command, tx_fixup, and rx_fixup functions are totally
> different?

This is only my personal opinion and does not count as more than that,
but I would have tried to identify as many common parts as possible in
these drivers and reuse as much code as possible instead of creating
slightly different copies.  That does not mean that there shouldn't be
anything different.  If the framing is completely different, then it
does of course not make any sense to share tx_fixup and rx_fixup.  But
my impression from looking briefly on these drivers is that a lof of the
code is very similar. I could of course be wrong...

Please note that modifying asix_common.c in this process is perfectly OK
if that is necessary.  The only requirement is that you don't break
anything that used to work.


Bjørn

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

* RE: [PATCH, resubmit] ax88179_178a: ASIX AX88179_178A USB 3.0/2.0 to gigabit ethernet adapter driver
  2013-01-28  9:07   ` Bjørn Mork
@ 2013-01-28 13:36     ` Freddy
  2013-01-28 14:24       ` Bjørn Mork
  2013-01-28 16:42       ` Michael Leun
  0 siblings, 2 replies; 27+ messages in thread
From: Freddy @ 2013-01-28 13:36 UTC (permalink / raw)
  To: 'Bjørn Mork', 'Michael Leun'
  Cc: netdev, linux-usb, linux-kernel, louis, davem, Support

> I would vote to not accept that driver for mainline as long as this 
> issues are not fixed.


Michael, could you give me more information about how do you test this driver? 
I have tried to reproduce the issue by using "ifconfig ethX mtu 1500", but I didn't confront the same issue.
Thank you in advance for your help.


> The vendor should not be able to claim "hooray, hooray, great device, 
> we even have an driver in linux main line" when it is actually such an 
> useless crap.

> Well, that is fortunately not how these things work. The main goal is getting the devices supported in the kernel. Bugs can be fixed.  If a vendor can get any positive gain out of having a driver in mainline, then that is good for everyone, isn't it?  Of course, we can all agree that the > > > effect of a *working* driver is more positive than a non-working driver...


> For now, the main focus should be fixing the issues which has been noted during review.  Your testing feedback is of course very useful, but you probably need to back them up with actual code change proposals if they are going to be dealt with at this stage.

> Of course I'm offering to help with any information or testing, but 
> unfortunately I do not have the knowhow to fix anything myself.

> I believe this is where you are totally wrong.  You obviuously have the ability to create a few simple test cases for yourself and see if the driver behaves as you expect.  That is very useful.

> And you have a device.  That is also useful.

> Now, the driver source code is available.  And there is another Asix driver in the kernel which already has been cleaned up and can be used as an example. And maybe even partly used for the new devices as well, if the code is duplicated?  I have not looked at this in detail, but I     > suspect that much of the problem with the ax88179_178a driver is that it has completely ignored all the work that has gone into the asix driver after it was mainlined.  I find it unlikely that there is no reusable code in the asix_devices.c, asix_common.c and ax88172a.c files.  Trying to > rewrite ax88179_178a to share as much code as possible seems like the best way to clean it up and fix bugs.

Bjørn, I am trying to reproduce the issue mentioned by Michael and I have a question about submitting this driver.
Should I merge this driver into asix_devices.c and asix_common.c even through the usb command, tx_fixup, and rx_fixup functions are totally different?
Thank you in advance for your reply.

Freddy



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

* Re: [PATCH, resubmit] ax88179_178a: ASIX AX88179_178A USB 3.0/2.0 to gigabit ethernet adapter driver
  2013-01-26 23:13 ` Michael Leun
@ 2013-01-28  9:07   ` Bjørn Mork
  2013-01-28 13:36     ` Freddy
  0 siblings, 1 reply; 27+ messages in thread
From: Bjørn Mork @ 2013-01-28  9:07 UTC (permalink / raw)
  To: Michael Leun
  Cc: Freddy Xin, netdev, linux-usb, linux-kernel, louis, davem, Support

Hello Michael,

Michael Leun <lkml20130126@newton.leun.net> writes:

> I would vote to not accept that driver for mainline as long as this
> issues are not fixed. 
>
> The vendor should not be able to claim "hooray, hooray, great device,
> we even have an driver in linux main line" when it is actually such an
> useless crap.

Well, that is fortunately not how these things work. The main goal is
getting the devices supported in the kernel. Bugs can be fixed.  If a
vendor can get any positive gain out of having a driver in mainline,
then that is good for everyone, isn't it?  Of course, we can all agree
that the effect of a *working* driver is more positive than a
non-working driver...

For now, the main focus should be fixing the issues which has been noted
during review.  Your testing feedback is of course very useful, but you
probably need to back them up with actual code change proposals if they
are going to be dealt with at this stage.

> Of course I'm offering to help with any information or testing, but
> unfortunately I do not have the knowhow to fix anything myself. 

I believe this is where you are totally wrong.  You obviuously have the
ability to create a few simple test cases for yourself and see if the
driver behaves as you expect.  That is very useful.

And you have a device.  That is also useful.

Now, the driver source code is available.  And there is another Asix
driver in the kernel which already has been cleaned up and can be used
as an example. And maybe even partly used for the new devices as well,
if the code is duplicated?  I have not looked at this in detail, but I
suspect that much of the problem with the ax88179_178a driver is that it
has completely ignored all the work that has gone into the asix driver
after it was mainlined.  I find it unlikely that there is no reusable
code in the asix_devices.c, asix_common.c and ax88172a.c files.  Trying
to rewrite ax88179_178a to share as much code as possible seems like the
best way to clean it up and fix bugs.

You have documents like
http://www.kernel.org/doc/Documentation/SubmittingPatches helping a lot
with cleanup jobs like this, because it is mostly about just following
that recipe.  If you do, then the code will be reviewable by the
community because the size will be reduced and the style readable.  And
the driver might even work, as the cleanup will quite possibly reveal a
number of bugs...

Please consider this option.  Reading and fixing up C code is not very
difficult. Getting this driver into the kernel in a good shape is mostly
just monkey work.  But some monkey has to do it :-)


Bjørn

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

* Re: [PATCH, resubmit] ax88179_178a: ASIX AX88179_178A USB 3.0/2.0 to gigabit ethernet adapter driver
  2013-01-23  2:32 Freddy Xin
  2013-01-23  4:15 ` David Miller
@ 2013-01-26 23:13 ` Michael Leun
  2013-01-28  9:07   ` Bjørn Mork
  1 sibling, 1 reply; 27+ messages in thread
From: Michael Leun @ 2013-01-26 23:13 UTC (permalink / raw)
  To: Freddy Xin; +Cc: netdev, linux-usb, linux-kernel, louis, davem, Support

On Wed, 23 Jan 2013 10:32:17 +0800
Freddy Xin <freddy@asix.com.tw> wrote:

> This is a resubmission.
> Fixed coding style errors.
[...]
> This patch adds a driver for ASIX's AX88179 family of USB 3.0/2.0
> to gigabit ethernet adapters. It's based on the AX88xxx driver but

I hope, there is some error/mistake on my side, otherwise I fear I
would have to say I find this driver to be total crap.

I tried it with an Digitus DN-3023 identifying itself as idVendor=0b95,
idProduct=1790 (some internet source claims it to be actually based on
asix reference design).

If I plug the device in the interface comes up with an mtu of 1488 -
this would be an very bad joke for any ethernet compliant device, even
more for an gigabit device where you clearly want to see at least a bit
of jumbo frames.

If I set the mtu to 1500 (the driver does not even complain when I set
the mtu to e.g. 2^31-1) and start to send bigger packets in some point
of time the device stops to work - means, after that you cannot even
send smaller packets, after some time you might get an kernel warning
"NETDEV WATCHDOG: eth3 (ax88179_178a): transmit queue 0 timed out" and
you have to unplug / replug the device to get it working again.

Note: With the W$ driver and exactly the same hardware you get at least
jumbo frames up to some 4000+X bytes - not great, but better than
nothing.

I would vote to not accept that driver for mainline as long as this
issues are not fixed. 

The vendor should not be able to claim "hooray, hooray, great device,
we even have an driver in linux main line" when it is actually such an
useless crap.

Of course I'm offering to help with any information or testing, but
unfortunately I do not have the knowhow to fix anything myself. 

-- 
MfG,

Michael Leun


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

* Re: [PATCH, resubmit] ax88179_178a: ASIX AX88179_178A USB 3.0/2.0 to gigabit ethernet adapter driver
  2013-01-23  2:32 Freddy Xin
@ 2013-01-23  4:15 ` David Miller
  2013-01-26 23:13 ` Michael Leun
  1 sibling, 0 replies; 27+ messages in thread
From: David Miller @ 2013-01-23  4:15 UTC (permalink / raw)
  To: freddy; +Cc: netdev, linux-usb, linux-kernel, louis

From: Freddy Xin <freddy@asix.com.tw>
Date: Wed, 23 Jan 2013 10:32:17 +0800

> Fixed coding style errors.

There are still many style issues remaining, you have a lot more
work to do, for example:

> +	return fn(dev, cmd, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
> +	       value, index, data, size);
> +

This empty line is unnecessary, get rid of it.

Also this function call is not indented properly.  The first character
on the second line of the fn() call should line up with the first
column after the openning parenthesis of the first line of the call.

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

* [PATCH, resubmit] ax88179_178a: ASIX AX88179_178A USB 3.0/2.0 to gigabit ethernet adapter driver
@ 2013-01-23  2:32 Freddy Xin
  2013-01-23  4:15 ` David Miller
  2013-01-26 23:13 ` Michael Leun
  0 siblings, 2 replies; 27+ messages in thread
From: Freddy Xin @ 2013-01-23  2:32 UTC (permalink / raw)
  To: netdev; +Cc: linux-usb, linux-kernel, louis, davem, Freddy Xin

From: Freddy Xin <freddy@asix.com.tw>

This is a resubmission.
Fixed coding style errors.
The _nopm version usb functions were added to access register in suspend and resume functions.
Serveral variables allocted dynamically were removed and replaced by stack variables.
ax88179_get_eeprom were modified from asix_get_eeprom in asix_common.

This patch adds a driver for ASIX's AX88179 family of USB 3.0/2.0
to gigabit ethernet adapters. It's based on the AX88xxx driver but
the usb commands used to access registers for AX88179 are completely different.
This driver had been verified on x86 system with AX88179/AX88178A and
Sitcomm LN-032 USB dongles.

Signed-off-by: Freddy Xin <freddy@asix.com.tw>
---
 drivers/net/usb/Kconfig        |   18 +
 drivers/net/usb/Makefile       |    1 +
 drivers/net/usb/ax88179_178a.c | 1361 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 1380 insertions(+)
 create mode 100644 drivers/net/usb/ax88179_178a.c

diff --git a/drivers/net/usb/Kconfig b/drivers/net/usb/Kconfig
index ef97621..75af401 100644
--- a/drivers/net/usb/Kconfig
+++ b/drivers/net/usb/Kconfig
@@ -158,6 +158,24 @@ config USB_NET_AX8817X
 	  This driver creates an interface named "ethX", where X depends on
 	  what other networking devices you have in use.
 
+config USB_NET_AX88179_178A
+	tristate "ASIX AX88179/178A USB 3.0/2.0 to Gigabit Ethernet"
+	depends on USB_USBNET
+	select CRC32
+	select PHYLIB
+	default y
+	help
+	  This option adds support for ASIX AX88179 based USB 3.0/2.0
+	  to Gigabit Ethernet adapters.
+
+ 	  This driver should work with at least the following devices:
+	    * ASIX AX88179
+	    * ASIX AX88178A
+	    * Sitcomm LN-032
+
+	  This driver creates an interface named "ethX", where X depends on
+	  what other networking devices you have in use.
+
 config USB_NET_CDCETHER
 	tristate "CDC Ethernet support (smart devices such as cable modems)"
 	depends on USB_USBNET
diff --git a/drivers/net/usb/Makefile b/drivers/net/usb/Makefile
index 4786913..119b06c 100644
--- a/drivers/net/usb/Makefile
+++ b/drivers/net/usb/Makefile
@@ -9,6 +9,7 @@ obj-$(CONFIG_USB_RTL8150)	+= rtl8150.o
 obj-$(CONFIG_USB_HSO)		+= hso.o
 obj-$(CONFIG_USB_NET_AX8817X)	+= asix.o
 asix-y := asix_devices.o asix_common.o ax88172a.o
+obj-$(CONFIG_USB_NET_AX88179_178A)      += ax88179_178a.o
 obj-$(CONFIG_USB_NET_CDCETHER)	+= cdc_ether.o
 obj-$(CONFIG_USB_NET_CDC_EEM)	+= cdc_eem.o
 obj-$(CONFIG_USB_NET_DM9601)	+= dm9601.o
diff --git a/drivers/net/usb/ax88179_178a.c b/drivers/net/usb/ax88179_178a.c
new file mode 100644
index 0000000..95d79bd
--- /dev/null
+++ b/drivers/net/usb/ax88179_178a.c
@@ -0,0 +1,1361 @@
+/*
+ * ASIX AX88179/178A USB 3.0/2.0 to Gigabit Ethernet Devices
+ *
+ * Copyright (C) 20011-2012 ASIX
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/etherdevice.h>
+#include <linux/mii.h>
+#include <linux/usb.h>
+#include <linux/crc32.h>
+#include <linux/usb/usbnet.h>
+
+#define AX88179_PHY_ID				0x03
+#define AX_EEPROM_LEN				0x100
+#define AX88179_EEPROM_MAGIC			0x17900b95
+#define AX_MCAST_FLTSIZE			8
+#define AX_MAX_MCAST				64
+#define AX_INT_PPLS_LINK			BIT(0)
+#define AX_RXHDR_L4_TYPE_UDP			1
+#define AX_RXHDR_L4_TYPE_TCP			4
+#define AX_ACCESS_MAC				0x01
+#define AX_ACCESS_PHY				0x02
+#define AX_ACCESS_EEPROM			0x04
+#define AX_ACCESS_EFUS				0x05
+#define AX_PAUSE_WATERLVL_HIGH			0x54
+#define AX_PAUSE_WATERLVL_LOW			0x55
+
+#define PHYSICAL_LINK_STATUS			0x02
+	#define	AX_USB_SS		0x04
+	#define	AX_USB_HS		0x02
+
+#define GENERAL_STATUS				0x03
+/* Check AX88179 version. UA1:Bit2 = 0,  UA2:Bit2 = 1 */
+	#define	AX_SECLD		0x04
+
+#define AX_SROM_ADDR				0x07
+#define AX_SROM_CMD				0x0a
+	#define EEP_RD			0x04
+	#define EEP_BUSY		0x10
+
+#define AX_SROM_DATA_LOW			0x08
+#define AX_SROM_DATA_HIGH			0x09
+
+#define AX_RX_CTL				0x0b
+	#define AX_RX_CTL_DROPCRCERR	0x0100
+	#define AX_RX_CTL_IPE		0x0200
+	#define AX_RX_CTL_START		0x0080
+	#define AX_RX_CTL_AP		0x0020
+	#define AX_RX_CTL_AM		0x0010
+	#define AX_RX_CTL_AB		0x0008
+	#define AX_RX_CTL_AMALL		0x0002
+	#define AX_RX_CTL_PRO		0x0001
+	#define AX_RX_CTL_STOP		0x0000
+
+#define AX_NODE_ID				0x10
+#define AX_MULFLTARY				0x16
+
+#define AX_MEDIUM_STATUS_MODE			0x22
+	#define AX_MEDIUM_GIGAMODE	0x01
+	#define AX_MEDIUM_FULL_DUPLEX	0x02
+	#define AX_MEDIUM_ALWAYS_ONE	0x04
+	#define AX_MEDIUM_RXFLOW_CTRLEN	0x10
+	#define AX_MEDIUM_TXFLOW_CTRLEN	0x20
+	#define AX_MEDIUM_RECEIVE_EN	0x100
+	#define AX_MEDIUM_PS		0x200
+
+#define AX_MONITOR_MOD				0x24
+	#define AX_MONITOR_MODE_RWLC	0x02
+	#define AX_MONITOR_MODE_RWMP	0x04
+	#define AX_MONITOR_MODE_PMEPOL	0x20
+	#define AX_MONITOR_MODE_PMETYPE	0x40
+
+#define AX_GPIO_CTRL				0x25
+	#define AX_GPIO_CTRL_GPIO3EN	0x80
+	#define AX_GPIO_CTRL_GPIO2EN	0x40
+	#define AX_GPIO_CTRL_GPIO1EN	0x20
+
+#define AX_PHYPWR_RSTCTL			0x26
+	#define AX_PHYPWR_RSTCTL_BZ	0x0010
+	#define AX_PHYPWR_RSTCTL_IPRL	0x0020
+	#define AX_PHYPWR_RSTCTL_AT	0x1000
+
+#define AX_RX_BULKIN_QCTRL			0x2e
+#define AX_CLK_SELECT				0x33
+	#define AX_CLK_SELECT_BCS	0x01
+	#define AX_CLK_SELECT_ACS	0x02
+	#define AX_CLK_SELECT_ULR	0x08
+
+#define AX_RXCOE_CTL				0x34
+	#define AX_RXCOE_IP		0x01
+	#define AX_RXCOE_TCP		0x02
+	#define AX_RXCOE_UDP		0x04
+	#define AX_RXCOE_TCPV6		0x20
+	#define AX_RXCOE_UDPV6		0x40
+
+#define AX_TXCOE_CTL				0x35
+	#define AX_TXCOE_IP		0x01
+	#define AX_TXCOE_TCP		0x02
+	#define AX_TXCOE_UDP		0x04
+	#define AX_TXCOE_TCPV6		0x20
+	#define AX_TXCOE_UDPV6		0x40
+
+#define AX_LEDCTRL				0x73
+
+#define GMII_PHY_PHYSR				0x11
+	#define GMII_PHY_PHYSR_SMASK	0xc000
+	#define GMII_PHY_PHYSR_GIGA	0x8000
+	#define GMII_PHY_PHYSR_100	0x4000
+	#define GMII_PHY_PHYSR_FULL	0x2000
+	#define GMII_PHY_PHYSR_LINK	0x400
+
+#define GMII_LED_ACT				0x1a
+	#define GMII_LED_ACTIVE_MASK	0xff8fd
+	#define GMII_LED0_ACTIVE	BIT(4)
+	#define GMII_LED1_ACTIVE	BIT(5)
+	#define GMII_LED2_ACTIVE	BIT(6)
+
+#define GMII_LED_LINK				0x1c
+	#define GMII_LED_LINK_MASK	0xf888
+	#define GMII_LED0_LINK_10	BIT(0)
+	#define GMII_LED0_LINK_100	BIT(1)
+	#define GMII_LED0_LINK_1000	BIT(2)
+	#define GMII_LED1_LINK_10	BIT(4)
+	#define GMII_LED1_LINK_100	BIT(5)
+	#define GMII_LED1_LINK_1000	BIT(6)
+	#define GMII_LED2_LINK_10	BIT(8)
+	#define GMII_LED2_LINK_100	BIT(9)
+	#define GMII_LED2_LINK_1000	BIT(10)
+	#define	LED0_ACTIVE		BIT(0)
+	#define	LED0_LINK_10		BIT(1)
+	#define	LED0_LINK_100		BIT(2)
+	#define	LED0_LINK_1000		BIT(3)
+	#define	LED0_FD			BIT(4)
+	#define LED0_USB3_MASK		0x001f
+	#define	LED1_ACTIVE		BIT(5)
+	#define	LED1_LINK_10		BIT(6)
+	#define	LED1_LINK_100		BIT(7)
+	#define	LED1_LINK_1000		BIT(8)
+	#define	LED1_FD			BIT(9)
+	#define LED1_USB3_MASK		0x03e0
+	#define	LED2_ACTIVE		BIT(10)
+	#define	LED2_LINK_1000		BIT(13)
+	#define	LED2_LINK_100		BIT(12)
+	#define	LED2_LINK_10		BIT(11)
+	#define	LED2_FD			BIT(14)
+	#define	LED_VALID		BIT(15)
+	#define LED2_USB3_MASK		0x7c00
+
+#define GMII_PHYPAGE				0x1e
+#define GMII_PHY_PAGE_SELECT			0x1f
+	#define GMII_PHY_PGSEL_EXT	0x0007
+	#define GMII_PHY_PGSEL_PAGE0	0X0000
+
+struct ax88179_data {
+	u16 rxctl;
+};
+
+struct ax88179_int_data {
+	u16 res1;
+	u8 link;
+	u16 res2;
+	u8 status;
+	u16 res3;
+} __packed;
+
+struct {unsigned char ctrl, timer_l, timer_h, size, ifg; }
+AX88179_BULKIN_SIZE[] =	{
+	{7, 0xa3, 0,	0x14, 0xff},
+	{7, 0x1e, 5,	0x18, 0xff},
+	{7, 0xae, 7,	0x18, 0xff},
+	{7, 0xcc, 0x4c, 0x18, 8},
+};
+
+struct ax88179_rx_pkt_header {
+	u8	l4_csum_err:1,
+		l3_csum_err:1,
+		l4_type:3,
+		l3_type:2,
+		ce:1;
+
+	u8	vlan_ind:3,
+		rx_ok:1,
+		pri:3,
+		bmc:1;
+
+	u16	len:13,
+		crc:1,
+		mii:1,
+		drop:1;
+} __packed;
+
+static int __ax88179_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,
+			   u16 size, void *data, int in_pm)
+{
+	int ret;
+	int (*fn)(struct usbnet *, u8, u8, u16, u16, void *, u16);
+
+	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 (ret != size && ret >= 0)
+		return -EINVAL;
+
+	return ret;
+}
+
+static int __ax88179_write_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,
+			    u16 size, void *data, int in_pm)
+{
+	int (*fn)(struct usbnet *, u8, u8, u16, u16, const void *, u16);
+
+	if (!in_pm)
+		fn = usbnet_write_cmd;
+	else
+		fn = usbnet_write_cmd_nopm;
+
+	return fn(dev, cmd, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+	       value, index, data, size);
+
+}
+
+static void ax88179_write_cmd_async(struct usbnet *dev, u8 cmd, u16 value,
+				 u16 index, u16 size, void *data)
+{
+	usbnet_write_cmd_async(dev, cmd, USB_DIR_OUT | USB_TYPE_VENDOR |
+			    USB_RECIP_DEVICE, value, index, data, size);
+}
+
+static int ax88179_read_cmd_nopm(struct usbnet *dev, u8 cmd, u16 value,
+			      u16 index, u16 size, void *data)
+{
+	return __ax88179_read_cmd(dev, cmd, value, index, size, data, 1);
+}
+
+static int ax88179_write_cmd_nopm(struct usbnet *dev, u8 cmd, u16 value,
+			       u16 index, u16 size, void *data)
+{
+	return __ax88179_write_cmd(dev, cmd, value, index, size, data, 1);
+}
+
+static int ax88179_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,
+			 u16 size, void *data)
+{
+	return __ax88179_read_cmd(dev, cmd, value, index, size, data, 0);
+}
+
+static int ax88179_write_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,
+			  u16 size, void *data)
+{
+	return __ax88179_write_cmd(dev, cmd, value, index, size, data, 0);
+}
+
+static void ax88179_status(struct usbnet *dev, struct urb *urb)
+{
+	struct ax88179_int_data *event;
+	int link;
+
+	if (urb->actual_length < 8)
+		return;
+
+	event = urb->transfer_buffer;
+	link = event->link & AX_INT_PPLS_LINK;
+	if (netif_carrier_ok(dev->net) != link) {
+		if (link)
+			usbnet_defer_kevent(dev, EVENT_LINK_RESET);
+		else
+			netif_carrier_off(dev->net);
+		netdev_info(dev->net, "ax88179 - Link status is: %d\n", link);
+	}
+}
+
+static int ax88179_mdio_read(struct net_device *netdev, int phy_id, int loc)
+{
+	struct usbnet *dev = netdev_priv(netdev);
+	__u16 res;
+
+	ax88179_read_cmd(dev, AX_ACCESS_PHY, phy_id, (__u16)loc, 2, &res);
+
+	return le16_to_cpu(res & 0xffff);
+}
+
+static void ax88179_mdio_write(struct net_device *netdev, int phy_id, int loc,
+			    int val)
+{
+	struct usbnet *dev = netdev_priv(netdev);
+	__le16 res;
+
+	res = cpu_to_le16(val);
+	ax88179_write_cmd(dev, AX_ACCESS_PHY, phy_id, (__u16)loc, 2, &res);
+}
+
+static int ax88179_suspend(struct usb_interface *intf, pm_message_t message)
+{
+	struct usbnet *dev = usb_get_intfdata(intf);
+	u16 tmp16;
+
+	usbnet_suspend(intf, message);
+
+	/* Disable RX path */
+	ax88179_read_cmd_nopm(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,
+			   2, 2, &tmp16);
+	tmp16 &= ~AX_MEDIUM_RECEIVE_EN;
+	ax88179_write_cmd_nopm(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,
+			    2, 2, &tmp16);
+
+	/* Force bulk-in zero length */
+	ax88179_read_cmd_nopm(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL,
+			   2, 2, &tmp16);
+
+	tmp16 |= AX_PHYPWR_RSTCTL_BZ | AX_PHYPWR_RSTCTL_IPRL;
+	ax88179_write_cmd_nopm(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL,
+			    2, 2, &tmp16);
+
+	/* change clock */
+	tmp16 = 0;
+	ax88179_write_cmd_nopm(dev, AX_ACCESS_MAC, AX_CLK_SELECT, 1, 1, &tmp16);
+
+	/* Configure RX control register => stop operation */
+	tmp16 = AX_RX_CTL_STOP;
+	ax88179_write_cmd_nopm(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2, &tmp16);
+
+	return 0;
+}
+
+/* This function is used to enable the autodetach function. */
+/* This function is determined by offset 0x43 of EEPROM */
+static int ax88179_auto_detach(struct usbnet *dev, int in_pm)
+{
+	u16 tmp;
+
+	if (__ax88179_read_cmd(dev, AX_ACCESS_EEPROM, 0x43, 1, 2,
+			    &tmp, in_pm) < 0)
+		return 0;
+
+	if ((tmp == 0xFFFF) || (!(tmp & 0x0100)))
+		return 0;
+
+	/* Enable Auto Detach bit */
+	tmp = 0;
+	__ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_CLK_SELECT,
+			1, 1, &tmp, in_pm);
+	tmp |= AX_CLK_SELECT_ULR;
+	__ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_CLK_SELECT,
+			 1, 1, &tmp, in_pm);
+
+	__ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL,
+			2, 2, &tmp, in_pm);
+	tmp |= AX_PHYPWR_RSTCTL_AT;
+	__ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL,
+			 2, 2, &tmp, in_pm);
+
+	return 0;
+}
+
+static int ax88179_resume(struct usb_interface *intf)
+{
+	struct usbnet *dev = usb_get_intfdata(intf);
+	u16 tmp16;
+
+	netif_carrier_off(dev->net);
+
+	/* Power up ethernet PHY */
+	tmp16 = 0;
+	ax88179_write_cmd_nopm(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL,
+			    2, 2, &tmp16);
+	udelay(1000);
+
+	tmp16 = AX_PHYPWR_RSTCTL_IPRL;
+	ax88179_write_cmd_nopm(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL,
+			    2, 2, &tmp16);
+	msleep(200);
+
+	/* Ethernet PHY Auto Detach*/
+	ax88179_auto_detach(dev, 1);
+
+	/* enable clock */
+	ax88179_read_cmd_nopm(dev, AX_ACCESS_MAC,  AX_CLK_SELECT, 1, 1, &tmp16);
+	tmp16 |= AX_CLK_SELECT_ACS | AX_CLK_SELECT_BCS;
+	ax88179_write_cmd_nopm(dev, AX_ACCESS_MAC, AX_CLK_SELECT, 1, 1, &tmp16);
+	msleep(100);
+
+	/* Configure RX control register => start operation */
+	tmp16 = AX_RX_CTL_DROPCRCERR | AX_RX_CTL_IPE | AX_RX_CTL_START
+		| AX_RX_CTL_AP | AX_RX_CTL_AMALL | AX_RX_CTL_AB;
+	ax88179_write_cmd_nopm(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2, &tmp16);
+
+	return usbnet_resume(intf);
+}
+
+static void
+ax88179_get_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo)
+{
+	struct usbnet *dev = netdev_priv(net);
+	u8 opt;
+
+	if (ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_MONITOR_MOD,
+			  1, 1, &opt) < 0) {
+		wolinfo->supported = 0;
+		wolinfo->wolopts = 0;
+		return;
+	}
+
+	wolinfo->supported = WAKE_PHY | WAKE_MAGIC;
+	wolinfo->wolopts = 0;
+	if (opt & AX_MONITOR_MODE_RWLC)
+		wolinfo->wolopts |= WAKE_PHY;
+	if (opt & AX_MONITOR_MODE_RWMP)
+		wolinfo->wolopts |= WAKE_MAGIC;
+}
+
+static int
+ax88179_set_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo)
+{
+	struct usbnet *dev = netdev_priv(net);
+	u8 opt = 0;
+
+	if (wolinfo->wolopts & WAKE_PHY)
+		opt |= AX_MONITOR_MODE_RWLC;
+	if (wolinfo->wolopts & WAKE_MAGIC)
+		opt |= AX_MONITOR_MODE_RWMP;
+
+	if (ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MONITOR_MOD,
+			   1, 1, &opt) < 0)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int ax88179_get_eeprom_len(struct net_device *net)
+{
+	return AX_EEPROM_LEN;
+}
+
+static int
+ax88179_get_eeprom(struct net_device *net, struct ethtool_eeprom *eeprom,
+		u8 *data)
+{
+	struct usbnet *dev = netdev_priv(net);
+	u16 *eeprom_buff;
+	int first_word, last_word;
+	int i;
+
+	if (eeprom->len == 0)
+		return -EINVAL;
+
+	eeprom->magic = AX88179_EEPROM_MAGIC;
+
+	first_word = eeprom->offset >> 1;
+	last_word = (eeprom->offset + eeprom->len - 1) >> 1;
+
+	eeprom_buff = kmalloc(sizeof(u16) * (last_word - first_word + 1),
+			   GFP_KERNEL);
+	if (!eeprom_buff)
+		return -ENOMEM;
+
+	/* ax88179/178A returns 2 bytes from eeprom on read */
+	for (i = first_word; i <= last_word; i++) {
+		if (ax88179_read_cmd(dev, AX_ACCESS_EEPROM, i, 1, 2,
+				  &(eeprom_buff[i - first_word])) < 0) {
+			kfree(eeprom_buff);
+			return -EIO;
+		}
+	}
+
+	memcpy(data, (u8 *)eeprom_buff + (eeprom->offset & 1), eeprom->len);
+	kfree(eeprom_buff);
+
+	return 0;
+}
+
+static int ax88179_get_settings(struct net_device *net, struct ethtool_cmd *cmd)
+{
+	struct usbnet *dev = netdev_priv(net);
+	return mii_ethtool_gset(&dev->mii, cmd);
+}
+
+static int ax88179_set_settings(struct net_device *net, struct ethtool_cmd *cmd)
+{
+	struct usbnet *dev = netdev_priv(net);
+	return mii_ethtool_sset(&dev->mii, cmd);
+}
+
+
+static int ax88179_ioctl(struct net_device *net, struct ifreq *rq, int cmd)
+{
+	struct usbnet *dev = netdev_priv(net);
+	return generic_mii_ioctl(&dev->mii, if_mii(rq), cmd, NULL);
+}
+
+static struct ethtool_ops ax88179_ethtool_ops = {
+	.get_link		= ethtool_op_get_link,
+	.get_msglevel		= usbnet_get_msglevel,
+	.set_msglevel		= usbnet_set_msglevel,
+	.get_wol		= ax88179_get_wol,
+	.set_wol		= ax88179_set_wol,
+	.get_eeprom_len		= ax88179_get_eeprom_len,
+	.get_eeprom		= ax88179_get_eeprom,
+	.get_settings		= ax88179_get_settings,
+	.set_settings		= ax88179_set_settings,
+	.nway_reset		= usbnet_nway_reset,
+};
+
+static void ax88179_set_multicast(struct net_device *net)
+{
+	struct usbnet *dev = netdev_priv(net);
+	struct ax88179_data *data = (struct ax88179_data *)dev->data;
+	u8 *m_filter = ((u8 *)dev->data) + 12;
+
+	data->rxctl = (AX_RX_CTL_START | AX_RX_CTL_AB | AX_RX_CTL_IPE);
+
+	if (net->flags & IFF_PROMISC) {
+		data->rxctl |= AX_RX_CTL_PRO;
+	} else if (net->flags & IFF_ALLMULTI
+		   || netdev_mc_count(net) > AX_MAX_MCAST) {
+		data->rxctl |= AX_RX_CTL_AMALL;
+	} else if (netdev_mc_empty(net)) {
+		/* just broadcast and directed */
+	} else {
+		/* We use the 20 byte dev->data for our 8 byte filter buffer
+		 * to avoid allocating memory that is tricky to free later
+		 */
+		u32 crc_bits;
+		struct netdev_hw_addr *ha;
+
+		memset(m_filter, 0, AX_MCAST_FLTSIZE);
+
+		netdev_for_each_mc_addr(ha, net) {
+			crc_bits = ether_crc(ETH_ALEN, ha->addr) >> 26;
+			*(m_filter + (crc_bits >> 3)) |= (1 << (crc_bits & 7));
+		}
+
+		ax88179_write_cmd_async(dev, AX_ACCESS_MAC, AX_MULFLTARY,
+				  AX_MCAST_FLTSIZE, AX_MCAST_FLTSIZE, m_filter);
+
+		data->rxctl |= AX_RX_CTL_AM;
+	}
+
+	ax88179_write_cmd_async(dev, AX_ACCESS_MAC, AX_RX_CTL,
+			     2, 2, &data->rxctl);
+}
+
+static int
+ax88179_set_features(struct net_device *net, netdev_features_t features)
+{
+	u8 tmp;
+	struct usbnet *dev = netdev_priv(net);
+	netdev_features_t changed = net->features ^ features;
+
+	if (changed & NETIF_F_TSO)
+		net->features ^= NETIF_F_TSO;
+
+	if (changed & NETIF_F_SG)
+		net->features ^= NETIF_F_SG;
+
+	if (changed & NETIF_F_IP_CSUM) {
+		ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, 1, 1, &tmp);
+		tmp ^= AX_TXCOE_TCP | AX_TXCOE_UDP;
+		ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, 1, 1, &tmp);
+		net->features ^= NETIF_F_IP_CSUM;
+	}
+
+	if (changed & NETIF_F_IPV6_CSUM) {
+		ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, 1, 1, &tmp);
+		tmp ^= AX_TXCOE_TCPV6 | AX_TXCOE_UDPV6;
+		ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, 1, 1, &tmp);
+		net->features ^= NETIF_F_IPV6_CSUM;
+	}
+
+	if (changed & NETIF_F_RXCSUM) {
+		ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_RXCOE_CTL, 1, 1, &tmp);
+		tmp ^= AX_RXCOE_IP | AX_RXCOE_TCP | AX_RXCOE_UDP |
+		       AX_RXCOE_TCPV6 | AX_RXCOE_UDPV6;
+		ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RXCOE_CTL, 1, 1, &tmp);
+		net->features ^= NETIF_F_RXCSUM;
+	}
+
+	return 0;
+}
+
+static int ax88179_set_mac_addr(struct net_device *net, void *p)
+{
+	struct usbnet *dev = netdev_priv(net);
+	struct sockaddr *addr = p;
+
+	if (netif_running(net))
+		return -EBUSY;
+	if (!is_valid_ether_addr(addr->sa_data))
+		return -EADDRNOTAVAIL;
+
+	memcpy(net->dev_addr, addr->sa_data, ETH_ALEN);
+
+	/* Set the MAC address */
+	return ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_NODE_ID, ETH_ALEN,
+			      ETH_ALEN, net->dev_addr);
+}
+
+static const struct net_device_ops ax88179_netdev_ops = {
+	.ndo_open		= usbnet_open,
+	.ndo_stop		= usbnet_stop,
+	.ndo_start_xmit		= usbnet_start_xmit,
+	.ndo_tx_timeout		= usbnet_tx_timeout,
+	.ndo_change_mtu		= usbnet_change_mtu,
+	.ndo_set_mac_address	= ax88179_set_mac_addr,
+	.ndo_validate_addr	= eth_validate_addr,
+	.ndo_do_ioctl		= ax88179_ioctl,
+	.ndo_set_rx_mode	= ax88179_set_multicast,
+	.ndo_set_features	= ax88179_set_features,
+};
+
+static int ax88179_check_eeprom(struct usbnet *dev)
+{
+	u8 i;
+	u8 buf;
+	u8 eeprom[20];
+	u16 csum, delay = HZ / 10;
+	unsigned long jtimeout;
+
+	/* Read EEPROM content */
+	for (i = 0; i < 6; i++) {
+		buf = i;
+		if (ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_SROM_ADDR,
+				   1, 1, &buf) < 0)
+			return -EINVAL;
+
+		buf = EEP_RD;
+		if (ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_SROM_CMD,
+				   1, 1, &buf) < 0)
+			return -EINVAL;
+
+		jtimeout = jiffies + delay;
+		do {
+			ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_SROM_CMD,
+				      1, 1, &buf);
+
+			if (time_after(jiffies, jtimeout))
+				return -EINVAL;
+
+		} while (buf & EEP_BUSY);
+
+		ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_SROM_DATA_LOW,
+			      2, 2, &eeprom[i * 2]);
+
+		if ((i == 0) && (eeprom[0] == 0xFF))
+			return -EINVAL;
+	}
+
+	csum = eeprom[6] + eeprom[7] + eeprom[8] + eeprom[9];
+	csum = (csum >> 8) + (csum & 0xff);
+	if ((csum + eeprom[10]) != 0xff)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int ax88179_check_efuse(struct usbnet *dev, u16 *ledmode)
+{
+	u8	i;
+	u8	efuse[64];
+	u16	csum = 0;
+
+	if (ax88179_read_cmd(dev, AX_ACCESS_EFUS, 0, 64, 64, efuse) < 0)
+		return -EINVAL;
+
+	if (*efuse == 0xFF)
+		return -EINVAL;
+
+	for (i = 0; i < 64; i++)
+		csum = csum + efuse[i];
+
+	while (csum > 255)
+		csum = (csum & 0x00FF) + ((csum >> 8) & 0x00FF);
+
+	if (csum != 0xFF)
+		return -EINVAL;
+
+	*ledmode = (efuse[51] << 8) | efuse[52];
+
+	return 0;
+}
+
+static int ax88179_convert_old_led(struct usbnet *dev, u16 *ledvalue)
+{
+	u16 led;
+
+	/* Loaded the old eFuse LED Mode */
+	if (ax88179_read_cmd(dev, AX_ACCESS_EEPROM, 0x3C, 1, 2, &led) < 0)
+		return -EINVAL;
+
+	led >>= 8;
+
+	switch (led) {
+	case 0xFF:
+		led = LED0_ACTIVE | LED1_LINK_10 | LED1_LINK_100 |
+			LED1_LINK_1000 | LED2_ACTIVE | LED2_LINK_10 |
+			LED2_LINK_100 | LED2_LINK_1000 | LED_VALID;
+		break;
+	case 0xFE:
+		led = LED0_ACTIVE | LED1_LINK_1000 | LED2_LINK_100 | LED_VALID;
+		break;
+	case 0xFD:
+		led = LED0_ACTIVE | LED1_LINK_1000 | LED2_LINK_100 |
+			LED2_LINK_10 | LED_VALID;
+		break;
+	case 0xFC:
+		led = LED0_ACTIVE | LED1_ACTIVE | LED1_LINK_1000 | LED2_ACTIVE |
+				LED2_LINK_100 | LED2_LINK_10 | LED_VALID;
+		break;
+	default:
+		led = LED0_ACTIVE | LED1_LINK_10 | LED1_LINK_100 |
+			LED1_LINK_1000 | LED2_ACTIVE | LED2_LINK_10 |
+			LED2_LINK_100 | LED2_LINK_1000 | LED_VALID;
+		break;
+	}
+
+	*ledvalue = led;
+
+	return 0;
+}
+
+static int ax88179_led_setting(struct usbnet *dev)
+{
+	u8 ledfd, value = 0;
+	u16 tmp, ledact, ledlink, ledvalue = 0, delay = HZ / 10;
+	unsigned long jtimeout;
+
+	/* Check AX88179 version. UA1 or UA2*/
+	ax88179_read_cmd(dev, AX_ACCESS_MAC, GENERAL_STATUS, 1, 1, &value);
+
+	if (!(value & AX_SECLD)) {	/* UA1 */
+		value = AX_GPIO_CTRL_GPIO3EN | AX_GPIO_CTRL_GPIO2EN |
+		    AX_GPIO_CTRL_GPIO1EN;
+		if (ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_GPIO_CTRL,
+				   1, 1, &value) < 0)
+			return -EINVAL;
+	}
+
+	/* Check EEPROM */
+	if (!ax88179_check_eeprom(dev)) {
+		value = 0x42;
+		if (ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_SROM_ADDR,
+				   1, 1, &value) < 0)
+			return -EINVAL;
+
+		value = EEP_RD;
+		if (ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_SROM_CMD,
+				   1, 1, &value) < 0)
+			return -EINVAL;
+
+		jtimeout = jiffies + delay;
+		do {
+			ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_SROM_CMD,
+				      1, 1, &value);
+
+			if (time_after(jiffies, jtimeout))
+				return -EINVAL;
+
+		} while (value & EEP_BUSY);
+
+		ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_SROM_DATA_HIGH,
+			      1, 1, &value);
+		ledvalue = (value << 8);
+
+		ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_SROM_DATA_LOW,
+			      1, 1, &value);
+		ledvalue |= value;
+
+		/* load internal ROM for defaule setting */
+		if ((ledvalue == 0xFFFF) || ((ledvalue & LED_VALID) == 0))
+			ax88179_convert_old_led(dev, &ledvalue);
+
+	} else if (!ax88179_check_efuse(dev, &ledvalue)) {
+		if ((ledvalue == 0xFFFF) || ((ledvalue & LED_VALID) == 0))
+			ax88179_convert_old_led(dev, &ledvalue);
+	} else {
+		ax88179_convert_old_led(dev, &ledvalue);
+	}
+
+	tmp = GMII_PHY_PGSEL_EXT;
+	ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
+		       GMII_PHY_PAGE_SELECT, 2, &tmp);
+
+	tmp = 0x2c;
+	ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
+		       GMII_PHYPAGE, 2, &tmp);
+
+	ax88179_read_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, GMII_LED_ACT,
+		      2, &ledact);
+
+	ax88179_read_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
+		      GMII_LED_LINK, 2, &ledlink);
+
+	ledact &= GMII_LED_ACTIVE_MASK;
+	ledlink &= GMII_LED_LINK_MASK;
+
+	if (ledvalue & LED0_ACTIVE)
+		ledact |= GMII_LED0_ACTIVE;
+
+	if (ledvalue & LED1_ACTIVE)
+		ledact |= GMII_LED1_ACTIVE;
+
+	if (ledvalue & LED2_ACTIVE)
+		ledact |= GMII_LED2_ACTIVE;
+
+	if (ledvalue & LED0_LINK_10)
+		ledlink |= GMII_LED0_LINK_10;
+
+	if (ledvalue & LED1_LINK_10)
+		ledlink |= GMII_LED1_LINK_10;
+
+	if (ledvalue & LED2_LINK_10)
+		ledlink |= GMII_LED2_LINK_10;
+
+	if (ledvalue & LED0_LINK_100)
+		ledlink |= GMII_LED0_LINK_100;
+
+	if (ledvalue & LED1_LINK_100)
+		ledlink |= GMII_LED1_LINK_100;
+
+	if (ledvalue & LED2_LINK_100)
+		ledlink |= GMII_LED2_LINK_100;
+
+	if (ledvalue & LED0_LINK_1000)
+		ledlink |= GMII_LED0_LINK_1000;
+
+	if (ledvalue & LED1_LINK_1000)
+		ledlink |= GMII_LED1_LINK_1000;
+
+	if (ledvalue & LED2_LINK_1000)
+		ledlink |= GMII_LED2_LINK_1000;
+
+	tmp = ledact;
+	ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, GMII_LED_ACT,
+		       2, &tmp);
+
+	tmp = ledlink;
+	ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, GMII_LED_LINK,
+		       2, &tmp);
+
+	tmp = GMII_PHY_PGSEL_PAGE0;
+	ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
+		       GMII_PHY_PAGE_SELECT, 2, &tmp);
+
+	/* LED full duplex setting */
+	ledfd = 0;
+	if (ledvalue & LED0_FD)
+		ledfd |= 0x01;
+	else if ((ledvalue & LED0_USB3_MASK) == 0)
+		ledfd |= 0x02;
+
+	if (ledvalue & LED1_FD)
+		ledfd |= 0x04;
+	else if ((ledvalue & LED1_USB3_MASK) == 0)
+		ledfd |= 0x08;
+
+	if (ledvalue & LED2_FD)
+		ledfd |= 0x10;
+	else if ((ledvalue & LED2_USB3_MASK) == 0)
+		ledfd |= 0x20;
+
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_LEDCTRL, 1, 1, &ledfd);
+
+	return 0;
+}
+
+static int ax88179_bind(struct usbnet *dev, struct usb_interface *intf)
+{
+	u8 buf[5];
+	u16 *tmp16;
+	u8 *tmp;
+	struct ax88179_data *ax179_data = (struct ax88179_data *)dev->data;
+
+	usbnet_get_endpoints(dev, intf);
+
+	tmp16 = (u16 *)buf;
+	tmp = (u8 *)buf;
+
+	memset(ax179_data, 0, sizeof(*ax179_data));
+
+	/* Power up ethernet PHY */
+	*tmp16 = 0;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, tmp16);
+	*tmp16 = AX_PHYPWR_RSTCTL_IPRL;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, tmp16);
+	msleep(200);
+
+	*tmp = AX_CLK_SELECT_ACS | AX_CLK_SELECT_BCS;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_CLK_SELECT, 1, 1, tmp);
+	msleep(100);
+
+	ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_NODE_ID, ETH_ALEN,
+		      ETH_ALEN, dev->net->dev_addr);
+	memcpy(dev->net->perm_addr, dev->net->dev_addr, ETH_ALEN);
+
+	/* RX bulk configuration */
+	memcpy(tmp, &AX88179_BULKIN_SIZE[0], 5);
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_BULKIN_QCTRL, 5, 5, tmp);
+	dev->rx_urb_size = 1024 * 20;
+
+	*tmp = 0x34;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PAUSE_WATERLVL_LOW, 1, 1, tmp);
+
+	*tmp = 0x52;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PAUSE_WATERLVL_HIGH,
+		       1, 1, tmp);
+
+	dev->net->hard_header_len += 12;
+	dev->net->netdev_ops = &ax88179_netdev_ops;
+	dev->net->ethtool_ops = &ax88179_ethtool_ops;
+	dev->net->needed_headroom = 8;
+
+	/* Initialize MII structure */
+	dev->mii.dev = dev->net;
+	dev->mii.mdio_read = ax88179_mdio_read;
+	dev->mii.mdio_write = ax88179_mdio_write;
+	dev->mii.phy_id_mask = 0xff;
+	dev->mii.reg_num_mask = 0xff;
+	dev->mii.phy_id = 0x03;
+	dev->mii.supports_gmii = 1;
+
+	dev->net->features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
+			      NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_TSO;
+
+	dev->net->hw_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
+				 NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_TSO;
+
+	/* Enable checksum offload */
+	*tmp = AX_RXCOE_IP | AX_RXCOE_TCP | AX_RXCOE_UDP |
+	       AX_RXCOE_TCPV6 | AX_RXCOE_UDPV6;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RXCOE_CTL, 1, 1, tmp);
+
+	*tmp = AX_TXCOE_IP | AX_TXCOE_TCP | AX_TXCOE_UDP |
+	       AX_TXCOE_TCPV6 | AX_TXCOE_UDPV6;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, 1, 1, tmp);
+
+	/* Configure RX control register => start operation */
+	*tmp16 = AX_RX_CTL_DROPCRCERR | AX_RX_CTL_IPE | AX_RX_CTL_START |
+		 AX_RX_CTL_AP | AX_RX_CTL_AMALL | AX_RX_CTL_AB;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, 0x0b, 2, 2, tmp16);
+
+	*tmp = AX_MONITOR_MODE_PMETYPE | AX_MONITOR_MODE_PMEPOL |
+	       AX_MONITOR_MODE_RWMP;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MONITOR_MOD, 1, 1, tmp);
+
+	/* Configure default medium type => giga */
+	*tmp16 = AX_MEDIUM_RECEIVE_EN | AX_MEDIUM_TXFLOW_CTRLEN |
+		 AX_MEDIUM_RXFLOW_CTRLEN | AX_MEDIUM_ALWAYS_ONE |
+		 AX_MEDIUM_FULL_DUPLEX | AX_MEDIUM_GIGAMODE;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,
+		       2, 2, tmp16);
+
+	ax88179_led_setting(dev);
+
+	/* Restart autoneg */
+	mii_nway_restart(&dev->mii);
+
+	netif_carrier_off(dev->net);
+
+	return 0;
+}
+
+static void ax88179_unbind(struct usbnet *dev, struct usb_interface *intf)
+{
+	u16 tmp16;
+
+	/* Configure RX control register => stop operation */
+	tmp16 = AX_RX_CTL_STOP;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2, &tmp16);
+
+	tmp16 = 0;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_CLK_SELECT, 1, 1, &tmp16);
+
+	/* Power down ethernet PHY */
+	tmp16 = 0;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, &tmp16);
+}
+
+static void
+ax88179_rx_checksum(struct sk_buff *skb, struct ax88179_rx_pkt_header *pkt_hdr)
+{
+	skb->ip_summed = CHECKSUM_NONE;
+
+	/* checksum error bit is set */
+	if (pkt_hdr->l3_csum_err || pkt_hdr->l4_csum_err)
+		return;
+
+	/* It must be a TCP or UDP packet with a valid checksum */
+	if ((pkt_hdr->l4_type == AX_RXHDR_L4_TYPE_TCP) ||
+	    (pkt_hdr->l4_type == AX_RXHDR_L4_TYPE_UDP))
+		skb->ip_summed = CHECKSUM_UNNECESSARY;
+}
+
+static int ax88179_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
+{
+	struct sk_buff *ax_skb;
+	int pkt_cnt;
+	u32 rx_hdr;
+	u16 hdr_off;
+	struct ax88179_rx_pkt_header *pkt_hdr;
+
+	skb_trim(skb, skb->len - 4);
+	memcpy(&rx_hdr, skb_tail_pointer(skb), sizeof(rx_hdr));
+	le32_to_cpus(&rx_hdr);
+
+	pkt_cnt = (u16)rx_hdr;
+	hdr_off = (u16)(rx_hdr >> 16);
+	pkt_hdr = (struct ax88179_rx_pkt_header *)(skb->data + hdr_off);
+
+	while (pkt_cnt--) {
+
+		u16 pkt_len;
+
+		le32_to_cpus((u32 *)pkt_hdr);
+		pkt_len = pkt_hdr->len;
+
+		/* Check CRC or runt packet */
+		if (pkt_hdr->crc || pkt_hdr->drop) {
+			skb_pull(skb, (pkt_len + 7) & 0xFFF8);
+			pkt_hdr++;
+			continue;
+		}
+
+		if (pkt_cnt == 0) {
+			/* Skip IP alignment psudo header */
+			skb_pull(skb, 2);
+			skb->len = pkt_len;
+			skb_set_tail_pointer(skb, pkt_len);
+			skb->truesize = pkt_len + sizeof(struct sk_buff);
+			ax88179_rx_checksum(skb, pkt_hdr);
+			return 1;
+		}
+
+		ax_skb = skb_clone(skb, GFP_ATOMIC);
+		if (ax_skb) {
+			ax_skb->len = pkt_len;
+			ax_skb->data = skb->data + 2;
+			skb_set_tail_pointer(ax_skb, pkt_len);
+			ax_skb->truesize = pkt_len + sizeof(struct sk_buff);
+			ax88179_rx_checksum(ax_skb, pkt_hdr);
+			usbnet_skb_return(dev, ax_skb);
+		} else {
+			return 0;
+		}
+
+		skb_pull(skb, (pkt_len + 7) & 0xFFF8);
+		pkt_hdr++;
+	}
+
+	return 1;
+}
+
+static struct sk_buff *
+ax88179_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags)
+{
+	u32 tx_hdr1, tx_hdr2;
+	int frame_size = dev->maxpacket;
+	int mss = skb_shinfo(skb)->gso_size;
+	int headroom;
+	int tailroom;
+
+	tx_hdr1 = skb->len;
+	tx_hdr2 = mss;
+	if (((skb->len + 8) % frame_size) == 0)	{
+		tx_hdr2 |= 0x80008000;	/* Enable padding */
+		skb->len += 2;
+	}
+
+	skb_linearize(skb);
+	headroom = skb_headroom(skb);
+	tailroom = skb_tailroom(skb);
+
+	if (!skb_header_cloned(skb) && !skb_cloned(skb) &&
+				(headroom + tailroom) >= 8) {
+		if (headroom < 8) {
+			skb->data = memmove(skb->head + 8, skb->data, skb->len);
+			skb_set_tail_pointer(skb, skb->len);
+		}
+	} else {
+		struct sk_buff *skb2;
+		skb2 = skb_copy_expand(skb, 8, 0, flags);
+		dev_kfree_skb_any(skb);
+		skb = skb2;
+		if (!skb)
+			return NULL;
+	}
+
+	skb_push(skb, 4);
+	cpu_to_le32s(&tx_hdr2);
+	skb_copy_to_linear_data(skb, &tx_hdr2, 4);
+
+	skb_push(skb, 4);
+	cpu_to_le32s(&tx_hdr1);
+	skb_copy_to_linear_data(skb, &tx_hdr1, 4);
+
+	return skb;
+}
+
+static int ax88179_link_reset(struct usbnet *dev)
+{
+	struct ax88179_data *ax179_data = (struct ax88179_data *)dev->data;
+	u8 tmp[5], link_sts;
+	u16 mode, tmp16, delay = HZ / 10;
+	u32 tmp32 = 0x40000000;
+	unsigned long jtimeout;
+
+	jtimeout = jiffies + delay;
+	while (tmp32 & 0x40000000) {
+		mode = 0;
+		ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2, &mode);
+		ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2,
+			       &ax179_data->rxctl);
+
+		/*link up, check the usb device control TX FIFO full or empty*/
+		ax88179_read_cmd(dev, 0x81, 0x8c, 0, 4, &tmp32);
+
+		if (time_after(jiffies, jtimeout))
+			return 0;
+	}
+
+	mode = AX_MEDIUM_RECEIVE_EN | AX_MEDIUM_TXFLOW_CTRLEN |
+	       AX_MEDIUM_RXFLOW_CTRLEN | AX_MEDIUM_ALWAYS_ONE;
+
+	ax88179_read_cmd(dev, AX_ACCESS_MAC, PHYSICAL_LINK_STATUS,
+		      1, 1, &link_sts);
+
+	ax88179_read_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID, GMII_PHY_PHYSR,
+		      2, &tmp16);
+
+	if (!(tmp16 & GMII_PHY_PHYSR_LINK)) {
+		return 0;
+	} else if (GMII_PHY_PHYSR_GIGA == (tmp16 & GMII_PHY_PHYSR_SMASK)) {
+		mode |= AX_MEDIUM_GIGAMODE;
+		if (link_sts & AX_USB_SS)
+			memcpy(tmp, &AX88179_BULKIN_SIZE[0], 5);
+		else if (link_sts & AX_USB_HS)
+			memcpy(tmp, &AX88179_BULKIN_SIZE[1], 5);
+		else
+			memcpy(tmp, &AX88179_BULKIN_SIZE[3], 5);
+	} else if (GMII_PHY_PHYSR_100 == (tmp16 & GMII_PHY_PHYSR_SMASK)) {
+		mode |= AX_MEDIUM_PS;
+		if (link_sts & (AX_USB_SS | AX_USB_HS))
+			memcpy(tmp, &AX88179_BULKIN_SIZE[2], 5);
+		else
+			memcpy(tmp, &AX88179_BULKIN_SIZE[3], 5);
+	} else {
+		memcpy(tmp, &AX88179_BULKIN_SIZE[3], 5);
+	}
+
+	/* RX bulk configuration */
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_BULKIN_QCTRL, 5, 5, tmp);
+
+	if (tmp16 & GMII_PHY_PHYSR_FULL)
+		mode |= AX_MEDIUM_FULL_DUPLEX;
+
+	dev->rx_urb_size = (1024 * (*(tmp + 3)));
+
+	mode |= 0x08;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,
+		       2, 2, &mode);
+
+	netif_carrier_on(dev->net);
+
+	return 0;
+}
+
+static int ax88179_reset(struct usbnet *dev)
+{
+	u8 buf[5];
+	u16 *tmp16;
+	u8 *tmp;
+
+	tmp16 = (u16 *)buf;
+	tmp = (u8 *)buf;
+
+	/* Power up ethernet PHY */
+	*tmp16 = 0;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, tmp16);
+
+	*tmp16 = AX_PHYPWR_RSTCTL_IPRL;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, tmp16);
+	msleep(200);
+
+	*tmp = AX_CLK_SELECT_ACS | AX_CLK_SELECT_BCS;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_CLK_SELECT, 1, 1, tmp);
+	msleep(100);
+
+	/* Ethernet PHY Auto Detach*/
+	ax88179_auto_detach(dev, 0);
+
+	ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_NODE_ID, ETH_ALEN, ETH_ALEN,
+		      dev->net->dev_addr);
+	memcpy(dev->net->perm_addr, dev->net->dev_addr, ETH_ALEN);
+
+	/* RX bulk configuration */
+	memcpy(tmp, &AX88179_BULKIN_SIZE[0], 5);
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_BULKIN_QCTRL, 5, 5, tmp);
+
+	dev->rx_urb_size = 1024 * 20;
+
+	*tmp = 0x34;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PAUSE_WATERLVL_LOW, 1, 1, tmp);
+
+	*tmp = 0x52;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PAUSE_WATERLVL_HIGH,
+		       1, 1, tmp);
+
+	dev->net->features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
+			      NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_TSO;
+
+	dev->net->hw_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
+				 NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_TSO;
+
+	/* Enable checksum offload */
+	*tmp = AX_RXCOE_IP | AX_RXCOE_TCP | AX_RXCOE_UDP |
+	       AX_RXCOE_TCPV6 | AX_RXCOE_UDPV6;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RXCOE_CTL, 1, 1, tmp);
+
+	*tmp = AX_TXCOE_IP | AX_TXCOE_TCP | AX_TXCOE_UDP |
+	       AX_TXCOE_TCPV6 | AX_TXCOE_UDPV6;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, 1, 1, tmp);
+
+	/* Configure RX control register => start operation */
+	*tmp16 = AX_RX_CTL_DROPCRCERR | AX_RX_CTL_IPE | AX_RX_CTL_START |
+		 AX_RX_CTL_AP | AX_RX_CTL_AMALL | AX_RX_CTL_AB;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, 0x0b, 2, 2, tmp16);
+
+	*tmp = AX_MONITOR_MODE_PMETYPE | AX_MONITOR_MODE_PMEPOL |
+	       AX_MONITOR_MODE_RWMP;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MONITOR_MOD, 1, 1, tmp);
+
+	/* Configure default medium type => giga */
+	*tmp16 = AX_MEDIUM_RECEIVE_EN | AX_MEDIUM_TXFLOW_CTRLEN |
+		 AX_MEDIUM_RXFLOW_CTRLEN | AX_MEDIUM_ALWAYS_ONE |
+		 AX_MEDIUM_FULL_DUPLEX | AX_MEDIUM_GIGAMODE;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,
+		       2, 2, tmp16);
+
+	ax88179_led_setting(dev);
+
+	/* Restart autoneg */
+	mii_nway_restart(&dev->mii);
+
+	netif_carrier_off(dev->net);
+
+	return 0;
+}
+
+static int ax88179_stop(struct usbnet *dev)
+{
+	u16 tmp16;
+
+	ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,
+		      2, 2, &tmp16);
+	tmp16 &= ~AX_MEDIUM_RECEIVE_EN;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,
+		       2, 2, &tmp16);
+
+	return 0;
+}
+
+static const struct driver_info ax88179_info = {
+	.description = "ASIX AX88179 USB 3.0 Gigibit Ethernet",
+	.bind = ax88179_bind,
+	.unbind = ax88179_unbind,
+	.status = ax88179_status,
+	.link_reset = ax88179_link_reset,
+	.reset = ax88179_reset,
+	.stop = ax88179_stop,
+	.flags = FLAG_ETHER | FLAG_FRAMING_AX,
+	.rx_fixup = ax88179_rx_fixup,
+	.tx_fixup = ax88179_tx_fixup,
+};
+
+static const struct driver_info ax88178a_info = {
+	.description = "ASIX AX88178A USB 2.0 Gigibit Ethernet",
+	.bind = ax88179_bind,
+	.unbind = ax88179_unbind,
+	.status = ax88179_status,
+	.link_reset = ax88179_link_reset,
+	.reset = ax88179_reset,
+	.stop = ax88179_stop,
+	.flags = FLAG_ETHER | FLAG_FRAMING_AX,
+	.rx_fixup = ax88179_rx_fixup,
+	.tx_fixup = ax88179_tx_fixup,
+};
+
+static const struct driver_info sitecom_info = {
+	.description = "Sitecom USB 3.0 to Gigabit Adapter",
+	.bind = ax88179_bind,
+	.unbind = ax88179_unbind,
+	.status = ax88179_status,
+	.link_reset = ax88179_link_reset,
+	.reset = ax88179_reset,
+	.stop = ax88179_stop,
+	.flags = FLAG_ETHER | FLAG_FRAMING_AX,
+	.rx_fixup = ax88179_rx_fixup,
+	.tx_fixup = ax88179_tx_fixup,
+};
+
+static const struct usb_device_id products[] = {
+{
+	/* ASIX AX88179 10/100/1000 */
+	USB_DEVICE(0x0b95, 0x1790),
+	.driver_info = (unsigned long)&ax88179_info,
+}, {
+	/* ASIX AX88178A 10/100/1000 */
+	USB_DEVICE(0x0b95, 0x178a),
+	.driver_info = (unsigned long)&ax88178a_info,
+}, {
+	/* Sitecom USB 3.0 to Gigabit Adapter */
+	USB_DEVICE(0x0df6, 0x0072),
+	.driver_info = (unsigned long) &sitecom_info,
+},
+	{ },
+};
+MODULE_DEVICE_TABLE(usb, products);
+
+static struct usb_driver asix_driver = {
+	.name =		"ax88179_178a",
+	.id_table =	products,
+	.probe =	usbnet_probe,
+	.suspend =	ax88179_suspend,
+	.resume =	ax88179_resume,
+	.disconnect =	usbnet_disconnect,
+};
+
+static int __init asix_init(void)
+{
+	return usb_register(&asix_driver);
+}
+module_init(asix_init);
+
+static void __exit asix_exit(void)
+{
+	usb_deregister(&asix_driver);
+}
+module_exit(asix_exit);
+
+MODULE_DESCRIPTION("ASIX AX88179/178A based USB 3.0/2.0 Gigabit Ethernet Devices");
+MODULE_LICENSE("GPL");
+
-- 
1.7.9.5


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

* Re: [PATCH, resubmit] ax88179_178a: ASIX AX88179_178A USB 3.0/2.0 to gigabit ethernet adapter driver
  2013-01-18 19:23 ` David Miller
@ 2013-01-18 20:29   ` Alan Stern
  0 siblings, 0 replies; 27+ messages in thread
From: Alan Stern @ 2013-01-18 20:29 UTC (permalink / raw)
  To: David Miller; +Cc: freddy, netdev, linux-usb, linux-kernel, louis

On Fri, 18 Jan 2013, David Miller wrote:

> > +	ret = fn(dev, cmd, USB_DIR_IN | USB_TYPE_VENDOR |
> > +			USB_RECIP_DEVICE, value, index, data, size);
> 
> This is not indented properly.  When a function call takes up
> multiple lines, the text on the second and subsequent lines must
> be left justified to the first column after the openning parenthesis
> of the function call, like this:
> 
> 	function(arg1, arg2,
> 		 arg3, arg4);
> 
> You must use the appropriate combination of TAB and space characters
> to achieve this.  If you are trying to only use TAB characters, you
> are doing it wrong.

Documentation/CodingStyle doesn't mention this.  Does the networking 
stack have its own special requirements, not listed in CodingStyle?

Alan Stern

P.S: The standard I have tried to follow for USB code is to indent
continuation lines by two tab stops more than the first line,
regardless of whether the break occurs inside a function call.  I have
seen other people consistently indent continuation lines by 1/2 tab
stop (i.e., four spaces) beyond the first line.


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

* Re: [PATCH, resubmit] ax88179_178a: ASIX AX88179_178A USB 3.0/2.0 to gigabit ethernet adapter driver
  2013-01-17  9:32 Freddy Xin
@ 2013-01-18 19:23 ` David Miller
  2013-01-18 20:29   ` Alan Stern
  0 siblings, 1 reply; 27+ messages in thread
From: David Miller @ 2013-01-18 19:23 UTC (permalink / raw)
  To: freddy; +Cc: netdev, linux-usb, linux-kernel, louis

From: Freddy Xin <freddy@asix.com.tw>
Date: Thu, 17 Jan 2013 17:32:54 +0800

> +struct ax88179_rx_pkt_header {
> +
> +	u8	l4_csum_err:1,

Get rid of such extraneous empty lines.  They do not add clarity,
rather they just take up space.

> +	ret = fn(dev, cmd, USB_DIR_IN | USB_TYPE_VENDOR |
> +			USB_RECIP_DEVICE, value, index, data, size);

This is not indented properly.  When a function call takes up
multiple lines, the text on the second and subsequent lines must
be left justified to the first column after the openning parenthesis
of the function call, like this:

	function(arg1, arg2,
		 arg3, arg4);

You must use the appropriate combination of TAB and space characters
to achieve this.  If you are trying to only use TAB characters, you
are doing it wrong.

This code has a lot of other similar coding style errors, please
put some effort into fixing them up before you consider resubmitting
this driver.

All of the coding style errors are probably why nobody reviewed your
driver the first time around, there's already enough properly styled
submissions to review.

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

* [PATCH, resubmit] ax88179_178a: ASIX AX88179_178A USB 3.0/2.0 to gigabit ethernet adapter driver
@ 2013-01-17  9:32 Freddy Xin
  2013-01-18 19:23 ` David Miller
  0 siblings, 1 reply; 27+ messages in thread
From: Freddy Xin @ 2013-01-17  9:32 UTC (permalink / raw)
  To: netdev; +Cc: linux-usb, linux-kernel, louis, davem, Freddy Xin


This is a resubmission.
The _nopm version usb functions were added to access register in suspend and resume functions.
Serveral variables allocted dynamically were removed and replaced by stack variables.
ax88179_get_eeprom were modified from asix_get_eeprom in asix_common.

This patch adds a driver for ASIX's AX88179 family of USB 3.0/2.0
to gigabit ethernet adapters. It's based on the AX88xxx driver but
the usb commands used to access registers for AX88179 are completely different.
This driver had been verified on x86 system with AX88179/AX88178A and
Sitcomm LN-032 USB dongles.


Signed-off-by: Freddy Xin <freddy@asix.com.tw>
---
 drivers/net/usb/Kconfig        |   18 +
 drivers/net/usb/Makefile       |    1 +
 drivers/net/usb/ax88179_178a.c | 1424 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 1443 insertions(+)
 create mode 100644 drivers/net/usb/ax88179_178a.c

diff --git a/drivers/net/usb/Kconfig b/drivers/net/usb/Kconfig
index ef97621..75af401 100644
--- a/drivers/net/usb/Kconfig
+++ b/drivers/net/usb/Kconfig
@@ -158,6 +158,24 @@ config USB_NET_AX8817X
 	  This driver creates an interface named "ethX", where X depends on
 	  what other networking devices you have in use.
 
+config USB_NET_AX88179_178A
+	tristate "ASIX AX88179/178A USB 3.0/2.0 to Gigabit Ethernet"
+	depends on USB_USBNET
+	select CRC32
+	select PHYLIB
+	default y
+	help
+	  This option adds support for ASIX AX88179 based USB 3.0/2.0
+	  to Gigabit Ethernet adapters.
+
+ 	  This driver should work with at least the following devices:
+	    * ASIX AX88179
+	    * ASIX AX88178A
+	    * Sitcomm LN-032
+
+	  This driver creates an interface named "ethX", where X depends on
+	  what other networking devices you have in use.
+
 config USB_NET_CDCETHER
 	tristate "CDC Ethernet support (smart devices such as cable modems)"
 	depends on USB_USBNET
diff --git a/drivers/net/usb/Makefile b/drivers/net/usb/Makefile
index 4786913..ce01578 100644
--- a/drivers/net/usb/Makefile
+++ b/drivers/net/usb/Makefile
@@ -9,6 +9,7 @@ obj-$(CONFIG_USB_RTL8150)	+= rtl8150.o
 obj-$(CONFIG_USB_HSO)		+= hso.o
 obj-$(CONFIG_USB_NET_AX8817X)	+= asix.o
 asix-y := asix_devices.o asix_common.o ax88172a.o
+obj-$(CONFIG_USB_NET_AX88179_178A)	+= ax88179_178a.o
 obj-$(CONFIG_USB_NET_CDCETHER)	+= cdc_ether.o
 obj-$(CONFIG_USB_NET_CDC_EEM)	+= cdc_eem.o
 obj-$(CONFIG_USB_NET_DM9601)	+= dm9601.o
diff --git a/drivers/net/usb/ax88179_178a.c b/drivers/net/usb/ax88179_178a.c
new file mode 100644
index 0000000..4aa9f28
--- /dev/null
+++ b/drivers/net/usb/ax88179_178a.c
@@ -0,0 +1,1424 @@
+/*
+ * ASIX AX88179/178A USB 3.0/2.0 to Gigabit Ethernet Devices
+ *
+ * Copyright (C) 20011-2012 ASIX
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/etherdevice.h>
+#include <linux/mii.h>
+#include <linux/usb.h>
+#include <linux/crc32.h>
+#include <linux/usb/usbnet.h>
+
+
+#define AX88179_PHY_ID				0x03
+#define AX_EEPROM_LEN				0x100
+#define AX_MCAST_FILTER_SIZE			8
+#define AX_MAX_MCAST				64
+#define AX_INT_PPLS_LINK			BIT(0)
+#define AX_RXHDR_L4_TYPE_UDP			1
+#define AX_RXHDR_L4_TYPE_TCP			4
+#define AX_ACCESS_MAC				0x01
+#define AX_ACCESS_PHY				0x02
+#define AX_ACCESS_EEPROM			0x04
+#define AX_ACCESS_EFUSE				0x05
+#define AX_PAUSE_WATERLVL_HIGH			0x54
+#define AX_PAUSE_WATERLVL_LOW			0x55
+
+#define PHYSICAL_LINK_STATUS			0x02
+	#define	AX_USB_SS		0x04
+	#define	AX_USB_HS		0x02
+
+#define GENERAL_STATUS				0x03
+/* Check AX88179 version. UA1:Bit2 = 0,  UA2:Bit2 = 1 */
+	#define	AX_SECLD		0x04
+
+#define AX_SROM_ADDR				0x07
+#define AX_SROM_CMD				0x0a
+	#define EEP_RD			0x04
+	#define EEP_BUSY		0x10
+
+
+#define AX_SROM_DATA_LOW			0x08
+#define AX_SROM_DATA_HIGH			0x09
+
+#define AX_RX_CTL				0x0b
+	#define AX_RX_CTL_DROPCRCERR	0x0100
+	#define AX_RX_CTL_IPE		0x0200
+	#define AX_RX_CTL_START		0x0080
+	#define AX_RX_CTL_AP		0x0020
+	#define AX_RX_CTL_AM		0x0010
+	#define AX_RX_CTL_AB		0x0008
+	#define AX_RX_CTL_AMALL		0x0002
+	#define AX_RX_CTL_PRO		0x0001
+	#define AX_RX_CTL_STOP		0x0000
+
+#define AX_NODE_ID				0x10
+#define AX_MULTI_FILTER_ARRY			0x16
+
+#define AX_MEDIUM_STATUS_MODE			0x22
+	#define AX_MEDIUM_GIGAMODE	0x01
+	#define AX_MEDIUM_FULL_DUPLEX	0x02
+	#define AX_MEDIUM_ALWAYS_ONE	0x04
+	#define AX_MEDIUM_RXFLOW_CTRLEN	0x10
+	#define AX_MEDIUM_TXFLOW_CTRLEN	0x20
+	#define AX_MEDIUM_RECEIVE_EN	0x100
+	#define AX_MEDIUM_PS		0x200
+
+#define AX_MONITOR_MODE				0x24
+	#define AX_MONITOR_MODE_RWLC	0x02
+	#define AX_MONITOR_MODE_RWMP	0x04
+	#define AX_MONITOR_MODE_PMEPOL	0x20
+	#define AX_MONITOR_MODE_PMETYPE	0x40
+
+#define AX_GPIO_CTRL				0x25
+	#define AX_GPIO_CTRL_GPIO3EN	0x80
+	#define AX_GPIO_CTRL_GPIO2EN	0x40
+	#define AX_GPIO_CTRL_GPIO1EN	0x20
+
+#define AX_PHYPWR_RSTCTL			0x26
+	#define AX_PHYPWR_RSTCTL_BZ	0x0010
+	#define AX_PHYPWR_RSTCTL_IPRL	0x0020
+	#define AX_PHYPWR_RSTCTL_AT	0x1000
+
+#define AX_RX_BULKIN_QCTRL			0x2e
+#define AX_CLK_SELECT				0x33
+	#define AX_CLK_SELECT_BCS	0x01
+	#define AX_CLK_SELECT_ACS	0x02
+	#define AX_CLK_SELECT_ULR	0x08
+
+#define AX_RXCOE_CTL				0x34
+	#define AX_RXCOE_IP		0x01
+	#define AX_RXCOE_TCP		0x02
+	#define AX_RXCOE_UDP		0x04
+	#define AX_RXCOE_TCPV6		0x20
+	#define AX_RXCOE_UDPV6		0x40
+
+#define AX_TXCOE_CTL				0x35
+	#define AX_TXCOE_IP		0x01
+	#define AX_TXCOE_TCP		0x02
+	#define AX_TXCOE_UDP		0x04
+	#define AX_TXCOE_TCPV6		0x20
+	#define AX_TXCOE_UDPV6		0x40
+
+#define AX_LEDCTRL				0x73
+
+#define GMII_PHY_PHYSR				0x11
+	#define GMII_PHY_PHYSR_SMASK	0xc000
+	#define GMII_PHY_PHYSR_GIGA	0x8000
+	#define GMII_PHY_PHYSR_100	0x4000
+	#define GMII_PHY_PHYSR_FULL	0x2000
+	#define GMII_PHY_PHYSR_LINK	0x400
+
+#define GMII_LED_ACTIVE				0x1a
+	#define GMII_LED_ACTIVE_MASK	0xff8fd
+	#define GMII_LED0_ACTIVE	BIT(4)
+	#define GMII_LED1_ACTIVE	BIT(5)
+	#define GMII_LED2_ACTIVE	BIT(6)
+
+#define GMII_LED_LINK				0x1c
+	#define GMII_LED_LINK_MASK	0xf888
+
+	#define GMII_LED0_LINK_10	BIT(0)
+	#define GMII_LED0_LINK_100	BIT(1)
+	#define GMII_LED0_LINK_1000	BIT(2)
+	#define GMII_LED1_LINK_10	BIT(4)
+	#define GMII_LED1_LINK_100	BIT(5)
+	#define GMII_LED1_LINK_1000	BIT(6)
+	#define GMII_LED2_LINK_10	BIT(8)
+	#define GMII_LED2_LINK_100	BIT(9)
+	#define GMII_LED2_LINK_1000	BIT(10)
+
+	#define	LED0_ACTIVE		BIT(0)
+	#define	LED0_LINK_10		BIT(1)
+	#define	LED0_LINK_100		BIT(2)
+	#define	LED0_LINK_1000		BIT(3)
+	#define	LED0_FD			BIT(4)
+	#define LED0_USB3_MASK		0x001f
+
+	#define	LED1_ACTIVE		BIT(5)
+	#define	LED1_LINK_10		BIT(6)
+	#define	LED1_LINK_100		BIT(7)
+	#define	LED1_LINK_1000		BIT(8)
+	#define	LED1_FD			BIT(9)
+	#define LED1_USB3_MASK		0x03e0
+
+	#define	LED2_ACTIVE		BIT(10)
+	#define	LED2_LINK_1000		BIT(13)
+	#define	LED2_LINK_100		BIT(12)
+	#define	LED2_LINK_10		BIT(11)
+	#define	LED2_FD			BIT(14)
+	#define	LED_VALID		BIT(15)
+	#define LED2_USB3_MASK		0x7c00
+
+#define GMII_PHYPAGE				0x1e
+#define GMII_PHY_PAGE_SELECT			0x1f
+	#define GMII_PHY_PGSEL_EXT	0x0007
+	#define GMII_PHY_PGSEL_PAGE0	0X0000
+
+#define AX88179_EEPROM_MAGIC		0x17900b95
+
+struct ax88179_data {
+	u16 rxctl;
+};
+
+struct ax88179_int_data {
+	u16 res1;
+	u8 link;
+	u16 res2;
+	u8 status;
+	u16 res3;
+} __packed;
+
+struct {unsigned char ctrl, timer_l, timer_h, size, ifg; }
+AX88179_BULKIN_SIZE[] =	{
+	{7, 0xa3, 0,	0x14, 0xff},
+	{7, 0x1e, 5,	0x18, 0xff},
+	{7, 0xae, 7,	0x18, 0xff},
+	{7, 0xcc, 0x4c, 0x18, 8},
+};
+
+struct ax88179_rx_pkt_header {
+
+	u8	l4_csum_err:1,
+		l3_csum_err:1,
+		l4_type:3,
+		l3_type:2,
+		ce:1;
+
+	u8	vlan_ind:3,
+		rx_ok:1,
+		pri:3,
+		bmc:1;
+
+	u16	len:13,
+		crc:1,
+		mii:1,
+		drop:1;
+
+} __packed;
+
+static int
+__ax88179_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,
+			    u16 size, void *data, int in_pm)
+{
+	int ret;
+	int (*fn)(struct usbnet *, u8, u8, u16, u16, void *, u16);
+
+	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 (ret != size && ret >= 0)
+		return -EINVAL;
+
+	return ret;
+}
+
+static int
+__ax88179_write_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,
+			     u16 size, void *data, int in_pm)
+{
+	int (*fn)(struct usbnet *, u8, u8, u16, u16, const void *, u16);
+
+	if (!in_pm)
+		fn = usbnet_write_cmd;
+	else
+		fn = usbnet_write_cmd_nopm;
+
+	return fn(dev, cmd, USB_DIR_OUT | USB_TYPE_VENDOR |
+			USB_RECIP_DEVICE, value, index, data, size);
+
+}
+
+static void
+ax88179_write_cmd_async(struct usbnet *dev, u8 cmd, u16 value, u16 index,
+				    u16 size, void *data)
+{
+	usbnet_write_cmd_async(dev, cmd,
+			       USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,
+			       value, index, data, size);
+}
+
+static int
+ax88179_read_cmd_nopm(struct usbnet *dev, u8 cmd, u16 value, u16 index,
+			    u16 size, void *data)
+{
+	return __ax88179_read_cmd(dev, cmd, value, index, size, data, 1);
+}
+
+static int
+ax88179_write_cmd_nopm(struct usbnet *dev, u8 cmd, u16 value, u16 index,
+			    u16 size, void *data)
+{
+	return __ax88179_write_cmd(dev, cmd, value, index, size, data, 1);
+}
+
+static int
+ax88179_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,
+			    u16 size, void *data)
+{
+	return __ax88179_read_cmd(dev, cmd, value, index, size, data, 0);
+}
+
+static int
+ax88179_write_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,
+			    u16 size, void *data)
+{
+	return __ax88179_write_cmd(dev, cmd, value, index, size, data, 0);
+}
+
+static void ax88179_status(struct usbnet *dev, struct urb *urb)
+{
+	struct ax88179_int_data *event;
+	int link;
+
+	if (urb->actual_length < 8)
+		return;
+
+	event = urb->transfer_buffer;
+	link = event->link & AX_INT_PPLS_LINK;
+	if (netif_carrier_ok(dev->net) != link) {
+		if (link)
+			usbnet_defer_kevent(dev, EVENT_LINK_RESET);
+		else
+			netif_carrier_off(dev->net);
+		netdev_info(dev->net, "ax88179 - Link status is: %d\n", link);
+	}
+}
+
+static int ax88179_mdio_read(struct net_device *netdev, int phy_id, int loc)
+{
+	struct usbnet *dev = netdev_priv(netdev);
+	__u16 res;
+	u16 ret;
+
+	ax88179_read_cmd(dev, AX_ACCESS_PHY, phy_id, (__u16)loc, 2, &res);
+
+	ret = res & 0xffff;
+
+	return le16_to_cpu(ret);
+}
+
+static void
+ax88179_mdio_write(struct net_device *netdev, int phy_id, int loc, int val)
+{
+	struct usbnet *dev = netdev_priv(netdev);
+	__le16 res;
+
+	res = cpu_to_le16(val);
+
+	ax88179_write_cmd(dev, AX_ACCESS_PHY, phy_id, (__u16)loc, 2, &res);
+
+}
+
+static int ax88179_suspend(struct usb_interface *intf, pm_message_t message)
+{
+	struct usbnet *dev = usb_get_intfdata(intf);
+	u16 tmp16;
+
+	usbnet_suspend(intf, message);
+
+	/* Disable RX path */
+	ax88179_read_cmd_nopm(dev, AX_ACCESS_MAC,
+				AX_MEDIUM_STATUS_MODE, 2, 2, &tmp16);
+	tmp16 &= ~AX_MEDIUM_RECEIVE_EN;
+	ax88179_write_cmd_nopm(dev, AX_ACCESS_MAC,
+				AX_MEDIUM_STATUS_MODE, 2, 2, &tmp16);
+
+	/* Force bulk-in zero length */
+	ax88179_read_cmd_nopm(dev, AX_ACCESS_MAC,
+			AX_PHYPWR_RSTCTL, 2, 2, &tmp16);
+
+	tmp16 |= AX_PHYPWR_RSTCTL_BZ | AX_PHYPWR_RSTCTL_IPRL;
+	ax88179_write_cmd_nopm(dev, AX_ACCESS_MAC,
+			AX_PHYPWR_RSTCTL, 2, 2, &tmp16);
+
+	/* change clock */
+	tmp16 = 0;
+	ax88179_write_cmd_nopm(dev, AX_ACCESS_MAC, AX_CLK_SELECT, 1, 1, &tmp16);
+
+	/* Configure RX control register => stop operation */
+	tmp16 = AX_RX_CTL_STOP;
+	ax88179_write_cmd_nopm(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2, &tmp16);
+
+	return 0;
+}
+
+/* This function is used to enable the autodetach function. */
+/* This function is determined by offset 0x43 of EEPROM */
+static int ax88179_auto_detach(struct usbnet *dev, int in_pm)
+{
+	u16 tmp;
+
+	if (__ax88179_read_cmd(dev, AX_ACCESS_EEPROM, 0x43,
+					1, 2, &tmp, in_pm) < 0)
+		return 0;
+
+	if ((tmp == 0xFFFF) || (!(tmp & 0x0100)))
+		return 0;
+
+	/* Enable Auto Detach bit */
+	tmp = 0;
+	__ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_CLK_SELECT,
+						1, 1, &tmp, in_pm);
+	tmp |= AX_CLK_SELECT_ULR;
+	__ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_CLK_SELECT,
+						1, 1, &tmp, in_pm);
+
+	__ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL,
+						2, 2, &tmp, in_pm);
+	tmp |= AX_PHYPWR_RSTCTL_AT;
+	__ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL,
+						2, 2, &tmp, in_pm);
+
+	return 0;
+}
+
+static int ax88179_resume(struct usb_interface *intf)
+{
+	struct usbnet *dev = usb_get_intfdata(intf);
+	u16 tmp16;
+
+	netif_carrier_off(dev->net);
+
+	/* Power up ethernet PHY */
+	tmp16 = 0;
+	ax88179_write_cmd_nopm(dev, AX_ACCESS_MAC,
+			AX_PHYPWR_RSTCTL, 2, 2, &tmp16);
+	udelay(1000);
+
+	tmp16 = AX_PHYPWR_RSTCTL_IPRL;
+	ax88179_write_cmd_nopm(dev, AX_ACCESS_MAC,
+			AX_PHYPWR_RSTCTL, 2, 2, &tmp16);
+	msleep(200);
+
+	/* Ethernet PHY Auto Detach*/
+	ax88179_auto_detach(dev, 1);
+
+	/* enable clock */
+	ax88179_read_cmd_nopm(dev, AX_ACCESS_MAC,  AX_CLK_SELECT, 1, 1, &tmp16);
+	tmp16 |= AX_CLK_SELECT_ACS | AX_CLK_SELECT_BCS;
+	ax88179_write_cmd_nopm(dev, AX_ACCESS_MAC, AX_CLK_SELECT, 1, 1, &tmp16);
+	msleep(100);
+
+	/* Configure RX control register => start operation */
+	tmp16 = AX_RX_CTL_DROPCRCERR | AX_RX_CTL_IPE | AX_RX_CTL_START |
+				AX_RX_CTL_AP | AX_RX_CTL_AMALL | AX_RX_CTL_AB;
+	ax88179_write_cmd_nopm(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2, &tmp16);
+
+	return usbnet_resume(intf);
+}
+
+static void
+ax88179_get_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo)
+{
+	struct usbnet *dev = netdev_priv(net);
+	u8 opt;
+
+	wolinfo->supported = 0;
+	wolinfo->wolopts = 0;
+
+	if (ax88179_read_cmd(dev, AX_ACCESS_MAC,
+				AX_MONITOR_MODE, 1, 1, &opt) < 0)
+		return;
+
+	wolinfo->supported = WAKE_PHY | WAKE_MAGIC;
+
+	if (opt & AX_MONITOR_MODE_RWLC)
+		wolinfo->wolopts |= WAKE_PHY;
+	if (opt & AX_MONITOR_MODE_RWMP)
+		wolinfo->wolopts |= WAKE_MAGIC;
+
+}
+
+static int
+ax88179_set_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo)
+{
+	struct usbnet *dev = netdev_priv(net);
+	u8 opt;
+
+	opt = 0;
+	if (ax88179_read_cmd(dev, AX_ACCESS_MAC,
+				AX_MONITOR_MODE, 1, 1, &opt) < 0)
+		return 0;
+
+	if (wolinfo->wolopts & WAKE_PHY)
+		opt |= AX_MONITOR_MODE_RWLC;
+	else
+		opt &= ~AX_MONITOR_MODE_RWLC;
+
+	if (wolinfo->wolopts & WAKE_MAGIC)
+		opt |= AX_MONITOR_MODE_RWMP;
+	else
+		opt &= ~AX_MONITOR_MODE_RWMP;
+
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MONITOR_MODE, 1, 1, &opt);
+
+	return 0;
+}
+
+static int ax88179_get_eeprom_len(struct net_device *net)
+{
+	return AX_EEPROM_LEN;
+}
+
+int ax88179_get_eeprom(struct net_device *net, struct ethtool_eeprom *eeprom,
+		    u8 *data)
+{
+	struct usbnet *dev = netdev_priv(net);
+	u16 *eeprom_buff;
+	int first_word, last_word;
+	int i;
+
+	if (eeprom->len == 0)
+		return -EINVAL;
+
+	eeprom->magic = AX88179_EEPROM_MAGIC;
+
+	first_word = eeprom->offset >> 1;
+	last_word = (eeprom->offset + eeprom->len - 1) >> 1;
+
+	eeprom_buff = kmalloc(sizeof(u16) * (last_word - first_word + 1),
+			      GFP_KERNEL);
+	if (!eeprom_buff)
+		return -ENOMEM;
+
+	/* ax88179/178A returns 2 bytes from eeprom on read */
+	for (i = first_word; i <= last_word; i++) {
+		if (ax88179_read_cmd(dev, AX_ACCESS_EEPROM,
+			i, 1, 2, &(eeprom_buff[i - first_word])) < 0) {
+			kfree(eeprom_buff);
+			return -EIO;
+		}
+	}
+
+	memcpy(data, (u8 *)eeprom_buff + (eeprom->offset & 1), eeprom->len);
+	kfree(eeprom_buff);
+	return 0;
+}
+
+static int ax88179_get_settings(struct net_device *net, struct ethtool_cmd *cmd)
+{
+	struct usbnet *dev = netdev_priv(net);
+	return mii_ethtool_gset(&dev->mii, cmd);
+}
+
+static int ax88179_set_settings(struct net_device *net, struct ethtool_cmd *cmd)
+{
+	struct usbnet *dev = netdev_priv(net);
+	return mii_ethtool_sset(&dev->mii, cmd);
+}
+
+
+static int ax88179_ioctl(struct net_device *net, struct ifreq *rq, int cmd)
+{
+	struct usbnet *dev = netdev_priv(net);
+	return generic_mii_ioctl(&dev->mii, if_mii(rq), cmd, NULL);
+}
+
+static struct ethtool_ops ax88179_ethtool_ops = {
+	.get_link		= ethtool_op_get_link,
+	.get_msglevel		= usbnet_get_msglevel,
+	.set_msglevel		= usbnet_set_msglevel,
+	.get_wol		= ax88179_get_wol,
+	.set_wol		= ax88179_set_wol,
+	.get_eeprom_len		= ax88179_get_eeprom_len,
+	.get_eeprom		= ax88179_get_eeprom,
+	.get_settings		= ax88179_get_settings,
+	.set_settings		= ax88179_set_settings,
+	.nway_reset		= usbnet_nway_reset,
+};
+
+static void ax88179_set_multicast(struct net_device *net)
+{
+	struct usbnet *dev = netdev_priv(net);
+	struct ax88179_data *data = (struct ax88179_data *)dev->data;
+	u8 *m_filter = ((u8 *)dev->data) + 12;
+	u16 rx_ctl = (AX_RX_CTL_START | AX_RX_CTL_AB | AX_RX_CTL_IPE);
+
+	if (net->flags & IFF_PROMISC) {
+		rx_ctl |= AX_RX_CTL_PRO;
+	} else if (net->flags & IFF_ALLMULTI
+		   || netdev_mc_count(net) > AX_MAX_MCAST) {
+		rx_ctl |= AX_RX_CTL_AMALL;
+	} else if (netdev_mc_empty(net)) {
+		/* just broadcast and directed */
+	} else {
+		/* We use the 20 byte dev->data for our 8 byte filter buffer
+		 * to avoid allocating memory that is tricky to free later
+		 */
+		u32 crc_bits;
+		struct netdev_hw_addr *ha;
+
+		memset(m_filter, 0, AX_MCAST_FILTER_SIZE);
+
+		netdev_for_each_mc_addr(ha, net) {
+			crc_bits = ether_crc(ETH_ALEN, ha->addr) >> 26;
+			*(m_filter + (crc_bits >> 3)) |=
+				1 << (crc_bits & 7);
+		}
+
+		ax88179_write_cmd_async(dev, AX_ACCESS_MAC,
+			AX_MULTI_FILTER_ARRY, AX_MCAST_FILTER_SIZE,
+					AX_MCAST_FILTER_SIZE, m_filter);
+
+		rx_ctl |= AX_RX_CTL_AM;
+	}
+
+	data->rxctl = rx_ctl;
+	ax88179_write_cmd_async(dev, AX_ACCESS_MAC,
+					AX_RX_CTL, 2, 2, &data->rxctl);
+}
+
+static int
+ax88179_set_features(struct net_device *net, netdev_features_t features)
+{
+	u8 tmp;
+	struct usbnet *dev = netdev_priv(net);
+	netdev_features_t changed = net->features ^ features;
+
+	if (changed & NETIF_F_TSO)
+		net->features ^= NETIF_F_TSO;
+
+	if (changed & NETIF_F_SG)
+		net->features ^= NETIF_F_SG;
+
+	if (changed & NETIF_F_IP_CSUM) {
+
+		ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, 1, 1, &tmp);
+
+		tmp ^= AX_TXCOE_TCP | AX_TXCOE_UDP;
+
+		ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, 1, 1, &tmp);
+
+		net->features ^= NETIF_F_IP_CSUM;
+	}
+
+	if (changed & NETIF_F_IPV6_CSUM) {
+		ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, 1, 1, &tmp);
+
+		tmp ^= AX_TXCOE_TCPV6 | AX_TXCOE_UDPV6;
+
+		ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, 1, 1, &tmp);
+
+		net->features ^= NETIF_F_IPV6_CSUM;
+	}
+
+	if (changed & NETIF_F_RXCSUM) {
+		ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_RXCOE_CTL, 1, 1, &tmp);
+
+		tmp ^= AX_RXCOE_IP | AX_RXCOE_TCP | AX_RXCOE_UDP |
+				AX_RXCOE_TCPV6 | AX_RXCOE_UDPV6;
+
+		ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RXCOE_CTL, 1, 1, &tmp);
+
+		net->features ^= NETIF_F_RXCSUM;
+	}
+
+	return 0;
+}
+
+static int ax88179_set_mac_addr(struct net_device *net, void *p)
+{
+	struct usbnet *dev = netdev_priv(net);
+	struct sockaddr *addr = p;
+
+	if (netif_running(net))
+		return -EBUSY;
+	if (!is_valid_ether_addr(addr->sa_data))
+		return -EADDRNOTAVAIL;
+
+	memcpy(net->dev_addr, addr->sa_data, ETH_ALEN);
+
+	/* Set the MAC address */
+	return ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_NODE_ID, ETH_ALEN,
+						ETH_ALEN, net->dev_addr);
+}
+
+static const struct net_device_ops ax88179_netdev_ops = {
+	.ndo_open		= usbnet_open,
+	.ndo_stop		= usbnet_stop,
+	.ndo_start_xmit		= usbnet_start_xmit,
+	.ndo_tx_timeout		= usbnet_tx_timeout,
+	.ndo_change_mtu		= usbnet_change_mtu,
+	.ndo_set_mac_address	= ax88179_set_mac_addr,
+	.ndo_validate_addr	= eth_validate_addr,
+	.ndo_do_ioctl		= ax88179_ioctl,
+	.ndo_set_rx_mode	= ax88179_set_multicast,
+	.ndo_set_features	= ax88179_set_features,
+};
+
+static int ax88179_check_eeprom(struct usbnet *dev)
+{
+	u8 i;
+	u8 buf;
+	u8 eeprom[20];
+	u16 csum, delay = HZ / 10;
+	unsigned long jtimeout;
+
+	/* Read EEPROM content */
+	for (i = 0; i < 6; i++) {
+		buf = i;
+		if (ax88179_write_cmd(dev, AX_ACCESS_MAC,
+					AX_SROM_ADDR, 1, 1, &buf) < 0)
+			return -EINVAL;
+
+		buf = EEP_RD;
+		if (ax88179_write_cmd(dev, AX_ACCESS_MAC,
+					AX_SROM_CMD, 1, 1, &buf) < 0)
+			return -EINVAL;
+
+		jtimeout = jiffies + delay;
+		do {
+			ax88179_read_cmd(dev, AX_ACCESS_MAC,
+						AX_SROM_CMD, 1, 1, &buf);
+
+			if (time_after(jiffies, jtimeout))
+				return -EINVAL;
+
+		} while (buf & EEP_BUSY);
+
+		ax88179_read_cmd(dev, AX_ACCESS_MAC,
+				AX_SROM_DATA_LOW, 2, 2, &eeprom[i * 2]);
+
+		if ((i == 0) && (eeprom[0] == 0xFF))
+			return -EINVAL;
+	}
+
+	csum = eeprom[6] + eeprom[7] + eeprom[8] + eeprom[9];
+	csum = (csum >> 8) + (csum & 0xff);
+
+	if ((csum + eeprom[10]) != 0xff)
+		return -EINVAL;
+
+	return 0;
+}
+
+static int ax88179_check_efuse(struct usbnet *dev, u16 *ledmode)
+{
+	u8	i;
+	u8	efuse[64];
+	u16	csum = 0;
+
+	if (ax88179_read_cmd(dev, AX_ACCESS_EFUSE, 0, 64, 64, efuse) < 0)
+		return -EINVAL;
+
+	if (*efuse == 0xFF)
+		return -EINVAL;
+
+	for (i = 0; i < 64; i++)
+		csum = csum + efuse[i];
+
+	while (csum > 255)
+		csum = (csum & 0x00FF) + ((csum >> 8) & 0x00FF);
+
+	if (csum != 0xFF)
+		return -EINVAL;
+
+	*ledmode = (efuse[51] << 8) | efuse[52];
+
+	return 0;
+
+}
+
+static int ax88179_convert_old_led(struct usbnet *dev, u16 *ledvalue)
+{
+	u16 led;
+
+	/* Loaded the old eFuse LED Mode */
+	if (ax88179_read_cmd(dev, AX_ACCESS_EEPROM, 0x3C, 1, 2, &led) < 0)
+		return -EINVAL;
+
+	led >>= 8;
+
+	switch (led) {
+	case 0xFF:
+		led = LED0_ACTIVE | LED1_LINK_10 | LED1_LINK_100 |
+			LED1_LINK_1000 | LED2_ACTIVE | LED2_LINK_10 |
+			LED2_LINK_100 | LED2_LINK_1000 | LED_VALID;
+		break;
+	case 0xFE:
+		led = LED0_ACTIVE | LED1_LINK_1000 | LED2_LINK_100 | LED_VALID;
+		break;
+	case 0xFD:
+		led = LED0_ACTIVE | LED1_LINK_1000 | LED2_LINK_100 |
+			LED2_LINK_10 | LED_VALID;
+		break;
+	case 0xFC:
+		led = LED0_ACTIVE | LED1_ACTIVE | LED1_LINK_1000 | LED2_ACTIVE |
+				LED2_LINK_100 | LED2_LINK_10 | LED_VALID;
+		break;
+	default:
+		led = LED0_ACTIVE | LED1_LINK_10 | LED1_LINK_100 |
+			LED1_LINK_1000 | LED2_ACTIVE | LED2_LINK_10 |
+			LED2_LINK_100 | LED2_LINK_1000 | LED_VALID;
+		break;
+	}
+
+	*ledvalue = led;
+
+	return 0;
+}
+
+static int ax88179_led_setting(struct usbnet *dev)
+{
+	u8 ledfd, value = 0;
+	u16 tmp, ledact, ledlink, ledvalue = 0, delay = HZ / 10;
+	unsigned long jtimeout;
+
+	/* Check AX88179 version. UA1 or UA2*/
+	ax88179_read_cmd(dev, AX_ACCESS_MAC, GENERAL_STATUS, 1, 1, &value);
+
+	if (!(value & AX_SECLD)) {	/* UA1 */
+		value = AX_GPIO_CTRL_GPIO3EN | AX_GPIO_CTRL_GPIO2EN |
+					AX_GPIO_CTRL_GPIO1EN;
+		if (ax88179_write_cmd(dev, AX_ACCESS_MAC,
+				AX_GPIO_CTRL, 1, 1, &value) < 0)
+			return -EINVAL;
+	}
+
+	/* Check EEPROM */
+	if (!ax88179_check_eeprom(dev)) {
+		value = 0x42;
+		if (ax88179_write_cmd(dev, AX_ACCESS_MAC,
+				AX_SROM_ADDR, 1, 1, &value) < 0)
+			return -EINVAL;
+
+		value = EEP_RD;
+		if (ax88179_write_cmd(dev, AX_ACCESS_MAC,
+				AX_SROM_CMD, 1, 1, &value) < 0)
+			return -EINVAL;
+
+		jtimeout = jiffies + delay;
+		do {
+			ax88179_read_cmd(dev, AX_ACCESS_MAC,
+				AX_SROM_CMD, 1, 1, &value);
+
+			if (time_after(jiffies, jtimeout))
+				return -EINVAL;
+		} while (value & EEP_BUSY);
+
+		ax88179_read_cmd(dev, AX_ACCESS_MAC,
+				AX_SROM_DATA_HIGH, 1, 1, &value);
+		ledvalue = (value << 8);
+		ax88179_read_cmd(dev, AX_ACCESS_MAC,
+				AX_SROM_DATA_LOW, 1, 1, &value);
+		ledvalue |= value;
+
+		/* load internal ROM for defaule setting */
+		if ((ledvalue == 0xFFFF) || ((ledvalue & LED_VALID) == 0))
+			ax88179_convert_old_led(dev, &ledvalue);
+
+	}
+
+	/* Check eFuse */
+	else if (!ax88179_check_efuse(dev, &ledvalue)) {
+		if ((ledvalue == 0xFFFF) || ((ledvalue & LED_VALID) == 0))
+			ax88179_convert_old_led(dev, &ledvalue);
+	} else
+		ax88179_convert_old_led(dev, &ledvalue);
+
+	tmp = GMII_PHY_PGSEL_EXT;
+	ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
+						GMII_PHY_PAGE_SELECT, 2, &tmp);
+
+	tmp = 0x2c;
+	ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
+						GMII_PHYPAGE, 2, &tmp);
+
+	ax88179_read_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
+						GMII_LED_ACTIVE, 2, &ledact);
+
+	ax88179_read_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
+						GMII_LED_LINK, 2, &ledlink);
+
+	ledact &= GMII_LED_ACTIVE_MASK;
+	ledlink &= GMII_LED_LINK_MASK;
+
+
+	if (ledvalue & LED0_ACTIVE)
+		ledact |= GMII_LED0_ACTIVE;
+
+	if (ledvalue & LED1_ACTIVE)
+		ledact |= GMII_LED1_ACTIVE;
+
+	if (ledvalue & LED2_ACTIVE)
+		ledact |= GMII_LED2_ACTIVE;
+
+	if (ledvalue & LED0_LINK_10)
+		ledlink |= GMII_LED0_LINK_10;
+
+	if (ledvalue & LED1_LINK_10)
+		ledlink |= GMII_LED1_LINK_10;
+
+	if (ledvalue & LED2_LINK_10)
+		ledlink |= GMII_LED2_LINK_10;
+
+	if (ledvalue & LED0_LINK_100)
+		ledlink |= GMII_LED0_LINK_100;
+
+	if (ledvalue & LED1_LINK_100)
+		ledlink |= GMII_LED1_LINK_100;
+
+	if (ledvalue & LED2_LINK_100)
+		ledlink |= GMII_LED2_LINK_100;
+
+	if (ledvalue & LED0_LINK_1000)
+		ledlink |= GMII_LED0_LINK_1000;
+
+	if (ledvalue & LED1_LINK_1000)
+		ledlink |= GMII_LED1_LINK_1000;
+
+	if (ledvalue & LED2_LINK_1000)
+		ledlink |= GMII_LED2_LINK_1000;
+
+
+	tmp = ledact;
+	ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
+						GMII_LED_ACTIVE, 2, &tmp);
+
+	tmp = ledlink;
+	ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
+						GMII_LED_LINK, 2, &tmp);
+
+	tmp = GMII_PHY_PGSEL_PAGE0;
+	ax88179_write_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
+						GMII_PHY_PAGE_SELECT, 2, &tmp);
+
+	/* LED full duplex setting */
+	ledfd = 0;
+	if (ledvalue & LED0_FD)
+		ledfd |= 0x01;
+	else if ((ledvalue & LED0_USB3_MASK) == 0)
+		ledfd |= 0x02;
+
+	if (ledvalue & LED1_FD)
+		ledfd |= 0x04;
+	else if ((ledvalue & LED1_USB3_MASK) == 0)
+		ledfd |= 0x08;
+
+	if (ledvalue & LED2_FD)
+		ledfd |= 0x10;
+	else if ((ledvalue & LED2_USB3_MASK) == 0)
+		ledfd |= 0x20;
+
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_LEDCTRL, 1, 1, &ledfd);
+	return 0;
+}
+
+static int ax88179_bind(struct usbnet *dev, struct usb_interface *intf)
+{
+	u8 buf[5];
+	u16 *tmp16;
+	u8 *tmp;
+	struct ax88179_data *ax179_data =
+			(struct ax88179_data *)dev->data;
+
+	usbnet_get_endpoints(dev, intf);
+
+	tmp16 = (u16 *)buf;
+	tmp = (u8 *)buf;
+
+	memset(ax179_data, 0, sizeof(*ax179_data));
+
+	/* Power up ethernet PHY */
+	*tmp16 = 0;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, tmp16);
+	*tmp16 = AX_PHYPWR_RSTCTL_IPRL;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, tmp16);
+	msleep(200);
+
+	*tmp = AX_CLK_SELECT_ACS | AX_CLK_SELECT_BCS;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_CLK_SELECT, 1, 1, tmp);
+	msleep(100);
+
+	ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_NODE_ID, ETH_ALEN, ETH_ALEN,
+						dev->net->dev_addr);
+	memcpy(dev->net->perm_addr, dev->net->dev_addr, ETH_ALEN);
+
+	/* RX bulk configuration */
+	memcpy(tmp, &AX88179_BULKIN_SIZE[0], 5);
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_BULKIN_QCTRL, 5, 5, tmp);
+	dev->rx_urb_size = 1024 * 20;
+
+	*tmp = 0x34;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PAUSE_WATERLVL_LOW, 1, 1, tmp);
+
+	*tmp = 0x52;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC,
+				AX_PAUSE_WATERLVL_HIGH, 1, 1, tmp);
+
+	dev->net->hard_header_len += 12;
+	dev->net->netdev_ops = &ax88179_netdev_ops;
+	dev->net->ethtool_ops = &ax88179_ethtool_ops;
+	dev->net->needed_headroom = 8;
+
+	/* Initialize MII structure */
+	dev->mii.dev = dev->net;
+	dev->mii.mdio_read = ax88179_mdio_read;
+	dev->mii.mdio_write = ax88179_mdio_write;
+	dev->mii.phy_id_mask = 0xff;
+	dev->mii.reg_num_mask = 0xff;
+	dev->mii.phy_id = 0x03;
+	dev->mii.supports_gmii = 1;
+
+	dev->net->features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
+			NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_TSO;
+
+	dev->net->hw_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
+			NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_TSO;
+
+	/* Enable checksum offload */
+	*tmp = AX_RXCOE_IP | AX_RXCOE_TCP | AX_RXCOE_UDP |
+				AX_RXCOE_TCPV6 | AX_RXCOE_UDPV6;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RXCOE_CTL, 1, 1, tmp);
+
+	*tmp = AX_TXCOE_IP | AX_TXCOE_TCP | AX_TXCOE_UDP |
+				AX_TXCOE_TCPV6 | AX_TXCOE_UDPV6;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, 1, 1, tmp);
+
+	/* Configure RX control register => start operation */
+	*tmp16 = AX_RX_CTL_DROPCRCERR | AX_RX_CTL_IPE | AX_RX_CTL_START |
+			AX_RX_CTL_AP | AX_RX_CTL_AMALL | AX_RX_CTL_AB;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, 0x0b, 2, 2, tmp16);
+
+	*tmp = AX_MONITOR_MODE_PMETYPE | AX_MONITOR_MODE_PMEPOL |
+						AX_MONITOR_MODE_RWMP;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MONITOR_MODE, 1, 1, tmp);
+
+	ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_MONITOR_MODE, 1, 1, tmp);
+
+	/* Configure default medium type => giga */
+	*tmp16 = AX_MEDIUM_RECEIVE_EN | AX_MEDIUM_TXFLOW_CTRLEN |
+		 AX_MEDIUM_RXFLOW_CTRLEN | AX_MEDIUM_ALWAYS_ONE |
+		 AX_MEDIUM_FULL_DUPLEX | AX_MEDIUM_GIGAMODE;
+
+	ax88179_write_cmd(dev, AX_ACCESS_MAC,
+				AX_MEDIUM_STATUS_MODE, 2, 2, tmp16);
+
+	ax88179_led_setting(dev);
+
+	/* Restart autoneg */
+	mii_nway_restart(&dev->mii);
+
+	netif_carrier_off(dev->net);
+
+	return 0;
+}
+
+static void ax88179_unbind(struct usbnet *dev, struct usb_interface *intf)
+{
+	u16 tmp16;
+
+	/* Configure RX control register => stop operation */
+	tmp16 = AX_RX_CTL_STOP;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2, &tmp16);
+
+	tmp16 = 0x0;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC,
+			AX_CLK_SELECT, 1, 1, &tmp16);
+
+	/* Power down ethernet PHY */
+	tmp16 = 0;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC,
+			AX_PHYPWR_RSTCTL, 2, 2, &tmp16);
+}
+
+static void
+ax88179_rx_checksum(struct sk_buff *skb, struct ax88179_rx_pkt_header *pkt_hdr)
+{
+	skb->ip_summed = CHECKSUM_NONE;
+
+	/* checksum error bit is set */
+	if (pkt_hdr->l3_csum_err || pkt_hdr->l4_csum_err)
+		return;
+
+	/* It must be a TCP or UDP packet with a valid checksum */
+	if ((pkt_hdr->l4_type == AX_RXHDR_L4_TYPE_TCP) ||
+	    (pkt_hdr->l4_type == AX_RXHDR_L4_TYPE_UDP))
+		skb->ip_summed = CHECKSUM_UNNECESSARY;
+}
+
+static int ax88179_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
+{
+	struct sk_buff *ax_skb;
+	int pkt_cnt;
+	u32 rx_hdr;
+	u16 hdr_off;
+	struct ax88179_rx_pkt_header *pkt_hdr;
+
+	skb_trim(skb, skb->len - 4);
+	memcpy(&rx_hdr, skb_tail_pointer(skb), sizeof(rx_hdr));
+	le32_to_cpus(&rx_hdr);
+
+	pkt_cnt = (u16)rx_hdr;
+	hdr_off = (u16)(rx_hdr >> 16);
+	pkt_hdr = (struct ax88179_rx_pkt_header *)(skb->data + hdr_off);
+
+	while (pkt_cnt--) {
+
+		u16 pkt_len;
+
+		le32_to_cpus((u32 *)pkt_hdr);
+		pkt_len = pkt_hdr->len;
+
+		/* Check CRC or runt packet */
+		if (pkt_hdr->crc || pkt_hdr->drop) {
+			skb_pull(skb, (pkt_len + 7) & 0xFFF8);
+			pkt_hdr++;
+			continue;
+		}
+
+		if (pkt_cnt == 0) {
+			/* Skip IP alignment psudo header */
+			skb_pull(skb, 2);
+
+			skb->len = pkt_len;
+
+			skb_set_tail_pointer(skb, pkt_len);
+
+			skb->truesize = pkt_len + sizeof(struct sk_buff);
+
+			ax88179_rx_checksum(skb, pkt_hdr);
+
+			return 2;
+		}
+
+		ax_skb = skb_clone(skb, GFP_ATOMIC);
+		if (ax_skb) {
+			ax_skb->len = pkt_len;
+			ax_skb->data = skb->data + 2;
+
+			skb_set_tail_pointer(ax_skb, pkt_len);
+
+			ax_skb->truesize = pkt_len + sizeof(struct sk_buff);
+
+			ax88179_rx_checksum(ax_skb, pkt_hdr);
+
+			usbnet_skb_return(dev, ax_skb);
+		} else {
+			return 0;
+		}
+
+		skb_pull(skb, (pkt_len + 7) & 0xFFF8);
+		pkt_hdr++;
+	}
+	return 0;
+}
+
+static struct sk_buff *
+ax88179_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags)
+{
+	u32 tx_hdr1, tx_hdr2;
+	int frame_size = dev->maxpacket;
+	int mss = skb_shinfo(skb)->gso_size;
+	int headroom;
+	int tailroom;
+
+	tx_hdr1 = skb->len;
+	tx_hdr2 = mss;
+	if (((skb->len + 8) % frame_size) == 0)	{
+		tx_hdr2 |= 0x80008000;	/* Enable padding */
+		skb->len += 2;
+	}
+
+	skb_linearize(skb);
+	headroom = skb_headroom(skb);
+	tailroom = skb_tailroom(skb);
+
+	if (!skb_header_cloned(skb) && !skb_cloned(skb) &&
+				(headroom + tailroom) >= 8) {
+		if (headroom < 8) {
+			skb->data = memmove(skb->head + 8, skb->data, skb->len);
+			skb_set_tail_pointer(skb, skb->len);
+		}
+	} else {
+		struct sk_buff *skb2;
+		skb2 = skb_copy_expand(skb, 8, 0, flags);
+		dev_kfree_skb_any(skb);
+		skb = skb2;
+		if (!skb)
+			return NULL;
+	}
+
+	skb_push(skb, 4);
+	cpu_to_le32s(&tx_hdr2);
+
+	skb_copy_to_linear_data(skb, &tx_hdr2, 4);
+
+	skb_push(skb, 4);
+	cpu_to_le32s(&tx_hdr1);
+
+	skb_copy_to_linear_data(skb, &tx_hdr1, 4);
+
+	return skb;
+}
+
+static int ax88179_link_reset(struct usbnet *dev)
+{
+	struct ax88179_data *ax179_data = (struct ax88179_data *)dev->data;
+	u8 tmp[5], link_sts;
+	u16 mode, tmp16, delay = HZ / 10;
+	u32 tmp32 = 0x40000000;
+	unsigned long jtimeout;
+
+	jtimeout = jiffies + delay;
+
+	while (tmp32 & 0x40000000) {
+		mode = 0;
+		ax88179_write_cmd(dev, AX_ACCESS_MAC,
+				AX_RX_CTL, 2, 2, &mode);
+		ax88179_write_cmd(dev, AX_ACCESS_MAC,
+				AX_RX_CTL, 2, 2, &ax179_data->rxctl);
+
+		/*link up, check the usb device control TX FIFO full or empty*/
+		ax88179_read_cmd(dev, 0x81, 0x8c, 0, 4, &tmp32);
+
+		if (time_after(jiffies, jtimeout))
+			return 0;
+	}
+
+	mode = AX_MEDIUM_RECEIVE_EN | AX_MEDIUM_TXFLOW_CTRLEN |
+		   AX_MEDIUM_RXFLOW_CTRLEN | AX_MEDIUM_ALWAYS_ONE;
+
+	ax88179_read_cmd(dev, AX_ACCESS_MAC,
+			PHYSICAL_LINK_STATUS, 1, 1, &link_sts);
+
+	ax88179_read_cmd(dev, AX_ACCESS_PHY, AX88179_PHY_ID,
+					GMII_PHY_PHYSR, 2, &tmp16);
+
+	if (!(tmp16 & GMII_PHY_PHYSR_LINK)) {
+		return 0;
+	} else if (GMII_PHY_PHYSR_GIGA == (tmp16 & GMII_PHY_PHYSR_SMASK)) {
+		mode |= AX_MEDIUM_GIGAMODE;
+		if (link_sts & AX_USB_SS)
+			memcpy(tmp, &AX88179_BULKIN_SIZE[0], 5);
+		else if (link_sts & AX_USB_HS)
+			memcpy(tmp, &AX88179_BULKIN_SIZE[1], 5);
+		else
+			memcpy(tmp, &AX88179_BULKIN_SIZE[3], 5);
+	} else if (GMII_PHY_PHYSR_100 == (tmp16 & GMII_PHY_PHYSR_SMASK)) {
+		mode |= AX_MEDIUM_PS;
+		if (link_sts & (AX_USB_SS | AX_USB_HS))
+			memcpy(tmp, &AX88179_BULKIN_SIZE[2], 5);
+		else
+			memcpy(tmp, &AX88179_BULKIN_SIZE[3], 5);
+	} else {
+		memcpy(tmp, &AX88179_BULKIN_SIZE[3], 5);
+	}
+
+	/* RX bulk configuration */
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_BULKIN_QCTRL, 5, 5, tmp);
+
+	if (tmp16 & GMII_PHY_PHYSR_FULL)
+		mode |= AX_MEDIUM_FULL_DUPLEX;
+
+	dev->rx_urb_size = (1024 * (*(tmp + 3)));
+
+	mode |= 0x08;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC,
+		AX_MEDIUM_STATUS_MODE, 2, 2, &mode);
+
+	netif_carrier_on(dev->net);
+
+	return 0;
+}
+
+static int ax88179_reset(struct usbnet *dev)
+{
+	u8 buf[5];
+	u16 *tmp16;
+	u8 *tmp;
+
+	tmp16 = (u16 *)buf;
+	tmp = (u8 *)buf;
+
+	/* Power up ethernet PHY */
+	*tmp16 = 0;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, tmp16);
+	*tmp16 = AX_PHYPWR_RSTCTL_IPRL;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PHYPWR_RSTCTL, 2, 2, tmp16);
+	msleep(200);
+	*tmp = AX_CLK_SELECT_ACS | AX_CLK_SELECT_BCS;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_CLK_SELECT, 1, 1, tmp);
+	msleep(100);
+
+	/* Ethernet PHY Auto Detach*/
+	ax88179_auto_detach(dev, 0);
+
+	ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_NODE_ID, ETH_ALEN,
+						ETH_ALEN, dev->net->dev_addr);
+	memcpy(dev->net->perm_addr, dev->net->dev_addr, ETH_ALEN);
+
+	/* RX bulk configuration */
+	memcpy(tmp, &AX88179_BULKIN_SIZE[0], 5);
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_BULKIN_QCTRL, 5, 5, tmp);
+
+	dev->rx_urb_size = 1024 * 20;
+
+	*tmp = 0x34;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PAUSE_WATERLVL_LOW, 1, 1, tmp);
+
+	*tmp = 0x52;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC,
+				AX_PAUSE_WATERLVL_HIGH, 1, 1, tmp);
+
+	dev->net->features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
+			NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_TSO;
+
+	dev->net->hw_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM |
+			NETIF_F_RXCSUM | NETIF_F_SG | NETIF_F_TSO;
+
+	/* Enable checksum offload */
+	*tmp = AX_RXCOE_IP | AX_RXCOE_TCP | AX_RXCOE_UDP |
+				AX_RXCOE_TCPV6 | AX_RXCOE_UDPV6;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RXCOE_CTL, 1, 1, tmp);
+
+	*tmp = AX_TXCOE_IP | AX_TXCOE_TCP | AX_TXCOE_UDP |
+				AX_TXCOE_TCPV6 | AX_TXCOE_UDPV6;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, 1, 1, tmp);
+
+	/* Configure RX control register => start operation */
+	*tmp16 = AX_RX_CTL_DROPCRCERR | AX_RX_CTL_IPE | AX_RX_CTL_START |
+			AX_RX_CTL_AP | AX_RX_CTL_AMALL | AX_RX_CTL_AB;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, 0x0b, 2, 2, tmp16);
+
+	*tmp = AX_MONITOR_MODE_PMETYPE | AX_MONITOR_MODE_PMEPOL |
+						AX_MONITOR_MODE_RWMP;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MONITOR_MODE, 1, 1, tmp);
+
+	ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_MONITOR_MODE, 1, 1, tmp);
+
+	/* Configure default medium type => giga */
+	*tmp16 = AX_MEDIUM_RECEIVE_EN | AX_MEDIUM_TXFLOW_CTRLEN |
+		 AX_MEDIUM_RXFLOW_CTRLEN | AX_MEDIUM_ALWAYS_ONE |
+		 AX_MEDIUM_FULL_DUPLEX | AX_MEDIUM_GIGAMODE;
+
+	ax88179_write_cmd(dev, AX_ACCESS_MAC,
+			AX_MEDIUM_STATUS_MODE, 2, 2, tmp16);
+
+	ax88179_led_setting(dev);
+
+	/* Restart autoneg */
+	mii_nway_restart(&dev->mii);
+
+	netif_carrier_off(dev->net);
+
+	return 0;
+}
+
+static int ax88179_stop(struct usbnet *dev)
+{
+	u16 tmp16;
+
+	ax88179_read_cmd(dev, AX_ACCESS_MAC,
+			AX_MEDIUM_STATUS_MODE, 2, 2, &tmp16);
+	tmp16 &= ~AX_MEDIUM_RECEIVE_EN;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC,
+			AX_MEDIUM_STATUS_MODE, 2, 2, &tmp16);
+
+	return 0;
+}
+
+static const struct driver_info ax88179_info = {
+	.description = "ASIX AX88179 USB 3.0 Gigibit Ethernet",
+	.bind = ax88179_bind,
+	.unbind = ax88179_unbind,
+	.status = ax88179_status,
+	.link_reset = ax88179_link_reset,
+	.reset = ax88179_reset,
+	.stop = ax88179_stop,
+	.flags = FLAG_ETHER | FLAG_FRAMING_AX,
+	.rx_fixup = ax88179_rx_fixup,
+	.tx_fixup = ax88179_tx_fixup,
+};
+
+static const struct driver_info ax88178a_info = {
+	.description = "ASIX AX88178A USB 2.0 Gigibit Ethernet",
+	.bind = ax88179_bind,
+	.unbind = ax88179_unbind,
+	.status = ax88179_status,
+	.link_reset = ax88179_link_reset,
+	.reset = ax88179_reset,
+	.stop = ax88179_stop,
+	.flags = FLAG_ETHER | FLAG_FRAMING_AX,
+	.rx_fixup = ax88179_rx_fixup,
+	.tx_fixup = ax88179_tx_fixup,
+};
+
+static const struct driver_info sitecom_info = {
+	.description = "Sitecom USB 3.0 to Gigabit Adapter",
+	.bind = ax88179_bind,
+	.unbind = ax88179_unbind,
+	.status = ax88179_status,
+	.link_reset = ax88179_link_reset,
+	.reset = ax88179_reset,
+	.stop = ax88179_stop,
+	.flags = FLAG_ETHER | FLAG_FRAMING_AX,
+	.rx_fixup = ax88179_rx_fixup,
+	.tx_fixup = ax88179_tx_fixup,
+};
+
+static const struct usb_device_id products[] = {
+{
+	/* ASIX AX88179 10/100/1000 */
+	USB_DEVICE(0x0b95, 0x1790),
+	.driver_info = (unsigned long)&ax88179_info,
+}, {
+	/* ASIX AX88178A 10/100/1000 */
+	USB_DEVICE(0x0b95, 0x178a),
+	.driver_info = (unsigned long)&ax88178a_info,
+}, {
+	/* Sitecom USB 3.0 to Gigabit Adapter */
+	USB_DEVICE(0x0df6, 0x0072),
+	.driver_info = (unsigned long) &sitecom_info,
+},
+	{ },
+};
+MODULE_DEVICE_TABLE(usb, products);
+
+static struct usb_driver asix_driver = {
+	.name =		"ax88179_178a",
+	.id_table =	products,
+	.probe =	usbnet_probe,
+	.suspend =	ax88179_suspend,
+	.resume =	ax88179_resume,
+	.disconnect =	usbnet_disconnect,
+};
+
+static int __init asix_init(void)
+{
+	return usb_register(&asix_driver);
+}
+module_init(asix_init);
+
+static void __exit asix_exit(void)
+{
+	usb_deregister(&asix_driver);
+}
+module_exit(asix_exit);
+
+MODULE_DESCRIPTION("ASIX AX88179/178A based USB 3.0/2.0 Gigabit Ethernet Devices");
+MODULE_LICENSE("GPL");
+
-- 
1.7.9.5


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

end of thread, other threads:[~2013-03-03  6:44 UTC | newest]

Thread overview: 27+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2013-02-07 13:39 [PATCH, resubmit] ax88179_178a: ASIX AX88179_178A USB 3.0/2.0 to gigabit ethernet adapter driver Freddy Xin
2013-02-07 20:02 ` Ben Hutchings
2013-02-07 20:05   ` David Miller
2013-02-08  3:41   ` Freddy
2013-02-08 10:23   ` David Laight
2013-02-08 19:23     ` David Miller
  -- strict thread matches above, loose matches on Subject: below --
2013-03-02 10:41 [PATCH " Freddy Xin
2013-03-03  6:44 ` David Miller
2013-02-27 12:01 Freddy Xin
2013-03-01 21:15 ` David Miller
2013-02-07  4:36 [PATCH, " Freddy Xin
2013-02-07  5:45 ` Stephen Hemminger
2013-02-07  6:45   ` Freddy
2013-02-07  5:46 ` Stephen Hemminger
2013-02-07  6:50   ` Freddy
2013-02-03 14:21 Daniel J Blueman
2013-12-04 12:48 ` Freddy
2013-01-23  2:32 Freddy Xin
2013-01-23  4:15 ` David Miller
2013-01-26 23:13 ` Michael Leun
2013-01-28  9:07   ` Bjørn Mork
2013-01-28 13:36     ` Freddy
2013-01-28 14:24       ` Bjørn Mork
2013-01-28 16:42       ` Michael Leun
2013-01-17  9:32 Freddy Xin
2013-01-18 19:23 ` David Miller
2013-01-18 20:29   ` Alan Stern

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