Skip to contentSkip to conversation

Add support for Aquantia AQtion USB to 5/2.5GbE devices

PATCH In review Igor Russkikh 2 participants 23 messages Nov 26, 2018 – Nov 28, 2018

Subsystems (5)
lore
Share & tools
lore
#0 lore raw

v4 cover Add support for Aquantia AQtion USB to 5/2.5GbE devices

This patchset introduces support for new multigig ethernet to USB dongle, developed jointly by Aquantia (Phy) and ASIX (USB MAC).

The driver has similar structure with other ASIX MAC drivers (AX88179), but with a number of important differences:
- Driver supports both direct Phy and custom firmware interface for Phy programming. This is due to different firmware modules available with this product.
- Driver handles new 2.5G/5G link speed configuration and reporting.
- Device support all speeds from 100M up to 5G.
- Device supports MTU up to 16K.

Device supports various standard networking features, like checksum offloads, vlan tagging/filtering, TSO.

The code of this driver is based on original ASIX sources and was extended by Aquantia for 5G multigig support.

Patchset v2 includes following changes:
- Function variables declarions fixed to reverse xmass tree
- Improve patch layout structure
- Remove unnecessary curly braces in switch/case statements
- Use 'packed' attribute for HW structures only
- Use eth_mac_addr function in set_mac_addr callback
- Remove unnecessary 'memset' calls.
- Read MAC address from EEPROM function has now better name
- Use driver_priv field to store context. It avoids ugly cast.
- Set max_mtu field. Remove check for MTU size
- Rewrite read/write functions. Add helpers for read/write 16/32 bit values
- Use mask and shifts instead of bitfields to support BE platforms.
- Use stack allocated buffer for configuring mcast filters
- Use AUTONEG_ENABLE when go to suspend state
- Pad out wol_cfg field from context structure. Use stack allocated instead
- Remove driver version
- Check field 'duplex' in set_link_ksetting callback as well
- Use already created defines in usb matching macro
- Rename phy_ops struct to phy_cfg
- Use ether_addr_copy for copying mac address
- Add fall-through comment in switch/case for avoid checkpatch warning
- Remove match for CDC ether device
- Add ASIX's HW id-s to match this driver
- Add all HW id-s with which driver can work to blacklist of cdc_ether driver

Patchset v3 includes following changes:
- Use linkmode_copy instead of bitmap_copy
- Remove Direct PHY access code since production HW will not have this mode anymore
- Fix line over 80 symbols and alignments in cdc_ether patch
- Add match for ECM configuration
On start our HW reports both ECM and vendor configs. Linux prefers to use ECM driver and chooses active configuration which is for ecm driver (not for vendor specific).
We need to match this configuration and forcibly switch configuration to vendor specific.

Patchset v4 includes following changes:
- Set gso_max_size.
- Optimize accessing to descriptors
- Use SKB_TRUESIZE macro.

Dmitry Bezrukov (21):
net: usb: aqc111: Driver skeleton for Aquantia AQtion USB to 5GbE
net: usb: aqc111: Add bind and empty unbind callbacks
net: usb: aqc111: Add implementation of read and write commands
net: usb: aqc111: Various callbacks implementation
net: usb: aqc111: Introduce PHY access
net: usb: aqc111: Introduce link management
net: usb: aqc111: Add support for getting and setting of MAC address
net: usb: aqc111: Implement TX data path
net: usb: aqc111: Implement RX data path
net: usb: aqc111: Add checksum offload support
net: usb: aqc111: Add support for changing MTU
net: usb: aqc111: Add support for enable/disable checksum offload
net: usb: aqc111: Add support for TSO
net: usb: aqc111: Implement set_rx_mode callback
net: usb: aqc111: Add support for VLAN_CTAG_TX/RX offload
net: usb: aqc111: Add RX VLAN filtering support
net: usb: aqc111: Initialize ethtool_ops structure
net: usb: aqc111: Implement get/set_link_ksettings callbacks
net: usb: aqc111: Add support for wake on LAN by MAGIC packet
net: usb: aqc111: Add ASIX's HW ids
net: usb: aqc111: Extend cdc_ether blacklist

 drivers/net/usb/Kconfig     |   12 +
 drivers/net/usb/Makefile    |    1 +
 drivers/net/usb/aqc111.c    | 1457 +++++++++++++++++++++++++++++++++++
 drivers/net/usb/aqc111.h    |  232 ++++++
 drivers/net/usb/cdc_ether.c |   26 +
 5 files changed, 1728 insertions(+)
 create mode 100644 drivers/net/usb/aqc111.c
 create mode 100644 drivers/net/usb/aqc111.h
Signature · 1 line
2.17.1
#1 lore raw

PATCH v4 1/21 net: usb: aqc111: Driver skeleton for Aquantia AQtion USB to 5GbE

From: Dmitry Bezrukov <[email protected]>

Initialize usb_driver structure skeleton

---
 drivers/net/usb/Kconfig  | 12 ++++++++++
 drivers/net/usb/Makefile |  1 +
 drivers/net/usb/aqc111.c | 48 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 61 insertions(+)
 create mode 100644 drivers/net/usb/aqc111.c
drivers/net/usb/Kconfig +12 -0
index 418b0904cecb..e5fb8ef2d815 100644
@@ -613,4 +613,16 @@ config USB_NET_CH9200
 	  To compile this driver as a module, choose M here: the
 	  module will be called ch9200.
 
+config USB_NET_AQC111
+	tristate "Aquantia AQtion USB to 5/2.5GbE Controllers support"
+	depends on USB_USBNET
+	select CRC32
+	default y
+	help
+	  This option adds support for Aquantia AQtion USB
+	  Ethernet adapters based on AQC111U/AQC112 chips.
+
+	  This driver should work with at least the following devices:
+	  * Aquantia AQtion USB to 5GbE
+
 endif # USB_NET_DRIVERS
drivers/net/usb/Makefile +1 -0
index 27307a4ab003..99fd12be2111 100644
@@ -40,3 +40,4 @@ obj-$(CONFIG_USB_VL600)		+= lg-vl600.o
 obj-$(CONFIG_USB_NET_QMI_WWAN)	+= qmi_wwan.o
 obj-$(CONFIG_USB_NET_CDC_MBIM)	+= cdc_mbim.o
 obj-$(CONFIG_USB_NET_CH9200)	+= ch9200.o
+obj-$(CONFIG_USB_NET_AQC111)	+= aqc111.o
drivers/net/usb/aqc111.c new file +48 -0

48 lines not on this page · raw message

#2 lore raw

PATCH v4 2/21 net: usb: aqc111: Add bind and empty unbind callbacks

In reply to Igor Russkikh

From: Dmitry Bezrukov <[email protected]>

Initialize net_device_ops structure

---
 drivers/net/usb/aqc111.c | 35 +++++++++++++++++++++++++++++++++++
 1 file changed, 35 insertions(+)
drivers/net/usb/aqc111.c +35 -0
index 96aca929b570..a2628856c580 100644
@@ -14,8 +14,43 @@
 #include <linux/usb/cdc.h>
 #include <linux/usb/usbnet.h>
 
+static const struct net_device_ops aqc111_netdev_ops = {
+	.ndo_open		= usbnet_open,
+	.ndo_stop		= usbnet_stop,
+};
+
+static int aqc111_bind(struct usbnet *dev, struct usb_interface *intf)
+{
+	struct usb_device *udev = interface_to_usbdev(intf);
+	int ret;
+
+	/* Check if vendor configuration */
+	if (udev->actconfig->desc.bConfigurationValue != 1) {
+		usb_driver_set_configuration(udev, 1);
+		return -ENODEV;
+	}
+
+	usb_reset_configuration(dev->udev);
+
+	ret = usbnet_get_endpoints(dev, intf);
+	if (ret < 0) {
+		netdev_dbg(dev->net, "usbnet_get_endpoints failed");
+		return ret;
+	}
+
+	dev->net->netdev_ops = &aqc111_netdev_ops;
+
+	return 0;
+}
+
+static void aqc111_unbind(struct usbnet *dev, struct usb_interface *intf)
+{
+}
+
 static const struct driver_info aqc111_info = {
 	.description	= "Aquantia AQtion USB to 5GbE Controller",
+	.bind		= aqc111_bind,
+	.unbind		= aqc111_unbind,
 };
 
 #define AQC111_USB_ETH_DEV(vid, pid, table) \
#3 lore raw

PATCH v4 3/21 net: usb: aqc111: Add implementation of read and write commands

In reply to Igor Russkikh

From: Dmitry Bezrukov <[email protected]>

Read/write command register defines and functions

---
 drivers/net/usb/aqc111.c | 92 ++++++++++++++++++++++++++++++++++++++++
 drivers/net/usb/aqc111.h | 18 ++++++++
 2 files changed, 110 insertions(+)
 create mode 100644 drivers/net/usb/aqc111.h
drivers/net/usb/aqc111.c +92 -0
index a2628856c580..fbd48b8a6944 100644
@@ -14,6 +14,98 @@
 #include <linux/usb/cdc.h>
 #include <linux/usb/usbnet.h>
 
+#include "aqc111.h"
+
+static int aqc111_read_cmd_nopm(struct usbnet *dev, u8 cmd, u16 value,
+				u16 index, u16 size, void *data)
+{
+	int ret;
+
+	ret = usbnet_read_cmd_nopm(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(0x%x) reg index 0x%04x: %d\n",
+			    cmd, index, ret);
+
+	return ret;
+}
+
+static int aqc111_read_cmd(struct usbnet *dev, u8 cmd, u16 value,
+			   u16 index, u16 size, void *data)
+{
+	int ret;
+
+	ret = usbnet_read_cmd(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(0x%x) reg index 0x%04x: %d\n",
+			    cmd, index, ret);
+
+	return ret;
+}
+
+static int __aqc111_write_cmd(struct usbnet *dev, u8 cmd, u8 reqtype,
+			      u16 value, u16 index, u16 size, const void *data)
+{
+	int err = -ENOMEM;
+	void *buf = NULL;
+
+	netdev_dbg(dev->net,
+		   "%s cmd=%#x reqtype=%#x value=%#x index=%#x size=%d\n",
+		   __func__, cmd, reqtype, value, index, size);
+
+	if (data) {
+		buf = kmemdup(data, size, GFP_KERNEL);
+		if (!buf)
+			goto out;
+	}
+
+	err = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, 0),
+			      cmd, reqtype, value, index, buf, size,
+			      (cmd == AQ_PHY_POWER) ? AQ_USB_PHY_SET_TIMEOUT :
+			      AQ_USB_SET_TIMEOUT);
+
+	if (unlikely(err < 0))
+		netdev_warn(dev->net,
+			    "Failed to write(0x%x) reg index 0x%04x: %d\n",
+			    cmd, index, err);
+	kfree(buf);
+
+out:
+	return err;
+}
+
+static int aqc111_write_cmd_nopm(struct usbnet *dev, u8 cmd, u16 value,
+				 u16 index, u16 size, void *data)
+{
+	int ret;
+
+	ret = __aqc111_write_cmd(dev, cmd, USB_DIR_OUT | USB_TYPE_VENDOR |
+				 USB_RECIP_DEVICE, value, index, size, data);
+
+	return ret;
+}
+
+static int aqc111_write_cmd(struct usbnet *dev, u8 cmd, u16 value,
+			    u16 index, u16 size, void *data)
+{
+	int ret;
+
+	if (usb_autopm_get_interface(dev->intf) < 0)
+		return -ENODEV;
+
+	ret = __aqc111_write_cmd(dev, cmd, USB_DIR_OUT | USB_TYPE_VENDOR |
+				 USB_RECIP_DEVICE, value, index, size, data);
+
+	usb_autopm_put_interface(dev->intf);
+
+	return ret;
+}
+
 static const struct net_device_ops aqc111_netdev_ops = {
 	.ndo_open		= usbnet_open,
 	.ndo_stop		= usbnet_stop,
drivers/net/usb/aqc111.h new file +18 -0
new file mode 100644
index 000000000000..f4302f7f0cc3
@@ -0,0 +1,18 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Aquantia Corp. Aquantia AQtion USB to 5GbE Controller
+ * Copyright (C) 2003-2005 David Hollis <[email protected]>
+ * Copyright (C) 2005 Phil Chang <[email protected]>
+ * Copyright (C) 2002-2003 TiVo Inc.
+ * Copyright (C) 2017-2018 ASIX
+ * Copyright (C) 2018 Aquantia Corp.
+ */
+
+#ifndef __LINUX_USBNET_AQC111_H
+#define __LINUX_USBNET_AQC111_H
+
+#define AQ_PHY_POWER			0x31
+
+#define AQ_USB_PHY_SET_TIMEOUT		10000
+#define AQ_USB_SET_TIMEOUT		4000
+
+#endif /* __LINUX_USBNET_AQC111_H */
#4 lore raw

PATCH v4 4/21 net: usb: aqc111: Various callbacks implementation

In reply to Igor Russkikh

From: Dmitry Bezrukov <[email protected]>

Reset, stop callbacks, driver unbind callback. More register defines required for these callbacks. Add helpers to read/write 16bit values

---
 drivers/net/usb/aqc111.c |  76 +++++++++++++++++++++++++++++
 drivers/net/usb/aqc111.h | 101 +++++++++++++++++++++++++++++++++++++++
 2 files changed, 177 insertions(+)
drivers/net/usb/aqc111.c +76 -0
index fbd48b8a6944..75849714abfa 100644
@@ -48,6 +48,17 @@ static int aqc111_read_cmd(struct usbnet *dev, u8 cmd, u16 value,
 	return ret;
 }
 
+static int aqc111_read16_cmd(struct usbnet *dev, u8 cmd, u16 value,
+			     u16 index, u16 *data)
+{
+	int ret = 0;
+
+	ret = aqc111_read_cmd(dev, cmd, value, index, sizeof(*data), data);
+	le16_to_cpus(data);
+
+	return ret;
+}
+
 static int __aqc111_write_cmd(struct usbnet *dev, u8 cmd, u8 reqtype,
 			      u16 value, u16 index, u16 size, const void *data)
 {
@@ -106,6 +117,26 @@ static int aqc111_write_cmd(struct usbnet *dev, u8 cmd, u16 value,
 	return ret;
 }
 
+static int aqc111_write16_cmd_nopm(struct usbnet *dev, u8 cmd, u16 value,
+				   u16 index, u16 *data)
+{
+	u16 tmp = *data;
+
+	cpu_to_le16s(&tmp);
+
+	return aqc111_write_cmd_nopm(dev, cmd, value, index, sizeof(tmp), &tmp);
+}
+
+static int aqc111_write16_cmd(struct usbnet *dev, u8 cmd, u16 value,
+			      u16 index, u16 *data)
+{
+	u16 tmp = *data;
+
+	cpu_to_le16s(&tmp);
+
+	return aqc111_write_cmd(dev, cmd, value, index, sizeof(tmp), &tmp);
+}
+
 static const struct net_device_ops aqc111_netdev_ops = {
 	.ndo_open		= usbnet_open,
 	.ndo_stop		= usbnet_stop,
@@ -137,12 +168,57 @@ static int aqc111_bind(struct usbnet *dev, struct usb_interface *intf)
 
 static void aqc111_unbind(struct usbnet *dev, struct usb_interface *intf)
 {
+	u16 reg16;
+
+	/* Force bz */
+	reg16 = SFR_PHYPWR_RSTCTL_BZ;
+	aqc111_write16_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_PHYPWR_RSTCTL,
+				2, &reg16);
+	reg16 = 0;
+	aqc111_write16_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_PHYPWR_RSTCTL,
+				2, &reg16);
+}
+
+static int aqc111_reset(struct usbnet *dev)
+{
+	u8 reg8 = 0;
+
+	reg8 = 0xFF;
+	aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_BM_INT_MASK, 1, 1, &reg8);
+
+	reg8 = 0x0;
+	aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_SWP_CTRL, 1, 1, &reg8);
+
+	aqc111_read_cmd(dev, AQ_ACCESS_MAC, SFR_MONITOR_MODE, 1, 1, &reg8);
+	reg8 &= ~(SFR_MONITOR_MODE_EPHYRW | SFR_MONITOR_MODE_RWLC |
+		  SFR_MONITOR_MODE_RWMP | SFR_MONITOR_MODE_RWWF |
+		  SFR_MONITOR_MODE_RW_FLAG);
+	aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_MONITOR_MODE, 1, 1, &reg8);
+
+	return 0;
+}
+
+static int aqc111_stop(struct usbnet *dev)
+{
+	u16 reg16 = 0;
+
+	aqc111_read16_cmd(dev, AQ_ACCESS_MAC, SFR_MEDIUM_STATUS_MODE,
+			  2, &reg16);
+	reg16 &= ~SFR_MEDIUM_RECEIVE_EN;
+	aqc111_write16_cmd(dev, AQ_ACCESS_MAC, SFR_MEDIUM_STATUS_MODE,
+			   2, &reg16);
+	reg16 = 0;
+	aqc111_write16_cmd(dev, AQ_ACCESS_MAC, SFR_RX_CTL, 2, &reg16);
+
+	return 0;
 }
 
 static const struct driver_info aqc111_info = {
 	.description	= "Aquantia AQtion USB to 5GbE Controller",
 	.bind		= aqc111_bind,
 	.unbind		= aqc111_unbind,
+	.reset		= aqc111_reset,
+	.stop		= aqc111_stop,
 };
 
 #define AQC111_USB_ETH_DEV(vid, pid, table) \
drivers/net/usb/aqc111.h +101 -0

110 lines not on this page · raw message

#5 lore raw

PATCH v4 5/21 net: usb: aqc111: Introduce PHY access

In reply to Igor Russkikh

From: Dmitry Bezrukov <[email protected]>

Add helpers to write 32bit values.
Implement PHY power up/down sequences.
AQC111, PHY is being controlled via vendor command interface.

---
 drivers/net/usb/aqc111.c | 65 ++++++++++++++++++++++++++++++++++++++++
 drivers/net/usb/aqc111.h | 33 ++++++++++++++++++++
 2 files changed, 98 insertions(+)
drivers/net/usb/aqc111.c +65 -0
index 75849714abfa..e277209df616 100644
@@ -137,14 +137,49 @@ static int aqc111_write16_cmd(struct usbnet *dev, u8 cmd, u16 value,
 	return aqc111_write_cmd(dev, cmd, value, index, sizeof(tmp), &tmp);
 }
 
+static int aqc111_write32_cmd_nopm(struct usbnet *dev, u8 cmd, u16 value,
+				   u16 index, u32 *data)
+{
+	u32 tmp = *data;
+
+	cpu_to_le32s(&tmp);
+
+	return aqc111_write_cmd_nopm(dev, cmd, value, index, sizeof(tmp), &tmp);
+}
+
+static int aqc111_write32_cmd(struct usbnet *dev, u8 cmd, u16 value,
+			      u16 index, u32 *data)
+{
+	u32 tmp = *data;
+
+	cpu_to_le32s(&tmp);
+
+	return aqc111_write_cmd(dev, cmd, value, index, sizeof(tmp), &tmp);
+}
+
 static const struct net_device_ops aqc111_netdev_ops = {
 	.ndo_open		= usbnet_open,
 	.ndo_stop		= usbnet_stop,
 };
 
+static void aqc111_read_fw_version(struct usbnet *dev,
+				   struct aqc111_data *aqc111_data)
+{
+	aqc111_read_cmd(dev, AQ_ACCESS_MAC, AQ_FW_VER_MAJOR,
+			1, 1, &aqc111_data->fw_ver.major);
+	aqc111_read_cmd(dev, AQ_ACCESS_MAC, AQ_FW_VER_MINOR,
+			1, 1, &aqc111_data->fw_ver.minor);
+	aqc111_read_cmd(dev, AQ_ACCESS_MAC, AQ_FW_VER_REV,
+			1, 1, &aqc111_data->fw_ver.rev);
+
+	if (aqc111_data->fw_ver.major & 0x80)
+		aqc111_data->fw_ver.major &= ~0x80;
+}
+
 static int aqc111_bind(struct usbnet *dev, struct usb_interface *intf)
 {
 	struct usb_device *udev = interface_to_usbdev(intf);
+	struct aqc111_data *aqc111_data;
 	int ret;
 
 	/* Check if vendor configuration */
@@ -161,13 +196,23 @@ static int aqc111_bind(struct usbnet *dev, struct usb_interface *intf)
 		return ret;
 	}
 
+	aqc111_data = kzalloc(sizeof(*aqc111_data), GFP_KERNEL);
+	if (!aqc111_data)
+		return -ENOMEM;
+
+	/* store aqc111_data pointer in device data field */
+	dev->driver_priv = aqc111_data;
+
 	dev->net->netdev_ops = &aqc111_netdev_ops;
 
+	aqc111_read_fw_version(dev, aqc111_data);
+
 	return 0;
 }
 
 static void aqc111_unbind(struct usbnet *dev, struct usb_interface *intf)
 {
+	struct aqc111_data *aqc111_data = dev->driver_priv;
 	u16 reg16;
 
 	/* Force bz */
@@ -177,12 +222,26 @@ static void aqc111_unbind(struct usbnet *dev, struct usb_interface *intf)
 	reg16 = 0;
 	aqc111_write16_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_PHYPWR_RSTCTL,
 				2, &reg16);
+
+	/* Power down ethernet PHY */
+	aqc111_data->phy_cfg |= AQ_LOW_POWER;
+	aqc111_data->phy_cfg &= ~AQ_PHY_POWER_EN;
+	aqc111_write32_cmd_nopm(dev, AQ_PHY_OPS, 0, 0,
+				&aqc111_data->phy_cfg);
+
+	kfree(aqc111_data);
 }
 
 static int aqc111_reset(struct usbnet *dev)
 {
+	struct aqc111_data *aqc111_data = dev->driver_priv;
 	u8 reg8 = 0;
 
+	/* Power up ethernet PHY */
+	aqc111_data->phy_cfg = AQ_PHY_POWER_EN;
+	aqc111_write32_cmd(dev, AQ_PHY_OPS, 0, 0,
+			   &aqc111_data->phy_cfg);
+
 	reg8 = 0xFF;
 	aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_BM_INT_MASK, 1, 1, &reg8);
 
@@ -200,6 +259,7 @@ static int aqc111_reset(struct usbnet *dev)
 
 static int aqc111_stop(struct usbnet *dev)
 {
+	struct aqc111_data *aqc111_data = dev->driver_priv;
 	u16 reg16 = 0;
 
 	aqc111_read16_cmd(dev, AQ_ACCESS_MAC, SFR_MEDIUM_STATUS_MODE,
@@ -210,6 +270,11 @@ static int aqc111_stop(struct usbnet *dev)
 	reg16 = 0;
 	aqc111_write16_cmd(dev, AQ_ACCESS_MAC, SFR_RX_CTL, 2, &reg16);
 
+	/* Put PHY to low power*/
+	aqc111_data->phy_cfg |= AQ_LOW_POWER;
+	aqc111_write32_cmd(dev, AQ_PHY_OPS, 0, 0,
+			   &aqc111_data->phy_cfg);
+
 	return 0;
 }
 
drivers/net/usb/aqc111.h +33 -0
index a252ccd78559..a93d34af76b9 100644
@@ -12,6 +12,7 @@
 
 #define AQ_ACCESS_MAC			0x01
 #define AQ_PHY_POWER			0x31
+#define AQ_PHY_OPS			0x61
 
 #define AQ_USB_PHY_SET_TIMEOUT		10000
 #define AQ_USB_SET_TIMEOUT		4000
@@ -102,6 +103,38 @@
 	#define SFR_BULK_OUT_FLUSH_EN		0x01
 	#define SFR_BULK_OUT_EFF_EN		0x02
 
+#define AQ_FW_VER_MAJOR			0xDA
+#define AQ_FW_VER_MINOR			0xDB
+#define AQ_FW_VER_REV			0xDC
+
+/*PHY_OPS**********************************************************************/
+
+#define AQ_ADV_100M	BIT(0)
+#define AQ_ADV_1G	BIT(1)
+#define AQ_ADV_2G5	BIT(2)
+#define AQ_ADV_5G	BIT(3)
+
+#define AQ_PAUSE	BIT(16)
+#define AQ_ASYM_PAUSE	BIT(17)
+#define AQ_LOW_POWER	BIT(18)
+#define AQ_PHY_POWER_EN	BIT(19)
+#define AQ_WOL		BIT(20)
+#define AQ_DOWNSHIFT	BIT(21)
+
+#define AQ_DSH_RETRIES_SHIFT	0x18
+#define AQ_DSH_RETRIES_MASK	0xF000000
+
+/******************************************************************************/
+
+struct aqc111_data {
+	struct {
+		u8 major;
+		u8 minor;
+		u8 rev;
+	} fw_ver;
+	u32 phy_cfg;
+};
+
 static struct {
 	unsigned char ctrl;
 	unsigned char timer_l;
#6 lore raw

PATCH v4 6/21 net: usb: aqc111: Introduce link management

In reply to Igor Russkikh

From: Dmitry Bezrukov <[email protected]>

Add full hardware initialization sequence and link configuration logic

---
 drivers/net/usb/aqc111.c | 235 +++++++++++++++++++++++++++++++++++++++
 drivers/net/usb/aqc111.h |  13 +++
 2 files changed, 248 insertions(+)
drivers/net/usb/aqc111.c +235 -0
index e277209df616..e33be16b506c 100644
@@ -157,6 +157,53 @@ static int aqc111_write32_cmd(struct usbnet *dev, u8 cmd, u16 value,
 	return aqc111_write_cmd(dev, cmd, value, index, sizeof(tmp), &tmp);
 }
 
+static void aqc111_set_phy_speed(struct usbnet *dev, u8 autoneg, u16 speed)
+{
+	struct aqc111_data *aqc111_data = dev->driver_priv;
+
+	aqc111_data->phy_cfg &= ~AQ_ADV_MASK;
+	aqc111_data->phy_cfg |= AQ_PAUSE;
+	aqc111_data->phy_cfg |= AQ_ASYM_PAUSE;
+	aqc111_data->phy_cfg |= AQ_DOWNSHIFT;
+	aqc111_data->phy_cfg &= ~AQ_DSH_RETRIES_MASK;
+	aqc111_data->phy_cfg |= (3 << AQ_DSH_RETRIES_SHIFT) &
+				AQ_DSH_RETRIES_MASK;
+
+	if (autoneg == AUTONEG_ENABLE) {
+		switch (speed) {
+		case SPEED_5000:
+			aqc111_data->phy_cfg |= AQ_ADV_5G;
+			/* fall-through */
+		case SPEED_2500:
+			aqc111_data->phy_cfg |= AQ_ADV_2G5;
+			/* fall-through */
+		case SPEED_1000:
+			aqc111_data->phy_cfg |= AQ_ADV_1G;
+			/* fall-through */
+		case SPEED_100:
+			aqc111_data->phy_cfg |= AQ_ADV_100M;
+			/* fall-through */
+		}
+	} else {
+		switch (speed) {
+		case SPEED_5000:
+			aqc111_data->phy_cfg |= AQ_ADV_5G;
+			break;
+		case SPEED_2500:
+			aqc111_data->phy_cfg |= AQ_ADV_2G5;
+			break;
+		case SPEED_1000:
+			aqc111_data->phy_cfg |= AQ_ADV_1G;
+			break;
+		case SPEED_100:
+			aqc111_data->phy_cfg |= AQ_ADV_100M;
+			break;
+		}
+	}
+
+	aqc111_write32_cmd(dev, AQ_PHY_OPS, 0, 0, &aqc111_data->phy_cfg);
+}
+
 static const struct net_device_ops aqc111_netdev_ops = {
 	.ndo_open		= usbnet_open,
 	.ndo_stop		= usbnet_stop,
@@ -179,6 +226,7 @@ static void aqc111_read_fw_version(struct usbnet *dev,
 static int aqc111_bind(struct usbnet *dev, struct usb_interface *intf)
 {
 	struct usb_device *udev = interface_to_usbdev(intf);
+	enum usb_device_speed usb_speed = udev->speed;
 	struct aqc111_data *aqc111_data;
 	int ret;
 
@@ -206,6 +254,9 @@ static int aqc111_bind(struct usbnet *dev, struct usb_interface *intf)
 	dev->net->netdev_ops = &aqc111_netdev_ops;
 
 	aqc111_read_fw_version(dev, aqc111_data);
+	aqc111_data->autoneg = AUTONEG_ENABLE;
+	aqc111_data->advertised_speed = (usb_speed == USB_SPEED_SUPER) ?
+					 SPEED_5000 : SPEED_1000;
 
 	return 0;
 }
@@ -224,6 +275,7 @@ static void aqc111_unbind(struct usbnet *dev, struct usb_interface *intf)
 				2, &reg16);
 
 	/* Power down ethernet PHY */
+	aqc111_data->phy_cfg &= ~AQ_ADV_MASK;
 	aqc111_data->phy_cfg |= AQ_LOW_POWER;
 	aqc111_data->phy_cfg &= ~AQ_PHY_POWER_EN;
 	aqc111_write32_cmd_nopm(dev, AQ_PHY_OPS, 0, 0,
@@ -232,6 +284,179 @@ static void aqc111_unbind(struct usbnet *dev, struct usb_interface *intf)
 	kfree(aqc111_data);
 }
 
+static void aqc111_status(struct usbnet *dev, struct urb *urb)
+{
+	struct aqc111_data *aqc111_data = dev->driver_priv;
+	u64 *event_data = NULL;
+	int link = 0;
+
+	if (urb->actual_length < sizeof(*event_data))
+		return;
+
+	event_data = urb->transfer_buffer;
+	le64_to_cpus(event_data);
+
+	if (*event_data & AQ_LS_MASK)
+		link = 1;
+	else
+		link = 0;
+
+	aqc111_data->link_speed = (*event_data & AQ_SPEED_MASK) >>
+				  AQ_SPEED_SHIFT;
+	aqc111_data->link = link;
+
+	if (netif_carrier_ok(dev->net) != link)
+		usbnet_defer_kevent(dev, EVENT_LINK_RESET);
+}
+
+static void aqc111_configure_rx(struct usbnet *dev,
+				struct aqc111_data *aqc111_data)
+{
+	enum usb_device_speed usb_speed = dev->udev->speed;
+	u16 link_speed = 0, usb_host = 0;
+	u8 buf[5] = { 0 };
+	u8 queue_num = 0;
+	u16 reg16 = 0;
+	u8 reg8 = 0;
+
+	buf[0] = 0x00;
+	buf[1] = 0xF8;
+	buf[2] = 0x07;
+	switch (aqc111_data->link_speed) {
+	case AQ_INT_SPEED_5G:
+		link_speed = 5000;
+		reg8 = 0x05;
+		reg16 = 0x001F;
+		break;
+	case AQ_INT_SPEED_2_5G:
+		link_speed = 2500;
+		reg16 = 0x003F;
+		break;
+	case AQ_INT_SPEED_1G:
+		link_speed = 1000;
+		reg16 = 0x009F;
+		break;
+	case AQ_INT_SPEED_100M:
+		link_speed = 100;
+		queue_num = 1;
+		reg16 = 0x063F;
+		buf[1] = 0xFB;
+		buf[2] = 0x4;
+		break;
+	}
+
+	aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_INTER_PACKET_GAP_0,
+			 1, 1, &reg8);
+
+	aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_TX_PAUSE_RESEND_T, 3, 3, buf);
+
+	switch (usb_speed) {
+	case USB_SPEED_SUPER:
+		usb_host = 3;
+		break;
+	case USB_SPEED_HIGH:
+		usb_host = 2;
+		break;
+	case USB_SPEED_FULL:
+	case USB_SPEED_LOW:
+		usb_host = 1;
+		queue_num = 0;
+		break;
+	default:
+		usb_host = 0;
+		break;
+	}
+
+	memcpy(buf, &AQC111_BULKIN_SIZE[queue_num], 5);
+	/* RX bulk configuration */
+	aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_RX_BULKIN_QCTRL, 5, 5, buf);
+
+	/* Set high low water level */
+	reg16 = 0x0810;
+
+	aqc111_write16_cmd(dev, AQ_ACCESS_MAC, SFR_PAUSE_WATERLVL_LOW,
+			   2, &reg16);
+	netdev_info(dev->net, "Link Speed %d, USB %d", link_speed, usb_host);
+}
+
+static int aqc111_link_reset(struct usbnet *dev)
+{
+	struct aqc111_data *aqc111_data = dev->driver_priv;
+	u16 reg16 = 0;
+	u8 reg8 = 0;
+
+	if (aqc111_data->link == 1) { /* Link up */
+		aqc111_configure_rx(dev, aqc111_data);
+
+		/* Vlan Tag Filter */
+		reg8 = SFR_VLAN_CONTROL_VSO;
+
+		aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_VLAN_ID_CONTROL,
+				 1, 1, &reg8);
+
+		reg8 = 0x0;
+		aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_BMRX_DMA_CONTROL,
+				 1, 1, &reg8);
+
+		aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_BMTX_DMA_CONTROL,
+				 1, 1, &reg8);
+
+		aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_ARC_CTRL, 1, 1, &reg8);
+
+		reg16 = SFR_RX_CTL_IPE | SFR_RX_CTL_AB;
+		aqc111_write16_cmd(dev, AQ_ACCESS_MAC, SFR_RX_CTL, 2, &reg16);
+
+		reg8 = SFR_RX_PATH_READY;
+		aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_ETH_MAC_PATH,
+				 1, 1, &reg8);
+
+		reg8 = SFR_BULK_OUT_EFF_EN;
+		aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_BULK_OUT_CTRL,
+				 1, 1, &reg8);
+
+		reg16 = 0;
+		aqc111_write16_cmd(dev, AQ_ACCESS_MAC, SFR_MEDIUM_STATUS_MODE,
+				   2, &reg16);
+
+		reg16 = SFR_MEDIUM_XGMIIMODE | SFR_MEDIUM_FULL_DUPLEX;
+		aqc111_write16_cmd(dev, AQ_ACCESS_MAC, SFR_MEDIUM_STATUS_MODE,
+				   2, &reg16);
+
+		aqc111_read16_cmd(dev, AQ_ACCESS_MAC, SFR_MEDIUM_STATUS_MODE,
+				  2, &reg16);
+
+		reg16 |= SFR_MEDIUM_RECEIVE_EN | SFR_MEDIUM_RXFLOW_CTRLEN |
+			 SFR_MEDIUM_TXFLOW_CTRLEN;
+		aqc111_write16_cmd(dev, AQ_ACCESS_MAC, SFR_MEDIUM_STATUS_MODE,
+				   2, &reg16);
+
+		reg16 = SFR_RX_CTL_IPE | SFR_RX_CTL_AB | SFR_RX_CTL_START;
+		aqc111_write16_cmd(dev, AQ_ACCESS_MAC, SFR_RX_CTL, 2, &reg16);
+
+		netif_carrier_on(dev->net);
+	} else {
+		aqc111_read16_cmd(dev, AQ_ACCESS_MAC, SFR_MEDIUM_STATUS_MODE,
+				  2, &reg16);
+		reg16 &= ~SFR_MEDIUM_RECEIVE_EN;
+		aqc111_write16_cmd(dev, AQ_ACCESS_MAC, SFR_MEDIUM_STATUS_MODE,
+				   2, &reg16);
+
+		aqc111_read16_cmd(dev, AQ_ACCESS_MAC, SFR_RX_CTL, 2, &reg16);
+		reg16 &= ~SFR_RX_CTL_START;
+		aqc111_write16_cmd(dev, AQ_ACCESS_MAC, SFR_RX_CTL, 2, &reg16);
+
+		reg8 = SFR_BULK_OUT_FLUSH_EN | SFR_BULK_OUT_EFF_EN;
+		aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_BULK_OUT_CTRL,
+				 1, 1, &reg8);
+		reg8 = SFR_BULK_OUT_EFF_EN;
+		aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_BULK_OUT_CTRL,
+				 1, 1, &reg8);
+
+		netif_carrier_off(dev->net);
+	}
+	return 0;
+}
+
 static int aqc111_reset(struct usbnet *dev)
 {
 	struct aqc111_data *aqc111_data = dev->driver_priv;
@@ -254,6 +479,12 @@ static int aqc111_reset(struct usbnet *dev)
 		  SFR_MONITOR_MODE_RW_FLAG);
 	aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_MONITOR_MODE, 1, 1, &reg8);
 
+	netif_carrier_off(dev->net);
+
+	/* Phy advertise */
+	aqc111_set_phy_speed(dev, aqc111_data->autoneg,
+			     aqc111_data->advertised_speed);
+
 	return 0;
 }
 
@@ -275,6 +506,8 @@ static int aqc111_stop(struct usbnet *dev)
 	aqc111_write32_cmd(dev, AQ_PHY_OPS, 0, 0,
 			   &aqc111_data->phy_cfg);
 
+	netif_carrier_off(dev->net);
+
 	return 0;
 }
 
@@ -282,6 +515,8 @@ static const struct driver_info aqc111_info = {
 	.description	= "Aquantia AQtion USB to 5GbE Controller",
 	.bind		= aqc111_bind,
 	.unbind		= aqc111_unbind,
+	.status		= aqc111_status,
+	.link_reset	= aqc111_link_reset,
 	.reset		= aqc111_reset,
 	.stop		= aqc111_stop,
 };
drivers/net/usb/aqc111.h +13 -0
index a93d34af76b9..f3b45d8ca4e3 100644
@@ -113,6 +113,7 @@
 #define AQ_ADV_1G	BIT(1)
 #define AQ_ADV_2G5	BIT(2)
 #define AQ_ADV_5G	BIT(3)
+#define AQ_ADV_MASK	0x0F
 
 #define AQ_PAUSE	BIT(16)
 #define AQ_ASYM_PAUSE	BIT(17)
@@ -127,6 +128,10 @@
 /******************************************************************************/
 
 struct aqc111_data {
+	u8 link_speed;
+	u8 link;
+	u8 autoneg;
+	u32 advertised_speed;
 	struct {
 		u8 major;
 		u8 minor;
@@ -135,6 +140,14 @@ struct aqc111_data {
 	u32 phy_cfg;
 };
 
+#define AQ_LS_MASK		0x8000
+#define AQ_SPEED_MASK		0x7F00
+#define AQ_SPEED_SHIFT		0x0008
+#define AQ_INT_SPEED_5G		0x000F
+#define AQ_INT_SPEED_2_5G	0x0010
+#define AQ_INT_SPEED_1G		0x0011
+#define AQ_INT_SPEED_100M	0x0013
+
 static struct {
 	unsigned char ctrl;
 	unsigned char timer_l;
#7 lore raw

PATCH v4 7/21 net: usb: aqc111: Add support for getting and setting of MAC address

In reply to Igor Russkikh

From: Dmitry Bezrukov <[email protected]>

---
 drivers/net/usb/aqc111.c | 47 ++++++++++++++++++++++++++++++++++++++++
 drivers/net/usb/aqc111.h |  1 +
 2 files changed, 48 insertions(+)
drivers/net/usb/aqc111.c +47 -0
index e33be16b506c..390ed6cbc3fd 100644
@@ -11,6 +11,7 @@
 #include <linux/netdevice.h>
 #include <linux/mii.h>
 #include <linux/usb.h>
+#include <linux/if_vlan.h>
 #include <linux/usb/cdc.h>
 #include <linux/usb/usbnet.h>
 
@@ -204,11 +205,43 @@ static void aqc111_set_phy_speed(struct usbnet *dev, u8 autoneg, u16 speed)
 	aqc111_write32_cmd(dev, AQ_PHY_OPS, 0, 0, &aqc111_data->phy_cfg);
 }
 
+static int aqc111_set_mac_addr(struct net_device *net, void *p)
+{
+	struct usbnet *dev = netdev_priv(net);
+	int ret = 0;
+
+	ret = eth_mac_addr(net, p);
+	if (ret < 0)
+		return ret;
+
+	/* Set the MAC address */
+	return aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_NODE_ID, ETH_ALEN,
+				ETH_ALEN, net->dev_addr);
+}
+
 static const struct net_device_ops aqc111_netdev_ops = {
 	.ndo_open		= usbnet_open,
 	.ndo_stop		= usbnet_stop,
+	.ndo_set_mac_address	= aqc111_set_mac_addr,
+	.ndo_validate_addr	= eth_validate_addr,
 };
 
+static int aqc111_read_perm_mac(struct usbnet *dev)
+{
+	u8 buf[ETH_ALEN];
+	int ret;
+
+	ret = aqc111_read_cmd(dev, AQ_FLASH_PARAMETERS, 0, 0, ETH_ALEN, buf);
+	if (ret < 0)
+		goto out;
+
+	ether_addr_copy(dev->net->perm_addr, buf);
+
+	return 0;
+out:
+	return ret;
+}
+
 static void aqc111_read_fw_version(struct usbnet *dev,
 				   struct aqc111_data *aqc111_data)
 {
@@ -251,6 +284,12 @@ static int aqc111_bind(struct usbnet *dev, struct usb_interface *intf)
 	/* store aqc111_data pointer in device data field */
 	dev->driver_priv = aqc111_data;
 
+	/* Init the MAC address */
+	ret = aqc111_read_perm_mac(dev);
+	if (ret)
+		goto out;
+
+	ether_addr_copy(dev->net->dev_addr, dev->net->perm_addr);
 	dev->net->netdev_ops = &aqc111_netdev_ops;
 
 	aqc111_read_fw_version(dev, aqc111_data);
@@ -259,6 +298,10 @@ static int aqc111_bind(struct usbnet *dev, struct usb_interface *intf)
 					 SPEED_5000 : SPEED_1000;
 
 	return 0;
+
+out:
+	kfree(aqc111_data);
+	return ret;
 }
 
 static void aqc111_unbind(struct usbnet *dev, struct usb_interface *intf)
@@ -467,6 +510,10 @@ static int aqc111_reset(struct usbnet *dev)
 	aqc111_write32_cmd(dev, AQ_PHY_OPS, 0, 0,
 			   &aqc111_data->phy_cfg);
 
+	/* Set the MAC address */
+	aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_NODE_ID, ETH_ALEN,
+			 ETH_ALEN, dev->net->dev_addr);
+
 	reg8 = 0xFF;
 	aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_BM_INT_MASK, 1, 1, &reg8);
 
drivers/net/usb/aqc111.h +1 -0
index f3b45d8ca4e3..0c8e1ee29893 100644
@@ -11,6 +11,7 @@
 #define __LINUX_USBNET_AQC111_H
 
 #define AQ_ACCESS_MAC			0x01
+#define AQ_FLASH_PARAMETERS		0x20
 #define AQ_PHY_POWER			0x31
 #define AQ_PHY_OPS			0x61
 
#8 lore raw

PATCH v4 8/21 net: usb: aqc111: Implement TX data path

In reply to Igor Russkikh

From: Dmitry Bezrukov <[email protected]>

---
 drivers/net/usb/aqc111.c | 72 ++++++++++++++++++++++++++++++++++++++++
 drivers/net/usb/aqc111.h |  8 +++++
 2 files changed, 80 insertions(+)
drivers/net/usb/aqc111.c +72 -0
index 390ed6cbc3fd..32a2de74ef3f 100644
@@ -222,6 +222,9 @@ static int aqc111_set_mac_addr(struct net_device *net, void *p)
 static const struct net_device_ops aqc111_netdev_ops = {
 	.ndo_open		= usbnet_open,
 	.ndo_stop		= usbnet_stop,
+	.ndo_start_xmit		= usbnet_start_xmit,
+	.ndo_tx_timeout		= usbnet_tx_timeout,
+	.ndo_get_stats64	= usbnet_get_stats64,
 	.ndo_set_mac_address	= aqc111_set_mac_addr,
 	.ndo_validate_addr	= eth_validate_addr,
 };
@@ -290,8 +293,19 @@ static int aqc111_bind(struct usbnet *dev, struct usb_interface *intf)
 		goto out;
 
 	ether_addr_copy(dev->net->dev_addr, dev->net->perm_addr);
+
+	/* Set TX needed headroom & tailroom */
+	dev->net->needed_headroom += sizeof(u64);
+	dev->net->needed_tailroom += sizeof(u64);
+
 	dev->net->netdev_ops = &aqc111_netdev_ops;
 
+	if (usb_device_no_sg_constraint(dev->udev))
+		dev->can_dma_sg = 1;
+
+	dev->net->hw_features |= AQ_SUPPORT_HW_FEATURE;
+	dev->net->features |= AQ_SUPPORT_FEATURE;
+
 	aqc111_read_fw_version(dev, aqc111_data);
 	aqc111_data->autoneg = AUTONEG_ENABLE;
 	aqc111_data->advertised_speed = (usb_speed == USB_SPEED_SUPER) ?
@@ -505,6 +519,12 @@ static int aqc111_reset(struct usbnet *dev)
 	struct aqc111_data *aqc111_data = dev->driver_priv;
 	u8 reg8 = 0;
 
+	if (usb_device_no_sg_constraint(dev->udev))
+		dev->can_dma_sg = 1;
+
+	dev->net->hw_features |= AQ_SUPPORT_HW_FEATURE;
+	dev->net->features |= AQ_SUPPORT_FEATURE;
+
 	/* Power up ethernet PHY */
 	aqc111_data->phy_cfg = AQ_PHY_POWER_EN;
 	aqc111_write32_cmd(dev, AQ_PHY_OPS, 0, 0,
@@ -558,6 +578,55 @@ static int aqc111_stop(struct usbnet *dev)
 	return 0;
 }
 
+static struct sk_buff *aqc111_tx_fixup(struct usbnet *dev, struct sk_buff *skb,
+				       gfp_t flags)
+{
+	int frame_size = dev->maxpacket;
+	struct sk_buff *new_skb = NULL;
+	u64 *tx_desc_ptr = NULL;
+	int padding_size = 0;
+	int headroom = 0;
+	int tailroom = 0;
+	u64 tx_desc = 0;
+
+	/*Length of actual data*/
+	tx_desc |= skb->len & AQ_TX_DESC_LEN_MASK;
+
+	headroom = (skb->len + sizeof(tx_desc)) % 8;
+	if (headroom != 0)
+		padding_size = 8 - headroom;
+
+	if (((skb->len + sizeof(tx_desc) + padding_size) % frame_size) == 0) {
+		padding_size += 8;
+		tx_desc |= AQ_TX_DESC_DROP_PADD;
+	}
+
+	if (!dev->can_dma_sg && (dev->net->features & NETIF_F_SG) &&
+	    skb_linearize(skb))
+		return NULL;
+
+	headroom = skb_headroom(skb);
+	tailroom = skb_tailroom(skb);
+
+	if (!(headroom >= sizeof(tx_desc) && tailroom >= padding_size)) {
+		new_skb = skb_copy_expand(skb, sizeof(tx_desc),
+					  padding_size, flags);
+		dev_kfree_skb_any(skb);
+		skb = new_skb;
+		if (!skb)
+			return NULL;
+	}
+	if (padding_size != 0)
+		skb_put_zero(skb, padding_size);
+	/* Copy TX header */
+	tx_desc_ptr = skb_push(skb, sizeof(tx_desc));
+	*tx_desc_ptr = cpu_to_le64(tx_desc);
+
+	usbnet_set_skb_tx_stats(skb, 1, 0);
+
+	return skb;
+}
+
 static const struct driver_info aqc111_info = {
 	.description	= "Aquantia AQtion USB to 5GbE Controller",
 	.bind		= aqc111_bind,
@@ -566,6 +635,9 @@ static const struct driver_info aqc111_info = {
 	.link_reset	= aqc111_link_reset,
 	.reset		= aqc111_reset,
 	.stop		= aqc111_stop,
+	.flags		= FLAG_ETHER | FLAG_FRAMING_AX |
+			  FLAG_AVOID_UNLINK_URBS | FLAG_MULTI_PACKET,
+	.tx_fixup	= aqc111_tx_fixup,
 };
 
 #define AQC111_USB_ETH_DEV(vid, pid, table) \
drivers/net/usb/aqc111.h +8 -0
index 0c8e1ee29893..c09f188b9f3d 100644
@@ -18,6 +18,10 @@
 #define AQ_USB_PHY_SET_TIMEOUT		10000
 #define AQ_USB_SET_TIMEOUT		4000
 
+/* Feature. ********************************************/
+#define AQ_SUPPORT_FEATURE	(NETIF_F_SG)
+#define AQ_SUPPORT_HW_FEATURE	(NETIF_F_SG)
+
 /* SFR Reg. ********************************************/
 
 #define SFR_GENERAL_STATUS		0x03
@@ -149,6 +153,10 @@ struct aqc111_data {
 #define AQ_INT_SPEED_1G		0x0011
 #define AQ_INT_SPEED_100M	0x0013
 
+/* TX Descriptor */
+#define AQ_TX_DESC_LEN_MASK	0x1FFFFF
+#define AQ_TX_DESC_DROP_PADD	BIT(28)
+
 static struct {
 	unsigned char ctrl;
 	unsigned char timer_l;
#9 lore raw

PATCH v4 9/21 net: usb: aqc111: Implement RX data path

In reply to Igor Russkikh

From: Dmitry Bezrukov <[email protected]>

---
 drivers/net/usb/aqc111.c | 102 +++++++++++++++++++++++++++++++++++++++
 drivers/net/usb/aqc111.h |  15 ++++++
 2 files changed, 117 insertions(+)
drivers/net/usb/aqc111.c +102 -0
index 32a2de74ef3f..7a1976ecf294 100644
@@ -294,6 +294,9 @@ static int aqc111_bind(struct usbnet *dev, struct usb_interface *intf)
 
 	ether_addr_copy(dev->net->dev_addr, dev->net->perm_addr);
 
+	/* Set Rx urb size */
+	dev->rx_urb_size = URB_SIZE;
+
 	/* Set TX needed headroom & tailroom */
 	dev->net->needed_headroom += sizeof(u64);
 	dev->net->needed_tailroom += sizeof(u64);
@@ -519,6 +522,8 @@ static int aqc111_reset(struct usbnet *dev)
 	struct aqc111_data *aqc111_data = dev->driver_priv;
 	u8 reg8 = 0;
 
+	dev->rx_urb_size = URB_SIZE;
+
 	if (usb_device_no_sg_constraint(dev->udev))
 		dev->can_dma_sg = 1;
 
@@ -578,6 +583,102 @@ static int aqc111_stop(struct usbnet *dev)
 	return 0;
 }
 
+static int aqc111_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
+{
+	struct sk_buff *new_skb = NULL;
+	u32 pkt_total_offset = 0;
+	u64 *pkt_desc_ptr = NULL;
+	u32 start_of_descs = 0;
+	u32 desc_offset = 0; /*RX Header Offset*/
+	u16 pkt_count = 0;
+	u64 desc_hdr = 0;
+	u32 skb_len = 0;
+
+	if (!skb)
+		goto err;
+
+	if (skb->len == 0)
+		goto err;
+
+	skb_len = skb->len;
+	/* RX Descriptor Header */
+	skb_trim(skb, skb->len - sizeof(desc_hdr));
+	desc_hdr = le64_to_cpup((u64 *)skb_tail_pointer(skb));
+
+	/* Check these packets */
+	desc_offset = (desc_hdr & AQ_RX_DH_DESC_OFFSET_MASK) >>
+		      AQ_RX_DH_DESC_OFFSET_SHIFT;
+	pkt_count = desc_hdr & AQ_RX_DH_PKT_CNT_MASK;
+	start_of_descs = skb_len - ((pkt_count + 1) *  sizeof(desc_hdr));
+
+	/* self check descs position */
+	if (start_of_descs != desc_offset)
+		goto err;
+
+	/* self check desc_offset from header*/
+	if (desc_offset >= skb_len)
+		goto err;
+
+	if (pkt_count == 0)
+		goto err;
+
+	/* Get the first RX packet descriptor */
+	pkt_desc_ptr = (u64 *)(skb->data + desc_offset);
+
+	while (pkt_count--) {
+		u64 pkt_desc = le64_to_cpup(pkt_desc_ptr);
+		u32 pkt_len_with_padd = 0;
+		u32 pkt_len = 0;
+
+		pkt_len = (u32)((pkt_desc & AQ_RX_PD_LEN_MASK) >>
+			  AQ_RX_PD_LEN_SHIFT);
+		pkt_len_with_padd = ((pkt_len + 7) & 0x7FFF8);
+
+		pkt_total_offset += pkt_len_with_padd;
+		if (pkt_total_offset > desc_offset ||
+		    (pkt_count == 0 && pkt_total_offset != desc_offset)) {
+			goto err;
+		}
+
+		if (pkt_desc & AQ_RX_PD_DROP ||
+		    !(pkt_desc & AQ_RX_PD_RX_OK) ||
+		    pkt_len > (dev->hard_mtu + AQ_RX_HW_PAD)) {
+			skb_pull(skb, pkt_len_with_padd);
+			/* Next RX Packet Descriptor */
+			pkt_desc_ptr++;
+			continue;
+		}
+
+		/* Clone SKB */
+		new_skb = skb_clone(skb, GFP_ATOMIC);
+
+		if (!new_skb)
+			goto err;
+
+		new_skb->len = pkt_len;
+		skb_pull(new_skb, AQ_RX_HW_PAD);
+		skb_set_tail_pointer(new_skb, new_skb->len);
+
+		new_skb->truesize = SKB_TRUESIZE(new_skb->len);
+
+		usbnet_skb_return(dev, new_skb);
+		if (pkt_count == 0)
+			break;
+
+		skb_pull(skb, pkt_len_with_padd);
+
+		/* Next RX Packet Header */
+		pkt_desc_ptr++;
+
+		new_skb = NULL;
+	}
+
+	return 1;
+
+err:
+	return 0;
+}
+
 static struct sk_buff *aqc111_tx_fixup(struct usbnet *dev, struct sk_buff *skb,
 				       gfp_t flags)
 {
@@ -637,6 +738,7 @@ static const struct driver_info aqc111_info = {
 	.stop		= aqc111_stop,
 	.flags		= FLAG_ETHER | FLAG_FRAMING_AX |
 			  FLAG_AVOID_UNLINK_URBS | FLAG_MULTI_PACKET,
+	.rx_fixup	= aqc111_rx_fixup,
 	.tx_fixup	= aqc111_tx_fixup,
 };
 
drivers/net/usb/aqc111.h +15 -0
index c09f188b9f3d..c5785f890022 100644
@@ -10,6 +10,8 @@
 #ifndef __LINUX_USBNET_AQC111_H
 #define __LINUX_USBNET_AQC111_H
 
+#define URB_SIZE	(1024 * 62)
+
 #define AQ_ACCESS_MAC			0x01
 #define AQ_FLASH_PARAMETERS		0x20
 #define AQ_PHY_POWER			0x31
@@ -157,6 +159,19 @@ struct aqc111_data {
 #define AQ_TX_DESC_LEN_MASK	0x1FFFFF
 #define AQ_TX_DESC_DROP_PADD	BIT(28)
 
+#define AQ_RX_HW_PAD			0x02
+
+/* RX Packet Descriptor */
+#define AQ_RX_PD_RX_OK		BIT(11)
+#define AQ_RX_PD_DROP		BIT(31)
+#define AQ_RX_PD_LEN_MASK	0x7FFF0000
+#define AQ_RX_PD_LEN_SHIFT	0x10
+
+/* RX Descriptor header */
+#define AQ_RX_DH_PKT_CNT_MASK		0x1FFF
+#define AQ_RX_DH_DESC_OFFSET_MASK	0xFFFFE000
+#define AQ_RX_DH_DESC_OFFSET_SHIFT	0x0D
+
 static struct {
 	unsigned char ctrl;
 	unsigned char timer_l;
#10 lore raw

PATCH v4 10/21 net: usb: aqc111: Add checksum offload support

In reply to Igor Russkikh

From: Dmitry Bezrukov <[email protected]>

---
 drivers/net/usb/aqc111.c | 38 ++++++++++++++++++++++++++++++++++++++
 drivers/net/usb/aqc111.h | 16 ++++++++++++++--
 2 files changed, 52 insertions(+), 2 deletions(-)
drivers/net/usb/aqc111.c +38 -0
index 7a1976ecf294..1c8812e082bf 100644
@@ -439,6 +439,26 @@ static void aqc111_configure_rx(struct usbnet *dev,
 	netdev_info(dev->net, "Link Speed %d, USB %d", link_speed, usb_host);
 }
 
+static void aqc111_configure_csum_offload(struct usbnet *dev)
+{
+	u8 reg8 = 0;
+
+	if (dev->net->features & NETIF_F_RXCSUM) {
+		reg8 |= SFR_RXCOE_IP | SFR_RXCOE_TCP | SFR_RXCOE_UDP |
+			SFR_RXCOE_TCPV6 | SFR_RXCOE_UDPV6;
+	}
+	aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_RXCOE_CTL, 1, 1, &reg8);
+
+	reg8 = 0;
+	if (dev->net->features & NETIF_F_IP_CSUM)
+		reg8 |= SFR_TXCOE_IP | SFR_TXCOE_TCP | SFR_TXCOE_UDP;
+
+	if (dev->net->features & NETIF_F_IPV6_CSUM)
+		reg8 |= SFR_TXCOE_TCPV6 | SFR_TXCOE_UDPV6;
+
+	aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_TXCOE_CTL, 1, 1, &reg8);
+}
+
 static int aqc111_link_reset(struct usbnet *dev)
 {
 	struct aqc111_data *aqc111_data = dev->driver_priv;
@@ -482,6 +502,8 @@ static int aqc111_link_reset(struct usbnet *dev)
 		aqc111_write16_cmd(dev, AQ_ACCESS_MAC, SFR_MEDIUM_STATUS_MODE,
 				   2, &reg16);
 
+		aqc111_configure_csum_offload(dev);
+
 		aqc111_read16_cmd(dev, AQ_ACCESS_MAC, SFR_MEDIUM_STATUS_MODE,
 				  2, &reg16);
 
@@ -583,6 +605,21 @@ static int aqc111_stop(struct usbnet *dev)
 	return 0;
 }
 
+static void aqc111_rx_checksum(struct sk_buff *skb, u64 pkt_desc)
+{
+	u32 pkt_type = 0;
+
+	skb->ip_summed = CHECKSUM_NONE;
+	/* checksum error bit is set */
+	if (pkt_desc & AQ_RX_PD_L4_ERR || pkt_desc & AQ_RX_PD_L3_ERR)
+		return;
+
+	pkt_type = pkt_desc & AQ_RX_PD_L4_TYPE_MASK;
+	/* It must be a TCP or UDP packet with a valid checksum */
+	if (pkt_type == AQ_RX_PD_L4_TCP || pkt_type == AQ_RX_PD_L4_UDP)
+		skb->ip_summed = CHECKSUM_UNNECESSARY;
+}
+
 static int aqc111_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
 {
 	struct sk_buff *new_skb = NULL;
@@ -660,6 +697,7 @@ static int aqc111_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
 		skb_set_tail_pointer(new_skb, new_skb->len);
 
 		new_skb->truesize = SKB_TRUESIZE(new_skb->len);
+		aqc111_rx_checksum(new_skb, pkt_desc);
 
 		usbnet_skb_return(dev, new_skb);
 		if (pkt_count == 0)
drivers/net/usb/aqc111.h +14 -2
index c5785f890022..ff43443bfd72 100644
@@ -21,8 +21,11 @@
 #define AQ_USB_SET_TIMEOUT		4000
 
 /* Feature. ********************************************/
-#define AQ_SUPPORT_FEATURE	(NETIF_F_SG)
-#define AQ_SUPPORT_HW_FEATURE	(NETIF_F_SG)
+#define AQ_SUPPORT_FEATURE	(NETIF_F_SG | NETIF_F_IP_CSUM |\
+				 NETIF_F_IPV6_CSUM | NETIF_F_RXCSUM)
+
+#define AQ_SUPPORT_HW_FEATURE	(NETIF_F_SG | NETIF_F_IP_CSUM |\
+				 NETIF_F_IPV6_CSUM | NETIF_F_RXCSUM)
 
 /* SFR Reg. ********************************************/
 
@@ -162,6 +165,15 @@ struct aqc111_data {
 #define AQ_RX_HW_PAD			0x02
 
 /* RX Packet Descriptor */
+#define AQ_RX_PD_L4_ERR		BIT(0)
+#define AQ_RX_PD_L3_ERR		BIT(1)
+#define AQ_RX_PD_L4_TYPE_MASK	0x1C
+#define AQ_RX_PD_L4_UDP		0x04
+#define AQ_RX_PD_L4_TCP		0x10
+#define AQ_RX_PD_L3_TYPE_MASK	0x60
+#define AQ_RX_PD_L3_IP		0x20
+#define AQ_RX_PD_L3_IP6		0x40
+
 #define AQ_RX_PD_RX_OK		BIT(11)
 #define AQ_RX_PD_DROP		BIT(31)
 #define AQ_RX_PD_LEN_MASK	0x7FFF0000
#11 lore raw

PATCH v4 11/21 net: usb: aqc111: Add support for changing MTU

In reply to Igor Russkikh

From: Dmitry Bezrukov <[email protected]>

---
 drivers/net/usb/aqc111.c | 60 +++++++++++++++++++++++++++++++++++++++-
 1 file changed, 59 insertions(+), 1 deletion(-)
drivers/net/usb/aqc111.c +59 -1
index 1c8812e082bf..641e68ee017a 100644
@@ -205,6 +205,48 @@ static void aqc111_set_phy_speed(struct usbnet *dev, u8 autoneg, u16 speed)
 	aqc111_write32_cmd(dev, AQ_PHY_OPS, 0, 0, &aqc111_data->phy_cfg);
 }
 
+static int aqc111_change_mtu(struct net_device *net, int new_mtu)
+{
+	struct usbnet *dev = netdev_priv(net);
+	u16 reg16 = 0;
+	u8 buf[5];
+
+	net->mtu = new_mtu;
+	dev->hard_mtu = net->mtu + net->hard_header_len;
+
+	aqc111_read16_cmd(dev, AQ_ACCESS_MAC, SFR_MEDIUM_STATUS_MODE,
+			  2, &reg16);
+	if (net->mtu > 1500)
+		reg16 |= SFR_MEDIUM_JUMBO_EN;
+	else
+		reg16 &= ~SFR_MEDIUM_JUMBO_EN;
+
+	aqc111_write16_cmd(dev, AQ_ACCESS_MAC, SFR_MEDIUM_STATUS_MODE,
+			   2, &reg16);
+
+	if (dev->net->mtu > 12500 && dev->net->mtu <= 16334) {
+		memcpy(buf, &AQC111_BULKIN_SIZE[2], 5);
+		/* RX bulk configuration */
+		aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_RX_BULKIN_QCTRL,
+				 5, 5, buf);
+	}
+
+	/* Set high low water level */
+	if (dev->net->mtu <= 4500)
+		reg16 = 0x0810;
+	else if (dev->net->mtu <= 9500)
+		reg16 = 0x1020;
+	else if (dev->net->mtu <= 12500)
+		reg16 = 0x1420;
+	else if (dev->net->mtu <= 16334)
+		reg16 = 0x1A20;
+
+	aqc111_write16_cmd(dev, AQ_ACCESS_MAC, SFR_PAUSE_WATERLVL_LOW,
+			   2, &reg16);
+
+	return 0;
+}
+
 static int aqc111_set_mac_addr(struct net_device *net, void *p)
 {
 	struct usbnet *dev = netdev_priv(net);
@@ -225,6 +267,7 @@ static const struct net_device_ops aqc111_netdev_ops = {
 	.ndo_start_xmit		= usbnet_start_xmit,
 	.ndo_tx_timeout		= usbnet_tx_timeout,
 	.ndo_get_stats64	= usbnet_get_stats64,
+	.ndo_change_mtu		= aqc111_change_mtu,
 	.ndo_set_mac_address	= aqc111_set_mac_addr,
 	.ndo_validate_addr	= eth_validate_addr,
 };
@@ -301,6 +344,8 @@ static int aqc111_bind(struct usbnet *dev, struct usb_interface *intf)
 	dev->net->needed_headroom += sizeof(u64);
 	dev->net->needed_tailroom += sizeof(u64);
 
+	dev->net->max_mtu = 16334;
+
 	dev->net->netdev_ops = &aqc111_netdev_ops;
 
 	if (usb_device_no_sg_constraint(dev->udev))
@@ -427,12 +472,22 @@ static void aqc111_configure_rx(struct usbnet *dev,
 		break;
 	}
 
+	if (dev->net->mtu > 12500 && dev->net->mtu <= 16334)
+		queue_num = 2; /* For Jumbo packet 16KB */
+
 	memcpy(buf, &AQC111_BULKIN_SIZE[queue_num], 5);
 	/* RX bulk configuration */
 	aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_RX_BULKIN_QCTRL, 5, 5, buf);
 
 	/* Set high low water level */
-	reg16 = 0x0810;
+	if (dev->net->mtu <= 4500)
+		reg16 = 0x0810;
+	else if (dev->net->mtu <= 9500)
+		reg16 = 0x1020;
+	else if (dev->net->mtu <= 12500)
+		reg16 = 0x1420;
+	else if (dev->net->mtu <= 16334)
+		reg16 = 0x1A20;
 
 	aqc111_write16_cmd(dev, AQ_ACCESS_MAC, SFR_PAUSE_WATERLVL_LOW,
 			   2, &reg16);
@@ -507,6 +562,9 @@ static int aqc111_link_reset(struct usbnet *dev)
 		aqc111_read16_cmd(dev, AQ_ACCESS_MAC, SFR_MEDIUM_STATUS_MODE,
 				  2, &reg16);
 
+		if (dev->net->mtu > 1500)
+			reg16 |= SFR_MEDIUM_JUMBO_EN;
+
 		reg16 |= SFR_MEDIUM_RECEIVE_EN | SFR_MEDIUM_RXFLOW_CTRLEN |
 			 SFR_MEDIUM_TXFLOW_CTRLEN;
 		aqc111_write16_cmd(dev, AQ_ACCESS_MAC, SFR_MEDIUM_STATUS_MODE,
#12 lore raw

PATCH v4 12/21 net: usb: aqc111: Add support for enable/disable checksum offload

In reply to Igor Russkikh

From: Dmitry Bezrukov <[email protected]>

---
 drivers/net/usb/aqc111.c | 45 +++++++++++++++++++++++++++++++++++++++-
 drivers/net/usb/aqc111.h |  1 +
 2 files changed, 45 insertions(+), 1 deletion(-)
drivers/net/usb/aqc111.c +44 -1
index 641e68ee017a..7b69d0a5fc4c 100644
@@ -261,6 +261,46 @@ static int aqc111_set_mac_addr(struct net_device *net, void *p)
 				ETH_ALEN, net->dev_addr);
 }
 
+static int aqc111_set_features(struct net_device *net,
+			       netdev_features_t features)
+{
+	struct usbnet *dev = netdev_priv(net);
+	struct aqc111_data *aqc111_data = dev->driver_priv;
+	netdev_features_t changed = net->features ^ features;
+	u8 reg8 = 0;
+
+	if (changed & NETIF_F_IP_CSUM) {
+		aqc111_read_cmd(dev, AQ_ACCESS_MAC, SFR_TXCOE_CTL, 1, 1, &reg8);
+		reg8 ^= SFR_TXCOE_TCP | SFR_TXCOE_UDP;
+		aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_TXCOE_CTL,
+				 1, 1, &reg8);
+	}
+
+	if (changed & NETIF_F_IPV6_CSUM) {
+		aqc111_read_cmd(dev, AQ_ACCESS_MAC, SFR_TXCOE_CTL, 1, 1, &reg8);
+		reg8 ^= SFR_TXCOE_TCPV6 | SFR_TXCOE_UDPV6;
+		aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_TXCOE_CTL,
+				 1, 1, &reg8);
+	}
+
+	if (changed & NETIF_F_RXCSUM) {
+		aqc111_read_cmd(dev, AQ_ACCESS_MAC, SFR_RXCOE_CTL, 1, 1, &reg8);
+		if (features & NETIF_F_RXCSUM) {
+			aqc111_data->rx_checksum = 1;
+			reg8 &= ~(SFR_RXCOE_IP | SFR_RXCOE_TCP | SFR_RXCOE_UDP |
+				  SFR_RXCOE_TCPV6 | SFR_RXCOE_UDPV6);
+		} else {
+			aqc111_data->rx_checksum = 0;
+			reg8 |= SFR_RXCOE_IP | SFR_RXCOE_TCP | SFR_RXCOE_UDP |
+				SFR_RXCOE_TCPV6 | SFR_RXCOE_UDPV6;
+		}
+
+		aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_RXCOE_CTL,
+				 1, 1, &reg8);
+	}
+	return 0;
+}
+
 static const struct net_device_ops aqc111_netdev_ops = {
 	.ndo_open		= usbnet_open,
 	.ndo_stop		= usbnet_stop,
@@ -270,6 +310,7 @@ static const struct net_device_ops aqc111_netdev_ops = {
 	.ndo_change_mtu		= aqc111_change_mtu,
 	.ndo_set_mac_address	= aqc111_set_mac_addr,
 	.ndo_validate_addr	= eth_validate_addr,
+	.ndo_set_features	= aqc111_set_features,
 };
 
 static int aqc111_read_perm_mac(struct usbnet *dev)
@@ -680,6 +721,7 @@ static void aqc111_rx_checksum(struct sk_buff *skb, u64 pkt_desc)
 
 static int aqc111_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
 {
+	struct aqc111_data *aqc111_data = dev->driver_priv;
 	struct sk_buff *new_skb = NULL;
 	u32 pkt_total_offset = 0;
 	u64 *pkt_desc_ptr = NULL;
@@ -755,7 +797,8 @@ static int aqc111_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
 		skb_set_tail_pointer(new_skb, new_skb->len);
 
 		new_skb->truesize = SKB_TRUESIZE(new_skb->len);
-		aqc111_rx_checksum(new_skb, pkt_desc);
+		if (aqc111_data->rx_checksum)
+			aqc111_rx_checksum(new_skb, pkt_desc);
 
 		usbnet_skb_return(dev, new_skb);
 		if (pkt_count == 0)
drivers/net/usb/aqc111.h +1 -0
index ff43443bfd72..165cdb8b022c 100644
@@ -138,6 +138,7 @@
 /******************************************************************************/
 
 struct aqc111_data {
+	u8 rx_checksum;
 	u8 link_speed;
 	u8 link;
 	u8 autoneg;
#13 lore raw

PATCH v4 13/21 net: usb: aqc111: Add support for TSO

In reply to Igor Russkikh

From: Dmitry Bezrukov <[email protected]>

---
 drivers/net/usb/aqc111.c | 6 ++++++
 drivers/net/usb/aqc111.h | 8 ++++++--
 2 files changed, 12 insertions(+), 2 deletions(-)
drivers/net/usb/aqc111.c +6 -0
index 7b69d0a5fc4c..fce2a4f65122 100644
@@ -395,6 +395,8 @@ static int aqc111_bind(struct usbnet *dev, struct usb_interface *intf)
 	dev->net->hw_features |= AQ_SUPPORT_HW_FEATURE;
 	dev->net->features |= AQ_SUPPORT_FEATURE;
 
+	netif_set_gso_max_size(dev->net, 65535);
+
 	aqc111_read_fw_version(dev, aqc111_data);
 	aqc111_data->autoneg = AUTONEG_ENABLE;
 	aqc111_data->advertised_speed = (usb_speed == USB_SPEED_SUPER) ?
@@ -832,6 +834,10 @@ static struct sk_buff *aqc111_tx_fixup(struct usbnet *dev, struct sk_buff *skb,
 	/*Length of actual data*/
 	tx_desc |= skb->len & AQ_TX_DESC_LEN_MASK;
 
+	/* TSO MSS */
+	tx_desc |= ((u64)(skb_shinfo(skb)->gso_size & AQ_TX_DESC_MSS_MASK)) <<
+		   AQ_TX_DESC_MSS_SHIFT;
+
 	headroom = (skb->len + sizeof(tx_desc)) % 8;
 	if (headroom != 0)
 		padding_size = 8 - headroom;
drivers/net/usb/aqc111.h +6 -2
index 165cdb8b022c..a7a333914461 100644
@@ -22,10 +22,12 @@
 
 /* Feature. ********************************************/
 #define AQ_SUPPORT_FEATURE	(NETIF_F_SG | NETIF_F_IP_CSUM |\
-				 NETIF_F_IPV6_CSUM | NETIF_F_RXCSUM)
+				 NETIF_F_IPV6_CSUM | NETIF_F_RXCSUM |\
+				 NETIF_F_TSO)
 
 #define AQ_SUPPORT_HW_FEATURE	(NETIF_F_SG | NETIF_F_IP_CSUM |\
-				 NETIF_F_IPV6_CSUM | NETIF_F_RXCSUM)
+				 NETIF_F_IPV6_CSUM | NETIF_F_RXCSUM |\
+				 NETIF_F_TSO)
 
 /* SFR Reg. ********************************************/
 
@@ -162,6 +164,8 @@ struct aqc111_data {
 /* TX Descriptor */
 #define AQ_TX_DESC_LEN_MASK	0x1FFFFF
 #define AQ_TX_DESC_DROP_PADD	BIT(28)
+#define AQ_TX_DESC_MSS_MASK	0x7FFF
+#define AQ_TX_DESC_MSS_SHIFT	0x20
 
 #define AQ_RX_HW_PAD			0x02
 
#14 lore raw

PATCH v4 14/21 net: usb: aqc111: Implement set_rx_mode callback

In reply to Igor Russkikh

From: Dmitry Bezrukov <[email protected]>

---
 drivers/net/usb/aqc111.c | 72 +++++++++++++++++++++++++++++++++++++---
 drivers/net/usb/aqc111.h |  4 +++
 2 files changed, 71 insertions(+), 5 deletions(-)
drivers/net/usb/aqc111.c +67 -5
index fce2a4f65122..29131b1e0eec 100644
@@ -11,6 +11,7 @@
 #include <linux/netdevice.h>
 #include <linux/mii.h>
 #include <linux/usb.h>
+#include <linux/crc32.h>
 #include <linux/if_vlan.h>
 #include <linux/usb/cdc.h>
 #include <linux/usb/usbnet.h>
@@ -158,6 +159,25 @@ static int aqc111_write32_cmd(struct usbnet *dev, u8 cmd, u16 value,
 	return aqc111_write_cmd(dev, cmd, value, index, sizeof(tmp), &tmp);
 }
 
+static int aqc111_write_cmd_async(struct usbnet *dev, u8 cmd, u16 value,
+				  u16 index, u16 size, void *data)
+{
+	return usbnet_write_cmd_async(dev, cmd, USB_DIR_OUT | USB_TYPE_VENDOR |
+				      USB_RECIP_DEVICE, value, index, data,
+				      size);
+}
+
+static int aqc111_write16_cmd_async(struct usbnet *dev, u8 cmd, u16 value,
+				    u16 index, u16 *data)
+{
+	u16 tmp = *data;
+
+	cpu_to_le16s(&tmp);
+
+	return aqc111_write_cmd_async(dev, cmd, value, index,
+				      sizeof(tmp), &tmp);
+}
+
 static void aqc111_set_phy_speed(struct usbnet *dev, u8 autoneg, u16 speed)
 {
 	struct aqc111_data *aqc111_data = dev->driver_priv;
@@ -261,6 +281,43 @@ static int aqc111_set_mac_addr(struct net_device *net, void *p)
 				ETH_ALEN, net->dev_addr);
 }
 
+static void aqc111_set_rx_mode(struct net_device *net)
+{
+	struct usbnet *dev = netdev_priv(net);
+	struct aqc111_data *aqc111_data = dev->driver_priv;
+	int mc_count = 0;
+
+	mc_count = netdev_mc_count(net);
+
+	aqc111_data->rxctl &= ~(SFR_RX_CTL_PRO | SFR_RX_CTL_AMALL |
+				SFR_RX_CTL_AM);
+
+	if (net->flags & IFF_PROMISC) {
+		aqc111_data->rxctl |= SFR_RX_CTL_PRO;
+	} else if ((net->flags & IFF_ALLMULTI) || mc_count > AQ_MAX_MCAST) {
+		aqc111_data->rxctl |= SFR_RX_CTL_AMALL;
+	} else if (!netdev_mc_empty(net)) {
+		u8 m_filter[AQ_MCAST_FILTER_SIZE] = { 0 };
+		struct netdev_hw_addr *ha = NULL;
+		u32 crc_bits = 0;
+
+		netdev_for_each_mc_addr(ha, net) {
+			crc_bits = ether_crc(ETH_ALEN, ha->addr) >> 26;
+			m_filter[crc_bits >> 3] |= BIT(crc_bits & 7);
+		}
+
+		aqc111_write_cmd_async(dev, AQ_ACCESS_MAC,
+				       SFR_MULTI_FILTER_ARRY,
+				       AQ_MCAST_FILTER_SIZE,
+				       AQ_MCAST_FILTER_SIZE, m_filter);
+
+		aqc111_data->rxctl |= SFR_RX_CTL_AM;
+	}
+
+	aqc111_write16_cmd_async(dev, AQ_ACCESS_MAC, SFR_RX_CTL,
+				 2, &aqc111_data->rxctl);
+}
+
 static int aqc111_set_features(struct net_device *net,
 			       netdev_features_t features)
 {
@@ -310,6 +367,7 @@ static const struct net_device_ops aqc111_netdev_ops = {
 	.ndo_change_mtu		= aqc111_change_mtu,
 	.ndo_set_mac_address	= aqc111_set_mac_addr,
 	.ndo_validate_addr	= eth_validate_addr,
+	.ndo_set_rx_mode	= aqc111_set_rx_mode,
 	.ndo_set_features	= aqc111_set_features,
 };
 
@@ -582,6 +640,7 @@ static int aqc111_link_reset(struct usbnet *dev)
 		aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_ARC_CTRL, 1, 1, &reg8);
 
 		reg16 = SFR_RX_CTL_IPE | SFR_RX_CTL_AB;
+		aqc111_data->rxctl = reg16;
 		aqc111_write16_cmd(dev, AQ_ACCESS_MAC, SFR_RX_CTL, 2, &reg16);
 
 		reg8 = SFR_RX_PATH_READY;
@@ -602,6 +661,8 @@ static int aqc111_link_reset(struct usbnet *dev)
 
 		aqc111_configure_csum_offload(dev);
 
+		aqc111_set_rx_mode(dev->net);
+
 		aqc111_read16_cmd(dev, AQ_ACCESS_MAC, SFR_MEDIUM_STATUS_MODE,
 				  2, &reg16);
 
@@ -613,8 +674,9 @@ static int aqc111_link_reset(struct usbnet *dev)
 		aqc111_write16_cmd(dev, AQ_ACCESS_MAC, SFR_MEDIUM_STATUS_MODE,
 				   2, &reg16);
 
-		reg16 = SFR_RX_CTL_IPE | SFR_RX_CTL_AB | SFR_RX_CTL_START;
-		aqc111_write16_cmd(dev, AQ_ACCESS_MAC, SFR_RX_CTL, 2, &reg16);
+		aqc111_data->rxctl |= SFR_RX_CTL_START;
+		aqc111_write16_cmd(dev, AQ_ACCESS_MAC, SFR_RX_CTL,
+				   2, &aqc111_data->rxctl);
 
 		netif_carrier_on(dev->net);
 	} else {
@@ -624,9 +686,9 @@ static int aqc111_link_reset(struct usbnet *dev)
 		aqc111_write16_cmd(dev, AQ_ACCESS_MAC, SFR_MEDIUM_STATUS_MODE,
 				   2, &reg16);
 
-		aqc111_read16_cmd(dev, AQ_ACCESS_MAC, SFR_RX_CTL, 2, &reg16);
-		reg16 &= ~SFR_RX_CTL_START;
-		aqc111_write16_cmd(dev, AQ_ACCESS_MAC, SFR_RX_CTL, 2, &reg16);
+		aqc111_data->rxctl &= ~SFR_RX_CTL_START;
+		aqc111_write16_cmd(dev, AQ_ACCESS_MAC, SFR_RX_CTL,
+				   2, &aqc111_data->rxctl);
 
 		reg8 = SFR_BULK_OUT_FLUSH_EN | SFR_BULK_OUT_EFF_EN;
 		aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_BULK_OUT_CTRL,
drivers/net/usb/aqc111.h +4 -0
index a7a333914461..a57c1184c422 100644
@@ -12,6 +12,9 @@
 
 #define URB_SIZE	(1024 * 62)
 
+#define AQ_MCAST_FILTER_SIZE		8
+#define AQ_MAX_MCAST			64
+
 #define AQ_ACCESS_MAC			0x01
 #define AQ_FLASH_PARAMETERS		0x20
 #define AQ_PHY_POWER			0x31
@@ -140,6 +143,7 @@
 /******************************************************************************/
 
 struct aqc111_data {
+	u16 rxctl;
 	u8 rx_checksum;
 	u8 link_speed;
 	u8 link;
#15 lore raw

PATCH v4 15/21 net: usb: aqc111: Add support for VLAN_CTAG_TX/RX offload

In reply to Igor Russkikh

From: Dmitry Bezrukov <[email protected]>

---
 drivers/net/usb/aqc111.c | 17 +++++++++++++++++
 drivers/net/usb/aqc111.h | 12 +++++++++++-
 2 files changed, 28 insertions(+), 1 deletion(-)
drivers/net/usb/aqc111.c +17 -0
index 29131b1e0eec..3590d18ac44d 100644
@@ -452,6 +452,7 @@ static int aqc111_bind(struct usbnet *dev, struct usb_interface *intf)
 
 	dev->net->hw_features |= AQ_SUPPORT_HW_FEATURE;
 	dev->net->features |= AQ_SUPPORT_FEATURE;
+	dev->net->vlan_features |= AQ_SUPPORT_VLAN_FEATURE;
 
 	netif_set_gso_max_size(dev->net, 65535);
 
@@ -714,6 +715,7 @@ static int aqc111_reset(struct usbnet *dev)
 
 	dev->net->hw_features |= AQ_SUPPORT_HW_FEATURE;
 	dev->net->features |= AQ_SUPPORT_FEATURE;
+	dev->net->vlan_features |= AQ_SUPPORT_VLAN_FEATURE;
 
 	/* Power up ethernet PHY */
 	aqc111_data->phy_cfg = AQ_PHY_POWER_EN;
@@ -793,6 +795,7 @@ static int aqc111_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
 	u32 desc_offset = 0; /*RX Header Offset*/
 	u16 pkt_count = 0;
 	u64 desc_hdr = 0;
+	u16 vlan_tag = 0;
 	u32 skb_len = 0;
 
 	if (!skb)
@@ -864,6 +867,12 @@ static int aqc111_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
 		if (aqc111_data->rx_checksum)
 			aqc111_rx_checksum(new_skb, pkt_desc);
 
+		if (pkt_desc & AQ_RX_PD_VLAN) {
+			vlan_tag = pkt_desc >> AQ_RX_PD_VLAN_SHIFT;
+			__vlan_hwaccel_put_tag(new_skb, htons(ETH_P_8021Q),
+					       vlan_tag & VLAN_VID_MASK);
+		}
+
 		usbnet_skb_return(dev, new_skb);
 		if (pkt_count == 0)
 			break;
@@ -892,6 +901,7 @@ static struct sk_buff *aqc111_tx_fixup(struct usbnet *dev, struct sk_buff *skb,
 	int headroom = 0;
 	int tailroom = 0;
 	u64 tx_desc = 0;
+	u16 tci = 0;
 
 	/*Length of actual data*/
 	tx_desc |= skb->len & AQ_TX_DESC_LEN_MASK;
@@ -909,6 +919,13 @@ static struct sk_buff *aqc111_tx_fixup(struct usbnet *dev, struct sk_buff *skb,
 		tx_desc |= AQ_TX_DESC_DROP_PADD;
 	}
 
+	/* Vlan Tag */
+	if (vlan_get_tag(skb, &tci) >= 0) {
+		tx_desc |= AQ_TX_DESC_VLAN;
+		tx_desc |= ((u64)tci & AQ_TX_DESC_VLAN_MASK) <<
+			   AQ_TX_DESC_VLAN_SHIFT;
+	}
+
 	if (!dev->can_dma_sg && (dev->net->features & NETIF_F_SG) &&
 	    skb_linearize(skb))
 		return NULL;
drivers/net/usb/aqc111.h +11 -1
index a57c1184c422..d09326d46273 100644
@@ -26,12 +26,17 @@
 /* Feature. ********************************************/
 #define AQ_SUPPORT_FEATURE	(NETIF_F_SG | NETIF_F_IP_CSUM |\
 				 NETIF_F_IPV6_CSUM | NETIF_F_RXCSUM |\
-				 NETIF_F_TSO)
+				 NETIF_F_TSO | NETIF_F_HW_VLAN_CTAG_TX |\
+				 NETIF_F_HW_VLAN_CTAG_RX)
 
 #define AQ_SUPPORT_HW_FEATURE	(NETIF_F_SG | NETIF_F_IP_CSUM |\
 				 NETIF_F_IPV6_CSUM | NETIF_F_RXCSUM |\
 				 NETIF_F_TSO)
 
+#define AQ_SUPPORT_VLAN_FEATURE (NETIF_F_SG | NETIF_F_IP_CSUM |\
+				 NETIF_F_IPV6_CSUM | NETIF_F_RXCSUM |\
+				 NETIF_F_TSO)
+
 /* SFR Reg. ********************************************/
 
 #define SFR_GENERAL_STATUS		0x03
@@ -168,8 +173,11 @@ struct aqc111_data {
 /* TX Descriptor */
 #define AQ_TX_DESC_LEN_MASK	0x1FFFFF
 #define AQ_TX_DESC_DROP_PADD	BIT(28)
+#define AQ_TX_DESC_VLAN		BIT(29)
 #define AQ_TX_DESC_MSS_MASK	0x7FFF
 #define AQ_TX_DESC_MSS_SHIFT	0x20
+#define AQ_TX_DESC_VLAN_MASK	0xFFFF
+#define AQ_TX_DESC_VLAN_SHIFT	0x30
 
 #define AQ_RX_HW_PAD			0x02
 
@@ -183,10 +191,12 @@ struct aqc111_data {
 #define AQ_RX_PD_L3_IP		0x20
 #define AQ_RX_PD_L3_IP6		0x40
 
+#define AQ_RX_PD_VLAN		BIT(10)
 #define AQ_RX_PD_RX_OK		BIT(11)
 #define AQ_RX_PD_DROP		BIT(31)
 #define AQ_RX_PD_LEN_MASK	0x7FFF0000
 #define AQ_RX_PD_LEN_SHIFT	0x10
+#define AQ_RX_PD_VLAN_SHIFT	0x20
 
 /* RX Descriptor header */
 #define AQ_RX_DH_PKT_CNT_MASK		0x1FFF
#16 lore raw

PATCH v4 16/21 net: usb: aqc111: Add RX VLAN filtering support

In reply to Igor Russkikh

From: Dmitry Bezrukov <[email protected]>

---
 drivers/net/usb/aqc111.c | 89 ++++++++++++++++++++++++++++++++++++++++
 drivers/net/usb/aqc111.h |  2 +-
 2 files changed, 90 insertions(+), 1 deletion(-)
drivers/net/usb/aqc111.c +89 -0
index 3590d18ac44d..3b42843d7017 100644
@@ -281,6 +281,57 @@ static int aqc111_set_mac_addr(struct net_device *net, void *p)
 				ETH_ALEN, net->dev_addr);
 }
 
+static int aqc111_vlan_rx_kill_vid(struct net_device *net,
+				   __be16 proto, u16 vid)
+{
+	struct usbnet *dev = netdev_priv(net);
+	u8 vlan_ctrl = 0;
+	u16 reg16 = 0;
+	u8 reg8 = 0;
+
+	aqc111_read_cmd(dev, AQ_ACCESS_MAC, SFR_VLAN_ID_CONTROL, 1, 1, &reg8);
+	vlan_ctrl = reg8;
+
+	/* Address */
+	reg8 = (vid / 16);
+	aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_VLAN_ID_ADDRESS, 1, 1, &reg8);
+	/* Data */
+	reg8 = vlan_ctrl | SFR_VLAN_CONTROL_RD;
+	aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_VLAN_ID_CONTROL, 1, 1, &reg8);
+	aqc111_read16_cmd(dev, AQ_ACCESS_MAC, SFR_VLAN_ID_DATA0, 2, &reg16);
+	reg16 &= ~(1 << (vid % 16));
+	aqc111_write16_cmd(dev, AQ_ACCESS_MAC, SFR_VLAN_ID_DATA0, 2, &reg16);
+	reg8 = vlan_ctrl | SFR_VLAN_CONTROL_WE;
+	aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_VLAN_ID_CONTROL, 1, 1, &reg8);
+
+	return 0;
+}
+
+static int aqc111_vlan_rx_add_vid(struct net_device *net, __be16 proto, u16 vid)
+{
+	struct usbnet *dev = netdev_priv(net);
+	u8 vlan_ctrl = 0;
+	u16 reg16 = 0;
+	u8 reg8 = 0;
+
+	aqc111_read_cmd(dev, AQ_ACCESS_MAC, SFR_VLAN_ID_CONTROL, 1, 1, &reg8);
+	vlan_ctrl = reg8;
+
+	/* Address */
+	reg8 = (vid / 16);
+	aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_VLAN_ID_ADDRESS, 1, 1, &reg8);
+	/* Data */
+	reg8 = vlan_ctrl | SFR_VLAN_CONTROL_RD;
+	aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_VLAN_ID_CONTROL, 1, 1, &reg8);
+	aqc111_read16_cmd(dev, AQ_ACCESS_MAC, SFR_VLAN_ID_DATA0, 2, &reg16);
+	reg16 |= (1 << (vid % 16));
+	aqc111_write16_cmd(dev, AQ_ACCESS_MAC, SFR_VLAN_ID_DATA0, 2, &reg16);
+	reg8 = vlan_ctrl | SFR_VLAN_CONTROL_WE;
+	aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_VLAN_ID_CONTROL, 1, 1, &reg8);
+
+	return 0;
+}
+
 static void aqc111_set_rx_mode(struct net_device *net)
 {
 	struct usbnet *dev = netdev_priv(net);
@@ -324,6 +375,7 @@ static int aqc111_set_features(struct net_device *net,
 	struct usbnet *dev = netdev_priv(net);
 	struct aqc111_data *aqc111_data = dev->driver_priv;
 	netdev_features_t changed = net->features ^ features;
+	u16 reg16 = 0;
 	u8 reg8 = 0;
 
 	if (changed & NETIF_F_IP_CSUM) {
@@ -355,6 +407,39 @@ static int aqc111_set_features(struct net_device *net,
 		aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_RXCOE_CTL,
 				 1, 1, &reg8);
 	}
+	if (changed & NETIF_F_HW_VLAN_CTAG_FILTER) {
+		if (features & NETIF_F_HW_VLAN_CTAG_FILTER) {
+			u16 i = 0;
+
+			for (i = 0; i < 256; i++) {
+				/* Address */
+				reg8 = i;
+				aqc111_write_cmd(dev, AQ_ACCESS_MAC,
+						 SFR_VLAN_ID_ADDRESS,
+						 1, 1, &reg8);
+				/* Data */
+				aqc111_write16_cmd(dev, AQ_ACCESS_MAC,
+						   SFR_VLAN_ID_DATA0,
+						   2, &reg16);
+				reg8 = SFR_VLAN_CONTROL_WE;
+				aqc111_write_cmd(dev, AQ_ACCESS_MAC,
+						 SFR_VLAN_ID_CONTROL,
+						 1, 1, &reg8);
+			}
+			aqc111_read_cmd(dev, AQ_ACCESS_MAC, SFR_VLAN_ID_CONTROL,
+					1, 1, &reg8);
+			reg8 |= SFR_VLAN_CONTROL_VFE;
+			aqc111_write_cmd(dev, AQ_ACCESS_MAC,
+					 SFR_VLAN_ID_CONTROL, 1, 1, &reg8);
+		} else {
+			aqc111_read_cmd(dev, AQ_ACCESS_MAC, SFR_VLAN_ID_CONTROL,
+					1, 1, &reg8);
+			reg8 &= ~SFR_VLAN_CONTROL_VFE;
+			aqc111_write_cmd(dev, AQ_ACCESS_MAC,
+					 SFR_VLAN_ID_CONTROL, 1, 1, &reg8);
+		}
+	}
+
 	return 0;
 }
 
@@ -367,6 +452,8 @@ static const struct net_device_ops aqc111_netdev_ops = {
 	.ndo_change_mtu		= aqc111_change_mtu,
 	.ndo_set_mac_address	= aqc111_set_mac_addr,
 	.ndo_validate_addr	= eth_validate_addr,
+	.ndo_vlan_rx_add_vid	= aqc111_vlan_rx_add_vid,
+	.ndo_vlan_rx_kill_vid	= aqc111_vlan_rx_kill_vid,
 	.ndo_set_rx_mode	= aqc111_set_rx_mode,
 	.ndo_set_features	= aqc111_set_features,
 };
@@ -627,6 +714,8 @@ static int aqc111_link_reset(struct usbnet *dev)
 
 		/* Vlan Tag Filter */
 		reg8 = SFR_VLAN_CONTROL_VSO;
+		if (dev->net->features & NETIF_F_HW_VLAN_CTAG_FILTER)
+			reg8 |= SFR_VLAN_CONTROL_VFE;
 
 		aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_VLAN_ID_CONTROL,
 				 1, 1, &reg8);
drivers/net/usb/aqc111.h +1 -1
index d09326d46273..0ce23a741211 100644
@@ -31,7 +31,7 @@
 
 #define AQ_SUPPORT_HW_FEATURE	(NETIF_F_SG | NETIF_F_IP_CSUM |\
 				 NETIF_F_IPV6_CSUM | NETIF_F_RXCSUM |\
-				 NETIF_F_TSO)
+				 NETIF_F_TSO | NETIF_F_HW_VLAN_CTAG_FILTER)
 
 #define AQ_SUPPORT_VLAN_FEATURE (NETIF_F_SG | NETIF_F_IP_CSUM |\
 				 NETIF_F_IPV6_CSUM | NETIF_F_RXCSUM |\
#17 lore raw

PATCH v4 17/21 net: usb: aqc111: Initialize ethtool_ops structure

In reply to Igor Russkikh

From: Dmitry Bezrukov <[email protected]>

Implement get_drvinfo, set/get_msglevel, get_link callbacks

---
 drivers/net/usb/aqc111.c | 28 ++++++++++++++++++++++++++++
 1 file changed, 28 insertions(+)
drivers/net/usb/aqc111.c +28 -0
index 3b42843d7017..9fc036d5354b 100644
@@ -9,6 +9,7 @@
 
 #include <linux/module.h>
 #include <linux/netdevice.h>
+#include <linux/ethtool.h>
 #include <linux/mii.h>
 #include <linux/usb.h>
 #include <linux/crc32.h>
@@ -18,6 +19,8 @@
 
 #include "aqc111.h"
 
+#define DRIVER_NAME "aqc111"
+
 static int aqc111_read_cmd_nopm(struct usbnet *dev, u8 cmd, u16 value,
 				u16 index, u16 size, void *data)
 {
@@ -178,6 +181,23 @@ static int aqc111_write16_cmd_async(struct usbnet *dev, u8 cmd, u16 value,
 				      sizeof(tmp), &tmp);
 }
 
+static void aqc111_get_drvinfo(struct net_device *net,
+			       struct ethtool_drvinfo *info)
+{
+	struct usbnet *dev = netdev_priv(net);
+	struct aqc111_data *aqc111_data = dev->driver_priv;
+
+	/* Inherit standard device info */
+	usbnet_get_drvinfo(net, info);
+	strlcpy(info->driver, DRIVER_NAME, sizeof(info->driver));
+	snprintf(info->fw_version, sizeof(info->fw_version), "%u.%u.%u",
+		 aqc111_data->fw_ver.major,
+		 aqc111_data->fw_ver.minor,
+		 aqc111_data->fw_ver.rev);
+	info->eedump_len = 0x00;
+	info->regdump_len = 0x00;
+}
+
 static void aqc111_set_phy_speed(struct usbnet *dev, u8 autoneg, u16 speed)
 {
 	struct aqc111_data *aqc111_data = dev->driver_priv;
@@ -225,6 +245,13 @@ static void aqc111_set_phy_speed(struct usbnet *dev, u8 autoneg, u16 speed)
 	aqc111_write32_cmd(dev, AQ_PHY_OPS, 0, 0, &aqc111_data->phy_cfg);
 }
 
+static const struct ethtool_ops aqc111_ethtool_ops = {
+	.get_drvinfo = aqc111_get_drvinfo,
+	.get_msglevel = usbnet_get_msglevel,
+	.set_msglevel = usbnet_set_msglevel,
+	.get_link = ethtool_op_get_link,
+};
+
 static int aqc111_change_mtu(struct net_device *net, int new_mtu)
 {
 	struct usbnet *dev = netdev_priv(net);
@@ -533,6 +560,7 @@ static int aqc111_bind(struct usbnet *dev, struct usb_interface *intf)
 	dev->net->max_mtu = 16334;
 
 	dev->net->netdev_ops = &aqc111_netdev_ops;
+	dev->net->ethtool_ops = &aqc111_ethtool_ops;
 
 	if (usb_device_no_sg_constraint(dev->udev))
 		dev->can_dma_sg = 1;
#18 lore raw

PATCH v4 18/21 net: usb: aqc111: Implement get/set_link_ksettings callbacks

In reply to Igor Russkikh

From: Dmitry Bezrukov <[email protected]>

---
 drivers/net/usb/aqc111.c | 124 +++++++++++++++++++++++++++++++++++++++
 1 file changed, 124 insertions(+)
drivers/net/usb/aqc111.c +124 -0
index 9fc036d5354b..650cea33f1a3 100644
@@ -16,6 +16,7 @@
 #include <linux/if_vlan.h>
 #include <linux/usb/cdc.h>
 #include <linux/usb/usbnet.h>
+#include <linux/linkmode.h>
 
 #include "aqc111.h"
 
@@ -198,6 +199,84 @@ static void aqc111_get_drvinfo(struct net_device *net,
 	info->regdump_len = 0x00;
 }
 
+static void aqc111_speed_to_link_mode(u32 speed,
+				      struct ethtool_link_ksettings *elk)
+{
+	switch (speed) {
+	case SPEED_5000:
+		ethtool_link_ksettings_add_link_mode(elk, advertising,
+						     5000baseT_Full);
+		break;
+	case SPEED_2500:
+		ethtool_link_ksettings_add_link_mode(elk, advertising,
+						     2500baseT_Full);
+		break;
+	case SPEED_1000:
+		ethtool_link_ksettings_add_link_mode(elk, advertising,
+						     1000baseT_Full);
+		break;
+	case SPEED_100:
+		ethtool_link_ksettings_add_link_mode(elk, advertising,
+						     100baseT_Full);
+		break;
+	}
+}
+
+static int aqc111_get_link_ksettings(struct net_device *net,
+				     struct ethtool_link_ksettings *elk)
+{
+	struct usbnet *dev = netdev_priv(net);
+	struct aqc111_data *aqc111_data = dev->driver_priv;
+	enum usb_device_speed usb_speed = dev->udev->speed;
+	u32 speed = SPEED_UNKNOWN;
+
+	ethtool_link_ksettings_zero_link_mode(elk, supported);
+	ethtool_link_ksettings_add_link_mode(elk, supported,
+					     100baseT_Full);
+	ethtool_link_ksettings_add_link_mode(elk, supported,
+					     1000baseT_Full);
+	if (usb_speed == USB_SPEED_SUPER) {
+		ethtool_link_ksettings_add_link_mode(elk, supported,
+						     2500baseT_Full);
+		ethtool_link_ksettings_add_link_mode(elk, supported,
+						     5000baseT_Full);
+	}
+	ethtool_link_ksettings_add_link_mode(elk, supported, TP);
+	ethtool_link_ksettings_add_link_mode(elk, supported, Autoneg);
+
+	elk->base.port = PORT_TP;
+	elk->base.transceiver = XCVR_INTERNAL;
+
+	elk->base.mdio_support = 0x00; /*Not supported*/
+
+	if (aqc111_data->autoneg)
+		linkmode_copy(elk->link_modes.advertising,
+			      elk->link_modes.supported);
+	else
+		aqc111_speed_to_link_mode(aqc111_data->advertised_speed, elk);
+
+	elk->base.autoneg = aqc111_data->autoneg;
+
+	switch (aqc111_data->link_speed) {
+	case AQ_INT_SPEED_5G:
+		speed = SPEED_5000;
+		break;
+	case AQ_INT_SPEED_2_5G:
+		speed = SPEED_2500;
+		break;
+	case AQ_INT_SPEED_1G:
+		speed = SPEED_1000;
+		break;
+	case AQ_INT_SPEED_100M:
+		speed = SPEED_100;
+		break;
+	}
+	elk->base.duplex = DUPLEX_FULL;
+	elk->base.speed = speed;
+
+	return 0;
+}
+
 static void aqc111_set_phy_speed(struct usbnet *dev, u8 autoneg, u16 speed)
 {
 	struct aqc111_data *aqc111_data = dev->driver_priv;
@@ -245,11 +324,56 @@ static void aqc111_set_phy_speed(struct usbnet *dev, u8 autoneg, u16 speed)
 	aqc111_write32_cmd(dev, AQ_PHY_OPS, 0, 0, &aqc111_data->phy_cfg);
 }
 
+static int aqc111_set_link_ksettings(struct net_device *net,
+				     const struct ethtool_link_ksettings *elk)
+{
+	struct usbnet *dev = netdev_priv(net);
+	struct aqc111_data *aqc111_data = dev->driver_priv;
+	enum usb_device_speed usb_speed = dev->udev->speed;
+	u8 autoneg = elk->base.autoneg;
+	u32 speed = elk->base.speed;
+
+	if (autoneg == AUTONEG_ENABLE) {
+		if (aqc111_data->autoneg != AUTONEG_ENABLE) {
+			aqc111_data->autoneg = AUTONEG_ENABLE;
+			aqc111_data->advertised_speed =
+					(usb_speed == USB_SPEED_SUPER) ?
+					 SPEED_5000 : SPEED_1000;
+			aqc111_set_phy_speed(dev, aqc111_data->autoneg,
+					     aqc111_data->advertised_speed);
+		}
+	} else {
+		if (speed != SPEED_100 &&
+		    speed != SPEED_1000 &&
+		    speed != SPEED_2500 &&
+		    speed != SPEED_5000 &&
+		    speed != SPEED_UNKNOWN)
+			return -EINVAL;
+
+		if (elk->base.duplex != DUPLEX_FULL)
+			return -EINVAL;
+
+		if (usb_speed != USB_SPEED_SUPER && speed > SPEED_1000)
+			return -EINVAL;
+
+		aqc111_data->autoneg = AUTONEG_DISABLE;
+		if (speed != SPEED_UNKNOWN)
+			aqc111_data->advertised_speed = speed;
+
+		aqc111_set_phy_speed(dev, aqc111_data->autoneg,
+				     aqc111_data->advertised_speed);
+	}
+
+	return 0;
+}
+
 static const struct ethtool_ops aqc111_ethtool_ops = {
 	.get_drvinfo = aqc111_get_drvinfo,
 	.get_msglevel = usbnet_get_msglevel,
 	.set_msglevel = usbnet_set_msglevel,
 	.get_link = ethtool_op_get_link,
+	.get_link_ksettings = aqc111_get_link_ksettings,
+	.set_link_ksettings = aqc111_set_link_ksettings
 };
 
 static int aqc111_change_mtu(struct net_device *net, int new_mtu)
#19 lore raw

PATCH v4 19/21 net: usb: aqc111: Add support for wake on LAN by MAGIC packet

In reply to Igor Russkikh

From: Dmitry Bezrukov <[email protected]>

---
 drivers/net/usb/aqc111.c | 180 +++++++++++++++++++++++++++++++++++++++
 drivers/net/usb/aqc111.h |  12 +++
 2 files changed, 192 insertions(+)
drivers/net/usb/aqc111.c +180 -0
index 650cea33f1a3..7c859b9417f1 100644
@@ -54,6 +54,17 @@ static int aqc111_read_cmd(struct usbnet *dev, u8 cmd, u16 value,
 	return ret;
 }
 
+static int aqc111_read16_cmd_nopm(struct usbnet *dev, u8 cmd, u16 value,
+				  u16 index, u16 *data)
+{
+	int ret = 0;
+
+	ret = aqc111_read_cmd_nopm(dev, cmd, value, index, sizeof(*data), data);
+	le16_to_cpus(data);
+
+	return ret;
+}
+
 static int aqc111_read16_cmd(struct usbnet *dev, u8 cmd, u16 value,
 			     u16 index, u16 *data)
 {
@@ -199,6 +210,35 @@ static void aqc111_get_drvinfo(struct net_device *net,
 	info->regdump_len = 0x00;
 }
 
+static void aqc111_get_wol(struct net_device *net,
+			   struct ethtool_wolinfo *wolinfo)
+{
+	struct usbnet *dev = netdev_priv(net);
+	struct aqc111_data *aqc111_data = dev->driver_priv;
+
+	wolinfo->supported = WAKE_MAGIC;
+	wolinfo->wolopts = 0;
+
+	if (aqc111_data->wol_flags & AQ_WOL_FLAG_MP)
+		wolinfo->wolopts |= WAKE_MAGIC;
+}
+
+static int aqc111_set_wol(struct net_device *net,
+			  struct ethtool_wolinfo *wolinfo)
+{
+	struct usbnet *dev = netdev_priv(net);
+	struct aqc111_data *aqc111_data = dev->driver_priv;
+
+	if (wolinfo->wolopts & ~WAKE_MAGIC)
+		return -EINVAL;
+
+	aqc111_data->wol_flags = 0;
+	if (wolinfo->wolopts & WAKE_MAGIC)
+		aqc111_data->wol_flags |= AQ_WOL_FLAG_MP;
+
+	return 0;
+}
+
 static void aqc111_speed_to_link_mode(u32 speed,
 				      struct ethtool_link_ksettings *elk)
 {
@@ -369,6 +409,8 @@ static int aqc111_set_link_ksettings(struct net_device *net,
 
 static const struct ethtool_ops aqc111_ethtool_ops = {
 	.get_drvinfo = aqc111_get_drvinfo,
+	.get_wol = aqc111_get_wol,
+	.set_wol = aqc111_set_wol,
 	.get_msglevel = usbnet_get_msglevel,
 	.set_msglevel = usbnet_set_msglevel,
 	.get_link = ethtool_op_get_link,
@@ -1207,6 +1249,142 @@ static const struct driver_info aqc111_info = {
 	.tx_fixup	= aqc111_tx_fixup,
 };
 
+static int aqc111_suspend(struct usb_interface *intf, pm_message_t message)
+{
+	struct usbnet *dev = usb_get_intfdata(intf);
+	struct aqc111_data *aqc111_data = dev->driver_priv;
+	u16 temp_rx_ctrl = 0x00;
+	u16 reg16;
+	u8 reg8;
+
+	usbnet_suspend(intf, message);
+
+	aqc111_read16_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_RX_CTL, 2, &reg16);
+	temp_rx_ctrl = reg16;
+	/* Stop RX operations*/
+	reg16 &= ~SFR_RX_CTL_START;
+	aqc111_write16_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_RX_CTL, 2, &reg16);
+	/* Force bz */
+	aqc111_read16_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_PHYPWR_RSTCTL,
+			       2, &reg16);
+	reg16 |= SFR_PHYPWR_RSTCTL_BZ;
+	aqc111_write16_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_PHYPWR_RSTCTL,
+				2, &reg16);
+
+	reg8 = SFR_BULK_OUT_EFF_EN;
+	aqc111_write_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_BULK_OUT_CTRL,
+			      1, 1, &reg8);
+
+	temp_rx_ctrl &= ~(SFR_RX_CTL_START | SFR_RX_CTL_RF_WAK |
+			  SFR_RX_CTL_AP | SFR_RX_CTL_AM);
+	aqc111_write16_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_RX_CTL,
+				2, &temp_rx_ctrl);
+
+	reg8 = 0x00;
+	aqc111_write_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_ETH_MAC_PATH,
+			      1, 1, &reg8);
+
+	if (aqc111_data->wol_flags) {
+		struct aqc111_wol_cfg wol_cfg = { 0 };
+
+		aqc111_data->phy_cfg |= AQ_WOL;
+		ether_addr_copy(wol_cfg.hw_addr, dev->net->dev_addr);
+		wol_cfg.flags = aqc111_data->wol_flags;
+
+		temp_rx_ctrl |= (SFR_RX_CTL_AB | SFR_RX_CTL_START);
+		aqc111_write16_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_RX_CTL,
+					2, &temp_rx_ctrl);
+		reg8 = 0x00;
+		aqc111_write_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_BM_INT_MASK,
+				      1, 1, &reg8);
+		reg8 = SFR_BMRX_DMA_EN;
+		aqc111_write_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_BMRX_DMA_CONTROL,
+				      1, 1, &reg8);
+		reg8 = SFR_RX_PATH_READY;
+		aqc111_write_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_ETH_MAC_PATH,
+				      1, 1, &reg8);
+		reg8 = 0x07;
+		aqc111_write_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_RX_BULKIN_QCTRL,
+				      1, 1, &reg8);
+		reg8 = 0x00;
+		aqc111_write_cmd_nopm(dev, AQ_ACCESS_MAC,
+				      SFR_RX_BULKIN_QTIMR_LOW, 1, 1, &reg8);
+		aqc111_write_cmd_nopm(dev, AQ_ACCESS_MAC,
+				      SFR_RX_BULKIN_QTIMR_HIGH, 1, 1, &reg8);
+		reg8 = 0xFF;
+		aqc111_write_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_RX_BULKIN_QSIZE,
+				      1, 1, &reg8);
+		aqc111_write_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_RX_BULKIN_QIFG,
+				      1, 1, &reg8);
+
+		aqc111_read16_cmd_nopm(dev, AQ_ACCESS_MAC,
+				       SFR_MEDIUM_STATUS_MODE, 2, &reg16);
+		reg16 |= SFR_MEDIUM_RECEIVE_EN;
+		aqc111_write16_cmd_nopm(dev, AQ_ACCESS_MAC,
+					SFR_MEDIUM_STATUS_MODE, 2, &reg16);
+
+		aqc111_write_cmd(dev, AQ_WOL_CFG, 0, 0,
+				 WOL_CFG_SIZE, &wol_cfg);
+		aqc111_write32_cmd(dev, AQ_PHY_OPS, 0, 0,
+				   &aqc111_data->phy_cfg);
+	} else {
+		aqc111_data->phy_cfg |= AQ_LOW_POWER;
+		aqc111_write32_cmd(dev, AQ_PHY_OPS, 0, 0,
+				   &aqc111_data->phy_cfg);
+
+		/* Disable RX path */
+		aqc111_read16_cmd_nopm(dev, AQ_ACCESS_MAC,
+				       SFR_MEDIUM_STATUS_MODE, 2, &reg16);
+		reg16 &= ~SFR_MEDIUM_RECEIVE_EN;
+		aqc111_write16_cmd_nopm(dev, AQ_ACCESS_MAC,
+					SFR_MEDIUM_STATUS_MODE, 2, &reg16);
+	}
+
+	return 0;
+}
+
+static int aqc111_resume(struct usb_interface *intf)
+{
+	struct usbnet *dev = usb_get_intfdata(intf);
+	struct aqc111_data *aqc111_data = dev->driver_priv;
+	u16 reg16;
+	u8 reg8;
+
+	netif_carrier_off(dev->net);
+
+	/* Power up ethernet PHY */
+	aqc111_data->phy_cfg |= AQ_PHY_POWER_EN;
+	aqc111_data->phy_cfg &= ~AQ_LOW_POWER;
+	aqc111_data->phy_cfg &= ~AQ_WOL;
+
+	reg8 = 0xFF;
+	aqc111_write_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_BM_INT_MASK,
+			      1, 1, &reg8);
+	/* Configure RX control register => start operation */
+	reg16 = aqc111_data->rxctl;
+	reg16 &= ~SFR_RX_CTL_START;
+	aqc111_write16_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_RX_CTL, 2, &reg16);
+
+	reg16 |= SFR_RX_CTL_START;
+	aqc111_write16_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_RX_CTL, 2, &reg16);
+
+	aqc111_set_phy_speed(dev, aqc111_data->autoneg,
+			     aqc111_data->advertised_speed);
+
+	aqc111_read16_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_MEDIUM_STATUS_MODE,
+			       2, &reg16);
+	reg16 |= SFR_MEDIUM_RECEIVE_EN;
+	aqc111_write16_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_MEDIUM_STATUS_MODE,
+				2, &reg16);
+	reg8 = SFR_RX_PATH_READY;
+	aqc111_write_cmd_nopm(dev, AQ_ACCESS_MAC, SFR_ETH_MAC_PATH,
+			      1, 1, &reg8);
+	reg8 = 0x0;
+	aqc111_write_cmd(dev, AQ_ACCESS_MAC, SFR_BMRX_DMA_CONTROL, 1, 1, &reg8);
+
+	return usbnet_resume(intf);
+}
+
 #define AQC111_USB_ETH_DEV(vid, pid, table) \
 	USB_DEVICE_INTERFACE_CLASS((vid), (pid), USB_CLASS_VENDOR_SPEC), \
 	.driver_info = (unsigned long)&(table) \
@@ -1228,6 +1406,8 @@ static struct usb_driver aq_driver = {
 	.name		= "aqc111",
 	.id_table	= products,
 	.probe		= usbnet_probe,
+	.suspend	= aqc111_suspend,
+	.resume		= aqc111_resume,
 	.disconnect	= usbnet_disconnect,
 };
 
drivers/net/usb/aqc111.h +12 -0
index 0ce23a741211..4d68b3a6067c 100644
@@ -18,6 +18,7 @@
 #define AQ_ACCESS_MAC			0x01
 #define AQ_FLASH_PARAMETERS		0x20
 #define AQ_PHY_POWER			0x31
+#define AQ_WOL_CFG			0x60
 #define AQ_PHY_OPS			0x61
 
 #define AQ_USB_PHY_SET_TIMEOUT		10000
@@ -145,8 +146,18 @@
 #define AQ_DSH_RETRIES_SHIFT	0x18
 #define AQ_DSH_RETRIES_MASK	0xF000000
 
+#define AQ_WOL_FLAG_MP			0x2
+
 /******************************************************************************/
 
+struct aqc111_wol_cfg {
+	u8 hw_addr[6];
+	u8 flags;
+	u8 rsvd[283];
+} __packed;
+
+#define WOL_CFG_SIZE sizeof(struct aqc111_wol_cfg)
+
 struct aqc111_data {
 	u16 rxctl;
 	u8 rx_checksum;
@@ -160,6 +171,7 @@ struct aqc111_data {
 		u8 rev;
 	} fw_ver;
 	u32 phy_cfg;
+	u8 wol_flags;
 };
 
 #define AQ_LS_MASK		0x8000
#20 lore raw

PATCH v4 20/21 net: usb: aqc111: Add ASIX's HW ids

In reply to Igor Russkikh

From: Dmitry Bezrukov <[email protected]>

It enables driver for ASIX products which are also based on aqc111/112U chips.

---
 drivers/net/usb/aqc111.c | 40 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 40 insertions(+)
drivers/net/usb/aqc111.c +40 -0
index 7c859b9417f1..f69d566bd523 100644
@@ -1249,6 +1249,44 @@ static const struct driver_info aqc111_info = {
 	.tx_fixup	= aqc111_tx_fixup,
 };
 
+#define ASIX111_DESC \
+"ASIX USB 3.1 Gen1 to 5G Multi-Gigabit Ethernet Adapter"
+
+static const struct driver_info asix111_info = {
+	.description	= ASIX111_DESC,
+	.bind		= aqc111_bind,
+	.unbind		= aqc111_unbind,
+	.status		= aqc111_status,
+	.link_reset	= aqc111_link_reset,
+	.reset		= aqc111_reset,
+	.stop		= aqc111_stop,
+	.flags		= FLAG_ETHER | FLAG_FRAMING_AX |
+			  FLAG_AVOID_UNLINK_URBS | FLAG_MULTI_PACKET,
+	.rx_fixup	= aqc111_rx_fixup,
+	.tx_fixup	= aqc111_tx_fixup,
+};
+
+#undef ASIX111_DESC
+
+#define ASIX112_DESC \
+"ASIX USB 3.1 Gen1 to 2.5G Multi-Gigabit Ethernet Adapter"
+
+static const struct driver_info asix112_info = {
+	.description	= ASIX112_DESC,
+	.bind		= aqc111_bind,
+	.unbind		= aqc111_unbind,
+	.status		= aqc111_status,
+	.link_reset	= aqc111_link_reset,
+	.reset		= aqc111_reset,
+	.stop		= aqc111_stop,
+	.flags		= FLAG_ETHER | FLAG_FRAMING_AX |
+			  FLAG_AVOID_UNLINK_URBS | FLAG_MULTI_PACKET,
+	.rx_fixup	= aqc111_rx_fixup,
+	.tx_fixup	= aqc111_tx_fixup,
+};
+
+#undef ASIX112_DESC
+
 static int aqc111_suspend(struct usb_interface *intf, pm_message_t message)
 {
 	struct usbnet *dev = usb_get_intfdata(intf);
@@ -1398,6 +1436,8 @@ static int aqc111_resume(struct usb_interface *intf)
 
 static const struct usb_device_id products[] = {
 	{AQC111_USB_ETH_DEV(0x2eca, 0xc101, aqc111_info)},
+	{AQC111_USB_ETH_DEV(0x0b95, 0x2790, asix111_info)},
+	{AQC111_USB_ETH_DEV(0x0b95, 0x2791, asix112_info)},
 	{ },/* END */
 };
 MODULE_DEVICE_TABLE(usb, products);
#21 lore raw

PATCH v4 21/21 net: usb: aqc111: Extend cdc_ether blacklist

In reply to Igor Russkikh

From: Dmitry Bezrukov <[email protected]>

Added Aquantia and ASIX device IDs to prevent loading cdc_ether for these devices. Our firmware reports CDC configuration simultaneously with vendor specific.

---
 drivers/net/usb/cdc_ether.c | 26 ++++++++++++++++++++++++++
 1 file changed, 26 insertions(+)
drivers/net/usb/cdc_ether.c +26 -0
index 5c42cf81a08b..b3b3c05903a1 100644
@@ -562,6 +562,8 @@ static const struct driver_info wwan_info = {
 #define MICROSOFT_VENDOR_ID	0x045e
 #define UBLOX_VENDOR_ID		0x1546
 #define TPLINK_VENDOR_ID	0x2357
+#define AQUANTIA_VENDOR_ID	0x2eca
+#define ASIX_VENDOR_ID		0x0b95
 
 static const struct usb_device_id	products[] = {
 /* BLACKLIST !!
@@ -821,6 +823,30 @@ static const struct usb_device_id	products[] = {
 	.driver_info = 0,
 },
 
+/* Aquantia AQtion USB to 5GbE Controller (based on AQC111U) */
+{
+	USB_DEVICE_AND_INTERFACE_INFO(AQUANTIA_VENDOR_ID, 0xc101,
+				      USB_CLASS_COMM, USB_CDC_SUBCLASS_ETHERNET,
+				      USB_CDC_PROTO_NONE),
+	.driver_info = 0,
+},
+
+/* ASIX USB 3.1 Gen1 to 5G Multi-Gigabit Ethernet Adapter(based on AQC111U) */
+{
+	USB_DEVICE_AND_INTERFACE_INFO(ASIX_VENDOR_ID, 0x2790, USB_CLASS_COMM,
+				      USB_CDC_SUBCLASS_ETHERNET,
+				      USB_CDC_PROTO_NONE),
+	.driver_info = 0,
+},
+
+/* ASIX USB 3.1 Gen1 to 2.5G Multi-Gigabit Ethernet Adapter(based on AQC112U) */
+{
+	USB_DEVICE_AND_INTERFACE_INFO(ASIX_VENDOR_ID, 0x2791, USB_CLASS_COMM,
+				      USB_CDC_SUBCLASS_ETHERNET,
+				      USB_CDC_PROTO_NONE),
+	.driver_info = 0,
+},
+
 /* WHITELIST!!!
  *
  * CDC Ether uses two interfaces, not necessarily consecutive.
maintainer
#22 lore raw
In reply to Igor Russkikh

From: Igor Russkikh <[email protected]>
Date: Mon, 26 Nov 2018 09:32:56 +0000

Quoted 2 lines

This patchset introduces support for new multigig ethernet to USB dongle, developed jointly by Aquantia (Phy) and ASIX (USB MAC).

Series applied, thanks.