AI Trajectory


Trajectory: (Expand/Collapse All)
Seq Timestamp Type Name Duration
0/0 2026/10/04 05:43 flow patch-triage
3m Args:
null

Results:
{
  "EnableConfigs": [
    "USB_NET_AX88179",
    "USB_USBNET",
    "USB_NET_DRIVERS",
    "AX88796B_PHY",
    "PHYLINK",
    "PHYLIB"
  ],
  "FocusSymbols": [
    "ax88179a_rx_fixup",
    "ax88179a_tx_fixup",
    "ax88179a_bind",
    "ax88179a_reset",
    "ax88179a_mac_link_up",
    "ax88179_set_features",
    "phylink_mac_interrupt",
    "asix_ax88279_read_status",
    "asix_ax88279_config_aneg"
  ],
  "KMSANReasoning": "The patch series refactors the ASIX AX88179/178A USB Ethernet driver into shared library components (`ax88179_lib.c`, `ax88179_lib.h`) and adds support for newer ASIX chip variants (AX88179A, AX88772D, AX88279) with phylink integration in `ax88179a_devices.c` and `ax88796b.c`.\n\nKMSAN vs KASAN / LOCKDEP analysis:\n1. Memory allocations: The private driver context `struct ax88179_data` is allocated using `kzalloc_obj` in `ax88179a_bind()` and `kzalloc` in `ax88179_bind()`. The MDIO bus is allocated via `mdiobus_alloc()` (zero-initialized). No struct members are left uninitialized.\n2. RX / TX path: In `ax88179a_rx_fixup()`, newly allocated skbs are populated via `skb_put()` and `memcpy()` for the exact packet length, avoiding uninitialized payload bytes. In `ax88179a_tx_fixup()`, any padding bytes are zeroed with `skb_put_zero()` and the 64-bit descriptor is initialized via `put_unaligned_le64()`.\n3. User-space copies / info-leaks: Ethtool handlers like `ax88179_get_eeprom()` allocate buffers using `kzalloc()` and strictly check that the read length matches before copying to the caller buffer; `ax88179a_get_drvinfo()` formats strings using `snprintf()`. No kernel stack or uninitialized heap padding is exposed to user space.\n4. Other bug detectors: Potential concurrency or locking issues (e.g. `rtnl_lock()` in `ax88179_change_mtu()`) are covered by LOCKDEP. Buffer boundary issues, packet parsing overflows, and use-after-free risks are covered by standard KASAN.\n\nThere are no uninitialized memory reads or information leak risks that would uniquely warrant a KMSAN fuzzing session.",
  "NeedsKMSAN": false,
  "Reasoning": "The patch series introduces support for ASIX AX88179A, AX88772D, and AX88279 USB Ethernet adapters. It adds new device driver logic in drivers/net/usb/ax88179a_devices.c, refactors shared functionality into ax88179_lib.c, adds new PHY drivers in drivers/net/phy/ax88796b.c, and adds phylink_mac_interrupt(). These USB network devices are reachable in virtualized environments through syzkaller's USB emulation (raw-gadget/dummy_hcd). The new driver logic includes complex packet descriptor processing and skb manipulation in ax88179a_rx_fixup() and ax88179a_tx_fixup(), as well as netdev, ethtool, and MDIO/phylink callbacks, making it prime for fuzzing.",
  "WorthFuzzing": true
}

1/1 2026/10/04 05:43 action read-patch-diff
0m Args:
null

Results:
{
  "PatchDiff": "commit aaa4065a354c1790e97c05295c45e12c578b32d1\nAuthor: syz-cluster \u003ctriage@syzkaller.com\u003e\nDate:   Sun Oct 4 05:43:49 2026 +0000\n\n    syz-cluster: applied patch under review\n\ndiff --git a/MAINTAINERS b/MAINTAINERS\nindex 3011f995437fd..7464e87d41ef8 100644\n--- a/MAINTAINERS\n+++ b/MAINTAINERS\n@@ -4020,6 +4020,12 @@ S:\tMaintained\n F:\tDocumentation/devicetree/bindings/net/asix,ax88796c.yaml\n F:\tdrivers/net/ethernet/asix/ax88796c_*\n \n+ASIX AX88179 USB ETHERNET DRIVER\n+M:\tBirger Koblitz \u003cmail@birger-koblitz.de\u003e\n+L:\tnetdev@vger.kernel.org\n+S:\tMaintained\n+F:\tdrivers/net/usb/ax88179*\n+\n ASIX PHY DRIVER [RUST]\n M:\tFUJITA Tomonori \u003cfujita.tomonori@gmail.com\u003e\n R:\tTrevor Gross \u003ctmgross@umich.edu\u003e\ndiff --git a/arch/loongarch/configs/loongson32_defconfig b/arch/loongarch/configs/loongson32_defconfig\nindex dbef879935f32..a039b8e8d2420 100644\n--- a/arch/loongarch/configs/loongson32_defconfig\n+++ b/arch/loongarch/configs/loongson32_defconfig\n@@ -641,7 +641,7 @@ CONFIG_USB_RTL8150=m\n CONFIG_USB_RTL8152=m\n CONFIG_USB_USBNET=m\n # CONFIG_USB_NET_AX8817X is not set\n-# CONFIG_USB_NET_AX88179_178A is not set\n+# CONFIG_USB_NET_AX88179 is not set\n CONFIG_USB_NET_CDC_EEM=m\n CONFIG_USB_NET_HUAWEI_CDC_NCM=m\n CONFIG_USB_NET_CDC_MBIM=m\ndiff --git a/arch/loongarch/configs/loongson64_defconfig b/arch/loongarch/configs/loongson64_defconfig\nindex 4f8bb77b8a1e6..b30415c03fa18 100644\n--- a/arch/loongarch/configs/loongson64_defconfig\n+++ b/arch/loongarch/configs/loongson64_defconfig\n@@ -657,7 +657,7 @@ CONFIG_USB_RTL8150=m\n CONFIG_USB_RTL8152=m\n CONFIG_USB_USBNET=m\n # CONFIG_USB_NET_AX8817X is not set\n-# CONFIG_USB_NET_AX88179_178A is not set\n+# CONFIG_USB_NET_AX88179 is not set\n CONFIG_USB_NET_CDC_EEM=m\n CONFIG_USB_NET_HUAWEI_CDC_NCM=m\n CONFIG_USB_NET_CDC_MBIM=m\ndiff --git a/drivers/net/phy/ax88796b.c b/drivers/net/phy/ax88796b.c\nindex f20ddf6491499..0f1437d1e3e9b 100644\n--- a/drivers/net/phy/ax88796b.c\n+++ b/drivers/net/phy/ax88796b.c\n@@ -13,6 +13,19 @@\n #define PHY_ID_ASIX_AX88772A\t\t0x003b1861\n #define PHY_ID_ASIX_AX88772C\t\t0x003b1881\n #define PHY_ID_ASIX_AX88796B\t\t0x003b1841\n+#define PHY_ID_ASIX_AX88772D\t\t0x003b772d\n+#define PHY_ID_ASIX_AX88179A\t\t0x003b179a\n+#define PHY_ID_ASIX_AX88279\t\t0x003b2790\n+\n+#define AX_ADVERTISE_2500\t\t0x1000\n+\n+/* MII Vendor registers */\n+#define AX_CTRL_STATUS\t\t\t0x1d\n+#define AX_CTRL_STATUS_SPEED_MASK\t0x0c\n+#define   AX_CTRL_STATUS_SPEED_10\t0x0\n+#define   AX_CTRL_STATUS_SPEED_100\t0x4\n+#define   AX_CTRL_STATUS_SPEED_1000\t0x8\n+#define   AX_CTRL_STATUS_SPEED_2500\t0xc\n \n MODULE_DESCRIPTION(\"Asix PHY driver\");\n MODULE_AUTHOR(\"Michael Schmitz \u003cschmitzmic@gmail.com\u003e\");\n@@ -83,6 +96,110 @@ static int asix_ax88772a_read_status(struct phy_device *phydev)\n \treturn 0;\n }\n \n+static int asix_ax88279_read_status(struct phy_device *phydev)\n+{\n+\tint ret, val;\n+\n+\tret = genphy_read_status(phydev);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tif (!phydev-\u003elink)\n+\t\treturn 0;\n+\n+\t/* Read actual speed from vendor register */\n+\tval = phy_read(phydev, AX_CTRL_STATUS);\n+\tif (val \u003c 0)\n+\t\treturn val;\n+\n+\tswitch (val \u0026 AX_CTRL_STATUS_SPEED_MASK) {\n+\tcase AX_CTRL_STATUS_SPEED_2500:\n+\t\tphydev-\u003espeed = SPEED_2500;\n+\t\tbreak;\n+\tcase AX_CTRL_STATUS_SPEED_1000:\n+\t\tphydev-\u003espeed = SPEED_1000;\n+\t\tbreak;\n+\tcase AX_CTRL_STATUS_SPEED_100:\n+\t\tphydev-\u003espeed = SPEED_100;\n+\t\tbreak;\n+\tcase AX_CTRL_STATUS_SPEED_10:\n+\t\tphydev-\u003espeed = SPEED_10;\n+\t}\n+\n+\t/* MDIO_AN_10GBT_STAT_LP2_5G is broken, but we can deduce that\n+\t * the link-partner advertised 2500M if remotely AN succceded\n+\t * for link speed \u003e 1000M and we locally have a link speed of\n+\t * 2500M\n+\t */\n+\tlinkmode_clear_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev-\u003elp_advertising);\n+\tval = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_STAT);\n+\tif (val \u003e= 0 \u0026\u0026 val \u0026 MDIO_AN_10GBT_STAT_REMOK) {\n+\t\tlinkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT,\n+\t\t\t\t phydev-\u003elp_advertising,\n+\t\t\t\t phydev-\u003espeed == SPEED_2500);\n+\t}\n+\t/* Only supports full duplex */\n+\tphydev-\u003eduplex = DUPLEX_FULL;\n+\n+\t/* PHY switches interface between 2.5GBit and slower modes */\n+\tif (phydev-\u003espeed == SPEED_2500)\n+\t\tphydev-\u003einterface = PHY_INTERFACE_MODE_2500BASEX;\n+\telse\n+\t\tphydev-\u003einterface = PHY_INTERFACE_MODE_SGMII;\n+\n+\treturn 0;\n+}\n+\n+static int asix_ax88279_config_aneg(struct phy_device *phydev)\n+{\n+\tbool adv_2500;\n+\tint ret;\n+\n+\tif (phydev-\u003eautoneg == AUTONEG_DISABLE) {\n+\t\tphydev_warn(phydev, \"Disabling autoneg is not supported\\n\");\n+\t\treturn -EOPNOTSUPP;\n+\t}\n+\n+\tret = genphy_config_aneg(phydev);\n+\n+\tif (ret \u003c 0)\n+\t\treturn ret;\n+\n+\tadv_2500 = linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev-\u003eadvertising);\n+\tret = phy_modify(phydev, MII_ADVERTISE, AX_ADVERTISE_2500,\n+\t\t\t adv_2500 ? AX_ADVERTISE_2500 : 0);\n+\n+\treturn ret;\n+}\n+\n+static int asix_ax88279_get_features(struct phy_device *phydev)\n+{\n+\tint ret;\n+\n+\t/* MDIO_DEVS1/2 empty, so set mmds_present bits to allow reading abilities */\n+\tphydev-\u003ec45_ids.mmds_present |= MDIO_DEVS_PMAPMD | MDIO_DEVS_AN;\n+\n+\tlinkmode_set_bit_array(phy_basic_ports_array, ARRAY_SIZE(phy_basic_ports_array),\n+\t\t\t       phydev-\u003esupported);\n+\n+\tret = genphy_c45_pma_read_abilities(phydev);\n+\tif (ret \u003c 0)\n+\t\treturn ret;\n+\n+\t/* AX88279 does not support reported 100baseT-half duplex mode */\n+\tlinkmode_clear_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT, phydev-\u003esupported);\n+\n+\treturn 0;\n+}\n+\n+static int asix_ax88279_config_init(struct phy_device *phydev)\n+{\n+\t__set_bit(PHY_INTERFACE_MODE_2500BASEX, phydev-\u003epossible_interfaces);\n+\t__set_bit(PHY_INTERFACE_MODE_SGMII, phydev-\u003epossible_interfaces);\n+\n+\treturn 0;\n+}\n+\n static void asix_ax88772a_link_change_notify(struct phy_device *phydev)\n {\n \t/* Reset PHY, otherwise MII_LPA will provide outdated information.\n@@ -94,6 +211,24 @@ static void asix_ax88772a_link_change_notify(struct phy_device *phydev)\n \t}\n }\n \n+static int asix_ax88772D_get_features(struct phy_device *phydev)\n+{\n+\tint ret;\n+\t__ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = {};\n+\n+\t/* MDIO_DEVS1/2 empty, so set mmds_present bits to allow reading abilities */\n+\tphydev-\u003ec45_ids.mmds_present |= MDIO_DEVS_PMAPMD | MDIO_DEVS_AN;\n+\n+\tret = genphy_read_abilities(phydev);\n+\tif (ret \u003c 0)\n+\t\treturn ret;\n+\n+\t/* AX88772D does not support reported 1000baseT mode */\n+\tlinkmode_clear_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, phydev-\u003esupported);\n+\n+\treturn 0;\n+}\n+\n static struct phy_driver asix_driver[] = {\n {\n \tPHY_ID_MATCH_EXACT(PHY_ID_ASIX_AX88772A),\n@@ -111,6 +246,35 @@ static struct phy_driver asix_driver[] = {\n \t.suspend\t= genphy_suspend,\n \t.resume\t\t= genphy_resume,\n \t.soft_reset\t= asix_soft_reset,\n+}, {\n+\tPHY_ID_MATCH_EXACT(PHY_ID_ASIX_AX88772D),\n+\t.name\t\t= \"Asix Electronics AX88772D\",\n+\t.flags\t\t= PHY_IS_INTERNAL,\n+\t.get_features\t= asix_ax88772D_get_features,\n+\t.read_mmd\t= genphy_read_mmd_c45,\n+\t.write_mmd\t= genphy_write_mmd_c45,\n+\t.suspend\t= genphy_suspend,\n+\t.resume\t\t= genphy_resume,\n+}, {\n+\tPHY_ID_MATCH_EXACT(PHY_ID_ASIX_AX88179A),\n+\t.name\t\t= \"Asix Electronics AX88179A\",\n+\t.flags\t\t= PHY_IS_INTERNAL,\n+\t.suspend\t= genphy_suspend,\n+\t.resume\t\t= genphy_resume,\n+\t.read_mmd\t= genphy_read_mmd_c45,\n+\t.write_mmd\t= genphy_write_mmd_c45,\n+}, {\n+\tPHY_ID_MATCH_EXACT(PHY_ID_ASIX_AX88279),\n+\t.name\t\t= \"Asix Electronics AX88279\",\n+\t.flags\t\t= PHY_IS_INTERNAL | PHY_BROKEN_FORCED,\n+\t.get_features\t= asix_ax88279_get_features,\n+\t.read_status\t= asix_ax88279_read_status,\n+\t.config_aneg\t= asix_ax88279_config_aneg,\n+\t.config_init\t= asix_ax88279_config_init,\n+\t.read_mmd\t= genphy_read_mmd_c45,\n+\t.write_mmd\t= genphy_write_mmd_c45,\n+\t.suspend\t= genphy_suspend,\n+\t.resume\t\t= genphy_resume,\n }, {\n \tPHY_ID_MATCH_MODEL(PHY_ID_ASIX_AX88796B),\n \t.name\t\t= \"Asix Electronics AX88796B\",\n@@ -124,6 +288,9 @@ static const struct mdio_device_id __maybe_unused asix_tbl[] = {\n \t{ PHY_ID_MATCH_EXACT(PHY_ID_ASIX_AX88772A) },\n \t{ PHY_ID_MATCH_EXACT(PHY_ID_ASIX_AX88772C) },\n \t{ PHY_ID_MATCH_MODEL(PHY_ID_ASIX_AX88796B) },\n+\t{ PHY_ID_MATCH_EXACT(PHY_ID_ASIX_AX88772D) },\n+\t{ PHY_ID_MATCH_EXACT(PHY_ID_ASIX_AX88179A) },\n+\t{ PHY_ID_MATCH_EXACT(PHY_ID_ASIX_AX88279) },\n \t{ }\n };\n \ndiff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c\nindex fce9bc7be3304..b83f938fe7c3b 100644\n--- a/drivers/net/phy/phy.c\n+++ b/drivers/net/phy/phy.c\n@@ -1190,6 +1190,10 @@ int phy_ethtool_ksettings_set(struct phy_device *phydev,\n \t      duplex != DUPLEX_FULL)))\n \t\treturn -EINVAL;\n \n+\tif (autoneg == AUTONEG_DISABLE \u0026\u0026\n+\t    (phydev-\u003edrv-\u003eflags \u0026 PHY_BROKEN_FORCED))\n+\t\treturn -EOPNOTSUPP;\n+\n \tmutex_lock(\u0026phydev-\u003elock);\n \tphydev-\u003eautoneg = autoneg;\n \ndiff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c\nindex a7d086cdc9b25..a3ca7993eb7e0 100644\n--- a/drivers/net/phy/phylink.c\n+++ b/drivers/net/phy/phylink.c\n@@ -1612,6 +1612,26 @@ static bool phylink_link_is_up(struct phylink *pl)\n \treturn pl-\u003enetdev ? netif_carrier_ok(pl-\u003enetdev) : pl-\u003eold_link_state;\n }\n \n+/**\n+ * phylink_mac_interrupt() - wrapper for phy_mac_interrupt()\n+ * @pl: a pointer to a \u0026struct phylink returned from phylink_create()\n+ *\n+ * Passes a link-change interrupt received by the MAC to phylib.\n+ * This function may be called in interrupt context, and therefore cannot take\n+ * a mutex. However, it is safe to assume that the reference to phy does not\n+ * disappear during the call, as only an SFP module may be unplugged to make a\n+ * PHY disappear. SFP modules however do not support interrupts.\n+ */\n+void phylink_mac_interrupt(struct phylink *pl)\n+{\n+\tstruct phy_device *phy;\n+\n+\tphy = pl-\u003ephydev;\n+\tif (phy)\n+\t\tphy_mac_interrupt(phy);\n+}\n+EXPORT_SYMBOL_GPL(phylink_mac_interrupt);\n+\n static void phylink_resolve(struct work_struct *w)\n {\n \tstruct phylink *pl = container_of(w, struct phylink, resolve);\ndiff --git a/drivers/net/usb/Kconfig b/drivers/net/usb/Kconfig\nindex 52a5c0922c79f..27862e380bdb4 100644\n--- a/drivers/net/usb/Kconfig\n+++ b/drivers/net/usb/Kconfig\n@@ -195,11 +195,12 @@ config USB_NET_AX8817X\n \t  This driver creates an interface named \"ethX\", where X depends on\n \t  what other networking devices you have in use.\n \n-config USB_NET_AX88179_178A\n-\ttristate \"ASIX AX88179/178A USB 3.0/2.0 to Gigabit Ethernet\"\n+config USB_NET_AX88179\n+\ttristate \"ASIX AX88179/179A/178A USB 2.0/3.0 to 100M/1G/2.5GbE Adapters\"\n \tdepends on USB_USBNET\n+\tselect PHYLINK\n+\tselect AX88796B_PHY\n \tselect CRC32\n-\tselect PHYLIB\n \tdefault y\n \thelp\n \t  This option adds support for ASIX AX88179 based USB 3.0/2.0\n@@ -207,6 +208,9 @@ config USB_NET_AX88179_178A\n \n \t  This driver should work with at least the following devices:\n \t    * ASIX AX88179\n+\t    * ASIX AX88179A/B\n+\t    * ASIX AX88279\n+\t    * ASIX AX88772D/E\n \t    * ASIX AX88178A\n \t    * Sitcomm LN-032\n \ndiff --git a/drivers/net/usb/Makefile b/drivers/net/usb/Makefile\nindex 4964f7b326fbc..4026f19ecb826 100644\n--- a/drivers/net/usb/Makefile\n+++ b/drivers/net/usb/Makefile\n@@ -12,7 +12,8 @@ obj-$(CONFIG_USB_HSO)\t\t+= hso.o\n obj-$(CONFIG_USB_LAN78XX)\t+= lan78xx.o\n obj-$(CONFIG_USB_NET_AX8817X)\t+= asix.o\n asix-y := asix_devices.o asix_common.o ax88172a.o\n-obj-$(CONFIG_USB_NET_AX88179_178A)      += ax88179_178a.o\n+obj-$(CONFIG_USB_NET_AX88179)      += ax88179.o\n+ax88179-y := ax88179_178a.o ax88179a_devices.o ax88179_lib.o\n obj-$(CONFIG_USB_NET_CDCETHER)\t+= cdc_ether.o\n obj-$(CONFIG_USB_NET_CDC_EEM)\t+= cdc_eem.o\n obj-$(CONFIG_USB_NET_DM9601)\t+= dm9601.o\ndiff --git a/drivers/net/usb/ax88179_178a.c b/drivers/net/usb/ax88179_178a.c\nindex 2ed46ab62c006..7ec7143805484 100644\n--- a/drivers/net/usb/ax88179_178a.c\n+++ b/drivers/net/usb/ax88179_178a.c\n@@ -8,179 +8,12 @@\n #include \u003clinux/module.h\u003e\n #include \u003clinux/etherdevice.h\u003e\n #include \u003clinux/mii.h\u003e\n-#include \u003clinux/usb.h\u003e\n-#include \u003clinux/crc32.h\u003e\n-#include \u003clinux/usb/usbnet.h\u003e\n #include \u003cuapi/linux/mdio.h\u003e\n #include \u003clinux/mdio.h\u003e\n-\n-#define AX88179_PHY_ID\t\t\t\t0x03\n-#define AX_EEPROM_LEN\t\t\t\t0x100\n-#define AX88179_EEPROM_MAGIC\t\t\t0x17900b95\n-#define AX_MCAST_FLTSIZE\t\t\t8\n-#define AX_MAX_MCAST\t\t\t\t64\n-#define AX_INT_PPLS_LINK\t\t\t((u32)BIT(16))\n-#define AX_RXHDR_L4_TYPE_MASK\t\t\t0x1c\n-#define AX_RXHDR_L4_TYPE_UDP\t\t\t4\n-#define AX_RXHDR_L4_TYPE_TCP\t\t\t16\n-#define AX_RXHDR_L3CSUM_ERR\t\t\t2\n-#define AX_RXHDR_L4CSUM_ERR\t\t\t1\n-#define AX_RXHDR_CRC_ERR\t\t\t((u32)BIT(29))\n-#define AX_RXHDR_DROP_ERR\t\t\t((u32)BIT(31))\n-#define AX_ACCESS_MAC\t\t\t\t0x01\n-#define AX_ACCESS_PHY\t\t\t\t0x02\n-#define AX_ACCESS_EEPROM\t\t\t0x04\n-#define AX_ACCESS_EFUS\t\t\t\t0x05\n-#define AX_RELOAD_EEPROM_EFUSE\t\t\t0x06\n-#define AX_PAUSE_WATERLVL_LOW\t\t\t0x54\n-#define AX_PAUSE_WATERLVL_HIGH\t\t\t0x55\n-\n-#define PHYSICAL_LINK_STATUS\t\t\t0x02\n-\t#define\tAX_USB_SS\t\t0x04\n-\t#define\tAX_USB_HS\t\t0x02\n-\n-#define GENERAL_STATUS\t\t\t\t0x03\n-/* Check AX88179 version. UA1:Bit2 = 0,  UA2:Bit2 = 1 */\n-\t#define\tAX_SECLD\t\t0x04\n-\n-#define AX_SROM_ADDR\t\t\t\t0x07\n-#define AX_SROM_CMD\t\t\t\t0x0a\n-\t#define EEP_RD\t\t\t0x04\n-\t#define EEP_BUSY\t\t0x10\n-\n-#define AX_SROM_DATA_LOW\t\t\t0x08\n-#define AX_SROM_DATA_HIGH\t\t\t0x09\n-\n-#define AX_RX_CTL\t\t\t\t0x0b\n-\t#define AX_RX_CTL_DROPCRCERR\t0x0100\n-\t#define AX_RX_CTL_IPE\t\t0x0200\n-\t#define AX_RX_CTL_START\t\t0x0080\n-\t#define AX_RX_CTL_AP\t\t0x0020\n-\t#define AX_RX_CTL_AM\t\t0x0010\n-\t#define AX_RX_CTL_AB\t\t0x0008\n-\t#define AX_RX_CTL_AMALL\t\t0x0002\n-\t#define AX_RX_CTL_PRO\t\t0x0001\n-\t#define AX_RX_CTL_STOP\t\t0x0000\n-\n-#define AX_NODE_ID\t\t\t\t0x10\n-#define AX_MULFLTARY\t\t\t\t0x16\n-\n-#define AX_MEDIUM_STATUS_MODE\t\t\t0x22\n-\t#define AX_MEDIUM_GIGAMODE\t0x01\n-\t#define AX_MEDIUM_FULL_DUPLEX\t0x02\n-\t#define AX_MEDIUM_EN_125MHZ\t0x08\n-\t#define AX_MEDIUM_RXFLOW_CTRLEN\t0x10\n-\t#define AX_MEDIUM_TXFLOW_CTRLEN\t0x20\n-\t#define AX_MEDIUM_RECEIVE_EN\t0x100\n-\t#define AX_MEDIUM_PS\t\t0x200\n-\t#define AX_MEDIUM_JUMBO_EN\t0x8040\n-\n-#define AX_MONITOR_MOD\t\t\t\t0x24\n-\t#define AX_MONITOR_MODE_RWLC\t0x02\n-\t#define AX_MONITOR_MODE_RWMP\t0x04\n-\t#define AX_MONITOR_MODE_PMEPOL\t0x20\n-\t#define AX_MONITOR_MODE_PMETYPE\t0x40\n-\n-#define AX_GPIO_CTRL\t\t\t\t0x25\n-\t#define AX_GPIO_CTRL_GPIO3EN\t0x80\n-\t#define AX_GPIO_CTRL_GPIO2EN\t0x40\n-\t#define AX_GPIO_CTRL_GPIO1EN\t0x20\n-\n-#define AX_PHYPWR_RSTCTL\t\t\t0x26\n-\t#define AX_PHYPWR_RSTCTL_BZ\t0x0010\n-\t#define AX_PHYPWR_RSTCTL_IPRL\t0x0020\n-\t#define AX_PHYPWR_RSTCTL_AT\t0x1000\n-\n-#define AX_RX_BULKIN_QCTRL\t\t\t0x2e\n-#define AX_CLK_SELECT\t\t\t\t0x33\n-\t#define AX_CLK_SELECT_BCS\t0x01\n-\t#define AX_CLK_SELECT_ACS\t0x02\n-\t#define AX_CLK_SELECT_ULR\t0x08\n-\n-#define AX_RXCOE_CTL\t\t\t\t0x34\n-\t#define AX_RXCOE_IP\t\t0x01\n-\t#define AX_RXCOE_TCP\t\t0x02\n-\t#define AX_RXCOE_UDP\t\t0x04\n-\t#define AX_RXCOE_TCPV6\t\t0x20\n-\t#define AX_RXCOE_UDPV6\t\t0x40\n-\n-#define AX_TXCOE_CTL\t\t\t\t0x35\n-\t#define AX_TXCOE_IP\t\t0x01\n-\t#define AX_TXCOE_TCP\t\t0x02\n-\t#define AX_TXCOE_UDP\t\t0x04\n-\t#define AX_TXCOE_TCPV6\t\t0x20\n-\t#define AX_TXCOE_UDPV6\t\t0x40\n-\n-#define AX_LEDCTRL\t\t\t\t0x73\n-\n-#define GMII_PHY_PHYSR\t\t\t\t0x11\n-\t#define GMII_PHY_PHYSR_SMASK\t0xc000\n-\t#define GMII_PHY_PHYSR_GIGA\t0x8000\n-\t#define GMII_PHY_PHYSR_100\t0x4000\n-\t#define GMII_PHY_PHYSR_FULL\t0x2000\n-\t#define GMII_PHY_PHYSR_LINK\t0x400\n-\n-#define GMII_LED_ACT\t\t\t\t0x1a\n-\t#define\tGMII_LED_ACTIVE_MASK\t0xff8f\n-\t#define\tGMII_LED0_ACTIVE\tBIT(4)\n-\t#define\tGMII_LED1_ACTIVE\tBIT(5)\n-\t#define\tGMII_LED2_ACTIVE\tBIT(6)\n-\n-#define GMII_LED_LINK\t\t\t\t0x1c\n-\t#define\tGMII_LED_LINK_MASK\t0xf888\n-\t#define\tGMII_LED0_LINK_10\tBIT(0)\n-\t#define\tGMII_LED0_LINK_100\tBIT(1)\n-\t#define\tGMII_LED0_LINK_1000\tBIT(2)\n-\t#define\tGMII_LED1_LINK_10\tBIT(4)\n-\t#define\tGMII_LED1_LINK_100\tBIT(5)\n-\t#define\tGMII_LED1_LINK_1000\tBIT(6)\n-\t#define\tGMII_LED2_LINK_10\tBIT(8)\n-\t#define\tGMII_LED2_LINK_100\tBIT(9)\n-\t#define\tGMII_LED2_LINK_1000\tBIT(10)\n-\t#define\tLED0_ACTIVE\t\tBIT(0)\n-\t#define\tLED0_LINK_10\t\tBIT(1)\n-\t#define\tLED0_LINK_100\t\tBIT(2)\n-\t#define\tLED0_LINK_1000\t\tBIT(3)\n-\t#define\tLED0_FD\t\t\tBIT(4)\n-\t#define\tLED0_USB3_MASK\t\t0x001f\n-\t#define\tLED1_ACTIVE\t\tBIT(5)\n-\t#define\tLED1_LINK_10\t\tBIT(6)\n-\t#define\tLED1_LINK_100\t\tBIT(7)\n-\t#define\tLED1_LINK_1000\t\tBIT(8)\n-\t#define\tLED1_FD\t\t\tBIT(9)\n-\t#define\tLED1_USB3_MASK\t\t0x03e0\n-\t#define\tLED2_ACTIVE\t\tBIT(10)\n-\t#define\tLED2_LINK_1000\t\tBIT(13)\n-\t#define\tLED2_LINK_100\t\tBIT(12)\n-\t#define\tLED2_LINK_10\t\tBIT(11)\n-\t#define\tLED2_FD\t\t\tBIT(14)\n-\t#define\tLED_VALID\t\tBIT(15)\n-\t#define\tLED2_USB3_MASK\t\t0x7c00\n-\n-#define GMII_PHYPAGE\t\t\t\t0x1e\n-#define GMII_PHY_PAGE_SELECT\t\t\t0x1f\n-\t#define GMII_PHY_PGSEL_EXT\t0x0007\n-\t#define GMII_PHY_PGSEL_PAGE0\t0x0000\n-\t#define GMII_PHY_PGSEL_PAGE3\t0x0003\n-\t#define GMII_PHY_PGSEL_PAGE5\t0x0005\n+#include \"ax88179_lib.h\"\n \n static int ax88179_reset(struct usbnet *dev);\n \n-struct ax88179_data {\n-\tu8  eee_enabled;\n-\tu8  eee_active;\n-\tu16 rxctl;\n-\tu8 in_pm;\n-\tu32 wol_supported;\n-\tu32 wolopts;\n-\tu8 disconnecting;\n-};\n-\n-struct ax88179_int_data {\n-\t__le32 intdata1;\n-\t__le32 intdata2;\n-};\n-\n static const struct {\n \tunsigned char ctrl, timer_l, timer_h, size, ifg;\n } AX88179_BULKIN_SIZE[] =\t{\n@@ -190,165 +23,6 @@ static const struct {\n \t{7, 0xcc, 0x4c, 0x18, 8},\n };\n \n-static void ax88179_set_pm_mode(struct usbnet *dev, bool pm_mode)\n-{\n-\tstruct ax88179_data *ax179_data = dev-\u003edriver_priv;\n-\n-\tax179_data-\u003ein_pm = pm_mode;\n-}\n-\n-static int ax88179_in_pm(struct usbnet *dev)\n-{\n-\tstruct ax88179_data *ax179_data = dev-\u003edriver_priv;\n-\n-\treturn ax179_data-\u003ein_pm;\n-}\n-\n-static int __ax88179_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,\n-\t\t\t      u16 size, void *data)\n-{\n-\tint ret;\n-\tint (*fn)(struct usbnet *, u8, u8, u16, u16, void *, u16);\n-\tstruct ax88179_data *ax179_data = dev-\u003edriver_priv;\n-\n-\tBUG_ON(!dev);\n-\n-\tif (!ax88179_in_pm(dev))\n-\t\tfn = usbnet_read_cmd;\n-\telse\n-\t\tfn = usbnet_read_cmd_nopm;\n-\n-\tret = fn(dev, cmd, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,\n-\t\t value, index, data, size);\n-\n-\tif (unlikely((ret \u003c 0) \u0026\u0026 !(ret == -ENODEV \u0026\u0026 ax179_data-\u003edisconnecting)))\n-\t\tnetdev_warn(dev-\u003enet, \"Failed to read reg index 0x%04x: %d\\n\",\n-\t\t\t    index, ret);\n-\n-\treturn ret;\n-}\n-\n-static int __ax88179_write_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,\n-\t\t\t       u16 size, const void *data)\n-{\n-\tint ret;\n-\tint (*fn)(struct usbnet *, u8, u8, u16, u16, const void *, u16);\n-\tstruct ax88179_data *ax179_data = dev-\u003edriver_priv;\n-\n-\tBUG_ON(!dev);\n-\n-\tif (!ax88179_in_pm(dev))\n-\t\tfn = usbnet_write_cmd;\n-\telse\n-\t\tfn = usbnet_write_cmd_nopm;\n-\n-\tret = fn(dev, cmd, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,\n-\t\t value, index, data, size);\n-\n-\tif (unlikely((ret \u003c 0) \u0026\u0026 !(ret == -ENODEV \u0026\u0026 ax179_data-\u003edisconnecting)))\n-\t\tnetdev_warn(dev-\u003enet, \"Failed to write reg index 0x%04x: %d\\n\",\n-\t\t\t    index, ret);\n-\n-\treturn ret;\n-}\n-\n-static void ax88179_write_cmd_async(struct usbnet *dev, u8 cmd, u16 value,\n-\t\t\t\t    u16 index, u16 size, void *data)\n-{\n-\tu16 buf;\n-\n-\tif (2 == size) {\n-\t\tbuf = *((u16 *)data);\n-\t\tcpu_to_le16s(\u0026buf);\n-\t\tusbnet_write_cmd_async(dev, cmd, USB_DIR_OUT | USB_TYPE_VENDOR |\n-\t\t\t\t       USB_RECIP_DEVICE, value, index, \u0026buf,\n-\t\t\t\t       size);\n-\t} else {\n-\t\tusbnet_write_cmd_async(dev, cmd, USB_DIR_OUT | USB_TYPE_VENDOR |\n-\t\t\t\t       USB_RECIP_DEVICE, value, index, data,\n-\t\t\t\t       size);\n-\t}\n-}\n-\n-static int ax88179_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,\n-\t\t\t    u16 size, void *data)\n-{\n-\tint ret;\n-\n-\tif (2 == size) {\n-\t\tu16 buf = 0;\n-\t\tret = __ax88179_read_cmd(dev, cmd, value, index, size, \u0026buf);\n-\t\tle16_to_cpus(\u0026buf);\n-\t\t*((u16 *)data) = buf;\n-\t} else if (4 == size) {\n-\t\tu32 buf = 0;\n-\t\tret = __ax88179_read_cmd(dev, cmd, value, index, size, \u0026buf);\n-\t\tle32_to_cpus(\u0026buf);\n-\t\t*((u32 *)data) = buf;\n-\t} else {\n-\t\tret = __ax88179_read_cmd(dev, cmd, value, index, size, data);\n-\t}\n-\n-\treturn ret;\n-}\n-\n-static int ax88179_write_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,\n-\t\t\t     u16 size, const void *data)\n-{\n-\tint ret;\n-\n-\tif (2 == size) {\n-\t\tu16 buf;\n-\t\tbuf = *((u16 *)data);\n-\t\tcpu_to_le16s(\u0026buf);\n-\t\tret = __ax88179_write_cmd(dev, cmd, value, index,\n-\t\t\t\t\t  size, \u0026buf);\n-\t} else {\n-\t\tret = __ax88179_write_cmd(dev, cmd, value, index,\n-\t\t\t\t\t  size, data);\n-\t}\n-\n-\treturn ret;\n-}\n-\n-static void ax88179_status(struct usbnet *dev, struct urb *urb)\n-{\n-\tstruct ax88179_int_data *event;\n-\tu32 link;\n-\n-\tif (urb-\u003eactual_length \u003c 8)\n-\t\treturn;\n-\n-\tevent = urb-\u003etransfer_buffer;\n-\tle32_to_cpus((void *)\u0026event-\u003eintdata1);\n-\n-\tlink = (((__force u32)event-\u003eintdata1) \u0026 AX_INT_PPLS_LINK) \u003e\u003e 16;\n-\n-\tif (netif_carrier_ok(dev-\u003enet) != link) {\n-\t\tusbnet_link_change(dev, link, 1);\n-\t\tif (!link)\n-\t\t\tnetdev_info(dev-\u003enet, \"ax88179 - Link status is: 0\\n\");\n-\t}\n-}\n-\n-static int ax88179_mdio_read(struct net_device *netdev, int phy_id, int loc)\n-{\n-\tstruct usbnet *dev = netdev_priv(netdev);\n-\tu16 res;\n-\n-\tax88179_read_cmd(dev, AX_ACCESS_PHY, phy_id, (__u16)loc, 2, \u0026res);\n-\treturn res;\n-}\n-\n-static void ax88179_mdio_write(struct net_device *netdev, int phy_id, int loc,\n-\t\t\t       int val)\n-{\n-\tstruct usbnet *dev = netdev_priv(netdev);\n-\tu16 res = (u16) val;\n-\n-\tax88179_write_cmd(dev, AX_ACCESS_PHY, phy_id, (__u16)loc, 2, \u0026res);\n-}\n-\n static inline int ax88179_phy_mmd_indirect(struct usbnet *dev, u16 prtad,\n \t\t\t\t\t   u16 devad)\n {\n@@ -510,146 +184,11 @@ static void ax88179_disconnect(struct usb_interface *intf)\n \tusbnet_disconnect(intf);\n }\n \n-static void\n-ax88179_get_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo)\n-{\n-\tstruct usbnet *dev = netdev_priv(net);\n-\tstruct ax88179_data *priv = dev-\u003edriver_priv;\n-\n-\twolinfo-\u003esupported = priv-\u003ewol_supported;\n-\twolinfo-\u003ewolopts = priv-\u003ewolopts;\n-}\n-\n-static int\n-ax88179_set_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo)\n-{\n-\tstruct usbnet *dev = netdev_priv(net);\n-\tstruct ax88179_data *priv = dev-\u003edriver_priv;\n-\n-\tif (wolinfo-\u003ewolopts \u0026 ~(priv-\u003ewol_supported))\n-\t\treturn -EINVAL;\n-\n-\tpriv-\u003ewolopts = wolinfo-\u003ewolopts;\n-\n-\treturn 0;\n-}\n-\n static int ax88179_get_eeprom_len(struct net_device *net)\n {\n \treturn AX_EEPROM_LEN;\n }\n \n-static int\n-ax88179_get_eeprom(struct net_device *net, struct ethtool_eeprom *eeprom,\n-\t\t   u8 *data)\n-{\n-\tstruct usbnet *dev = netdev_priv(net);\n-\tu16 *eeprom_buff;\n-\tint first_word, last_word;\n-\tint i, ret;\n-\n-\tif (eeprom-\u003elen == 0)\n-\t\treturn -EINVAL;\n-\n-\teeprom-\u003emagic = AX88179_EEPROM_MAGIC;\n-\n-\tfirst_word = eeprom-\u003eoffset \u003e\u003e 1;\n-\tlast_word = (eeprom-\u003eoffset + eeprom-\u003elen - 1) \u003e\u003e 1;\n-\teeprom_buff = kmalloc_array(last_word - first_word + 1, sizeof(u16),\n-\t\t\t\t    GFP_KERNEL);\n-\tif (!eeprom_buff)\n-\t\treturn -ENOMEM;\n-\n-\t/* ax88179/178A returns 2 bytes from eeprom on read */\n-\tfor (i = first_word; i \u003c= last_word; i++) {\n-\t\tret = __ax88179_read_cmd(dev, AX_ACCESS_EEPROM, i, 1, 2,\n-\t\t\t\t\t \u0026eeprom_buff[i - first_word]);\n-\t\tif (ret \u003c 0) {\n-\t\t\tkfree(eeprom_buff);\n-\t\t\treturn -EIO;\n-\t\t}\n-\t}\n-\n-\tmemcpy(data, (u8 *)eeprom_buff + (eeprom-\u003eoffset \u0026 1), eeprom-\u003elen);\n-\tkfree(eeprom_buff);\n-\treturn 0;\n-}\n-\n-static int\n-ax88179_set_eeprom(struct net_device *net, struct ethtool_eeprom *eeprom,\n-\t\t   u8 *data)\n-{\n-\tstruct usbnet *dev = netdev_priv(net);\n-\tu16 *eeprom_buff;\n-\tint first_word;\n-\tint last_word;\n-\tint ret;\n-\tint i;\n-\n-\tnetdev_dbg(net, \"write EEPROM len %d, offset %d, magic 0x%x\\n\",\n-\t\t   eeprom-\u003elen, eeprom-\u003eoffset, eeprom-\u003emagic);\n-\n-\tif (eeprom-\u003elen == 0)\n-\t\treturn -EINVAL;\n-\n-\tif (eeprom-\u003emagic != AX88179_EEPROM_MAGIC)\n-\t\treturn -EINVAL;\n-\n-\tfirst_word = eeprom-\u003eoffset \u003e\u003e 1;\n-\tlast_word = (eeprom-\u003eoffset + eeprom-\u003elen - 1) \u003e\u003e 1;\n-\n-\teeprom_buff = kmalloc_array(last_word - first_word + 1, sizeof(u16),\n-\t\t\t\t    GFP_KERNEL);\n-\tif (!eeprom_buff)\n-\t\treturn -ENOMEM;\n-\n-\t/* align data to 16 bit boundaries, read the missing data from\n-\t   the EEPROM */\n-\tif (eeprom-\u003eoffset \u0026 1) {\n-\t\tret = ax88179_read_cmd(dev, AX_ACCESS_EEPROM, first_word, 1, 2,\n-\t\t\t\t       \u0026eeprom_buff[0]);\n-\t\tif (ret \u003c 0) {\n-\t\t\tnetdev_err(net, \"Failed to read EEPROM at offset 0x%02x.\\n\", first_word);\n-\t\t\tgoto free;\n-\t\t}\n-\t}\n-\n-\tif ((eeprom-\u003eoffset + eeprom-\u003elen) \u0026 1) {\n-\t\tret = ax88179_read_cmd(dev, AX_ACCESS_EEPROM, last_word, 1, 2,\n-\t\t\t\t       \u0026eeprom_buff[last_word - first_word]);\n-\t\tif (ret \u003c 0) {\n-\t\t\tnetdev_err(net, \"Failed to read EEPROM at offset 0x%02x.\\n\", last_word);\n-\t\t\tgoto free;\n-\t\t}\n-\t}\n-\n-\tmemcpy((u8 *)eeprom_buff + (eeprom-\u003eoffset \u0026 1), data, eeprom-\u003elen);\n-\n-\tfor (i = first_word; i \u003c= last_word; i++) {\n-\t\tnetdev_dbg(net, \"write to EEPROM at offset 0x%02x, data 0x%04x\\n\",\n-\t\t\t   i, eeprom_buff[i - first_word]);\n-\t\tret = ax88179_write_cmd(dev, AX_ACCESS_EEPROM, i, 1, 2,\n-\t\t\t\t\t\u0026eeprom_buff[i - first_word]);\n-\t\tif (ret \u003c 0) {\n-\t\t\tnetdev_err(net, \"Failed to write EEPROM at offset 0x%02x.\\n\", i);\n-\t\t\tgoto free;\n-\t\t}\n-\t\tmsleep(20);\n-\t}\n-\n-\t/* reload EEPROM data */\n-\tret = ax88179_write_cmd(dev, AX_RELOAD_EEPROM_EFUSE, 0x0000, 0, 0, NULL);\n-\tif (ret \u003c 0) {\n-\t\tnetdev_err(net, \"Failed to reload EEPROM data\\n\");\n-\t\tgoto free;\n-\t}\n-\n-\tret = 0;\n-free:\n-\tkfree(eeprom_buff);\n-\treturn ret;\n-}\n-\n static int ax88179_get_link_ksettings(struct net_device *net,\n \t\t\t\t      struct ethtool_link_ksettings *cmd)\n {\n@@ -864,125 +403,6 @@ static const struct ethtool_ops ax88179_ethtool_ops = {\n \t.get_ts_info\t\t= ethtool_op_get_ts_info,\n };\n \n-static void ax88179_set_multicast(struct net_device *net)\n-{\n-\tstruct usbnet *dev = netdev_priv(net);\n-\tstruct ax88179_data *data = dev-\u003edriver_priv;\n-\tu8 *m_filter = ((u8 *)dev-\u003edata);\n-\n-\tdata-\u003erxctl = (AX_RX_CTL_START | AX_RX_CTL_AB | AX_RX_CTL_IPE);\n-\n-\tif (net-\u003eflags \u0026 IFF_PROMISC) {\n-\t\tdata-\u003erxctl |= AX_RX_CTL_PRO;\n-\t} else if (net-\u003eflags \u0026 IFF_ALLMULTI ||\n-\t\t   netdev_mc_count(net) \u003e AX_MAX_MCAST) {\n-\t\tdata-\u003erxctl |= AX_RX_CTL_AMALL;\n-\t} else if (netdev_mc_empty(net)) {\n-\t\t/* just broadcast and directed */\n-\t} else {\n-\t\t/* We use dev-\u003edata for our 8 byte filter buffer\n-\t\t * to avoid allocating memory that is tricky to free later\n-\t\t */\n-\t\tu32 crc_bits;\n-\t\tstruct netdev_hw_addr *ha;\n-\n-\t\tmemset(m_filter, 0, AX_MCAST_FLTSIZE);\n-\n-\t\tnetdev_for_each_mc_addr(ha, net) {\n-\t\t\tcrc_bits = ether_crc(ETH_ALEN, ha-\u003eaddr) \u003e\u003e 26;\n-\t\t\t*(m_filter + (crc_bits \u003e\u003e 3)) |= (1 \u003c\u003c (crc_bits \u0026 7));\n-\t\t}\n-\n-\t\tax88179_write_cmd_async(dev, AX_ACCESS_MAC, AX_MULFLTARY,\n-\t\t\t\t\tAX_MCAST_FLTSIZE, AX_MCAST_FLTSIZE,\n-\t\t\t\t\tm_filter);\n-\n-\t\tdata-\u003erxctl |= AX_RX_CTL_AM;\n-\t}\n-\n-\tax88179_write_cmd_async(dev, AX_ACCESS_MAC, AX_RX_CTL,\n-\t\t\t\t2, 2, \u0026data-\u003erxctl);\n-}\n-\n-static int\n-ax88179_set_features(struct net_device *net, netdev_features_t features)\n-{\n-\tu8 tmp;\n-\tstruct usbnet *dev = netdev_priv(net);\n-\tnetdev_features_t changed = net-\u003efeatures ^ features;\n-\n-\tif (changed \u0026 NETIF_F_IP_CSUM) {\n-\t\tax88179_read_cmd(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, 1, 1, \u0026tmp);\n-\t\ttmp ^= AX_TXCOE_TCP | AX_TXCOE_UDP;\n-\t\tax88179_write_cmd(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, 1, 1, \u0026tmp);\n-\t}\n-\n-\tif (changed \u0026 NETIF_F_IPV6_CSUM) {\n-\t\tax88179_read_cmd(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, 1, 1, \u0026tmp);\n-\t\ttmp ^= AX_TXCOE_TCPV6 | AX_TXCOE_UDPV6;\n-\t\tax88179_write_cmd(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, 1, 1, \u0026tmp);\n-\t}\n-\n-\tif (changed \u0026 NETIF_F_RXCSUM) {\n-\t\tax88179_read_cmd(dev, AX_ACCESS_MAC, AX_RXCOE_CTL, 1, 1, \u0026tmp);\n-\t\ttmp ^= AX_RXCOE_IP | AX_RXCOE_TCP | AX_RXCOE_UDP |\n-\t\t       AX_RXCOE_TCPV6 | AX_RXCOE_UDPV6;\n-\t\tax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RXCOE_CTL, 1, 1, \u0026tmp);\n-\t}\n-\n-\treturn 0;\n-}\n-\n-static int ax88179_change_mtu(struct net_device *net, int new_mtu)\n-{\n-\tstruct usbnet *dev = netdev_priv(net);\n-\tu16 tmp16;\n-\n-\tWRITE_ONCE(net-\u003emtu, new_mtu);\n-\tdev-\u003ehard_mtu = net-\u003emtu + net-\u003ehard_header_len;\n-\n-\tif (net-\u003emtu \u003e 1500) {\n-\t\tax88179_read_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,\n-\t\t\t\t 2, 2, \u0026tmp16);\n-\t\ttmp16 |= AX_MEDIUM_JUMBO_EN;\n-\t\tax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,\n-\t\t\t\t  2, 2, \u0026tmp16);\n-\t} else {\n-\t\tax88179_read_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,\n-\t\t\t\t 2, 2, \u0026tmp16);\n-\t\ttmp16 \u0026= ~AX_MEDIUM_JUMBO_EN;\n-\t\tax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,\n-\t\t\t\t  2, 2, \u0026tmp16);\n-\t}\n-\n-\t/* max qlen depend on hard_mtu and rx_urb_size */\n-\tusbnet_update_max_qlen(dev);\n-\n-\treturn 0;\n-}\n-\n-static int ax88179_set_mac_addr(struct net_device *net, void *p)\n-{\n-\tstruct usbnet *dev = netdev_priv(net);\n-\tstruct sockaddr *addr = p;\n-\tint ret;\n-\n-\tif (netif_running(net))\n-\t\treturn -EBUSY;\n-\tif (!is_valid_ether_addr(addr-\u003esa_data))\n-\t\treturn -EADDRNOTAVAIL;\n-\n-\teth_hw_addr_set(net, addr-\u003esa_data);\n-\n-\t/* Set the MAC address */\n-\tret = ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_NODE_ID, ETH_ALEN,\n-\t\t\t\t ETH_ALEN, net-\u003edev_addr);\n-\tif (ret \u003c 0)\n-\t\treturn ret;\n-\n-\treturn 0;\n-}\n-\n static const struct net_device_ops ax88179_netdev_ops = {\n \t.ndo_open\t\t= usbnet_open,\n \t.ndo_stop\t\t= usbnet_stop,\n@@ -1249,35 +669,6 @@ static int ax88179_led_setting(struct usbnet *dev)\n \treturn 0;\n }\n \n-static void ax88179_get_mac_addr(struct usbnet *dev)\n-{\n-\tu8 mac[ETH_ALEN];\n-\n-\tmemset(mac, 0, sizeof(mac));\n-\n-\t/* Maybe the boot loader passed the MAC address via device tree */\n-\tif (!eth_platform_get_mac_address(\u0026dev-\u003eudev-\u003edev, mac)) {\n-\t\tnetif_dbg(dev, ifup, dev-\u003enet,\n-\t\t\t  \"MAC address read from device tree\");\n-\t} else {\n-\t\tax88179_read_cmd(dev, AX_ACCESS_MAC, AX_NODE_ID, ETH_ALEN,\n-\t\t\t\t ETH_ALEN, mac);\n-\t\tnetif_dbg(dev, ifup, dev-\u003enet,\n-\t\t\t  \"MAC address read from ASIX chip\");\n-\t}\n-\n-\tif (is_valid_ether_addr(mac)) {\n-\t\teth_hw_addr_set(dev-\u003enet, mac);\n-\t\tif (!is_local_ether_addr(mac))\n-\t\t\tdev-\u003enet-\u003eaddr_assign_type = NET_ADDR_PERM;\n-\t} else {\n-\t\tnetdev_info(dev-\u003enet, \"invalid MAC address, using random\\n\");\n-\t}\n-\n-\tax88179_write_cmd(dev, AX_ACCESS_MAC, AX_NODE_ID, ETH_ALEN, ETH_ALEN,\n-\t\t\t  dev-\u003enet-\u003edev_addr);\n-}\n-\n static int ax88179_bind(struct usbnet *dev, struct usb_interface *intf)\n {\n \tstruct ax88179_data *ax179_data;\n@@ -1293,6 +684,22 @@ static int ax88179_bind(struct usbnet *dev, struct usb_interface *intf)\n \n \tdev-\u003edriver_priv = ax179_data;\n \n+\tax179_data-\u003eresume = ax88179_resume;\n+\tax179_data-\u003esuspend = ax88179_suspend;\n+\n+\tret = ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_CHIP_STATUS,\n+\t\t\t       1, 1, \u0026ax179_data-\u003echip_version);\n+\tif (ret \u003c 0)\n+\t\tgoto err_nodev;\n+\n+\tax179_data-\u003echip_version = (ax179_data-\u003echip_version \u0026 0xf0) \u003e\u003e 4;\n+\tax179_data-\u003eis_ax88772d = 0;\n+\tax179_data-\u003eip_align = 1;\n+\tax179_data-\u003eeeprom_read_cmd = AX_ACCESS_EEPROM;\n+\tax179_data-\u003eeeprom_write_cmd = AX_ACCESS_EEPROM;\n+\tax179_data-\u003eeeprom_block = 2;\n+\tax179_data-\u003eeeprom_wen = 0;\n+\n \tdev-\u003enet-\u003enetdev_ops = \u0026ax88179_netdev_ops;\n \tdev-\u003enet-\u003eethtool_ops = \u0026ax88179_ethtool_ops;\n \tdev-\u003enet-\u003eneeded_headroom = 8;\n@@ -1317,6 +724,12 @@ static int ax88179_bind(struct usbnet *dev, struct usb_interface *intf)\n \tax88179_reset(dev);\n \n \treturn 0;\n+\n+err_nodev:\n+\tkfree(ax179_data);\n+\tax179_data = NULL;\n+\n+\treturn ret;\n }\n \n static void ax88179_unbind(struct usbnet *dev, struct usb_interface *intf)\n@@ -1879,6 +1292,18 @@ static const struct driver_info at_umc2000sp_info = {\n \n static const struct usb_device_id products[] = {\n {\n+\t/* ASIX AX88179A USB 3.2 1000Mbit Ethernet */\n+\tUSB_DEVICE_VER(0x0b95, 0x1790, 0x0200, 0x0200),\n+\t.driver_info = (unsigned long)\u0026ax88179a_info,\n+}, {\n+\t/* ASIX AX88772D USB 2.0 100Mbit Ethernet */\n+\tUSB_DEVICE_VER(0x0b95, 0x1790, 0x0300, 0x0300),\n+\t.driver_info = (unsigned long)\u0026ax88772d_info,\n+}, {\n+\t/* ASIX AX88279 USB 3.2 2500Mbit Ethernet */\n+\tUSB_DEVICE_VER(0x0b95, 0x1790, 0x0400, 0x0400),\n+\t.driver_info = (unsigned long)\u0026ax88279_info,\n+}, {\n \t/* ASIX AX88179 10/100/1000 */\n \tUSB_DEVICE_AND_INTERFACE_INFO(0x0b95, 0x1790, 0xff, 0xff, 0),\n \t.driver_info = (unsigned long)\u0026ax88179_info,\n@@ -1935,19 +1360,20 @@ static const struct usb_device_id products[] = {\n };\n MODULE_DEVICE_TABLE(usb, products);\n \n-static struct usb_driver ax88179_178a_driver = {\n-\t.name =\t\t\"ax88179_178a\",\n+static struct usb_driver ax88179_driver = {\n+\t.name =\t\t\"ax88179\",\n \t.id_table =\tproducts,\n \t.probe =\tusbnet_probe,\n-\t.suspend =\tax88179_suspend,\n-\t.resume =\tax88179_resume,\n-\t.reset_resume =\tax88179_resume,\n+\t.suspend =\tax88179_suspend_wrapper,\n+\t.resume =\tax88179_resume_wrapper,\n+\t.reset_resume =\tax88179_resume_wrapper,\n \t.disconnect =\tax88179_disconnect,\n \t.supports_autosuspend = 1,\n \t.disable_hub_initiated_lpm = 1,\n };\n \n-module_usb_driver(ax88179_178a_driver);\n+module_usb_driver(ax88179_driver);\n \n-MODULE_DESCRIPTION(\"ASIX AX88179/178A based USB 3.0/2.0 Gigabit Ethernet Devices\");\n+MODULE_DESCRIPTION(\"ASIX AX88179/179A/178A/279 based USB 3.0/2.0 100M/1G/2.5GbE Controllers\");\n MODULE_LICENSE(\"GPL\");\n+MODULE_ALIAS(\"ax88179_178a\");\ndiff --git a/drivers/net/usb/ax88179_lib.c b/drivers/net/usb/ax88179_lib.c\nnew file mode 100644\nindex 0000000000000..de849650f7871\n--- /dev/null\n+++ b/drivers/net/usb/ax88179_lib.c\n@@ -0,0 +1,530 @@\n+// SPDX-License-Identifier: GPL-2.0-or-later\n+/*\n+ * ASIX AX88179/178A USB 3.0/2.0 to Gigabit Ethernet Devices\n+ *\n+ * Copyright (C) 2011-2013 ASIX\n+ */\n+\n+#include \u003clinux/etherdevice.h\u003e\n+#include \u003clinux/rtnetlink.h\u003e\n+#include \"ax88179_lib.h\"\n+\n+void ax88179_set_pm_mode(struct usbnet *dev, bool pm_mode)\n+{\n+\tstruct ax88179_data *ax179_data = dev-\u003edriver_priv;\n+\n+\tax179_data-\u003ein_pm = pm_mode;\n+}\n+\n+static int ax88179_in_pm(struct usbnet *dev)\n+{\n+\tstruct ax88179_data *ax179_data = dev-\u003edriver_priv;\n+\n+\treturn ax179_data-\u003ein_pm;\n+}\n+\n+int __ax88179_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,\n+\t\t       u16 size, void *data)\n+{\n+\tint (*fn)(struct usbnet *dev, u8 cmd, u8 rtype, u16 val, u16 i, void *d, u16 size);\n+\tstruct ax88179_data *ax179_data = dev-\u003edriver_priv;\n+\tint ret;\n+\n+\tif (!ax88179_in_pm(dev))\n+\t\tfn = usbnet_read_cmd;\n+\telse\n+\t\tfn = usbnet_read_cmd_nopm;\n+\n+\tret = fn(dev, cmd, USB_DIR_IN | USB_TYPE_VENDOR | USB_RECIP_DEVICE,\n+\t\t value, index, data, size);\n+\n+\tif (unlikely(ret \u003c 0 \u0026\u0026 !(ret == -ENODEV \u0026\u0026 ax179_data-\u003edisconnecting)))\n+\t\tnetdev_warn(dev-\u003enet, \"Failed to read reg index 0x%04x: %d\\n\",\n+\t\t\t    index, ret);\n+\n+\treturn ret;\n+}\n+\n+static int __ax88179_write_cmd(struct usbnet *dev, u8 cmd, u16 value,\n+\t\t\t       u16 index, u16 size, const void *data)\n+{\n+\tint (*fn)(struct usbnet *dev, u8 cmd, u8 rtype, u16 val, u16 i, const void *d, u16 size);\n+\tstruct ax88179_data *ax179_data = dev-\u003edriver_priv;\n+\tint ret;\n+\n+\tif (!ax88179_in_pm(dev))\n+\t\tfn = usbnet_write_cmd;\n+\telse\n+\t\tfn = usbnet_write_cmd_nopm;\n+\n+\tret = fn(dev, cmd, USB_DIR_OUT | USB_TYPE_VENDOR | USB_RECIP_DEVICE,\n+\t\t value, index, data, size);\n+\n+\tif (unlikely(ret \u003c 0 \u0026\u0026 !(ret == -ENODEV \u0026\u0026 ax179_data-\u003edisconnecting)))\n+\t\tnetdev_warn(dev-\u003enet, \"Failed to write reg index 0x%04x: %d\\n\",\n+\t\t\t    index, ret);\n+\n+\treturn ret;\n+}\n+\n+void ax88179_write_cmd_async(struct usbnet *dev, u8 cmd, u16 value,\n+\t\t\t     u16 index, u16 size, void *data)\n+{\n+\tu16 buf;\n+\n+\tif (size == 2) {\n+\t\tbuf = *((u16 *)data);\n+\t\tcpu_to_le16s(\u0026buf);\n+\t\tusbnet_write_cmd_async(dev, cmd, USB_DIR_OUT | USB_TYPE_VENDOR |\n+\t\t\t\t       USB_RECIP_DEVICE, value, index, \u0026buf,\n+\t\t\t\t       size);\n+\t} else {\n+\t\tusbnet_write_cmd_async(dev, cmd, USB_DIR_OUT | USB_TYPE_VENDOR |\n+\t\t\t\t       USB_RECIP_DEVICE, value, index, data,\n+\t\t\t\t       size);\n+\t}\n+}\n+\n+int ax88179_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,\n+\t\t     u16 size, void *data)\n+{\n+\tint ret;\n+\n+\tif (size == 2) {\n+\t\tu16 buf = 0;\n+\n+\t\tret = __ax88179_read_cmd(dev, cmd, value, index, size, \u0026buf);\n+\t\tle16_to_cpus(\u0026buf);\n+\t\t*((u16 *)data) = buf;\n+\t} else if (size == 4) {\n+\t\tu32 buf = 0;\n+\n+\t\tret = __ax88179_read_cmd(dev, cmd, value, index, size, \u0026buf);\n+\t\tle32_to_cpus(\u0026buf);\n+\t\t*((u32 *)data) = buf;\n+\t} else {\n+\t\tret = __ax88179_read_cmd(dev, cmd, value, index, size, data);\n+\t}\n+\n+\treturn ret;\n+}\n+\n+int ax88179_write_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,\n+\t\t      u16 size, const void *data)\n+{\n+\tint ret;\n+\n+\tif (size == 2) {\n+\t\tu16 buf;\n+\n+\t\tbuf = *((u16 *)data);\n+\t\tcpu_to_le16s(\u0026buf);\n+\t\tret = __ax88179_write_cmd(dev, cmd, value, index,\n+\t\t\t\t\t  size, \u0026buf);\n+\t} else {\n+\t\tret = __ax88179_write_cmd(dev, cmd, value, index,\n+\t\t\t\t\t  size, data);\n+\t}\n+\n+\treturn ret;\n+}\n+\n+int ax88179_write_u16(struct usbnet *dev, u8 cmd, u16 value, u16 index, u16 data)\n+{\n+\tu16 buf = data;\n+\n+\tcpu_to_le16s(\u0026buf);\n+\treturn __ax88179_write_cmd(dev, cmd, value, index, 2, \u0026buf);\n+}\n+\n+int ax88179_write_u8(struct usbnet *dev, u8 cmd, u16 value, u16 index, u8 data)\n+{\n+\tu8 buf = data;\n+\n+\treturn __ax88179_write_cmd(dev, cmd, value, index, 1, \u0026buf);\n+}\n+\n+struct ax88179_data *netdev2data(struct net_device *net)\n+{\n+\tstruct usbnet *dev = netdev_priv(net);\n+\n+\treturn dev-\u003edriver_priv;\n+}\n+\n+void ax88179_status(struct usbnet *dev, struct urb *urb)\n+{\n+\tstruct ax88179_int_data *event;\n+\tu32 link;\n+\n+\tif (urb-\u003eactual_length \u003c 8)\n+\t\treturn;\n+\n+\tevent = urb-\u003etransfer_buffer;\n+\tle32_to_cpus((void *)\u0026event-\u003eintdata1);\n+\n+\tlink = (((__force u32)event-\u003eintdata1) \u0026 AX_INT_PPLS_LINK) \u003e\u003e 16;\n+\n+\tif (netif_carrier_ok(dev-\u003enet) != link) {\n+\t\tusbnet_link_change(dev, link, 1);\n+\t\tif (!link)\n+\t\t\tnetdev_info(dev-\u003enet, \"ax88179 - Link status is: 0\\n\");\n+\t}\n+}\n+\n+int ax88179_mdio_read(struct net_device *netdev, int phy_id, int loc)\n+{\n+\tstruct usbnet *dev = netdev_priv(netdev);\n+\tu16 res;\n+\n+\tax88179_read_cmd(dev, AX_ACCESS_PHY, phy_id, (__u16)loc, 2, \u0026res);\n+\treturn res;\n+}\n+\n+void ax88179_mdio_write(struct net_device *netdev, int phy_id, int loc, int val)\n+{\n+\tstruct usbnet *dev = netdev_priv(netdev);\n+\tu16 res = (u16)val;\n+\n+\tax88179_write_cmd(dev, AX_ACCESS_PHY, phy_id, (__u16)loc, 2, \u0026res);\n+}\n+\n+void ax88179_get_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo)\n+{\n+\tstruct usbnet *dev = netdev_priv(net);\n+\tstruct ax88179_data *priv = dev-\u003edriver_priv;\n+\n+\twolinfo-\u003esupported = priv-\u003ewol_supported;\n+\twolinfo-\u003ewolopts = priv-\u003ewolopts;\n+}\n+\n+int ax88179_set_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo)\n+{\n+\tstruct usbnet *dev = netdev_priv(net);\n+\tstruct ax88179_data *priv = dev-\u003edriver_priv;\n+\n+\tif (wolinfo-\u003ewolopts \u0026 ~(priv-\u003ewol_supported))\n+\t\treturn -EINVAL;\n+\n+\tpriv-\u003ewolopts = wolinfo-\u003ewolopts;\n+\n+\treturn 0;\n+}\n+\n+static void\n+ax88179_eeprom_access_params(struct ax88179_data *ax179_data, int i, u16 *value, u16 *idx)\n+{\n+\t/* AX88179 has a word-addressable EEPROM\n+\t * AX88179A uses EFUSES with 20 bytes length\n+\t * AX88279 has an EEPROM addressable in 256 byte blocks\n+\t */\n+\tif (ax179_data-\u003echip_version \u003c AX_VERSION_AX88179A) {\n+\t\t*value = i;\n+\t\t*idx = 1;\n+\t} else if (ax179_data-\u003echip_version == AX_VERSION_AX88279) {\n+\t\t*value = (i * ax179_data-\u003eeeprom_block) \u003e\u003e 16;\n+\t\t*idx = (i * ax179_data-\u003eeeprom_block) \u0026 0xffff;\n+\t} else {\n+\t\t*value = i \u003c\u003c 4;\n+\t\t*idx = 0;\n+\t}\n+}\n+\n+int ax88179_get_eeprom(struct net_device *net, struct ethtool_eeprom *eeprom, u8 *data)\n+{\n+\tstruct usbnet *dev = netdev_priv(net);\n+\tstruct ax88179_data *ax179_data;\n+\tint first, last, i, ret;\n+\tu8 *eeprom_buff;\n+\n+\tax179_data = dev-\u003edriver_priv;\n+\n+\tif (eeprom-\u003elen == 0)\n+\t\treturn -EINVAL;\n+\n+\teeprom-\u003emagic = AX88179_EEPROM_MAGIC;\n+\n+\tfirst = eeprom-\u003eoffset / ax179_data-\u003eeeprom_block;\n+\tlast = (eeprom-\u003eoffset + eeprom-\u003elen - 1) / ax179_data-\u003eeeprom_block;\n+\n+\teeprom_buff = kzalloc((last - first + 1) * ax179_data-\u003eeeprom_block, GFP_KERNEL);\n+\tif (!eeprom_buff)\n+\t\treturn -ENOMEM;\n+\n+\tfor (i = first; i \u003c= last; i++) {\n+\t\tu16 value, idx;\n+\n+\t\tax88179_eeprom_access_params(ax179_data, i, \u0026value, \u0026idx);\n+\t\tret = __ax88179_read_cmd(dev, ax179_data-\u003eeeprom_read_cmd,\n+\t\t\t\t\t value, idx, ax179_data-\u003eeeprom_block,\n+\t\t\t\t\t eeprom_buff + (i - first) * ax179_data-\u003eeeprom_block);\n+\n+\t\tif (ret != ax179_data-\u003eeeprom_block) {\n+\t\t\tkfree(eeprom_buff);\n+\t\t\treturn -EIO;\n+\t\t}\n+\t}\n+\n+\tmemcpy(data, eeprom_buff + eeprom-\u003eoffset % ax179_data-\u003eeeprom_block, eeprom-\u003elen);\n+\tkfree(eeprom_buff);\n+\treturn 0;\n+}\n+\n+int ax88179_set_eeprom(struct net_device *net, struct ethtool_eeprom *eeprom, u8 *data)\n+{\n+\tstruct usbnet *dev = netdev_priv(net);\n+\tstruct ax88179_data *ax179_data;\n+\tu16 *eeprom_buff;\n+\tint first_word;\n+\tint last_word;\n+\tint ret;\n+\tint i;\n+\n+\tax179_data = dev-\u003edriver_priv;\n+\tif (ax179_data-\u003echip_version \u003e= AX_VERSION_AX88179A)\n+\t\treturn -EOPNOTSUPP;\n+\n+\tnetdev_dbg(net, \"write EEPROM len %d, offset %d, magic 0x%x\\n\",\n+\t\t   eeprom-\u003elen, eeprom-\u003eoffset, eeprom-\u003emagic);\n+\n+\tif (eeprom-\u003elen == 0)\n+\t\treturn -EINVAL;\n+\n+\tif (eeprom-\u003emagic != AX88179_EEPROM_MAGIC)\n+\t\treturn -EINVAL;\n+\n+\tfirst_word = eeprom-\u003eoffset \u003e\u003e 1;\n+\tlast_word = (eeprom-\u003eoffset + eeprom-\u003elen - 1) \u003e\u003e 1;\n+\n+\teeprom_buff = kmalloc_array(last_word - first_word + 1, sizeof(u16),\n+\t\t\t\t    GFP_KERNEL);\n+\tif (!eeprom_buff)\n+\t\treturn -ENOMEM;\n+\n+\t/* align data to 16 bit boundaries, read the missing data from\n+\t * the EEPROM\n+\t */\n+\tif (eeprom-\u003eoffset \u0026 1) {\n+\t\tret = ax88179_read_cmd(dev, AX_ACCESS_EEPROM, first_word, 1, 2,\n+\t\t\t\t       \u0026eeprom_buff[0]);\n+\t\tif (ret \u003c 0) {\n+\t\t\tnetdev_err(net, \"Failed to read EEPROM at offset 0x%02x.\\n\", first_word);\n+\t\t\tgoto free;\n+\t\t}\n+\t}\n+\n+\tif ((eeprom-\u003eoffset + eeprom-\u003elen) \u0026 1) {\n+\t\tret = ax88179_read_cmd(dev, AX_ACCESS_EEPROM, last_word, 1, 2,\n+\t\t\t\t       \u0026eeprom_buff[last_word - first_word]);\n+\t\tif (ret \u003c 0) {\n+\t\t\tnetdev_err(net, \"Failed to read EEPROM at offset 0x%02x.\\n\", last_word);\n+\t\t\tgoto free;\n+\t\t}\n+\t}\n+\n+\tmemcpy((u8 *)eeprom_buff + (eeprom-\u003eoffset \u0026 1), data, eeprom-\u003elen);\n+\n+\tfor (i = first_word; i \u003c= last_word; i++) {\n+\t\tnetdev_dbg(net, \"write to EEPROM at offset 0x%02x, data 0x%04x\\n\",\n+\t\t\t   i, eeprom_buff[i - first_word]);\n+\t\tret = ax88179_write_cmd(dev, AX_ACCESS_EEPROM, i, 1, 2,\n+\t\t\t\t\t\u0026eeprom_buff[i - first_word]);\n+\t\tif (ret \u003c 0) {\n+\t\t\tnetdev_err(net, \"Failed to write EEPROM at offset 0x%02x.\\n\", i);\n+\t\t\tgoto free;\n+\t\t}\n+\t\tmsleep(20);\n+\t}\n+\n+\t/* reload EEPROM data */\n+\tret = ax88179_write_cmd(dev, AX_RELOAD_EEPROM_EFUSE, 0x0000, 0, 0, NULL);\n+\tif (ret \u003c 0) {\n+\t\tnetdev_err(net, \"Failed to reload EEPROM data\\n\");\n+\t\tgoto free;\n+\t}\n+\n+\tret = 0;\n+free:\n+\tkfree(eeprom_buff);\n+\treturn ret;\n+}\n+\n+void ax88179_set_multicast(struct net_device *net)\n+{\n+\tstruct usbnet *dev = netdev_priv(net);\n+\tu8 *m_filter = ((u8 *)dev-\u003edata);\n+\tstruct ax88179_data *data;\n+\n+\tdata = dev-\u003edriver_priv;\n+\n+\tdata-\u003erxctl = (AX_RX_CTL_START | AX_RX_CTL_AB | AX_RX_CTL_DROPCRCERR);\n+\tif (data-\u003eip_align)\n+\t\tdata-\u003erxctl |= AX_RX_CTL_IPE;\n+\n+\tif (net-\u003eflags \u0026 IFF_PROMISC) {\n+\t\tdata-\u003erxctl |= AX_RX_CTL_PRO;\n+\t} else if (net-\u003eflags \u0026 IFF_ALLMULTI ||\n+\t\t   netdev_mc_count(net) \u003e AX_MAX_MCAST) {\n+\t\tdata-\u003erxctl |= AX_RX_CTL_AMALL;\n+\t} else if (netdev_mc_empty(net)) {\n+\t\t/* just broadcast and directed */\n+\t} else {\n+\t\t/* We use dev-\u003edata for our 8 byte filter buffer\n+\t\t * to avoid allocating memory that is tricky to free later\n+\t\t */\n+\t\tu32 crc_bits;\n+\t\tstruct netdev_hw_addr *ha;\n+\n+\t\tmemset(m_filter, 0, AX_MCAST_FLTSIZE);\n+\n+\t\tnetdev_for_each_mc_addr(ha, net) {\n+\t\t\tcrc_bits = ether_crc(ETH_ALEN, ha-\u003eaddr) \u003e\u003e 26;\n+\t\t\t*(m_filter + (crc_bits \u003e\u003e 3)) |= (1 \u003c\u003c (crc_bits \u0026 7));\n+\t\t}\n+\n+\t\tax88179_write_cmd_async(dev, AX_ACCESS_MAC, AX_MULFLTARY,\n+\t\t\t\t\tAX_MCAST_FLTSIZE, AX_MCAST_FLTSIZE,\n+\t\t\t\t\tm_filter);\n+\n+\t\tdata-\u003erxctl |= AX_RX_CTL_AM;\n+\t}\n+\n+\tax88179_write_cmd_async(dev, AX_ACCESS_MAC, AX_RX_CTL,\n+\t\t\t\t2, 2, \u0026data-\u003erxctl);\n+}\n+\n+int ax88179_set_features(struct net_device *net, netdev_features_t features)\n+{\n+\tu8 tmp;\n+\tstruct usbnet *dev = netdev_priv(net);\n+\tstruct ax88179_data *data = dev-\u003edriver_priv;\n+\tnetdev_features_t changed = net-\u003efeatures ^ features;\n+\n+\tif (changed \u0026 NETIF_F_IP_CSUM) {\n+\t\tax88179_read_cmd(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, 1, 1, \u0026tmp);\n+\t\ttmp ^= AX_TXCOE_TCP | AX_TXCOE_UDP;\n+\t\tax88179_write_cmd(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, 1, 1, \u0026tmp);\n+\t}\n+\n+\tif (changed \u0026 NETIF_F_IPV6_CSUM) {\n+\t\tax88179_read_cmd(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, 1, 1, \u0026tmp);\n+\t\ttmp ^= AX_TXCOE_TCPV6 | AX_TXCOE_UDPV6;\n+\t\tax88179_write_cmd(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, 1, 1, \u0026tmp);\n+\t}\n+\n+\tif (changed \u0026 NETIF_F_RXCSUM) {\n+\t\t/* When we disable rx-checksumming, we need to clear the rx_checksum\n+\t\t * flag before disabling the engine\n+\t\t */\n+\t\tif (!(features \u0026 NETIF_F_RXCSUM))\n+\t\t\tdata-\u003erx_checksum = 0;\n+\n+\t\tax88179_read_cmd(dev, AX_ACCESS_MAC, AX_RXCOE_CTL, 1, 1, \u0026tmp);\n+\t\ttmp ^= AX_RXCOE_IP | AX_RXCOE_TCP | AX_RXCOE_UDP |\n+\t\t       AX_RXCOE_TCPV6 | AX_RXCOE_UDPV6;\n+\t\tax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RXCOE_CTL, 1, 1, \u0026tmp);\n+\t\tdata-\u003erx_checksum = !!(features \u0026 NETIF_F_RXCSUM);\n+\t}\n+\n+\treturn 0;\n+}\n+\n+void ax88179_get_mac_addr(struct usbnet *dev)\n+{\n+\tu8 mac[ETH_ALEN];\n+\n+\tmemset(mac, 0, sizeof(mac));\n+\n+\t/* Maybe the boot loader passed the MAC address via device tree */\n+\tif (!eth_platform_get_mac_address(\u0026dev-\u003eudev-\u003edev, mac)) {\n+\t\tnetif_dbg(dev, ifup, dev-\u003enet,\n+\t\t\t  \"MAC address read from device tree\");\n+\t} else {\n+\t\tax88179_read_cmd(dev, AX_ACCESS_MAC, AX_NODE_ID, ETH_ALEN,\n+\t\t\t\t ETH_ALEN, mac);\n+\t\tnetif_dbg(dev, ifup, dev-\u003enet,\n+\t\t\t  \"MAC address read from ASIX chip\");\n+\t}\n+\n+\tif (is_valid_ether_addr(mac)) {\n+\t\teth_hw_addr_set(dev-\u003enet, mac);\n+\t\tif (!is_local_ether_addr(mac))\n+\t\t\tdev-\u003enet-\u003eaddr_assign_type = NET_ADDR_PERM;\n+\t} else {\n+\t\tnetdev_info(dev-\u003enet, \"invalid MAC address, using random\\n\");\n+\t}\n+\n+\tax88179_write_cmd(dev, AX_ACCESS_MAC, AX_NODE_ID, ETH_ALEN, ETH_ALEN,\n+\t\t\t  dev-\u003enet-\u003edev_addr);\n+}\n+\n+int ax88179_change_mtu(struct net_device *net, int new_mtu)\n+{\n+\tstruct usbnet *dev = netdev_priv(net);\n+\tu16 tmp16;\n+\n+\tWRITE_ONCE(net-\u003emtu, new_mtu);\n+\tdev-\u003ehard_mtu = net-\u003emtu + net-\u003ehard_header_len;\n+\n+\trtnl_lock();\n+\tif (net-\u003emtu \u003e 1500) {\n+\t\tax88179_read_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,\n+\t\t\t\t 2, 2, \u0026tmp16);\n+\t\ttmp16 |= AX_MEDIUM_JUMBO_EN;\n+\t\tax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,\n+\t\t\t\t  2, 2, \u0026tmp16);\n+\t} else {\n+\t\tax88179_read_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,\n+\t\t\t\t 2, 2, \u0026tmp16);\n+\t\ttmp16 \u0026= ~AX_MEDIUM_JUMBO_EN;\n+\t\tax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE,\n+\t\t\t\t  2, 2, \u0026tmp16);\n+\t}\n+\trtnl_unlock();\n+\n+\t/* max qlen depend on hard_mtu and rx_urb_size */\n+\tusbnet_update_max_qlen(dev);\n+\n+\treturn 0;\n+}\n+\n+int ax88179_set_mac_addr(struct net_device *net, void *p)\n+{\n+\tstruct usbnet *dev = netdev_priv(net);\n+\tstruct sockaddr *addr = p;\n+\tint ret;\n+\n+\tif (netif_running(net))\n+\t\treturn -EBUSY;\n+\tif (!is_valid_ether_addr(addr-\u003esa_data))\n+\t\treturn -EADDRNOTAVAIL;\n+\n+\teth_hw_addr_set(net, addr-\u003esa_data);\n+\n+\t/* Set the MAC address */\n+\tret = ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_NODE_ID, ETH_ALEN,\n+\t\t\t\tETH_ALEN, net-\u003edev_addr);\n+\tif (ret \u003c 0)\n+\t\treturn ret;\n+\n+\treturn 0;\n+}\n+\n+int ax88179_suspend_wrapper(struct usb_interface *intf, pm_message_t message)\n+{\n+\tstruct usbnet *dev = usb_get_intfdata(intf);\n+\tstruct ax88179_data *priv;\n+\n+\tpriv = dev-\u003edriver_priv;\n+\n+\treturn priv-\u003esuspend(intf, message);\n+}\n+\n+int ax88179_resume_wrapper(struct usb_interface *intf)\n+{\n+\tstruct usbnet *dev = usb_get_intfdata(intf);\n+\tstruct ax88179_data *priv;\n+\n+\tpriv = dev-\u003edriver_priv;\n+\treturn priv-\u003eresume(intf);\n+}\n+\ndiff --git a/drivers/net/usb/ax88179_lib.h b/drivers/net/usb/ax88179_lib.h\nnew file mode 100644\nindex 0000000000000..a9235ada982b8\n--- /dev/null\n+++ b/drivers/net/usb/ax88179_lib.h\n@@ -0,0 +1,359 @@\n+/* SPDX-License-Identifier: GPL-2.0-or-later */\n+\n+#include \u003clinux/usb.h\u003e\n+#include \u003clinux/crc32.h\u003e\n+#include \u003clinux/phylink.h\u003e\n+#include \u003clinux/usb/usbnet.h\u003e\n+\n+#ifndef __LINUX_USBNET_AX88179_H\n+#define __LINUX_USBNET_AX88179_H\n+\n+#define AX88179_PHY_ID\t\t\t\t0x03\n+#define AX_EEPROM_LEN\t\t\t\t0x100\n+#define AX88179_EEPROM_MAGIC\t\t\t0x17900b95\n+#define AX_MCAST_FLTSIZE\t\t\t8\n+#define AX_MAX_MCAST\t\t\t\t64\n+#define AX_INT_PPLS_LINK\t\t\t((u32)BIT(16))\n+#define AX_RXHDR_L4_TYPE_MASK\t\t\t0x1c\n+#define AX_RXHDR_L4_TYPE_UDP\t\t\t4\n+#define AX_RXHDR_L4_TYPE_TCP\t\t\t16\n+#define AX_RXHDR_L3CSUM_ERR\t\t\t2\n+#define AX_RXHDR_L4CSUM_ERR\t\t\t1\n+#define AX_RXHDR_CRC_ERR\t\t\t((u32)BIT(29))\n+#define AX_RXHDR_DROP_ERR\t\t\t((u32)BIT(31))\n+#define AX_ACCESS_MAC\t\t\t\t0x01\n+#define AX_ACCESS_PHY\t\t\t\t0x02\n+#define AX_ACCESS_EEPROM\t\t\t0x04\n+#define AX_ACCESS_EFUS\t\t\t\t0x05\n+#define AX_RELOAD_EEPROM_EFUSE\t\t\t0x06\n+#define AX88179A_WAKEUP_SETTING\t\t\t0x07\n+#define AX_FW_MODE\t\t\t\t0x08\n+#define AX_GPHY_CTL\t\t\t\t0x0F\n+#define AX88179A_FLASH_READ\t\t\t0x21\n+#define AX88179A_FLASH_WEN\t\t\t0x22\n+#define AX88179A_FLASH_WDIS\t\t\t0x23\n+#define AX88179A_FLASH_WRITE\t\t\t0x24\n+#define AX88179A_PHY_CLAUSE45\t\t\t0x27\n+#define AX88179A_FLASH_ERASE_SECTION\t\t0x28\n+#define AX88179A_ACCESS_BL\t\t\t0x2A\n+#define AX88179A_PHY_POWER\t\t\t0x31\n+#define AX88179A_AUTODETACH\t\t\t0xC0\n+\n+#define AX_PAUSE_WATERLVL_LOW\t\t\t0x54\n+#define AX_PAUSE_WATERLVL_HIGH\t\t\t0x55\n+\n+#define AX_FW_MODE_179A\t\t\t\t0x01\n+#define PHYSICAL_LINK_STATUS\t\t\t0x02\n+\t#define\tAX_USB_SS\t\t0x04\n+\t#define\tAX_USB_HS\t\t0x02\n+\t#define AX_USB_FS\t\t0x01\n+\n+#define GENERAL_STATUS\t\t\t\t0x03\n+/* Check AX88179 version. UA1:Bit2 = 0,  UA2:Bit2 = 1 */\n+\t#define\tAX_SECLD\t\t0x04\n+\n+#define AX_CHIP_STATUS\t\t\t\t0x05\n+\n+#define AX_SROM_ADDR\t\t\t\t0x07\n+#define AX_SROM_CMD\t\t\t\t0x0a\n+\t#define EEP_RD\t\t\t0x04\n+\t#define EEP_BUSY\t\t0x10\n+\n+#define AX_SROM_DATA_LOW\t\t\t0x08\n+#define AX_SROM_DATA_HIGH\t\t\t0x09\n+\n+#define AX_RX_CTL\t\t\t\t0x0b\n+\t#define AX_RX_CTL_DROPCRCERR\t0x0100\n+\t#define AX_RX_CTL_IPE\t\t0x0200\n+\t#define AX_RX_CTL_START\t\t0x0080\n+\t#define AX_RX_CTL_AP\t\t0x0020\n+\t#define AX_RX_CTL_AM\t\t0x0010\n+\t#define AX_RX_CTL_AB\t\t0x0008\n+\t#define AX_RX_CTL_AMALL\t\t0x0002\n+\t#define AX_RX_CTL_PRO\t\t0x0001\n+\t#define AX_RX_CTL_STOP\t\t0x0000\n+\n+#define AX88179A_ETH_TX_GAP\t\t\t0x0D\n+\n+#define AX88179A_BFM_DATA\t\t\t0x0E\n+\t#define AX_TX_QUEUE_CFG\t\t0x02\n+\t#define AX_TX_QUEUE_SET\t\t0x08\n+\t#define AX_TX_Q1_AHB_FC_EN\t0x10\n+\t#define AX_TX_Q2_AHB_FC_EN\t0x20\n+\t#define AX_XGMII_EN\t\t0x80\n+\n+#define AX_NODE_ID\t\t\t\t0x10\n+#define AX_MULFLTARY\t\t\t\t0x16\n+\n+#define AX_MEDIUM_STATUS_MODE\t\t\t0x22\n+\t#define AX_MEDIUM_GIGAMODE\t0x01\n+\t#define AX_MEDIUM_FULL_DUPLEX\t0x02\n+\t#define AX_MEDIUM_EN_125MHZ\t0x08\n+\t#define AX_MEDIUM_RXFLOW_CTRLEN\t0x10\n+\t#define AX_MEDIUM_TXFLOW_CTRLEN\t0x20\n+\t#define AX_MEDIUM_RECEIVE_EN\t0x100\n+\t#define AX_MEDIUM_PS\t\t0x200\n+\t#define AX_MEDIUM_JUMBO_EN\t0x8040\n+\n+#define AX_MONITOR_MOD\t\t\t\t0x24\n+\t#define AX_MONITOR_MODE_RWLC\t0x02\n+\t#define AX_MONITOR_MODE_RWMP\t0x04\n+\t#define AX_MONITOR_MODE_PMEPOL\t0x20\n+\t#define AX_MONITOR_MODE_PMETYPE\t0x40\n+\n+#define AX_GPIO_CTRL\t\t\t\t0x25\n+\t#define AX_GPIO_CTRL_GPIO3EN\t0x80\n+\t#define AX_GPIO_CTRL_GPIO2EN\t0x40\n+\t#define AX_GPIO_CTRL_GPIO1EN\t0x20\n+\n+#define AX_PHYPWR_RSTCTL\t\t\t0x26\n+\t#define AX_PHYPWR_RSTCTL_BZ\t0x0010\n+\t#define AX_PHYPWR_RSTCTL_IPRL\t0x0020\n+\t#define AX_PHYPWR_RSTCTL_AT\t0x1000\n+\n+#define AX88179A_VLAN_ID_ADDRESS\t\t0x2A\n+\n+#define AX88179A_VLAN_ID_CONTROL\t\t0x2B\n+\t#define AX_VLAN_CONTROL_WE\t0x0001\n+\t#define AX_VLAN_CONTROL_RD\t0x0002\n+\t#define AX_VLAN_CONTROL_VSO\t0x0010\n+\t#define AX_VLAN_CONTROL_VFE\t0x0020\n+\n+#define AX88179A_VLAN_ID_DATA0\t\t\t0x2C\n+#define AX88179A_VLAN_ID_DATA1\t\t\t0x2D\n+\n+#define AX_RX_BULKIN_QCTRL\t\t\t0x2e\n+#define AX_GPHY_EEE_CTRL\t\t\t0x01\n+\n+#define AX_CLK_SELECT\t\t\t\t0x33\n+\t#define AX_CLK_SELECT_BCS\t0x01\n+\t#define AX_CLK_SELECT_ACS\t0x02\n+\t#define AX_CLK_SELECT_ULR\t0x08\n+\n+#define AX_RXCOE_CTL\t\t\t\t0x34\n+\t#define AX_RXCOE_IP\t\t0x01\n+\t#define AX_RXCOE_TCP\t\t0x02\n+\t#define AX_RXCOE_UDP\t\t0x04\n+\t#define AX_RXCOE_TCPV6\t\t0x20\n+\t#define AX_RXCOE_UDPV6\t\t0x40\n+\n+#define AX_TXCOE_CTL\t\t\t\t0x35\n+\t#define AX_TXCOE_IP\t\t0x01\n+\t#define AX_TXCOE_TCP\t\t0x02\n+\t#define AX_TXCOE_UDP\t\t0x04\n+\t#define AX_TXCOE_TCPV6\t\t0x20\n+\t#define AX_TXCOE_UDPV6\t\t0x40\n+\n+#define AX88179A_MAC_BM_INT_MASK\t\t0x41\n+#define AX88179A_MAC_BM_RX_DMA_CTL\t\t0x43\n+#define AX88179A_MAC_BM_TX_DMA_CTL\t\t0x46\n+\n+#define AX88179A_MAC_RX_STATUS_CDC\t\t0x6D\n+\t#define AX_LSOFC_WCNT_7_ACCESS\t0x03\n+\t#define AX_GMII_CRC_APPEND\t0x10\n+\n+#define AX_LEDCTRL\t\t\t\t0x73\n+#define AX88179A_MAC_ARC_CTRL\t\t\t0x9E\n+#define AX88179A_MAC_SWP_CTRL\t\t\t0xB1\n+\n+#define AX88179A_MAC_TX_PAUSE\t\t\t0xB2\n+\n+#define AX88179A_MAC_CDC_DELAY_TX\t\t0xB5\n+\n+#define AX88179A_MAC_PATH\t\t\t0xB7\n+\t#define AX_MAC_RX_PATH_READY\t0x01\n+\t#define AX_MAC_TX_PATH_READY\t0x02\n+\n+#define AX88179A_NEW_PAUSE_CTRL\t\t\t0xB8\n+\t#define AX_NEW_PAUSE_EN\t\t0x01\n+\n+#define AX88179A_MAC_BULK_OUT_CTRL\t\t0xB9\n+\t#define AX_MAC_EFF_EN\t\t0x02\n+\n+#define AX88179A_MAC_RX_DATA_CDC_CNT\t\t0xC0\n+\t#define AX_MAC_LSO_ERR_EN\t0x04\n+\t#define AX_MAC_MIQFFCTRL_FORMAT\t0x10\n+\t#define AX_MAC_MIQFFCTRL_DROP_CRC 0x20\n+\n+#define AX88179A_AUTODETACH_DELAY\t(5UL \u003c\u003c 8)\n+#define AX88179A_AUTODETACH_EN\t\t1\n+\n+#define AX88179A_MAC_LSO_ENHANCE_CTRL\t\t0xC3\n+\t#define AX_LSO_ENHANCE_EN\t0x01\n+\n+#define AX88179A_MAC_TX_HDR_CKSUM\t\t0xCC\n+#define AX88179A_EP5_EHR\t\t\t0xF9\n+\n+#define AX_PHY_POWER\t\t\t\t0x02\n+\n+#define EPHY_LOW_POWER_EN\t\t\t0x01\n+#define S5_WOL_EN\t\t\t\t0x04\n+#define S5_WOL_LOW_POWER\t\t\t0x20\n+\n+#define GMII_PHY_PHYSR\t\t\t\t0x11\n+\t#define GMII_PHY_PHYSR_SMASK\t0xc000\n+\t#define GMII_PHY_PHYSR_GIGA\t0x8000\n+\t#define GMII_PHY_PHYSR_100\t0x4000\n+\t#define GMII_PHY_PHYSR_FULL\t0x2000\n+\t#define GMII_PHY_PHYSR_LINK\t0x400\n+\n+#define GMII_LED_ACT\t\t\t\t0x1a\n+\t#define\tGMII_LED_ACTIVE_MASK\t0xff8f\n+\t#define\tGMII_LED0_ACTIVE\tBIT(4)\n+\t#define\tGMII_LED1_ACTIVE\tBIT(5)\n+\t#define\tGMII_LED2_ACTIVE\tBIT(6)\n+\n+#define GMII_LED_LINK\t\t\t\t0x1c\n+\t#define\tGMII_LED_LINK_MASK\t0xf888\n+\t#define\tGMII_LED0_LINK_10\tBIT(0)\n+\t#define\tGMII_LED0_LINK_100\tBIT(1)\n+\t#define\tGMII_LED0_LINK_1000\tBIT(2)\n+\t#define\tGMII_LED1_LINK_10\tBIT(4)\n+\t#define\tGMII_LED1_LINK_100\tBIT(5)\n+\t#define\tGMII_LED1_LINK_1000\tBIT(6)\n+\t#define\tGMII_LED2_LINK_10\tBIT(8)\n+\t#define\tGMII_LED2_LINK_100\tBIT(9)\n+\t#define\tGMII_LED2_LINK_1000\tBIT(10)\n+\t#define\tLED0_ACTIVE\t\tBIT(0)\n+\t#define\tLED0_LINK_10\t\tBIT(1)\n+\t#define\tLED0_LINK_100\t\tBIT(2)\n+\t#define\tLED0_LINK_1000\t\tBIT(3)\n+\t#define\tLED0_FD\t\t\tBIT(4)\n+\t#define\tLED0_USB3_MASK\t\t0x001f\n+\t#define\tLED1_ACTIVE\t\tBIT(5)\n+\t#define\tLED1_LINK_10\t\tBIT(6)\n+\t#define\tLED1_LINK_100\t\tBIT(7)\n+\t#define\tLED1_LINK_1000\t\tBIT(8)\n+\t#define\tLED1_FD\t\t\tBIT(9)\n+\t#define\tLED1_USB3_MASK\t\t0x03e0\n+\t#define\tLED2_ACTIVE\t\tBIT(10)\n+\t#define\tLED2_LINK_1000\t\tBIT(13)\n+\t#define\tLED2_LINK_100\t\tBIT(12)\n+\t#define\tLED2_LINK_10\t\tBIT(11)\n+\t#define\tLED2_FD\t\t\tBIT(14)\n+\t#define\tLED_VALID\t\tBIT(15)\n+\t#define\tLED2_USB3_MASK\t\t0x7c00\n+\n+#define GMII_PHYPAGE\t\t\t\t0x1e\n+#define GMII_PHY_PAGE_SELECT\t\t\t0x1f\n+\t#define GMII_PHY_PGSEL_EXT\t0x0007\n+\t#define GMII_PHY_PGSEL_PAGE0\t0x0000\n+\t#define GMII_PHY_PGSEL_PAGE3\t0x0003\n+\t#define GMII_PHY_PGSEL_PAGE5\t0x0005\n+\n+#define AX88179A_SW_REVISION\t\t0xFC\n+#define AX88179A_SW_VERSION\t\t0xFD\n+\n+/* TX Descriptor */\n+#define AX179A_TX_DESC_LEN_MASK\t\t0x1FFFFF\n+#define AX179A_TX_DESC_DROP_PADD\tBIT(28)\n+#define AX179A_TX_DESC_VLAN\t\tBIT(29)\n+#define AX179A_TX_DESC_MSS\t\tGENMASK(46, 32)\n+#define AX179A_TX_DESC_VLAN_ID\t\tGENMASK(63, 48)\n+\n+/* RX Packet Descriptor */\n+#define AX179A_RX_PD_L4_ERR\t\tBIT(0)\n+#define AX179A_RX_PD_L3_ERR\t\tBIT(1)\n+#define AX179A_RX_PD_L4_TYPE_MASK\t0x1C\n+#define AX179A_RX_PD_L4_UDP\t\t0x04\n+#define AX179A_RX_PD_L4_TCP\t\t0x10\n+#define AX179A_RX_PD_L3_TYPE_MASK\t0x60\n+#define AX179A_RX_PD_L3_IP\t\t0x20\n+#define AX179A_RX_PD_L3_IP6\t\t0x40\n+\n+#define AX179A_RX_PD_VLAN\t\tBIT(10)\n+#define AX179A_RX_PD_RX_OK\t\tBIT(11)\n+#define AX179A_RX_PD_DROP\t\tBIT(31)\n+#define AX179A_RX_PD_LEN\t\tGENMASK(30, 16)\n+#define AX179A_RX_PD_VLAN_SHIFT\t\t0x20\n+\n+/* RX Descriptor header */\n+#define AX179A_RX_DH_PKT_CNT_MASK\t0x1FFF\n+#define AX179A_RX_DH_DESC_OFFSET\tGENMASK(31, 13)\n+\n+#define AX179A_RX_HW_PAD\t\t0x02\n+\n+#define AX_ADVERTISE_2500\t\t0x1000\n+\n+enum ax_ether_link_speed {\n+\tETHER_LINK_NONE = 0,\n+\tETHER_LINK_10   = 1,\n+\tETHER_LINK_100  = 2,\n+\tETHER_LINK_1000 = 3,\n+\tETHER_LINK_2500 = 4,\n+};\n+\n+enum ax_chip_version {\n+\tAX_VERSION_INVALID\t\t= 0x0,\n+\tAX_VERSION_AX88179\t\t= 0x4,\n+\tAX_VERSION_AX88179A\t\t= 0x6,\t/* Also AX88772D */\n+\tAX_VERSION_AX88279\t\t= 0x7,\n+};\n+\n+struct ax88179_data {\n+\tu8  eee_enabled;\n+\tu8  eee_active;\n+\tu16 rxctl;\n+\tu8 in_pm;\n+\tu32 wol_supported;\n+\tu32 wolopts;\n+\tu8 disconnecting;\n+\tu8 chip_version;\n+\tu8 fw_version[4];\n+\tu8 is_ax88772d;\n+\tu8 ip_align;\n+\tu8 link;\n+\tu8 speed;\n+\tu8 full_duplex;\n+\tu8 rx_checksum;\n+\tu8 eeprom_read_cmd;\n+\tu8 eeprom_write_cmd;\n+\tu8 eeprom_wen;\n+\tu16 eeprom_block;\n+\tstruct mii_bus *mdio;\n+\tstruct phy_device *phydev;\n+\tstruct phylink *phylink;\n+\tstruct phylink_config phylink_config;\n+\tint (*resume)(struct usb_interface *intf);\n+\tint (*suspend)(struct usb_interface *intf, pm_message_t message);\n+};\n+\n+struct ax88179_int_data {\n+\t__le32 intdata1;\n+\t__le32 intdata2;\n+};\n+\n+struct ax_bulkin_settings {\n+\tunsigned char ctrl, timer_l, timer_h, size, ifg;\n+};\n+\n+void ax88179_set_pm_mode(struct usbnet *dev, bool pm_mode);\n+int __ax88179_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index, u16 size, void *data);\n+int ax88179_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index, u16 size, void *data);\n+int ax88179_write_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index, u16 size,\n+\t\t      const void *data);\n+void ax88179_write_cmd_async(struct usbnet *dev, u8 cmd, u16 value, u16 index,\n+\t\t\t     u16 size, void *data);\n+int ax88179_write_u16(struct usbnet *dev, u8 cmd, u16 value, u16 index, u16 data);\n+int ax88179_write_u8(struct usbnet *dev, u8 cmd, u16 value, u16 index, u8 data);\n+int ax88179_mdio_read(struct net_device *netdev, int phy_id, int loc);\n+void ax88179_mdio_write(struct net_device *netdev, int phy_id, int loc, int val);\n+struct ax88179_data *netdev2data(struct net_device *net);\n+void ax88179_status(struct usbnet *dev, struct urb *urb);\n+void ax88179_get_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo);\n+int ax88179_set_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo);\n+int ax88179_get_eeprom(struct net_device *net, struct ethtool_eeprom *eeprom, u8 *data);\n+int ax88179_set_eeprom(struct net_device *net, struct ethtool_eeprom *eeprom, u8 *data);\n+void ax88179_set_multicast(struct net_device *net);\n+int ax88179_set_features(struct net_device *net, netdev_features_t features);\n+void ax88179_get_mac_addr(struct usbnet *dev);\n+int ax88179_change_mtu(struct net_device *net, int new_mtu);\n+int ax88179_set_mac_addr(struct net_device *net, void *p);\n+int ax88179_suspend_wrapper(struct usb_interface *intf, pm_message_t message);\n+int ax88179_resume_wrapper(struct usb_interface *intf);\n+\n+extern const struct driver_info ax88179a_info;\n+extern const struct driver_info ax88772d_info;\n+extern const struct driver_info ax88279_info;\n+\n+#endif /*__LINUX_USBNET_AX88179_H */\ndiff --git a/drivers/net/usb/ax88179a_devices.c b/drivers/net/usb/ax88179a_devices.c\nnew file mode 100644\nindex 0000000000000..f2140e2bb4431\n--- /dev/null\n+++ b/drivers/net/usb/ax88179a_devices.c\n@@ -0,0 +1,1285 @@\n+// SPDX-License-Identifier: GPL-2.0-or-later\n+\n+#include \u003clinux/module.h\u003e\n+#include \u003clinux/phylink.h\u003e\n+#include \u003clinux/if_vlan.h\u003e\n+#include \"ax88179_lib.h\"\n+\n+static int ax88179a_reset(struct usbnet *dev);\n+\n+#define AX88279_EEPROM_LEN\t\t\t0x4000\n+#define AX88179A_EEPROM_LEN\t\t\t(32 * 20)\n+\n+enum ax_bulk_in_speeds {\n+\tBULK_IN_SPEED_2G5 = 0,\n+\tBULK_IN_SPEED_1G_SS = 1,\n+\tBULK_IN_SPEED_1G_HS   = 2,\n+\tBULK_IN_SPEED_100_FULL_SS = 3,\n+\tBULK_IN_SPEED_100_HALF_SS = 4,\n+\tBULK_IN_SPEED_100_FULL_HS = 5,\n+\tBULK_IN_SPEED_100_HALF_HS = 6,\n+\tBULK_IN_SPEED_FS = 7,\n+};\n+\n+static const struct ax_bulkin_settings AX88179A_BULKIN_SIZE[] = {\n+\t[BULK_IN_SPEED_1G_SS]\t\t= {5, 0x7B, 0x00, 0x17, 0x0F},\n+\t[BULK_IN_SPEED_1G_HS]\t\t= {5, 0xC0, 0x02, 0x06, 0x0F},\n+\t[BULK_IN_SPEED_100_FULL_SS]\t= {7, 0xF0, 0x00, 0x0C, 0x0F},\n+\t[BULK_IN_SPEED_100_HALF_SS]\t= {6, 0x00, 0x00, 0x06, 0x0F},\n+\t[BULK_IN_SPEED_100_FULL_HS]\t= {5, 0xC0, 0x04, 0x06, 0x0F},\n+\t[BULK_IN_SPEED_100_HALF_HS]\t= {7, 0xC0, 0x04, 0x06, 0x0F},\n+\t[BULK_IN_SPEED_FS]\t\t= {7, 0x00, 0x00, 0x03, 0x3F},\n+};\n+\n+static const struct ax_bulkin_settings AX88772D_BULKIN_SIZE[] = {\n+\t[BULK_IN_SPEED_100_FULL_HS]\t= {5, 0xC0, 0x04, 0x06, 0x0F},\n+\t[BULK_IN_SPEED_100_HALF_HS]\t= {7, 0xC0, 0x04, 0x06, 0x0F},\n+\t[BULK_IN_SPEED_FS]\t\t= {7, 0x00, 0x00, 0x03, 0x3F},\n+};\n+\n+static const struct ax_bulkin_settings AX88279_BULKIN_SIZE[] = {\n+\t[BULK_IN_SPEED_2G5]\t\t= {5, 0x10, 0x01, 0x11, 0x0F},\n+\t[BULK_IN_SPEED_1G_SS]\t\t= {7, 0xB3, 0x01, 0x11, 0x0F},\n+\t[BULK_IN_SPEED_1G_HS]\t\t= {7, 0xC0, 0x02, 0x06, 0x0F},\n+\t[BULK_IN_SPEED_100_FULL_SS]\t= {7, 0x80, 0x01, 0x03, 0x0F},\n+\t[BULK_IN_SPEED_100_HALF_SS]\t= {7, 0x80, 0x01, 0x03, 0x0F},\n+\t[BULK_IN_SPEED_100_FULL_HS]\t= {7, 0x80, 0x01, 0x03, 0x0F},\n+\t[BULK_IN_SPEED_100_HALF_HS]\t= {7, 0x80, 0x01, 0x03, 0x0F},\n+\t[BULK_IN_SPEED_FS]\t\t= {7, 0x00, 0x00, 0x03, 0x3F},\n+};\n+\n+static int ax88179_mdiobus_read(struct mii_bus *bus, int phy_id, int regnum)\n+{\n+\tstruct usbnet *dev = bus-\u003epriv;\n+\tstruct ax88179_data *priv;\n+\tu16 res;\n+\tint ret;\n+\n+\tpriv = dev-\u003edriver_priv;\n+\t/* When reading PHYSID, return unused PHY-IDs from the ASIX vendor range */\n+\tif (phy_id == AX88179_PHY_ID \u0026\u0026 regnum == MII_PHYSID1)\n+\t\treturn 0x003b;\n+\tif (phy_id == AX88179_PHY_ID \u0026\u0026 regnum == MII_PHYSID2) {\n+\t\tif (priv-\u003echip_version == AX_VERSION_AX88179A \u0026\u0026 priv-\u003eis_ax88772d)\n+\t\t\treturn 0x772d;\n+\t\telse if (priv-\u003echip_version == AX_VERSION_AX88179A)\n+\t\t\treturn 0x179a;\n+\t\telse if (priv-\u003echip_version == AX_VERSION_AX88279)\n+\t\t\treturn 0x2790;\n+\t}\n+\n+\tret = ax88179_read_cmd(dev, AX_ACCESS_PHY, phy_id, (__u16)regnum, 2, \u0026res);\n+\tif (ret \u003c 0)\n+\t\treturn ret;\n+\treturn res;\n+}\n+\n+static int ax88179_mdiobus_write(struct mii_bus *bus, int phy_id, int regnum, u16 val)\n+{\n+\tstruct usbnet *dev = bus-\u003epriv;\n+\n+\treturn ax88179_write_u16(dev, AX_ACCESS_PHY, phy_id, (__u16)regnum, val);\n+}\n+\n+static int ax179a_read_mmd(struct usbnet *dev, u16 dev_addr, u16 reg)\n+{\n+\tu16 res;\n+\tint ret;\n+\n+\tret = ax88179_read_cmd(dev, AX88179A_PHY_CLAUSE45, dev_addr, reg, 2, \u0026res);\n+\tif (ret \u003c 0)\n+\t\treturn ret;\n+\treturn res;\n+}\n+\n+static int ax179a_write_mmd(struct usbnet *dev, u16 dev_addr, u16 reg, u16 data)\n+{\n+\treturn ax88179_write_cmd(dev, AX88179A_PHY_CLAUSE45, dev_addr, reg, 2, \u0026data);\n+}\n+\n+static int ax88179_mdiobus_read_c45(struct mii_bus *bus, int addr, int devnum, int regnum)\n+{\n+\tstruct usbnet *dev = bus-\u003epriv;\n+\n+\tif (addr != AX88179_PHY_ID)\n+\t\treturn -EINVAL;\n+\n+\treturn ax179a_read_mmd(dev, devnum, regnum);\n+}\n+\n+static int ax88179_mdiobus_write_c45(struct mii_bus *bus, int addr, int devnum,\n+\t\t\t\t     int regnum, u16 val)\n+{\n+\tstruct usbnet *dev = bus-\u003epriv;\n+\n+\tif (addr != AX88179_PHY_ID)\n+\t\treturn -EINVAL;\n+\n+\treturn ax179a_write_mmd(dev, devnum, regnum, val);\n+}\n+\n+static void ax88179a_status(struct usbnet *dev, struct urb *urb)\n+{\n+\tstruct ax88179_data *data = dev-\u003edriver_priv;\n+\n+\tif (urb-\u003eactual_length \u003c 8)\n+\t\treturn;\n+\n+\tphylink_mac_interrupt(data-\u003ephylink);\n+}\n+\n+static int ax88179a_suspend(struct usb_interface *intf, pm_message_t message)\n+{\n+\tstruct usbnet *dev = usb_get_intfdata(intf);\n+\tstruct ax88179_data *priv;\n+\tu8 tmp8 = 0;\n+\tu16 tmp16;\n+\tint ret;\n+\n+\tpriv = dev-\u003edriver_priv;\n+\tax88179_set_pm_mode(dev, true);\n+\n+\tif (netif_running(dev-\u003enet)) {\n+\t\trtnl_lock();\n+\t\tphylink_suspend(priv-\u003ephylink, !!priv-\u003ewolopts);\n+\t\trtnl_unlock();\n+\t}\n+\n+\tret = usbnet_suspend(intf, message);\n+\tif (ret) {\n+\t\trtnl_lock();\n+\t\tphylink_resume(priv-\u003ephylink);\n+\t\trtnl_unlock();\n+\t\treturn ret;\n+\t}\n+\n+\t/* Enable WoL */\n+\tif (priv-\u003ewolopts) {\n+\t\tax88179_read_cmd(dev, AX_ACCESS_MAC, AX_MONITOR_MOD, 1, 1, \u0026tmp8);\n+\t\ttmp8 \u0026= ~(AX_MONITOR_MODE_RWLC | AX_MONITOR_MODE_RWMP);\n+\t\tif (priv-\u003ewolopts \u0026 WAKE_PHY)\n+\t\t\ttmp8 |= AX_MONITOR_MODE_RWLC;\n+\t\tif (priv-\u003ewolopts \u0026 WAKE_MAGIC)\n+\t\t\ttmp8 |= AX_MONITOR_MODE_RWMP;\n+\n+\t\tax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MONITOR_MOD, 1, 1, \u0026tmp8);\n+\n+\t\tax88179_read_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE, 2, 2, \u0026tmp16);\n+\t\ttmp16 |= AX_MEDIUM_RECEIVE_EN;\n+\t\tax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE, 2, 2, \u0026tmp16);\n+\n+\t\tif (priv-\u003echip_version == AX_VERSION_AX88279)\n+\t\t\tax88179_write_cmd(dev, AX88179A_WAKEUP_SETTING, 8,\n+\t\t\t\t\t  EPHY_LOW_POWER_EN | S5_WOL_EN\n+\t\t\t\t\t  | S5_WOL_LOW_POWER | 0x8000, 0, NULL);\n+\t\telse\n+\t\t\tax88179_write_cmd(dev, AX88179A_WAKEUP_SETTING, 0,\n+\t\t\t\t\t  EPHY_LOW_POWER_EN, 0, NULL);\n+\n+\t} else if (priv-\u003echip_version == AX_VERSION_AX88279) {\n+\t\tax88179_write_cmd(dev, AX88179A_WAKEUP_SETTING, 8, 0x8000, 0, NULL);\n+\t}\n+\n+\tax88179_set_pm_mode(dev, false);\n+\treturn 0;\n+}\n+\n+static int ax88179a_auto_detach(struct usbnet *dev)\n+{\n+\tu16 tmp16;\n+\n+\ttmp16 = AX88179A_AUTODETACH_DELAY;\n+\tax88179_write_cmd(dev, AX88179A_AUTODETACH, tmp16, 0, 0, NULL);\n+\treturn 0;\n+}\n+\n+static int ax88179a_resume(struct usb_interface *intf)\n+{\n+\tstruct usbnet *dev = usb_get_intfdata(intf);\n+\tstruct ax88179_data *ax179_data;\n+\tu8 reg8 = 0;\n+\tint ret;\n+\n+\tax179_data = dev-\u003edriver_priv;\n+\tax88179_set_pm_mode(dev, true);\n+\n+\tret = ax88179_read_cmd(dev, AX88179A_PHY_POWER, 0, 0, 1, \u0026reg8);\n+\tif (ret \u003c 0) {\n+\t\tax88179_set_pm_mode(dev, false);\n+\t\treturn ret;\n+\t}\n+\tif (!(reg8 \u0026 AX_PHY_POWER)) {\n+\t\treg8 = AX_PHY_POWER;\n+\t\tax88179_write_cmd(dev, AX88179A_PHY_POWER, 0, 0, 1, \u0026reg8);\n+\t\tmsleep(250);\n+\t}\n+\tax88179_write_cmd(dev, AX_FW_MODE, AX_FW_MODE_179A, 0, 0, NULL);\n+\n+\t/* Now, that AX_FW_MODE_179A is enabled, the PHY needs a power-cycle.\n+\t * PHY-power is re-enabled in ax88179a_reset()\n+\t */\n+\tax88179_write_u8(dev, AX88179A_PHY_POWER, 0, 0, 0);\n+\tmsleep(250);\n+\n+\tax88179a_reset(dev);\n+\n+\tif (netif_running(dev-\u003enet)) {\n+\t\trtnl_lock();\n+\t\tphylink_resume(ax179_data-\u003ephylink);\n+\t\trtnl_unlock();\n+\t}\n+\n+\tax88179_set_pm_mode(dev, false);\n+\n+\treturn usbnet_resume(intf);\n+}\n+\n+static void ax88179a_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *info)\n+{\n+\tstruct ax88179_data *priv = netdev2data(net);\n+\n+\t/* Inherit standard device info */\n+\tusbnet_get_drvinfo(net, info);\n+\tif (priv-\u003echip_version \u003c AX_VERSION_AX88179A)\n+\t\treturn;\n+\n+\tsnprintf(info-\u003efw_version, sizeof(info-\u003efw_version), \"%d.%d.%d.%d\",\n+\t\t priv-\u003efw_version[0], priv-\u003efw_version[1],\n+\t\t priv-\u003efw_version[2], priv-\u003efw_version[3]);\n+}\n+\n+static void ax88179a_bulkin_config(struct usbnet *dev, u8 link_sts, u8 speed, bool full_duplex)\n+{\n+\tstruct ax88179_data *ax179_data = dev-\u003edriver_priv;\n+\tconst struct ax_bulkin_settings *bulkin_data;\n+\tint index = 0;\n+\n+\tswitch (speed) {\n+\tcase ETHER_LINK_2500:\t/* AX88279 only */\n+\t\tindex = BULK_IN_SPEED_2G5;\n+\t\tbreak;\n+\n+\tcase ETHER_LINK_1000:\t/* AX88279 \u0026 AX88178A */\n+\t\tif (link_sts \u0026 AX_USB_SS)\n+\t\t\tindex = BULK_IN_SPEED_1G_SS;\n+\t\telse if (link_sts \u0026 AX_USB_HS)\n+\t\t\tindex = BULK_IN_SPEED_1G_HS;\n+\t\tbreak;\n+\n+\tcase ETHER_LINK_100:\n+\t\tif (link_sts \u0026 AX_USB_SS)\n+\t\t\tindex = BULK_IN_SPEED_100_FULL_SS;\n+\t\telse if (link_sts \u0026 AX_USB_HS)\n+\t\t\tindex = BULK_IN_SPEED_100_FULL_HS;\n+\t\tif (!full_duplex)\n+\t\t\tindex++;\n+\t\tbreak;\n+\n+\tcase ETHER_LINK_10:\n+\t\tindex = BULK_IN_SPEED_FS;\n+\t\tbreak;\n+\n+\tdefault:\t/* No link */\n+\t\tindex = BULK_IN_SPEED_FS;\n+\t}\n+\n+\tif (link_sts \u0026 AX_USB_FS)\n+\t\tindex = BULK_IN_SPEED_FS;\n+\n+\tif (ax179_data-\u003echip_version == AX_VERSION_AX88279) {\n+\t\tbulkin_data = AX88279_BULKIN_SIZE;\n+\t} else {\n+\t\tif (ax179_data-\u003eis_ax88772d)\n+\t\t\tbulkin_data = AX88772D_BULKIN_SIZE;\n+\t\telse\n+\t\t\tbulkin_data = AX88179A_BULKIN_SIZE;\n+\t}\n+\n+\tax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_BULKIN_QCTRL, 5, 5, \u0026bulkin_data[index]);\n+}\n+\n+static void ax88179a_get_pauseparam(struct net_device *net, struct ethtool_pauseparam *pause)\n+{\n+\tstruct ax88179_data *data = netdev2data(net);\n+\n+\tphylink_ethtool_get_pauseparam(data-\u003ephylink, pause);\n+}\n+\n+static int ax88179a_set_pauseparam(struct net_device *net, struct ethtool_pauseparam *pause)\n+{\n+\tstruct ax88179_data *data = netdev2data(net);\n+\n+\treturn phylink_ethtool_set_pauseparam(data-\u003ephylink, pause);\n+}\n+\n+static int ax88179a_get_link_ksettings(struct net_device *net,\n+\t\t\t\t       struct ethtool_link_ksettings *cmd)\n+{\n+\tstruct ax88179_data *data = netdev2data(net);\n+\n+\treturn phylink_ethtool_ksettings_get(data-\u003ephylink, cmd);\n+}\n+\n+static int ax88179a_set_link_ksettings(struct net_device *net,\n+\t\t\t\t       const struct ethtool_link_ksettings *cmd)\n+{\n+\tstruct ax88179_data *data = netdev2data(net);\n+\n+\treturn phylink_ethtool_ksettings_set(data-\u003ephylink, cmd);\n+}\n+\n+static int ax88179a_get_eeprom_len(struct net_device *net)\n+{\n+\tstruct ax88179_data *ax179_data = netdev2data(net);\n+\n+\tif (ax179_data-\u003echip_version \u003e= AX_VERSION_AX88279)\n+\t\treturn AX88279_EEPROM_LEN;\n+\telse\n+\t\treturn AX88179A_EEPROM_LEN;\n+}\n+\n+static int ax88179a_get_eee(struct net_device *net, struct ethtool_keee *edata)\n+{\n+\tstruct ax88179_data *ax179_data = netdev2data(net);\n+\n+\treturn phylink_ethtool_get_eee(ax179_data-\u003ephylink, edata);\n+}\n+\n+static int ax88179a_set_eee(struct net_device *net, struct ethtool_keee *edata)\n+{\n+\tstruct ax88179_data *ax179_data = netdev2data(net);\n+\n+\treturn phylink_ethtool_set_eee(ax179_data-\u003ephylink, edata);\n+}\n+\n+static const struct ethtool_ops ax88179a_ethtool_ops = {\n+\t.get_drvinfo            = ax88179a_get_drvinfo,\n+\t.get_link\t\t= ethtool_op_get_link,\n+\t.get_msglevel\t\t= usbnet_get_msglevel,\n+\t.set_msglevel\t\t= usbnet_set_msglevel,\n+\t.get_wol\t\t= ax88179_get_wol,\n+\t.set_wol\t\t= ax88179_set_wol,\n+\t.get_eeprom_len\t\t= ax88179a_get_eeprom_len,\n+\t.get_eeprom\t\t= ax88179_get_eeprom,\n+\t.set_eeprom\t\t= ax88179_set_eeprom,\n+\t.get_eee\t\t= ax88179a_get_eee,\n+\t.set_eee\t\t= ax88179a_set_eee,\n+\t.nway_reset\t\t= phy_ethtool_nway_reset,\n+\t.get_link_ksettings\t= ax88179a_get_link_ksettings,\n+\t.set_link_ksettings\t= ax88179a_set_link_ksettings,\n+\t.get_pauseparam\t\t= ax88179a_get_pauseparam,\n+\t.set_pauseparam\t\t= ax88179a_set_pauseparam,\n+\t.get_ts_info\t\t= ethtool_op_get_ts_info,\n+};\n+\n+static int ax88179a_vlan_rx_kill_vid(struct net_device *net, __be16 proto, u16 vid)\n+{\n+\tstruct usbnet *dev = netdev_priv(net);\n+\tu8 vlan_ctrl;\n+\tint ret = 0;\n+\tu16 reg16;\n+\tu8 reg8;\n+\n+\tret = ax88179_read_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_CONTROL, 1, 1, \u0026reg8);\n+\tif (ret \u003c 0)\n+\t\treturn ret;\n+\tvlan_ctrl = reg8;\n+\n+\t/* Address */\n+\treg8 = (vid / 16);\n+\tret = ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_ADDRESS, 1, 1, \u0026reg8);\n+\tif (ret \u003c 0)\n+\t\treturn ret;\n+\n+\t/* Data */\n+\treg8 = vlan_ctrl | AX_VLAN_CONTROL_RD;\n+\tret = ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_CONTROL, 1, 1, \u0026reg8);\n+\tif (ret \u003c 0)\n+\t\treturn ret;\n+\n+\tret = ax88179_read_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_DATA0, 2, 2, \u0026reg16);\n+\tif (ret \u003c 0)\n+\t\treturn ret;\n+\treg16 \u0026= ~(1 \u003c\u003c (vid % 16));\n+\tret = ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_DATA0, 2, 2, \u0026reg16);\n+\tif (ret \u003c 0)\n+\t\treturn ret;\n+\n+\treg8 = vlan_ctrl | AX_VLAN_CONTROL_WE;\n+\tret = ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_CONTROL, 1, 1, \u0026reg8);\n+\tif (ret \u003c 0)\n+\t\treturn ret;\n+\n+\treturn 0;\n+}\n+\n+static int ax88179a_vlan_rx_add_vid(struct net_device *net, __be16 proto, u16 vid)\n+{\n+\tstruct usbnet *dev = netdev_priv(net);\n+\tu8 vlan_ctrl;\n+\tint ret = 0;\n+\tu16 reg16;\n+\tu8 reg8;\n+\n+\tret = ax88179_read_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_CONTROL, 1, 1, \u0026reg8);\n+\tif (ret \u003c 0)\n+\t\treturn ret;\n+\tvlan_ctrl = reg8;\n+\n+\t/* Address */\n+\treg8 = (vid / 16);\n+\tret = ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_ADDRESS, 1, 1, \u0026reg8);\n+\tif (ret \u003c 0)\n+\t\treturn ret;\n+\n+\t/* Data */\n+\treg8 = vlan_ctrl | AX_VLAN_CONTROL_RD;\n+\tret = ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_CONTROL, 1, 1, \u0026reg8);\n+\tif (ret \u003c 0)\n+\t\treturn ret;\n+\n+\tret = ax88179_read_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_DATA0, 2, 2, \u0026reg16);\n+\tif (ret \u003c 0)\n+\t\treturn ret;\n+\treg16 |= (1 \u003c\u003c (vid % 16));\n+\tret = ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_DATA0, 2, 2, \u0026reg16);\n+\tif (ret \u003c 0)\n+\t\treturn ret;\n+\n+\treg8 = vlan_ctrl | AX_VLAN_CONTROL_WE;\n+\tret = ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_CONTROL, 1, 1, \u0026reg8);\n+\tif (ret \u003c 0)\n+\t\treturn ret;\n+\n+\treturn 0;\n+}\n+\n+static void ax88179a_mdio_unregister(struct ax88179_data *data)\n+{\n+\tmdiobus_unregister(data-\u003emdio);\n+\tmdiobus_free(data-\u003emdio);\n+}\n+\n+static int ax88179a_init_phy(struct usbnet *dev)\n+{\n+\tstruct ax88179_data *data = dev-\u003edriver_priv;\n+\tint ret;\n+\n+\tdata-\u003ephydev = mdiobus_get_phy(data-\u003emdio, AX88179_PHY_ID);\n+\tif (!data-\u003ephydev) {\n+\t\tnetdev_err(dev-\u003enet, \"Could not find PHY\\n\");\n+\t\treturn -ENODEV;\n+\t}\n+\n+\tdata-\u003ephydev-\u003eirq = PHY_MAC_INTERRUPT;\n+\tret = phylink_connect_phy(data-\u003ephylink, data-\u003ephydev);\n+\tif (ret) {\n+\t\tnetdev_err(dev-\u003enet, \"Could not connect PHY\\n\");\n+\t\treturn ret;\n+\t}\n+\n+\tphy_suspend(data-\u003ephydev);\n+\tdata-\u003ephydev-\u003emac_managed_pm = true;\n+\n+\tphy_attached_info(data-\u003ephydev);\n+\n+\treturn 0;\n+}\n+\n+static void ax88179a_mac_config(struct phylink_config *config, unsigned int mode,\n+\t\t\t\tconst struct phylink_link_state *state)\n+{\n+\t/* Nothing to do */\n+}\n+\n+static void ax88179a_mac_link_down(struct phylink_config *config,\n+\t\t\t\t   unsigned int mode, phy_interface_t interface)\n+{\n+\t/* Nothing to do */\n+}\n+\n+static void ax88179a_mac_link_up(struct phylink_config *config,\n+\t\t\t\t struct phy_device *phy,\n+\t\t\t\t unsigned int phy_mode, phy_interface_t interface,\n+\t\t\t\t int speed, int duplex,\n+\t\t\t\t bool tx_pause, bool rx_pause)\n+{\n+\tstruct usbnet *dev = netdev_priv(to_net_dev(config-\u003edev));\n+\tstruct ax88179_data *ax179_data = dev-\u003edriver_priv;\n+\tu8 link_sts = 0, reg8[3];\n+\tu8 bulk_config_speed = 0;\n+\tu16 mode;\n+\n+\t/* Stop RX/TX for link configuration */\n+\tax88179_write_u16(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, AX_RX_CTL_STOP);\n+\tax88179_write_u8(dev, AX_ACCESS_MAC, AX88179A_MAC_PATH, 1, 0);\n+\n+\tax88179_write_u8(dev, AX_ACCESS_MAC, AX88179A_MAC_CDC_DELAY_TX, 1, 0xa5);\n+\n+\tax88179_write_u16(dev, AX_ACCESS_MAC, AX_PAUSE_WATERLVL_LOW, 2, 0x0410);\n+\n+\tax88179_write_u8(dev, AX_ACCESS_MAC, AX88179A_ETH_TX_GAP, 1, 0);\n+\n+\tax88179_write_u8(dev, AX_ACCESS_MAC, AX88179A_EP5_EHR, 1, 0x07);\n+\n+\tax88179_write_u8(dev, AX_ACCESS_MAC, AX88179A_NEW_PAUSE_CTRL, 1,\n+\t\t\t 0x28 | AX_NEW_PAUSE_EN);\n+\n+\tmode = AX_MEDIUM_RECEIVE_EN;\n+\tif (tx_pause)\n+\t\tmode |= AX_MEDIUM_TXFLOW_CTRLEN;\n+\tif (rx_pause)\n+\t\tmode |= AX_MEDIUM_RXFLOW_CTRLEN;\n+\n+\tswitch (speed) {\n+\tcase SPEED_2500:\n+\t\treg8[0] = 0x00;\n+\t\treg8[1] = 0xF8;\n+\t\treg8[2] = 0x07;\n+\t\tax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_MAC_TX_PAUSE, 3, 3, reg8);\n+\n+\t\treg8[0] = 0x78;\n+\t\treg8[1] = (AX_LSOFC_WCNT_7_ACCESS \u003c\u003c 5);\n+\t\treg8[2] = 0;\n+\t\tax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_MAC_RX_STATUS_CDC, 3, 3, reg8);\n+\n+\t\tax88179_write_u8(dev, AX_ACCESS_MAC, AX88179A_MAC_RX_DATA_CDC_CNT, 1, 0x40);\n+\t\tax88179_write_u8(dev, AX_ACCESS_MAC, AX88179A_MAC_RX_DATA_CDC_CNT + 1, 1,\n+\t\t\t\t AX_MAC_MIQFFCTRL_FORMAT | AX_MAC_MIQFFCTRL_DROP_CRC |\n+\t\t\t\t AX_MAC_LSO_ERR_EN);\n+\n+\t\tax88179_write_u8(dev, AX_ACCESS_MAC, AX88179A_BFM_DATA, 1, AX_XGMII_EN);\n+\n+\t\tax88179_write_u8(dev, AX_ACCESS_MAC, AX88179A_MAC_LSO_ENHANCE_CTRL, 1,\n+\t\t\t\t 0x1C | AX_LSO_ENHANCE_EN);\n+\n+\t\tmode |= AX_MEDIUM_GIGAMODE | AX_MEDIUM_FULL_DUPLEX;\n+\t\tbulk_config_speed = ETHER_LINK_2500;\n+\n+\t\tbreak;\n+\n+\tcase SPEED_1000:\n+\t\tmode |= AX_MEDIUM_GIGAMODE;\n+\t\tbulk_config_speed = ETHER_LINK_1000;\n+\t\tfallthrough;\n+\n+\tcase SPEED_100:\n+\t\treg8[0] = 0x78;\n+\t\treg8[1] = (AX_LSOFC_WCNT_7_ACCESS \u003c\u003c 5) | AX_GMII_CRC_APPEND;\n+\t\treg8[2] = 0;\n+\t\tax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_MAC_RX_STATUS_CDC, 3, 3, reg8);\n+\n+\t\tax88179_write_u8(dev, AX_ACCESS_MAC, AX88179A_MAC_RX_DATA_CDC_CNT, 1, 0x40);\n+\t\tif (!bulk_config_speed)\n+\t\t\tbulk_config_speed = ETHER_LINK_100;\n+\t\tbreak;\n+\n+\tcase SPEED_10:\n+\t\treg8[0] = 0xFA;\n+\t\treg8[1] = (AX_LSOFC_WCNT_7_ACCESS \u003c\u003c 5) | AX_GMII_CRC_APPEND;\n+\t\treg8[2] = 0xFF;\n+\t\tax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_MAC_RX_STATUS_CDC, 3, 3, reg8);\n+\n+\t\tax88179_write_u8(dev, AX_ACCESS_MAC, AX88179A_MAC_RX_DATA_CDC_CNT, 1, 0xFA);\n+\n+\t\tbulk_config_speed = ETHER_LINK_10;\n+\t\tbreak;\n+\t}\n+\n+\tax88179_read_cmd(dev, AX_ACCESS_MAC, PHYSICAL_LINK_STATUS, 1, 1, \u0026link_sts);\n+\tax88179a_bulkin_config(dev, link_sts, bulk_config_speed, !!duplex);\n+\n+\tif (speed != SPEED_2500)\n+\t\tax88179_write_u8(dev, AX_ACCESS_MAC, AX88179A_BFM_DATA, 1, 0);\n+\n+\tif (duplex)\n+\t\tmode |= AX_MEDIUM_FULL_DUPLEX;\n+\n+\trtnl_lock();\n+\tif (dev-\u003enet-\u003emtu \u003e 1500)\n+\t\tmode |= AX_MEDIUM_JUMBO_EN;\n+\tax88179_write_u16(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE, 2, mode);\n+\n+\tax88179_write_u16(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, READ_ONCE(ax179_data-\u003erxctl));\n+\trtnl_unlock();\n+\n+\tax88179_write_u8(dev, AX_ACCESS_MAC, AX88179A_MAC_PATH, 1,\n+\t\t\t AX_MAC_RX_PATH_READY | AX_MAC_TX_PATH_READY);\n+}\n+\n+static void ax88179a_mac_disable_tx_lpi(struct phylink_config *config)\n+{\n+\tstruct usbnet *dev = netdev_priv(to_net_dev(config-\u003edev));\n+\n+\tax88179_write_cmd(dev, AX_GPHY_CTL, AX_GPHY_EEE_CTRL, false, 0, NULL);\n+}\n+\n+static int ax88179a_mac_enable_tx_lpi(struct phylink_config *config, u32 timer, bool tx_clk_stop)\n+{\n+\tstruct usbnet *dev = netdev_priv(to_net_dev(config-\u003edev));\n+\n+\t/* AX88179A does not provide LPI timer registers */\n+\treturn ax88179_write_cmd(dev, AX_GPHY_CTL, AX_GPHY_EEE_CTRL, true, 0, NULL);\n+}\n+\n+static const struct phylink_mac_ops ax88179a_phylink_mac_ops = {\n+\t.mac_config = ax88179a_mac_config,\n+\t.mac_link_down = ax88179a_mac_link_down,\n+\t.mac_link_up = ax88179a_mac_link_up,\n+\t.mac_disable_tx_lpi = ax88179a_mac_disable_tx_lpi,\n+\t.mac_enable_tx_lpi = ax88179a_mac_enable_tx_lpi,\n+};\n+\n+static int ax88179a_phylink_setup(struct usbnet *dev)\n+{\n+\tstruct ax88179_data *data = dev-\u003edriver_priv;\n+\tphy_interface_t phy_if_mode;\n+\tstruct phylink *phylink;\n+\n+\tdata-\u003ephylink_config.dev = \u0026dev-\u003enet-\u003edev;\n+\tdata-\u003ephylink_config.type = PHYLINK_NETDEV;\n+\tdata-\u003ephylink_config.mac_capabilities = MAC_SYM_PAUSE | MAC_ASYM_PAUSE | MAC_100;\n+\tif (data-\u003eis_ax88772d)\n+\t\tdata-\u003ephylink_config.mac_capabilities |= MAC_10;\n+\telse if (data-\u003echip_version \u003c AX_VERSION_AX88279)\n+\t\tdata-\u003ephylink_config.mac_capabilities |= MAC_10 | MAC_1000;\n+\telse\n+\t\tdata-\u003ephylink_config.mac_capabilities |= MAC_1000 | MAC_2500FD;\n+\n+\tif (!data-\u003eis_ax88772d) {\n+\t\tdata-\u003ephylink_config.lpi_capabilities = MAC_100FD | MAC_1000FD;\n+\t\tdata-\u003ephylink_config.eee_enabled_default = false;\n+\t}\n+\n+\tif (data-\u003echip_version == AX_VERSION_AX88279) {\n+\t\t__set_bit(PHY_INTERFACE_MODE_2500BASEX,\n+\t\t\t  data-\u003ephylink_config.supported_interfaces);\n+\t\t__set_bit(PHY_INTERFACE_MODE_SGMII,\n+\t\t\t  data-\u003ephylink_config.supported_interfaces);\n+\t\tphy_if_mode = PHY_INTERFACE_MODE_2500BASEX;\n+\t} else {\n+\t\t__set_bit(PHY_INTERFACE_MODE_SGMII,\n+\t\t\t  data-\u003ephylink_config.supported_interfaces);\n+\t\tphy_if_mode = PHY_INTERFACE_MODE_SGMII;\n+\t}\n+\n+\tmemcpy(data-\u003ephylink_config.lpi_interfaces,\n+\t       data-\u003ephylink_config.supported_interfaces,\n+\t       sizeof(data-\u003ephylink_config.lpi_interfaces));\n+\n+\tphylink = phylink_create(\u0026data-\u003ephylink_config, dev-\u003enet-\u003edev.fwnode,\n+\t\t\t\t phy_if_mode, \u0026ax88179a_phylink_mac_ops);\n+\tif (IS_ERR(phylink))\n+\t\treturn PTR_ERR(phylink);\n+\n+\tdata-\u003ephylink = phylink;\n+\treturn 0;\n+}\n+\n+static int ax88179a_init_mdio(struct usbnet *dev)\n+{\n+\tstruct ax88179_data *data = dev-\u003edriver_priv;\n+\tint ret;\n+\n+\tdata-\u003emdio = mdiobus_alloc();\n+\tif (!data-\u003emdio)\n+\t\treturn -ENOMEM;\n+\n+\tdata-\u003emdio-\u003epriv = dev;\n+\tdata-\u003emdio-\u003eread = ax88179_mdiobus_read;\n+\tdata-\u003emdio-\u003ewrite = ax88179_mdiobus_write;\n+\tdata-\u003emdio-\u003eread_c45 = ax88179_mdiobus_read_c45;\n+\tdata-\u003emdio-\u003ewrite_c45 = ax88179_mdiobus_write_c45;\n+\tdata-\u003emdio-\u003ename = \"AX88179A MDIO Bus\";\n+\tdata-\u003emdio-\u003ephy_mask = ~(1 \u003c\u003c AX88179_PHY_ID);\n+\t/* mii bus name is usb-\u003cusb bus number\u003e-\u003cusb device number\u003e */\n+\tsnprintf(data-\u003emdio-\u003eid, MII_BUS_ID_SIZE, \"usb-%03d:%03d\",\n+\t\t dev-\u003eudev-\u003ebus-\u003ebusnum, dev-\u003eudev-\u003edevnum);\n+\n+\tret = mdiobus_register(data-\u003emdio);\n+\tif (ret) {\n+\t\tnetdev_err(dev-\u003enet, \"Could not register MDIO bus (err %d)\\n\", ret);\n+\t\tmdiobus_free(data-\u003emdio);\n+\t\tdata-\u003emdio = NULL;\n+\t}\n+\n+\treturn ret;\n+}\n+\n+static int ax88179a_mii_ioctl(struct net_device *net, struct ifreq *rq, int cmd)\n+{\n+\tstruct ax88179_data *data = netdev2data(net);\n+\n+\treturn phylink_mii_ioctl(data-\u003ephylink, rq, cmd);\n+}\n+\n+static int ax88179a_set_features(struct net_device *net, netdev_features_t features)\n+{\n+\tstruct usbnet *dev = netdev_priv(net);\n+\tnetdev_features_t changed;\n+\tint ret;\n+\tu8 tmp;\n+\n+\tchanged = net-\u003efeatures ^ features;\n+\n+\tret = ax88179_set_features(net, features);\n+\tif (ret)\n+\t\treturn ret;\n+\n+\tif (changed \u0026 NETIF_F_HW_VLAN_CTAG_FILTER) {\n+\t\tif (features \u0026 NETIF_F_HW_VLAN_CTAG_FILTER) {\n+\t\t\tfor (int i = 0; i \u003c 256; i++) {\n+\t\t\t\t/* Address */\n+\t\t\t\ttmp = i;\n+\t\t\t\tax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_ADDRESS,\n+\t\t\t\t\t\t  1, 1, \u0026tmp);\n+\t\t\t\t/* Data */\n+\t\t\t\tax88179_write_u16(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_DATA0,\n+\t\t\t\t\t\t  2, 0);\n+\t\t\t\tax88179_write_u8(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_CONTROL,\n+\t\t\t\t\t\t 1, AX_VLAN_CONTROL_WE);\n+\t\t\t}\n+\t\t\tret = ax88179_read_cmd(dev, AX_ACCESS_MAC,\n+\t\t\t\t\t       AX88179A_VLAN_ID_CONTROL, 1, 1, \u0026tmp);\n+\t\t\tif (ret \u003c 0)\n+\t\t\t\treturn ret;\n+\t\t\ttmp ^= AX_VLAN_CONTROL_VFE;\n+\t\t\tret = ax88179_write_cmd(dev, AX_ACCESS_MAC,\n+\t\t\t\t\t\tAX88179A_VLAN_ID_CONTROL, 1, 1, \u0026tmp);\n+\t\t\tif (ret \u003c 0)\n+\t\t\t\treturn ret;\n+\t\t}\n+\t}\n+\n+\tif (changed \u0026 NETIF_F_HW_VLAN_CTAG_RX) {\n+\t\tret = ax88179_read_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_CONTROL,\n+\t\t\t\t       1, 1, \u0026tmp);\n+\t\tif (ret \u003c 0)\n+\t\t\treturn ret;\n+\t\ttmp ^= AX_VLAN_CONTROL_VSO;\n+\t\tret = ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_CONTROL,\n+\t\t\t\t\t1, 1, \u0026tmp);\n+\t\tif (ret \u003c 0)\n+\t\t\treturn ret;\n+\t}\n+\treturn 0;\n+}\n+\n+static const struct net_device_ops ax88179a_netdev_ops = {\n+\t.ndo_open\t\t= usbnet_open,\n+\t.ndo_stop\t\t= usbnet_stop,\n+\t.ndo_start_xmit\t\t= usbnet_start_xmit,\n+\t.ndo_tx_timeout\t\t= usbnet_tx_timeout,\n+\t.ndo_get_stats64\t= dev_get_tstats64,\n+\t.ndo_change_mtu\t\t= ax88179_change_mtu,\n+\t.ndo_set_mac_address\t= ax88179_set_mac_addr,\n+\t.ndo_validate_addr\t= eth_validate_addr,\n+\t.ndo_eth_ioctl\t\t= ax88179a_mii_ioctl,\n+\t.ndo_set_rx_mode\t= ax88179_set_multicast,\n+\t.ndo_set_features\t= ax88179a_set_features,\n+\t.ndo_vlan_rx_add_vid\t= ax88179a_vlan_rx_add_vid,\n+\t.ndo_vlan_rx_kill_vid\t= ax88179a_vlan_rx_kill_vid,\n+};\n+\n+static int ax88179a_bind(struct usbnet *dev, struct usb_interface *intf)\n+{\n+\tstruct usb_device *udev = interface_to_usbdev(intf);\n+\tstruct ax88179_data *ax179_data;\n+\tint ret;\n+\n+\t/* Check if vendor configuration */\n+\tif (udev-\u003eactconfig-\u003edesc.bConfigurationValue != 1) {\n+\t\tnetdev_info(dev-\u003enet, \"Switching to vendor mode\\n\");\n+\t\tusb_driver_set_configuration(udev, 1);\n+\t\treturn -ENODEV;\n+\t}\n+\n+\tret = usbnet_get_endpoints(dev, intf);\n+\tif (ret \u003c 0)\n+\t\treturn ret;\n+\n+\tax179_data = kzalloc_obj(*ax179_data);\n+\tif (!ax179_data)\n+\t\treturn -ENOMEM;\n+\n+\tdev-\u003edriver_priv = ax179_data;\n+\n+\tret = ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_CHIP_STATUS,\n+\t\t\t       1, 1, \u0026ax179_data-\u003echip_version);\n+\tif (ret \u003c 0)\n+\t\tgoto err_nodev;\n+\n+\tax179_data-\u003echip_version = (ax179_data-\u003echip_version \u0026 0xf0) \u003e\u003e 4;\n+\tax179_data-\u003eis_ax88772d = 0;\n+\tif (ax179_data-\u003echip_version == AX_VERSION_AX88179A) {\n+\t\tif (le16_to_cpu(udev-\u003edescriptor.bcdDevice) == 0x300)\n+\t\t\tax179_data-\u003eis_ax88772d = 1;\n+\t}\n+\n+\tfor (int i = 0; i \u003c 3; i++) {\n+\t\tret = ax88179_read_cmd(dev, AX88179A_ACCESS_BL, AX88179A_SW_VERSION + i,\n+\t\t\t\t       1, 1, \u0026ax179_data-\u003efw_version[i]);\n+\t\tif (ret \u003c 0)\n+\t\t\tax179_data-\u003efw_version[i] = 0xff;\n+\t}\n+\tret = ax88179_read_cmd(dev, AX88179A_ACCESS_BL, AX88179A_SW_REVISION,\n+\t\t\t       1, 1, \u0026ax179_data-\u003efw_version[3]);\n+\tif (ret \u003c 0)\n+\t\tax179_data-\u003efw_version[3] = 0xff;\n+\n+\tnetdev_info(dev-\u003enet, \"AX88179A/279/772D Chip Version: %x, FW: %d.%d.%d.%d\\n\",\n+\t\t    ax179_data-\u003echip_version,\n+\t\t    ax179_data-\u003efw_version[0], ax179_data-\u003efw_version[1],\n+\t\t    ax179_data-\u003efw_version[2], ax179_data-\u003efw_version[3]);\n+\n+\t/* The AX88279 requires both the AX_RX_CTL_IPE and AX_RX_CTL_DROPCRCERR\n+\t * bits set in AX_RX_CTL for creating correct RX-URBs. AX_RX_CTL_DROPCRCERR\n+\t * is anyway set for all chips, make sure AX_RX_CTL_IPE is set via ip_align.\n+\t * Also configure eeprom access parameters.\n+\t */\n+\tif (ax179_data-\u003echip_version == AX_VERSION_AX88279) {\n+\t\tax179_data-\u003eip_align = 1;\n+\t\tax179_data-\u003eeeprom_read_cmd = AX88179A_FLASH_READ;\n+\t\tax179_data-\u003eeeprom_write_cmd = AX88179A_FLASH_WRITE;\n+\t\tax179_data-\u003eeeprom_block = 256;\n+\t\tax179_data-\u003eeeprom_wen = 1;\n+\t} else {\n+\t\tax179_data-\u003eip_align = 0;\n+\t\tax179_data-\u003eeeprom_read_cmd = AX_ACCESS_EFUS;\n+\t\tax179_data-\u003eeeprom_write_cmd = AX_ACCESS_EFUS;\n+\t\tax179_data-\u003eeeprom_block = 20;\n+\t\tax179_data-\u003eeeprom_wen = 0;\n+\t}\n+\n+\tax179_data-\u003eresume = ax88179a_resume;\n+\tax179_data-\u003esuspend = ax88179a_suspend;\n+\n+\tdev-\u003enet-\u003enetdev_ops = \u0026ax88179a_netdev_ops;\n+\tdev-\u003enet-\u003eethtool_ops = \u0026ax88179a_ethtool_ops;\n+\tdev-\u003enet-\u003eneeded_headroom = 8;\n+\tdev-\u003enet-\u003eneeded_tailroom = 8;\n+\tdev-\u003enet-\u003emin_mtu = ETH_MIN_MTU;\n+\tdev-\u003ehard_mtu = 9 * 1024;\n+\tdev-\u003enet-\u003emax_mtu = dev-\u003ehard_mtu - dev-\u003enet-\u003ehard_header_len;\n+\n+\tif (!ax179_data-\u003eis_ax88772d)\n+\t\tdev-\u003emii.supports_gmii = 1;\n+\n+\tdev-\u003enet-\u003efeatures |= NETIF_F_SG | NETIF_F_IP_CSUM |\n+\t\t\t      NETIF_F_IPV6_CSUM | NETIF_F_RXCSUM | NETIF_F_TSO |\n+\t\t\t      NETIF_F_HW_VLAN_CTAG_TX;\n+\n+\tdev-\u003enet-\u003ehw_features |= dev-\u003enet-\u003efeatures;\n+\tdev-\u003enet-\u003ehw_features |= NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_HW_VLAN_CTAG_FILTER;\n+\n+\tdev-\u003enet-\u003evlan_features = NETIF_F_SG | NETIF_F_IP_CSUM |\n+\t\t\t\t  NETIF_F_IPV6_CSUM | NETIF_F_RXCSUM | NETIF_F_TSO;\n+\n+\tnetif_set_tso_max_size(dev-\u003enet, 16384);\n+\n+\t/* Enable Transmission of Link Speed byte in interrupt URB */\n+\tax88179_write_cmd(dev, AX_FW_MODE, AX_FW_MODE_179A, 0, 0, NULL);\n+\tax88179_write_cmd(dev, AX_RELOAD_EEPROM_EFUSE, 0, 0, 0, NULL);\n+\n+\t/* Read MAC address from DTB or ASIX chip */\n+\tax88179_get_mac_addr(dev);\n+\tmemcpy(dev-\u003enet-\u003eperm_addr, dev-\u003enet-\u003edev_addr, ETH_ALEN);\n+\n+\t/* Power PHY for probing */\n+\tax88179_write_u8(dev, AX88179A_PHY_POWER, 0, 0, AX_PHY_POWER);\n+\tmsleep(250);\n+\n+\tret = ax88179a_init_mdio(dev);\n+\tif (ret)\n+\t\tgoto err_nodev;\n+\n+\tret = ax88179a_phylink_setup(dev);\n+\tif (ret)\n+\t\tgoto phylink_err;\n+\n+\tret = ax88179a_init_phy(dev);\n+\tif (ret)\n+\t\tgoto initphy_err;\n+\n+\t/* Keep this interface runtime-PM active by taking a usage ref.\n+\t * Prevents runtime suspend while bound and avoids resume paths\n+\t * that could deadlock (autoresume under RTNL while USB PM lock\n+\t * is held, phylink/MDIO wants RTNL).\n+\t */\n+\tpm_runtime_get_noresume(\u0026intf-\u003edev);\n+\n+\treturn 0;\n+\n+initphy_err:\n+\tphylink_destroy(ax179_data-\u003ephylink);\n+phylink_err:\n+\tax88179a_mdio_unregister(ax179_data);\n+err_nodev:\n+\tkfree(ax179_data);\n+\tax179_data = NULL;\n+\n+\treturn ret;\n+}\n+\n+static void ax88179a_unbind(struct usbnet *dev, struct usb_interface *intf)\n+{\n+\tstruct ax88179_data *ax179_data = dev-\u003edriver_priv;\n+\n+\t/* Configure RX control register =\u003e stop operation */\n+\tax88179_write_u16(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, AX_RX_CTL_STOP);\n+\n+\trtnl_lock();\n+\tphylink_disconnect_phy(ax179_data-\u003ephylink);\n+\trtnl_unlock();\n+\tphylink_destroy(ax179_data-\u003ephylink);\n+\tax88179a_mdio_unregister(ax179_data);\n+\n+\tax88179_write_u8(dev, AX88179A_PHY_POWER, 0, 0, 0);\n+\n+\tkfree(ax179_data);\n+}\n+\n+static void ax88179a_rx_checksum(struct sk_buff *skb, u64 pkt_desc)\n+{\n+\tu32 pkt_type;\n+\n+\tskb-\u003eip_summed = CHECKSUM_NONE;\n+\t/* checksum error bit is set */\n+\tif (pkt_desc \u0026 AX179A_RX_PD_L4_ERR || pkt_desc \u0026 AX179A_RX_PD_L3_ERR)\n+\t\treturn;\n+\n+\tpkt_type = pkt_desc \u0026 AX179A_RX_PD_L4_TYPE_MASK;\n+\t/* It must be a TCP or UDP packet with a valid checksum */\n+\tif (pkt_type == AX179A_RX_PD_L4_TCP || pkt_type == AX179A_RX_PD_L4_UDP)\n+\t\tskb-\u003eip_summed = CHECKSUM_UNNECESSARY;\n+}\n+\n+static int ax88179a_rx_fixup(struct usbnet *dev, struct sk_buff *skb)\n+{\n+\tstruct ax88179_data *ax179_data = dev-\u003edriver_priv;\n+\tstruct sk_buff *ax_skb;\n+\tu32 hdr_off, pkt_end;\n+\tu64 *pkt_desc_ptr;\n+\tu16 vlan_tag;\n+\tu16 pkt_cnt;\n+\tu64 rx_hdr;\n+\n+\t/* SKB contents for AX179A-based chips:\n+\t *   \u003cpacket 1\u003e\n+\t *   ...\n+\t *   \u003cpacket N\u003e\n+\t *   \u003cper-packet metadata entry 1\u003e\n+\t *   ...\n+\t *   \u003cper-packet metadata entry N\u003e\n+\t *   \u003crx_hdr\u003e\n+\t *\n+\t * where:\n+\t *   \u003cpacket N\u003e contains pkt_len data bytes and padding:\n+\t *\t\t2 bytes of IP alignment (optional, depends on AX_RX_CTL_IPE flag)\n+\t *\t\tpacket data received\n+\t *\t\toptional padding to 8-bytes boundary\n+\t *   \u003cper-packet metadata entry N\u003e contains 8 bytes:\n+\t *\t\tpkt_len and fields AX_RXHDR_*\n+\t *   \u003crx-hdr\u003e\tcontains 8 bytes:\n+\t *\t\tpkt_cnt and hdr_off (offset of \u003cper-packet metadata entry 1\u003e)\n+\t *\n+\t * pkt_cnt is number of entries in the per-packet metadata array.\n+\t */\n+\n+\tif (!skb || skb-\u003elen \u003c sizeof(rx_hdr))\n+\t\tgoto err;\n+\n+\t/* RX Descriptor Header */\n+\tskb_trim(skb, skb-\u003elen - sizeof(rx_hdr));\n+\trx_hdr = *(u64 *)skb_tail_pointer(skb);\n+\tle64_to_cpus(rx_hdr);\n+\n+\t/* Check these packets */\n+\thdr_off = FIELD_GET(AX179A_RX_DH_DESC_OFFSET, rx_hdr);\n+\tpkt_cnt = rx_hdr \u0026 AX179A_RX_DH_PKT_CNT_MASK;\n+\n+\t/* Consistency check header position */\n+\tif (hdr_off != skb-\u003elen - (pkt_cnt * sizeof(rx_hdr)))\n+\t\tgoto err;\n+\n+\t/* Make sure that the bounds of the metadata array are inside the SKB\n+\t * (and in front of the counter at the end).\n+\t */\n+\tif (pkt_cnt * 8 + hdr_off \u003e skb-\u003elen)\n+\t\tgoto err;\n+\n+\t/* Packets must not overlap the metadata array */\n+\tskb_trim(skb, hdr_off);\n+\n+\tif (!pkt_cnt)\n+\t\tgoto err;\n+\n+\t/* Get the first RX packet descriptor */\n+\tpkt_desc_ptr = (u64 *)(skb-\u003edata + hdr_off);\n+\n+\tpkt_end = 0;\n+\twhile (pkt_cnt--) {\n+\t\tu64 pkt_desc = le64_to_cpu(*pkt_desc_ptr);\n+\t\tu32 pkt_len_plus_padd;\n+\t\tu32 pkt_len;\n+\n+\t\tpkt_len = FIELD_GET(AX179A_RX_PD_LEN, pkt_desc);\n+\t\tpkt_len -= (ax179_data-\u003eip_align ? 2 : 0);\n+\t\tpkt_len_plus_padd = ((pkt_len + 7 + (ax179_data-\u003eip_align ? 2 : 0)) \u0026 0x7FFF8);\n+\n+\t\tpkt_end += pkt_len_plus_padd;\n+\t\tif (pkt_end \u003e hdr_off || (pkt_cnt == 0 \u0026\u0026 pkt_end != hdr_off))\n+\t\t\tgoto err;\n+\n+\t\tif (pkt_desc \u0026 AX179A_RX_PD_DROP || !(pkt_desc \u0026 AX179A_RX_PD_RX_OK) ||\n+\t\t    pkt_len \u003e (dev-\u003ehard_mtu + AX179A_RX_HW_PAD) || pkt_len \u003c 2 + ETH_HLEN) {\n+\t\t\tskb_pull(skb, pkt_len_plus_padd);\n+\n+\t\t\t/* Next RX Packet Descriptor */\n+\t\t\tpkt_desc_ptr++;\n+\t\t\tcontinue;\n+\t\t}\n+\n+\t\tax_skb = netdev_alloc_skb_ip_align(dev-\u003enet, pkt_len);\n+\t\tif (!ax_skb)\n+\t\t\tgoto err;\n+\n+\t\tskb_put(ax_skb, pkt_len);\n+\t\tmemcpy(ax_skb-\u003edata, skb-\u003edata + (ax179_data-\u003eip_align ? AX179A_RX_HW_PAD : 0),\n+\t\t       pkt_len);\n+\n+\t\tif (ax179_data-\u003erx_checksum)\n+\t\t\tax88179a_rx_checksum(ax_skb, pkt_desc);\n+\n+\t\tif (pkt_desc \u0026 AX179A_RX_PD_VLAN) {\n+\t\t\tvlan_tag = pkt_desc \u003e\u003e AX179A_RX_PD_VLAN_SHIFT;\n+\t\t\t__vlan_hwaccel_put_tag(ax_skb, htons(ETH_P_8021Q), vlan_tag);\n+\t\t}\n+\n+\t\tusbnet_skb_return(dev, ax_skb);\n+\t\tskb_pull(skb, pkt_len_plus_padd);\n+\n+\t\t/* Next RX Packet Header */\n+\t\tpkt_desc_ptr++;\n+\t}\n+\n+\treturn 1;\n+\n+err:\n+\treturn 0;\n+}\n+\n+static struct sk_buff *ax88179a_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags)\n+{\n+\tu64 tx_desc = skb-\u003elen \u0026 AX179A_TX_DESC_LEN_MASK;\n+\tint frame_size = dev-\u003emaxpacket;\n+\tstruct sk_buff *ax_skb;\n+\tu64 *tx_desc_ptr;\n+\tint padding_size;\n+\tint headroom;\n+\tint tailroom;\n+\tu16 tci = 0;\n+\n+\t/* TSO MSS */\n+\ttx_desc |= FIELD_PREP(AX179A_TX_DESC_MSS, (u64)skb_shinfo(skb)-\u003egso_size);\n+\n+\theadroom = (skb-\u003elen + sizeof(tx_desc)) % 8;\n+\tpadding_size = headroom ? 8 - headroom : 0;\n+\n+\tif (((skb-\u003elen + sizeof(tx_desc) + padding_size) % frame_size) == 0) {\n+\t\tpadding_size += 8;\n+\t\ttx_desc |= AX179A_TX_DESC_DROP_PADD;\n+\t}\n+\n+\tif ((dev-\u003enet-\u003efeatures \u0026 NETIF_F_HW_VLAN_CTAG_TX) \u0026\u0026 (vlan_get_tag(skb, \u0026tci) \u003e= 0)) {\n+\t\ttx_desc |= AX179A_TX_DESC_VLAN;\n+\t\ttx_desc |= FIELD_PREP(AX179A_TX_DESC_VLAN_ID, (u64)tci);\n+\t}\n+\n+\tif (!dev-\u003ecan_dma_sg \u0026\u0026 (dev-\u003enet-\u003efeatures \u0026 NETIF_F_SG) \u0026\u0026 skb_linearize(skb)) {\n+\t\tdev_kfree_skb_any(skb);\n+\t\treturn NULL;\n+\t}\n+\n+\theadroom = skb_headroom(skb);\n+\ttailroom = skb_tailroom(skb);\n+\n+\tif (!(headroom \u003e= sizeof(tx_desc) \u0026\u0026 tailroom \u003e= padding_size)) {\n+\t\tax_skb = skb_copy_expand(skb, sizeof(tx_desc), padding_size, flags);\n+\t\tdev_kfree_skb_any(skb);\n+\t\tskb = ax_skb;\n+\t\tif (!skb)\n+\t\t\treturn NULL;\n+\t}\n+\tif (padding_size != 0)\n+\t\tskb_put_zero(skb, padding_size);\n+\t/* Copy TX header */\n+\ttx_desc_ptr = skb_push(skb, sizeof(tx_desc));\n+\tput_unaligned_le64(tx_desc, tx_desc_ptr);\n+\n+\tusbnet_set_skb_tx_stats(skb, 1, 0);\n+\n+\treturn skb;\n+}\n+\n+static int ax88179a_reset(struct usbnet *dev)\n+{\n+\tstruct ax88179_data *ax179_data = dev-\u003edriver_priv;\n+\tu16 *tmp16;\n+\tu8 buf[5];\n+\tu16 rxctl;\n+\tu8 *tmp;\n+\n+\ttmp16 = (u16 *)buf;\n+\ttmp = (u8 *)buf;\n+\n+\t/* Power up ethernet PHY */\n+\tax88179_write_u8(dev, AX88179A_PHY_POWER, 0, 0, AX_PHY_POWER);\n+\tmsleep(250);\n+\n+\t/* Ethernet PHY Auto Detach*/\n+\tax88179a_auto_detach(dev);\n+\n+\tax88179_write_u8(dev, AX_ACCESS_MAC, AX88179A_MAC_BULK_OUT_CTRL, 1, AX_MAC_EFF_EN);\n+\n+\tax88179_write_u16(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 0);\n+\n+\tax88179_write_u8(dev, AX_ACCESS_MAC, AX_PAUSE_WATERLVL_LOW, 1, 0x04);\n+\tax88179_write_u8(dev, AX_ACCESS_MAC, AX_PAUSE_WATERLVL_HIGH, 1, 0x10);\n+\n+\t*tmp = 0;\n+\tif (dev-\u003enet-\u003efeatures \u0026 NETIF_F_HW_VLAN_CTAG_FILTER)\n+\t\t*tmp |= AX_VLAN_CONTROL_VFE;\n+\tif (dev-\u003enet-\u003efeatures \u0026 NETIF_F_HW_VLAN_CTAG_RX)\n+\t\t*tmp |= AX_VLAN_CONTROL_VSO;\n+\tax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_CONTROL, 1, 1, tmp);\n+\n+\tax88179_write_u8(dev, AX_ACCESS_MAC, AX88179A_MAC_BM_INT_MASK, 1, 0xff);\n+\n+\tax88179_write_u8(dev, AX_ACCESS_MAC, AX88179A_MAC_BM_RX_DMA_CTL, 1, 0);\n+\tax88179_write_u8(dev, AX_ACCESS_MAC, AX88179A_MAC_BM_TX_DMA_CTL, 1, 0);\n+\tax88179_write_u8(dev, AX_ACCESS_MAC, AX88179A_MAC_ARC_CTRL, 1, 0);\n+\tax88179_write_u8(dev, AX_ACCESS_MAC, AX88179A_MAC_SWP_CTRL, 1, 0);\n+\tax88179_write_u8(dev, AX_ACCESS_MAC, AX88179A_MAC_TX_HDR_CKSUM, 1, 0);\n+\n+\t/* Read MAC address from DTB or asix chip */\n+\tax88179_get_mac_addr(dev);\n+\tmemcpy(dev-\u003enet-\u003eperm_addr, dev-\u003enet-\u003edev_addr, ETH_ALEN);\n+\n+\t/* The Bulk-Register configuration for the AX88179A is done in\n+\t * ax88179a_mac_link_up(), once the link is up for a given link and USB-speed.\n+\t */\n+\tif (ax179_data-\u003eis_ax88772d)\n+\t\tdev-\u003erx_urb_size = 1024 * 24;\n+\telse\n+\t\tdev-\u003erx_urb_size = 1024 * 48;\n+\n+\t/* Enable checksum offload */\n+\tif (dev-\u003enet-\u003efeatures \u0026 NETIF_F_RXCSUM) {\n+\t\tax88179_write_u8(dev, AX_ACCESS_MAC, AX_RXCOE_CTL, 1,\n+\t\t\t\t AX_RXCOE_IP | AX_RXCOE_TCP | AX_RXCOE_UDP |\n+\t\t\t\t AX_RXCOE_TCPV6 | AX_RXCOE_UDPV6);\n+\t\tax179_data-\u003erx_checksum = 1;\n+\t} else {\n+\t\tax179_data-\u003erx_checksum = 0;\n+\t\tax88179_write_u8(dev, AX_ACCESS_MAC, AX_RXCOE_CTL, 1, 0);\n+\t}\n+\n+\tax88179_write_u8(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, 1,\n+\t\t\t AX_TXCOE_IP | AX_TXCOE_TCP | AX_TXCOE_UDP |\n+\t\t\t AX_TXCOE_TCPV6 | AX_TXCOE_UDPV6);\n+\n+\t/* Configure RX control register =\u003e start operation */\n+\trxctl = AX_RX_CTL_DROPCRCERR | AX_RX_CTL_START | AX_RX_CTL_AP |\n+\t\tAX_RX_CTL_AMALL | AX_RX_CTL_AB;\n+\tif (ax179_data-\u003eip_align)\n+\t\trxctl |= AX_RX_CTL_IPE;\n+\tif (dev-\u003enet-\u003eflags \u0026 IFF_PROMISC)\n+\t\trxctl |= AX_RX_CTL_PRO;\n+\tWRITE_ONCE(ax179_data-\u003erxctl, rxctl);\n+\tax88179_write_u16(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, rxctl);\n+\n+\tif (ax179_data-\u003echip_version \u003c AX_VERSION_AX88179A)\n+\t\t*tmp = AX_MONITOR_MODE_PMETYPE | AX_MONITOR_MODE_PMEPOL | AX_MONITOR_MODE_RWMP;\n+\telse\n+\t\t*tmp = AX_MONITOR_MODE_RWMP;\n+\tax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MONITOR_MOD, 1, 1, tmp);\n+\n+\t/* Configure default medium type =\u003e giga */\n+\t*tmp16 = AX_MEDIUM_RECEIVE_EN | AX_MEDIUM_TXFLOW_CTRLEN |\n+\t\t AX_MEDIUM_RXFLOW_CTRLEN | AX_MEDIUM_FULL_DUPLEX;\n+\tif (!ax179_data-\u003eis_ax88772d)\n+\t\t*tmp16 |= AX_MEDIUM_GIGAMODE;\n+\tax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE, 2, 2, tmp16);\n+\n+\t/* Check if WoL is supported */\n+\tax179_data-\u003ewol_supported = 0;\n+\tif (ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_MONITOR_MOD,\n+\t\t\t     1, 1, tmp) \u003e 0)\n+\t\tax179_data-\u003ewol_supported = WAKE_MAGIC | WAKE_PHY;\n+\n+\t/* Ensure EEE state is off, consistent with phylink setup */\n+\tax88179_write_cmd(dev, AX_GPHY_CTL, AX_GPHY_EEE_CTRL, false, 0, NULL);\n+\n+\t/* ax88179a_reset() may also be called from resume context,\n+\t * phylink_resume() will start phylink, then.\n+\t */\n+\tif (!ax179_data-\u003ein_pm)\n+\t\tphylink_start(ax179_data-\u003ephylink);\n+\n+\treturn 0;\n+}\n+\n+static int ax88179a_stop(struct usbnet *dev)\n+{\n+\tstruct ax88179_data *ax179_data = dev-\u003edriver_priv;\n+\tu16 reg16;\n+\n+\tphylink_stop(ax179_data-\u003ephylink);\n+\n+\tax88179_read_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE, 2, 2, \u0026reg16);\n+\treg16 \u0026= ~AX_MEDIUM_RECEIVE_EN;\n+\tax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE, 2, 2, \u0026reg16);\n+\n+\tax88179_write_u16(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 0);\n+\n+\tax88179_write_u8(dev, AX88179A_PHY_POWER, 0, 0, 0);\n+\n+\treturn 0;\n+}\n+\n+const struct driver_info ax88179a_info = {\n+\t.description = \"ASIX AX88179A USB 3.2 Gigabit Ethernet\",\n+\t.bind = ax88179a_bind,\n+\t.unbind = ax88179a_unbind,\n+\t.status = ax88179a_status,\n+\t.reset = ax88179a_reset,\n+\t.stop = ax88179a_stop,\n+\t.flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_MULTI_PACKET | FLAG_AVOID_UNLINK_URBS,\n+\t.rx_fixup = ax88179a_rx_fixup,\n+\t.tx_fixup = ax88179a_tx_fixup,\n+};\n+\n+const struct driver_info ax88772d_info = {\n+\t.description = \"ASIX AX88772D/E USB 2.0 Fast Ethernet\",\n+\t.bind = ax88179a_bind,\n+\t.unbind = ax88179a_unbind,\n+\t.status = ax88179a_status,\n+\t.reset = ax88179a_reset,\n+\t.stop = ax88179a_stop,\n+\t.flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_MULTI_PACKET | FLAG_AVOID_UNLINK_URBS,\n+\t.rx_fixup = ax88179a_rx_fixup,\n+\t.tx_fixup = ax88179a_tx_fixup,\n+};\n+\n+const struct driver_info ax88279_info = {\n+\t.description = \"ASIX AX88279 USB 3.2 2.5Gigabit Ethernet\",\n+\t.bind = ax88179a_bind,\n+\t.unbind = ax88179a_unbind,\n+\t.status = ax88179a_status,\n+\t.reset = ax88179a_reset,\n+\t.stop = ax88179a_stop,\n+\t.flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_MULTI_PACKET | FLAG_AVOID_UNLINK_URBS,\n+\t.rx_fixup = ax88179a_rx_fixup,\n+\t.tx_fixup = ax88179a_tx_fixup,\n+};\ndiff --git a/include/linux/phy.h b/include/linux/phy.h\nindex 7c5098a0dd6cd..4143bffdcdb9b 100644\n--- a/include/linux/phy.h\n+++ b/include/linux/phy.h\n@@ -64,6 +64,7 @@ extern const int phy_basic_ports_array[3];\n #define PHY_RST_AFTER_CLK_EN\t0x00000002\n #define PHY_POLL_CABLE_TEST\t0x00000004\n #define PHY_ALWAYS_CALL_SUSPEND\t0x00000008\n+#define PHY_BROKEN_FORCED\t0x00000010\n #define MDIO_DEVICE_IS_PHY\t0x80000000\n \n /**\ndiff --git a/include/linux/phylink.h b/include/linux/phylink.h\nindex 3a88a69882a61..a7b6294cc4a07 100644\n--- a/include/linux/phylink.h\n+++ b/include/linux/phylink.h\n@@ -768,6 +768,7 @@ int phylink_ethtool_set_eee(struct phylink *link, struct ethtool_keee *eee);\n int phylink_mii_ioctl(struct phylink *, struct ifreq *, int);\n int phylink_speed_down(struct phylink *pl, bool sync);\n int phylink_speed_up(struct phylink *pl);\n+void phylink_mac_interrupt(struct phylink *pl);\n \n #define phylink_zero(bm) \\\n \tbitmap_zero(bm, __ETHTOOL_LINK_MODE_MASK_NBITS)\n"
}

2/1 2026/10/04 05:43 agent patch-evaluator
1m Model:
core

Args:
null

Results:
{
  "EnableConfigs": [
    "USB_NET_AX88179",
    "USB_USBNET",
    "USB_NET_DRIVERS",
    "AX88796B_PHY",
    "PHYLINK",
    "PHYLIB"
  ],
  "FocusSymbols": [
    "ax88179a_rx_fixup",
    "ax88179a_tx_fixup",
    "ax88179a_bind",
    "ax88179a_reset",
    "ax88179a_mac_link_up",
    "ax88179_set_features",
    "phylink_mac_interrupt",
    "asix_ax88279_read_status",
    "asix_ax88279_config_aneg"
  ],
  "Reasoning": "The patch series introduces support for ASIX AX88179A, AX88772D, and AX88279 USB Ethernet adapters. It adds new device driver logic in drivers/net/usb/ax88179a_devices.c, refactors shared functionality into ax88179_lib.c, adds new PHY drivers in drivers/net/phy/ax88796b.c, and adds phylink_mac_interrupt(). These USB network devices are reachable in virtualized environments through syzkaller's USB emulation (raw-gadget/dummy_hcd). The new driver logic includes complex packet descriptor processing and skb manipulation in ax88179a_rx_fixup() and ax88179a_tx_fixup(), as well as netdev, ethtool, and MDIO/phylink callbacks, making it prime for fuzzing.",
  "WorthFuzzing": true
}

Instruction:
You are an expert Linux kernel maintainer and security engineer.
Your job is to review a provided patch series and evaluate whether it warrants fuzzing with syzkaller.

IMPORTANT: The changes have ALREADY been applied and committed as the HEAD commit in
your workspace. Do NOT rely on internal assumptions. You must actively use your code access
tools to inspect the actual source code, callers, and surrounding context.

================================================================================
1. CORE TRIAGE PHILOSOPHY
================================================================================
The goal of patch fuzzing is to discover crashes, regressions, exposed latent bugs,
and newly triggered assertions introduced by the patch series.

- REACHABILITY IS THE PRIMARY GATE:
  Fuzzing can only discover bugs in code that can actually execute in standard virtualized
  environments (GCE or QEMU, utilizing software-emulated devices like USB gadgets, netdev, tun/tap).
  If the modified code is structurally unreachable (see Section 2), it MUST NOT be fuzzed,
  regardless of whether it adds assertions or complex logic.

- DO NOT BLINDLY TRUST "NO FUNCTIONAL CHANGE" (NFCI) OR "REFACTORING" CLAIMS:
  Patch authors routinely label changes as "cleanups", "refactorings", or state
  "No functional change intended". Do NOT take these claims at face value.
  Code refactorings that rearrange logic, introduce helper functions, or alter state management
  in core subsystems frequently introduce subtle semantic shifts or uncover latent kernel bugs.
  If reachable executable code is modified or refactored, it MUST be fuzzed.

- NEW OR MODIFIED ASSERTIONS IN REACHABLE CODE MUST BE FUZZED:
  When a patch introduces or modifies runtime checks or assertions (e.g., WARN_ON*, VM_WARN_ON*,
  BUG_ON*, lockdep_assert*) in reachable code paths, it enforces new or stricter invariants.
  Even if the author believes the invariant always holds, fuzzing is essential to verify whether
  an unusual sequence of operations can violate it.

================================================================================
2. WHEN TO RETURN WorthFuzzing=false (NEGATIVE CRITERIA)
================================================================================
Return WorthFuzzing=false ONLY IF all modified code falls strictly into one or more of these categories:

- Non-kernel and non-executable changes:
  * Modifications to Documentation/, comments, or spelling fixes.
  * User-space directories, self-tests, samples, or scripts (e.g., tools/, samples/, scripts/, usr/)
    that do not affect the compiled kernel image (vmlinux) or kernel modules.
  * Purely decorative logging (e.g., message strings in pr_err, printk, dev_info) or tracepoints
    that do not alter control flow or data structures.
  * Build system or Kconfig changes that do not alter compiled C logic.
- Structurally unreachable hardware:
  * Vendor-specific PCIe switches, SmartNICs, or GPU drivers (e.g., mlxsw, pds_core, qed,
    ionic, amdgpu) requiring physical ASIC/PCIe cards not emulated in standard QEMU.
- Unreachable execution paths:
  * Driver teardown callbacks (.remove, .shutdown, pci_unregister_driver) executed only during
    physical PCI hot-unplug or manual sysfs driver unbinding.
  * Code paths exclusive to architectures other than the target architecture.

================================================================================
3. WHEN TO RETURN WorthFuzzing=true (POSITIVE CRITERIA)
================================================================================
Return WorthFuzzing=true whenever the patch touches reachable executable code, including:
- Core Subsystems:
  * Any logic modifications in memory management (mm/), synchronization/locking (kernel/locking/),
    BPF, scheduler, core networking, VFS, or syscall handling.
- Refactorings and Code Cleanups:
  * Any restructuring of reachable data structures, helper abstractions, or algorithm flows.
- Runtime Assertions and Defensive Checks:
  * Any introduction or alteration of assertions (WARN_ON*, VM_WARN_ON*, BUG_ON*, etc.) in reachable paths.
- Reachable Drivers and Protocols:
  * Drivers accessible via virtual buses (virtio, USB gadget, loopback, netlink, binder, sockets, etc.).

================================================================================
4. EXTRACTING FocusSymbols (PREVENTING DILUTION)
================================================================================
When WorthFuzzing=true, you must extract specific kernel functions into FocusSymbols to guide the fuzzer:

- AVOID UBIQUITOUS LIFECYCLE HOT-PATHS:
  Do NOT list generic, ubiquitous functions called by almost every program in the corpus
  (including, but not limited to: general memory allocators and deallocators, page fault
  and trap handlers, or core synchronization primitives; this is not an exhaustive list).
  Listing ubiquitous functions causes the fuzzer to classify thousands of unrelated tests as "focused",
  which severely dilutes fuzzing effort away from the actual changes.

- TARGET SPECIFIC FEATURE LOGIC AND ENTRYPOINTS:
  List functions that specifically implement the logic being added or altered, or direct API entrypoints
  for the subsystem feature under review.

- HANDLING STATIC INLINE FUNCTIONS IN HEADERS (.h):
  Compiler-inlined static functions (such as static inlines in mm/*.h or include/linux/*.h) lack
  distinct symbol addresses in vmlinux and cannot be targeted directly by symbol coverage filters.
  If the changes are primarily in static inline helpers, identify non-static, feature-specific caller
  functions in .c files that exercise them (avoiding ubiquitous lifecycle wrappers).

================================================================================
5. IDENTIFYING EnableConfigs
================================================================================
Identify any specific CONFIG_ options required to properly compile and reach the modified code:
- Inspect Kconfig files and #ifdef guards; do not make assumptions.
- Check "depends on" lines in Kconfig to include any non-standard parent subsystem configs needed.
- Strip any 'CONFIG_' prefix (e.g., return "NET_IPV4" instead of "CONFIG_NET_IPV4").
Prefer calling several tools at the same time to save round-trips.


Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.

Prompt:
Target architecture: amd64

For your convenience, here is the diff of the changes:
commit aaa4065a354c1790e97c05295c45e12c578b32d1
Author: syz-cluster <triage@syzkaller.com>
Date:   Sun Oct 4 05:43:49 2026 +0000

    syz-cluster: applied patch under review

diff --git a/MAINTAINERS b/MAINTAINERS
index 3011f995437fd..7464e87d41ef8 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4020,6 +4020,12 @@ S:	Maintained
 F:	Documentation/devicetree/bindings/net/asix,ax88796c.yaml
 F:	drivers/net/ethernet/asix/ax88796c_*
 
+ASIX AX88179 USB ETHERNET DRIVER
+M:	Birger Koblitz <mail@birger-koblitz.de>
+L:	netdev@vger.kernel.org
+S:	Maintained
+F:	drivers/net/usb/ax88179*
+
 ASIX PHY DRIVER [RUST]
 M:	FUJITA Tomonori <fujita.tomonori@gmail.com>
 R:	Trevor Gross <tmgross@umich.edu>
diff --git a/arch/loongarch/configs/loongson32_defconfig b/arch/loongarch/configs/loongson32_defconfig
index dbef879935f32..a039b8e8d2420 100644
--- a/arch/loongarch/configs/loongson32_defconfig
+++ b/arch/loongarch/configs/loongson32_defconfig
@@ -641,7 +641,7 @@ CONFIG_USB_RTL8150=m
 CONFIG_USB_RTL8152=m
 CONFIG_USB_USBNET=m
 # CONFIG_USB_NET_AX8817X is not set
-# CONFIG_USB_NET_AX88179_178A is not set
+# CONFIG_USB_NET_AX88179 is not set
 CONFIG_USB_NET_CDC_EEM=m
 CONFIG_USB_NET_HUAWEI_CDC_NCM=m
 CONFIG_USB_NET_CDC_MBIM=m
diff --git a/arch/loongarch/configs/loongson64_defconfig b/arch/loongarch/configs/loongson64_defconfig
index 4f8bb77b8a1e6..b30415c03fa18 100644
--- a/arch/loongarch/configs/loongson64_defconfig
+++ b/arch/loongarch/configs/loongson64_defconfig
@@ -657,7 +657,7 @@ CONFIG_USB_RTL8150=m
 CONFIG_USB_RTL8152=m
 CONFIG_USB_USBNET=m
 # CONFIG_USB_NET_AX8817X is not set
-# CONFIG_USB_NET_AX88179_178A is not set
+# CONFIG_USB_NET_AX88179 is not set
 CONFIG_USB_NET_CDC_EEM=m
 CONFIG_USB_NET_HUAWEI_CDC_NCM=m
 CONFIG_USB_NET_CDC_MBIM=m
diff --git a/drivers/net/phy/ax88796b.c b/drivers/net/phy/ax88796b.c
index f20ddf6491499..0f1437d1e3e9b 100644
--- a/drivers/net/phy/ax88796b.c
+++ b/drivers/net/phy/ax88796b.c
@@ -13,6 +13,19 @@
 #define PHY_ID_ASIX_AX88772A		0x003b1861
 #define PHY_ID_ASIX_AX88772C		0x003b1881
 #define PHY_ID_ASIX_AX88796B		0x003b1841
+#define PHY_ID_ASIX_AX88772D		0x003b772d
+#define PHY_ID_ASIX_AX88179A		0x003b179a
+#define PHY_ID_ASIX_AX88279		0x003b2790
+
+#define AX_ADVERTISE_2500		0x1000
+
+/* MII Vendor registers */
+#define AX_CTRL_STATUS			0x1d
+#define AX_CTRL_STATUS_SPEED_MASK	0x0c
+#define   AX_CTRL_STATUS_SPEED_10	0x0
+#define   AX_CTRL_STATUS_SPEED_100	0x4
+#define   AX_CTRL_STATUS_SPEED_1000	0x8
+#define   AX_CTRL_STATUS_SPEED_2500	0xc
 
 MODULE_DESCRIPTION("Asix PHY driver");
 MODULE_AUTHOR("Michael Schmitz <schmitzmic@gmail.com>");
@@ -83,6 +96,110 @@ static int asix_ax88772a_read_status(struct phy_device *phydev)
 	return 0;
 }
 
+static int asix_ax88279_read_status(struct phy_device *phydev)
+{
+	int ret, val;
+
+	ret = genphy_read_status(phydev);
+	if (ret)
+		return ret;
+
+	if (!phydev->link)
+		return 0;
+
+	/* Read actual speed from vendor register */
+	val = phy_read(phydev, AX_CTRL_STATUS);
+	if (val < 0)
+		return val;
+
+	switch (val & AX_CTRL_STATUS_SPEED_MASK) {
+	case AX_CTRL_STATUS_SPEED_2500:
+		phydev->speed = SPEED_2500;
+		break;
+	case AX_CTRL_STATUS_SPEED_1000:
+		phydev->speed = SPEED_1000;
+		break;
+	case AX_CTRL_STATUS_SPEED_100:
+		phydev->speed = SPEED_100;
+		break;
+	case AX_CTRL_STATUS_SPEED_10:
+		phydev->speed = SPEED_10;
+	}
+
+	/* MDIO_AN_10GBT_STAT_LP2_5G is broken, but we can deduce that
+	 * the link-partner advertised 2500M if remotely AN succceded
+	 * for link speed > 1000M and we locally have a link speed of
+	 * 2500M
+	 */
+	linkmode_clear_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->lp_advertising);
+	val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_STAT);
+	if (val >= 0 && val & MDIO_AN_10GBT_STAT_REMOK) {
+		linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT,
+				 phydev->lp_advertising,
+				 phydev->speed == SPEED_2500);
+	}
+	/* Only supports full duplex */
+	phydev->duplex = DUPLEX_FULL;
+
+	/* PHY switches interface between 2.5GBit and slower modes */
+	if (phydev->speed == SPEED_2500)
+		phydev->interface = PHY_INTERFACE_MODE_2500BASEX;
+	else
+		phydev->interface = PHY_INTERFACE_MODE_SGMII;
+
+	return 0;
+}
+
+static int asix_ax88279_config_aneg(struct phy_device *phydev)
+{
+	bool adv_2500;
+	int ret;
+
+	if (phydev->autoneg == AUTONEG_DISABLE) {
+		phydev_warn(phydev, "Disabling autoneg is not supported\n");
+		return -EOPNOTSUPP;
+	}
+
+	ret = genphy_config_aneg(phydev);
+
+	if (ret < 0)
+		return ret;
+
+	adv_2500 = linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->advertising);
+	ret = phy_modify(phydev, MII_ADVERTISE, AX_ADVERTISE_2500,
+			 adv_2500 ? AX_ADVERTISE_2500 : 0);
+
+	return ret;
+}
+
+static int asix_ax88279_get_features(struct phy_device *phydev)
+{
+	int ret;
+
+	/* MDIO_DEVS1/2 empty, so set mmds_present bits to allow reading abilities */
+	phydev->c45_ids.mmds_present |= MDIO_DEVS_PMAPMD | MDIO_DEVS_AN;
+
+	linkmode_set_bit_array(phy_basic_ports_array, ARRAY_SIZE(phy_basic_ports_array),
+			       phydev->supported);
+
+	ret = genphy_c45_pma_read_abilities(phydev);
+	if (ret < 0)
+		return ret;
+
+	/* AX88279 does not support reported 100baseT-half duplex mode */
+	linkmode_clear_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT, phydev->supported);
+
+	return 0;
+}
+
+static int asix_ax88279_config_init(struct phy_device *phydev)
+{
+	__set_bit(PHY_INTERFACE_MODE_2500BASEX, phydev->possible_interfaces);
+	__set_bit(PHY_INTERFACE_MODE_SGMII, phydev->possible_interfaces);
+
+	return 0;
+}
+
 static void asix_ax88772a_link_change_notify(struct phy_device *phydev)
 {
 	/* Reset PHY, otherwise MII_LPA will provide outdated information.
@@ -94,6 +211,24 @@ static void asix_ax88772a_link_change_notify(struct phy_device *phydev)
 	}
 }
 
+static int asix_ax88772D_get_features(struct phy_device *phydev)
+{
+	int ret;
+	__ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = {};
+
+	/* MDIO_DEVS1/2 empty, so set mmds_present bits to allow reading abilities */
+	phydev->c45_ids.mmds_present |= MDIO_DEVS_PMAPMD | MDIO_DEVS_AN;
+
+	ret = genphy_read_abilities(phydev);
+	if (ret < 0)
+		return ret;
+
+	/* AX88772D does not support reported 1000baseT mode */
+	linkmode_clear_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, phydev->supported);
+
+	return 0;
+}
+
 static struct phy_driver asix_driver[] = {
 {
 	PHY_ID_MATCH_EXACT(PHY_ID_ASIX_AX88772A),
@@ -111,6 +246,35 @@ static struct phy_driver asix_driver[] = {
 	.suspend	= genphy_suspend,
 	.resume		= genphy_resume,
 	.soft_reset	= asix_soft_reset,
+}, {
+	PHY_ID_MATCH_EXACT(PHY_ID_ASIX_AX88772D),
+	.name		= "Asix Electronics AX88772D",
+	.flags		= PHY_IS_INTERNAL,
+	.get_features	= asix_ax88772D_get_features,
+	.read_mmd	= genphy_read_mmd_c45,
+	.write_mmd	= genphy_write_mmd_c45,
+	.suspend	= genphy_suspend,
+	.resume		= genphy_resume,
+}, {
+	PHY_ID_MATCH_EXACT(PHY_ID_ASIX_AX88179A),
+	.name		= "Asix Electronics AX88179A",
+	.flags		= PHY_IS_INTERNAL,
+	.suspend	= genphy_suspend,
+	.resume		= genphy_resume,
+	.read_mmd	= genphy_read_mmd_c45,
+	.write_mmd	= genphy_write_mmd_c45,
+}, {
+	PHY_ID_MATCH_EXACT(PHY_ID_ASIX_AX88279),
+	.name		= "Asix Electronics AX88279",
+	.flags		= PHY_IS_INTERNAL | PHY_BROKEN_FORCED,
+	.get_features	= asix_ax88279_get_features,
+	.read_status	= asix_ax88279_read_status,
+	.config_aneg	= asix_ax88279_config_aneg,
+	.config_init	= asix_ax88279_config_init,
+	.read_mmd	= genphy_read_mmd_c45,
+	.write_mmd	= genphy_write_mmd_c45,
+	.suspend	= genphy_suspend,
+	.resume		= genphy_resume,
 }, {
 	PHY_ID_MATCH_MODEL(PHY_ID_ASIX_AX88796B),
 	.name		= "Asix Electronics AX88796B",
@@ -124,6 +288,9 @@ static const struct mdio_device_id __maybe_unused asix_tbl[] = {
 	{ PHY_ID_MATCH_EXACT(PHY_ID_ASIX_AX88772A) },
 	{ PHY_ID_MATCH_EXACT(PHY_ID_ASIX_AX88772C) },
 	{ PHY_ID_MATCH_MODEL(PHY_ID_ASIX_AX88796B) },
+	{ PHY_ID_MATCH_EXACT(PHY_ID_ASIX_AX88772D) },
+	{ PHY_ID_MATCH_EXACT(PHY_ID_ASIX_AX88179A) },
+	{ PHY_ID_MATCH_EXACT(PHY_ID_ASIX_AX88279) },
 	{ }
 };
 
diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
index fce9bc7be3304..b83f938fe7c3b 100644
--- a/drivers/net/phy/phy.c
+++ b/drivers/net/phy/phy.c
@@ -1190,6 +1190,10 @@ int phy_ethtool_ksettings_set(struct phy_device *phydev,
 	      duplex != DUPLEX_FULL)))
 		return -EINVAL;
 
+	if (autoneg == AUTONEG_DISABLE &&
+	    (phydev->drv->flags & PHY_BROKEN_FORCED))
+		return -EOPNOTSUPP;
+
 	mutex_lock(&phydev->lock);
 	phydev->autoneg = autoneg;
 
diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c
index a7d086cdc9b25..a3ca7993eb7e0 100644
--- a/drivers/net/phy/phylink.c
+++ b/drivers/net/phy/phylink.c
@@ -1612,6 +1612,26 @@ static bool phylink_link_is_up(struct phylink *pl)
 	return pl->netdev ? netif_carrier_ok(pl->netdev) : pl->old_link_state;
 }
 
+/**
+ * phylink_mac_interrupt() - wrapper for phy_mac_interrupt()
+ * @pl: a pointer to a &struct phylink returned from phylink_create()
+ *
+ * Passes a link-change interrupt received by the MAC to phylib.
+ * This function may be called in interrupt context, and therefore cannot take
+ * a mutex. However, it is safe to assume that the reference to phy does not
+ * disappear during the call, as only an SFP module may be unplugged to make a
+ * PHY disappear. SFP modules however do not support interrupts.
+ */
+void phylink_mac_interrupt(struct phylink *pl)
+{
+	struct phy_device *phy;
+
+	phy = pl->phydev;
+	if (phy)
+		phy_mac_interrupt(phy);
+}
+EXPORT_SYMBOL_GPL(phylink_mac_interrupt);
+
 static void phylink_resolve(struct work_struct *w)
 {
 	struct phylink *pl = container_of(w, struct phylink, resolve);
diff --git a/drivers/net/usb/Kconfig b/drivers/net/usb/Kconfig
index 52a5c0922c79f..27862e380bdb4 100644
--- a/drivers/net/usb/Kconfig
+++ b/drivers/net/usb/Kconfig
@@ -195,11 +195,12 @@ 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"
+config USB_NET_AX88179
+	tristate "ASIX AX88179/179A/178A USB 2.0/3.0 to 100M/1G/2.5GbE Adapters"
 	depends on USB_USBNET
+	select PHYLINK
+	select AX88796B_PHY
 	select CRC32
-	select PHYLIB
 	default y
 	help
 	  This option adds support for ASIX AX88179 based USB 3.0/2.0
@@ -207,6 +208,9 @@ config USB_NET_AX88179_178A
 
 	  This driver should work with at least the following devices:
 	    * ASIX AX88179
+	    * ASIX AX88179A/B
+	    * ASIX AX88279
+	    * ASIX AX88772D/E
 	    * ASIX AX88178A
 	    * Sitcomm LN-032
 
diff --git a/drivers/net/usb/Makefile b/drivers/net/usb/Makefile
index 4964f7b326fbc..4026f19ecb826 100644
--- a/drivers/net/usb/Makefile
+++ b/drivers/net/usb/Makefile
@@ -12,7 +12,8 @@ obj-$(CONFIG_USB_HSO)		+= hso.o
 obj-$(CONFIG_USB_LAN78XX)	+= lan78xx.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_AX88179)      += ax88179.o
+ax88179-y := ax88179_178a.o ax88179a_devices.o ax88179_lib.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
index 2ed46ab62c006..7ec7143805484 100644
--- a/drivers/net/usb/ax88179_178a.c
+++ b/drivers/net/usb/ax88179_178a.c
@@ -8,179 +8,12 @@
 #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>
 #include <uapi/linux/mdio.h>
 #include <linux/mdio.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(29))
-#define AX_RXHDR_DROP_ERR			((u32)BIT(31))
-#define AX_ACCESS_MAC				0x01
-#define AX_ACCESS_PHY				0x02
-#define AX_ACCESS_EEPROM			0x04
-#define AX_ACCESS_EFUS				0x05
-#define AX_RELOAD_EEPROM_EFUSE			0x06
-#define AX_PAUSE_WATERLVL_LOW			0x54
-#define AX_PAUSE_WATERLVL_HIGH			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_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
-	#define GMII_PHY_PGSEL_PAGE3	0x0003
-	#define GMII_PHY_PGSEL_PAGE5	0x0005
+#include "ax88179_lib.h"
 
 static int ax88179_reset(struct usbnet *dev);
 
-struct ax88179_data {
-	u8  eee_enabled;
-	u8  eee_active;
-	u16 rxctl;
-	u8 in_pm;
-	u32 wol_supported;
-	u32 wolopts;
-	u8 disconnecting;
-};
-
-struct ax88179_int_data {
-	__le32 intdata1;
-	__le32 intdata2;
-};
-
 static const struct {
 	unsigned char ctrl, timer_l, timer_h, size, ifg;
 } AX88179_BULKIN_SIZE[] =	{
@@ -190,165 +23,6 @@ static const struct {
 	{7, 0xcc, 0x4c, 0x18, 8},
 };
 
-static void ax88179_set_pm_mode(struct usbnet *dev, bool pm_mode)
-{
-	struct ax88179_data *ax179_data = dev->driver_priv;
-
-	ax179_data->in_pm = pm_mode;
-}
-
-static int ax88179_in_pm(struct usbnet *dev)
-{
-	struct ax88179_data *ax179_data = dev->driver_priv;
-
-	return ax179_data->in_pm;
-}
-
-static int __ax88179_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,
-			      u16 size, void *data)
-{
-	int ret;
-	int (*fn)(struct usbnet *, u8, u8, u16, u16, void *, u16);
-	struct ax88179_data *ax179_data = dev->driver_priv;
-
-	BUG_ON(!dev);
-
-	if (!ax88179_in_pm(dev))
-		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) && !(ret == -ENODEV && ax179_data->disconnecting)))
-		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, const void *data)
-{
-	int ret;
-	int (*fn)(struct usbnet *, u8, u8, u16, u16, const void *, u16);
-	struct ax88179_data *ax179_data = dev->driver_priv;
-
-	BUG_ON(!dev);
-
-	if (!ax88179_in_pm(dev))
-		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) && !(ret == -ENODEV && ax179_data->disconnecting)))
-		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(struct usbnet *dev, u8 cmd, u16 value, u16 index,
-			    u16 size, void *data)
-{
-	int ret;
-
-	if (2 == size) {
-		u16 buf = 0;
-		ret = __ax88179_read_cmd(dev, cmd, value, index, size, &buf);
-		le16_to_cpus(&buf);
-		*((u16 *)data) = buf;
-	} else if (4 == size) {
-		u32 buf = 0;
-		ret = __ax88179_read_cmd(dev, cmd, value, index, size, &buf);
-		le32_to_cpus(&buf);
-		*((u32 *)data) = buf;
-	} else {
-		ret = __ax88179_read_cmd(dev, cmd, value, index, size, data);
-	}
-
-	return ret;
-}
-
-static int ax88179_write_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,
-			     u16 size, const 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);
-	} else {
-		ret = __ax88179_write_cmd(dev, cmd, value, index,
-					  size, data);
-	}
-
-	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) {
-		usbnet_link_change(dev, link, 1);
-		if (!link)
-			netdev_info(dev->net, "ax88179 - Link status is: 0\n");
-	}
-}
-
-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 inline int ax88179_phy_mmd_indirect(struct usbnet *dev, u16 prtad,
 					   u16 devad)
 {
@@ -510,146 +184,11 @@ static void ax88179_disconnect(struct usb_interface *intf)
 	usbnet_disconnect(intf);
 }
 
-static void
-ax88179_get_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo)
-{
-	struct usbnet *dev = netdev_priv(net);
-	struct ax88179_data *priv = dev->driver_priv;
-
-	wolinfo->supported = priv->wol_supported;
-	wolinfo->wolopts = priv->wolopts;
-}
-
-static int
-ax88179_set_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo)
-{
-	struct usbnet *dev = netdev_priv(net);
-	struct ax88179_data *priv = dev->driver_priv;
-
-	if (wolinfo->wolopts & ~(priv->wol_supported))
-		return -EINVAL;
-
-	priv->wolopts = wolinfo->wolopts;
-
-	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_array(last_word - first_word + 1, sizeof(u16),
-				    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]);
-		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_set_eeprom(struct net_device *net, struct ethtool_eeprom *eeprom,
-		   u8 *data)
-{
-	struct usbnet *dev = netdev_priv(net);
-	u16 *eeprom_buff;
-	int first_word;
-	int last_word;
-	int ret;
-	int i;
-
-	netdev_dbg(net, "write EEPROM len %d, offset %d, magic 0x%x\n",
-		   eeprom->len, eeprom->offset, eeprom->magic);
-
-	if (eeprom->len == 0)
-		return -EINVAL;
-
-	if (eeprom->magic != AX88179_EEPROM_MAGIC)
-		return -EINVAL;
-
-	first_word = eeprom->offset >> 1;
-	last_word = (eeprom->offset + eeprom->len - 1) >> 1;
-
-	eeprom_buff = kmalloc_array(last_word - first_word + 1, sizeof(u16),
-				    GFP_KERNEL);
-	if (!eeprom_buff)
-		return -ENOMEM;
-
-	/* align data to 16 bit boundaries, read the missing data from
-	   the EEPROM */
-	if (eeprom->offset & 1) {
-		ret = ax88179_read_cmd(dev, AX_ACCESS_EEPROM, first_word, 1, 2,
-				       &eeprom_buff[0]);
-		if (ret < 0) {
-			netdev_err(net, "Failed to read EEPROM at offset 0x%02x.\n", first_word);
-			goto free;
-		}
-	}
-
-	if ((eeprom->offset + eeprom->len) & 1) {
-		ret = ax88179_read_cmd(dev, AX_ACCESS_EEPROM, last_word, 1, 2,
-				       &eeprom_buff[last_word - first_word]);
-		if (ret < 0) {
-			netdev_err(net, "Failed to read EEPROM at offset 0x%02x.\n", last_word);
-			goto free;
-		}
-	}
-
-	memcpy((u8 *)eeprom_buff + (eeprom->offset & 1), data, eeprom->len);
-
-	for (i = first_word; i <= last_word; i++) {
-		netdev_dbg(net, "write to EEPROM at offset 0x%02x, data 0x%04x\n",
-			   i, eeprom_buff[i - first_word]);
-		ret = ax88179_write_cmd(dev, AX_ACCESS_EEPROM, i, 1, 2,
-					&eeprom_buff[i - first_word]);
-		if (ret < 0) {
-			netdev_err(net, "Failed to write EEPROM at offset 0x%02x.\n", i);
-			goto free;
-		}
-		msleep(20);
-	}
-
-	/* reload EEPROM data */
-	ret = ax88179_write_cmd(dev, AX_RELOAD_EEPROM_EFUSE, 0x0000, 0, 0, NULL);
-	if (ret < 0) {
-		netdev_err(net, "Failed to reload EEPROM data\n");
-		goto free;
-	}
-
-	ret = 0;
-free:
-	kfree(eeprom_buff);
-	return ret;
-}
-
 static int ax88179_get_link_ksettings(struct net_device *net,
 				      struct ethtool_link_ksettings *cmd)
 {
@@ -864,125 +403,6 @@ static const struct ethtool_ops ax88179_ethtool_ops = {
 	.get_ts_info		= ethtool_op_get_ts_info,
 };
 
-static void ax88179_set_multicast(struct net_device *net)
-{
-	struct usbnet *dev = netdev_priv(net);
-	struct ax88179_data *data = dev->driver_priv;
-	u8 *m_filter = ((u8 *)dev->data);
-
-	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 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;
-
-	WRITE_ONCE(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);
-	}
-
-	/* max qlen depend on hard_mtu and rx_urb_size */
-	usbnet_update_max_qlen(dev);
-
-	return 0;
-}
-
-static int ax88179_set_mac_addr(struct net_device *net, void *p)
-{
-	struct usbnet *dev = netdev_priv(net);
-	struct sockaddr *addr = p;
-	int ret;
-
-	if (netif_running(net))
-		return -EBUSY;
-	if (!is_valid_ether_addr(addr->sa_data))
-		return -EADDRNOTAVAIL;
-
-	eth_hw_addr_set(net, addr->sa_data);
-
-	/* Set the MAC address */
-	ret = ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_NODE_ID, ETH_ALEN,
-				 ETH_ALEN, net->dev_addr);
-	if (ret < 0)
-		return ret;
-
-	return 0;
-}
-
 static const struct net_device_ops ax88179_netdev_ops = {
 	.ndo_open		= usbnet_open,
 	.ndo_stop		= usbnet_stop,
@@ -1249,35 +669,6 @@ static int ax88179_led_setting(struct usbnet *dev)
 	return 0;
 }
 
-static void ax88179_get_mac_addr(struct usbnet *dev)
-{
-	u8 mac[ETH_ALEN];
-
-	memset(mac, 0, sizeof(mac));
-
-	/* Maybe the boot loader passed the MAC address via device tree */
-	if (!eth_platform_get_mac_address(&dev->udev->dev, mac)) {
-		netif_dbg(dev, ifup, dev->net,
-			  "MAC address read from device tree");
-	} else {
-		ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_NODE_ID, ETH_ALEN,
-				 ETH_ALEN, mac);
-		netif_dbg(dev, ifup, dev->net,
-			  "MAC address read from ASIX chip");
-	}
-
-	if (is_valid_ether_addr(mac)) {
-		eth_hw_addr_set(dev->net, mac);
-		if (!is_local_ether_addr(mac))
-			dev->net->addr_assign_type = NET_ADDR_PERM;
-	} else {
-		netdev_info(dev->net, "invalid MAC address, using random\n");
-	}
-
-	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_NODE_ID, ETH_ALEN, ETH_ALEN,
-			  dev->net->dev_addr);
-}
-
 static int ax88179_bind(struct usbnet *dev, struct usb_interface *intf)
 {
 	struct ax88179_data *ax179_data;
@@ -1293,6 +684,22 @@ static int ax88179_bind(struct usbnet *dev, struct usb_interface *intf)
 
 	dev->driver_priv = ax179_data;
 
+	ax179_data->resume = ax88179_resume;
+	ax179_data->suspend = ax88179_suspend;
+
+	ret = ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_CHIP_STATUS,
+			       1, 1, &ax179_data->chip_version);
+	if (ret < 0)
+		goto err_nodev;
+
+	ax179_data->chip_version = (ax179_data->chip_version & 0xf0) >> 4;
+	ax179_data->is_ax88772d = 0;
+	ax179_data->ip_align = 1;
+	ax179_data->eeprom_read_cmd = AX_ACCESS_EEPROM;
+	ax179_data->eeprom_write_cmd = AX_ACCESS_EEPROM;
+	ax179_data->eeprom_block = 2;
+	ax179_data->eeprom_wen = 0;
+
 	dev->net->netdev_ops = &ax88179_netdev_ops;
 	dev->net->ethtool_ops = &ax88179_ethtool_ops;
 	dev->net->needed_headroom = 8;
@@ -1317,6 +724,12 @@ static int ax88179_bind(struct usbnet *dev, struct usb_interface *intf)
 	ax88179_reset(dev);
 
 	return 0;
+
+err_nodev:
+	kfree(ax179_data);
+	ax179_data = NULL;
+
+	return ret;
 }
 
 static void ax88179_unbind(struct usbnet *dev, struct usb_interface *intf)
@@ -1879,6 +1292,18 @@ static const struct driver_info at_umc2000sp_info = {
 
 static const struct usb_device_id products[] = {
 {
+	/* ASIX AX88179A USB 3.2 1000Mbit Ethernet */
+	USB_DEVICE_VER(0x0b95, 0x1790, 0x0200, 0x0200),
+	.driver_info = (unsigned long)&ax88179a_info,
+}, {
+	/* ASIX AX88772D USB 2.0 100Mbit Ethernet */
+	USB_DEVICE_VER(0x0b95, 0x1790, 0x0300, 0x0300),
+	.driver_info = (unsigned long)&ax88772d_info,
+}, {
+	/* ASIX AX88279 USB 3.2 2500Mbit Ethernet */
+	USB_DEVICE_VER(0x0b95, 0x1790, 0x0400, 0x0400),
+	.driver_info = (unsigned long)&ax88279_info,
+}, {
 	/* ASIX AX88179 10/100/1000 */
 	USB_DEVICE_AND_INTERFACE_INFO(0x0b95, 0x1790, 0xff, 0xff, 0),
 	.driver_info = (unsigned long)&ax88179_info,
@@ -1935,19 +1360,20 @@ static const struct usb_device_id products[] = {
 };
 MODULE_DEVICE_TABLE(usb, products);
 
-static struct usb_driver ax88179_178a_driver = {
-	.name =		"ax88179_178a",
+static struct usb_driver ax88179_driver = {
+	.name =		"ax88179",
 	.id_table =	products,
 	.probe =	usbnet_probe,
-	.suspend =	ax88179_suspend,
-	.resume =	ax88179_resume,
-	.reset_resume =	ax88179_resume,
+	.suspend =	ax88179_suspend_wrapper,
+	.resume =	ax88179_resume_wrapper,
+	.reset_resume =	ax88179_resume_wrapper,
 	.disconnect =	ax88179_disconnect,
 	.supports_autosuspend = 1,
 	.disable_hub_initiated_lpm = 1,
 };
 
-module_usb_driver(ax88179_178a_driver);
+module_usb_driver(ax88179_driver);
 
-MODULE_DESCRIPTION("ASIX AX88179/178A based USB 3.0/2.0 Gigabit Ethernet Devices");
+MODULE_DESCRIPTION("ASIX AX88179/179A/178A/279 based USB 3.0/2.0 100M/1G/2.5GbE Controllers");
 MODULE_LICENSE("GPL");
+MODULE_ALIAS("ax88179_178a");
diff --git a/drivers/net/usb/ax88179_lib.c b/drivers/net/usb/ax88179_lib.c
new file mode 100644
index 0000000000000..de849650f7871
--- /dev/null
+++ b/drivers/net/usb/ax88179_lib.c
@@ -0,0 +1,530 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * ASIX AX88179/178A USB 3.0/2.0 to Gigabit Ethernet Devices
+ *
+ * Copyright (C) 2011-2013 ASIX
+ */
+
+#include <linux/etherdevice.h>
+#include <linux/rtnetlink.h>
+#include "ax88179_lib.h"
+
+void ax88179_set_pm_mode(struct usbnet *dev, bool pm_mode)
+{
+	struct ax88179_data *ax179_data = dev->driver_priv;
+
+	ax179_data->in_pm = pm_mode;
+}
+
+static int ax88179_in_pm(struct usbnet *dev)
+{
+	struct ax88179_data *ax179_data = dev->driver_priv;
+
+	return ax179_data->in_pm;
+}
+
+int __ax88179_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,
+		       u16 size, void *data)
+{
+	int (*fn)(struct usbnet *dev, u8 cmd, u8 rtype, u16 val, u16 i, void *d, u16 size);
+	struct ax88179_data *ax179_data = dev->driver_priv;
+	int ret;
+
+	if (!ax88179_in_pm(dev))
+		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 && !(ret == -ENODEV && ax179_data->disconnecting)))
+		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, const void *data)
+{
+	int (*fn)(struct usbnet *dev, u8 cmd, u8 rtype, u16 val, u16 i, const void *d, u16 size);
+	struct ax88179_data *ax179_data = dev->driver_priv;
+	int ret;
+
+	if (!ax88179_in_pm(dev))
+		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 && !(ret == -ENODEV && ax179_data->disconnecting)))
+		netdev_warn(dev->net, "Failed to write reg index 0x%04x: %d\n",
+			    index, ret);
+
+	return ret;
+}
+
+void ax88179_write_cmd_async(struct usbnet *dev, u8 cmd, u16 value,
+			     u16 index, u16 size, void *data)
+{
+	u16 buf;
+
+	if (size == 2) {
+		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);
+	}
+}
+
+int ax88179_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,
+		     u16 size, void *data)
+{
+	int ret;
+
+	if (size == 2) {
+		u16 buf = 0;
+
+		ret = __ax88179_read_cmd(dev, cmd, value, index, size, &buf);
+		le16_to_cpus(&buf);
+		*((u16 *)data) = buf;
+	} else if (size == 4) {
+		u32 buf = 0;
+
+		ret = __ax88179_read_cmd(dev, cmd, value, index, size, &buf);
+		le32_to_cpus(&buf);
+		*((u32 *)data) = buf;
+	} else {
+		ret = __ax88179_read_cmd(dev, cmd, value, index, size, data);
+	}
+
+	return ret;
+}
+
+int ax88179_write_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,
+		      u16 size, const void *data)
+{
+	int ret;
+
+	if (size == 2) {
+		u16 buf;
+
+		buf = *((u16 *)data);
+		cpu_to_le16s(&buf);
+		ret = __ax88179_write_cmd(dev, cmd, value, index,
+					  size, &buf);
+	} else {
+		ret = __ax88179_write_cmd(dev, cmd, value, index,
+					  size, data);
+	}
+
+	return ret;
+}
+
+int ax88179_write_u16(struct usbnet *dev, u8 cmd, u16 value, u16 index, u16 data)
+{
+	u16 buf = data;
+
+	cpu_to_le16s(&buf);
+	return __ax88179_write_cmd(dev, cmd, value, index, 2, &buf);
+}
+
+int ax88179_write_u8(struct usbnet *dev, u8 cmd, u16 value, u16 index, u8 data)
+{
+	u8 buf = data;
+
+	return __ax88179_write_cmd(dev, cmd, value, index, 1, &buf);
+}
+
+struct ax88179_data *netdev2data(struct net_device *net)
+{
+	struct usbnet *dev = netdev_priv(net);
+
+	return dev->driver_priv;
+}
+
+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) {
+		usbnet_link_change(dev, link, 1);
+		if (!link)
+			netdev_info(dev->net, "ax88179 - Link status is: 0\n");
+	}
+}
+
+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;
+}
+
+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);
+}
+
+void ax88179_get_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo)
+{
+	struct usbnet *dev = netdev_priv(net);
+	struct ax88179_data *priv = dev->driver_priv;
+
+	wolinfo->supported = priv->wol_supported;
+	wolinfo->wolopts = priv->wolopts;
+}
+
+int ax88179_set_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo)
+{
+	struct usbnet *dev = netdev_priv(net);
+	struct ax88179_data *priv = dev->driver_priv;
+
+	if (wolinfo->wolopts & ~(priv->wol_supported))
+		return -EINVAL;
+
+	priv->wolopts = wolinfo->wolopts;
+
+	return 0;
+}
+
+static void
+ax88179_eeprom_access_params(struct ax88179_data *ax179_data, int i, u16 *value, u16 *idx)
+{
+	/* AX88179 has a word-addressable EEPROM
+	 * AX88179A uses EFUSES with 20 bytes length
+	 * AX88279 has an EEPROM addressable in 256 byte blocks
+	 */
+	if (ax179_data->chip_version < AX_VERSION_AX88179A) {
+		*value = i;
+		*idx = 1;
+	} else if (ax179_data->chip_version == AX_VERSION_AX88279) {
+		*value = (i * ax179_data->eeprom_block) >> 16;
+		*idx = (i * ax179_data->eeprom_block) & 0xffff;
+	} else {
+		*value = i << 4;
+		*idx = 0;
+	}
+}
+
+int ax88179_get_eeprom(struct net_device *net, struct ethtool_eeprom *eeprom, u8 *data)
+{
+	struct usbnet *dev = netdev_priv(net);
+	struct ax88179_data *ax179_data;
+	int first, last, i, ret;
+	u8 *eeprom_buff;
+
+	ax179_data = dev->driver_priv;
+
+	if (eeprom->len == 0)
+		return -EINVAL;
+
+	eeprom->magic = AX88179_EEPROM_MAGIC;
+
+	first = eeprom->offset / ax179_data->eeprom_block;
+	last = (eeprom->offset + eeprom->len - 1) / ax179_data->eeprom_block;
+
+	eeprom_buff = kzalloc((last - first + 1) * ax179_data->eeprom_block, GFP_KERNEL);
+	if (!eeprom_buff)
+		return -ENOMEM;
+
+	for (i = first; i <= last; i++) {
+		u16 value, idx;
+
+		ax88179_eeprom_access_params(ax179_data, i, &value, &idx);
+		ret = __ax88179_read_cmd(dev, ax179_data->eeprom_read_cmd,
+					 value, idx, ax179_data->eeprom_block,
+					 eeprom_buff + (i - first) * ax179_data->eeprom_block);
+
+		if (ret != ax179_data->eeprom_block) {
+			kfree(eeprom_buff);
+			return -EIO;
+		}
+	}
+
+	memcpy(data, eeprom_buff + eeprom->offset % ax179_data->eeprom_block, eeprom->len);
+	kfree(eeprom_buff);
+	return 0;
+}
+
+int ax88179_set_eeprom(struct net_device *net, struct ethtool_eeprom *eeprom, u8 *data)
+{
+	struct usbnet *dev = netdev_priv(net);
+	struct ax88179_data *ax179_data;
+	u16 *eeprom_buff;
+	int first_word;
+	int last_word;
+	int ret;
+	int i;
+
+	ax179_data = dev->driver_priv;
+	if (ax179_data->chip_version >= AX_VERSION_AX88179A)
+		return -EOPNOTSUPP;
+
+	netdev_dbg(net, "write EEPROM len %d, offset %d, magic 0x%x\n",
+		   eeprom->len, eeprom->offset, eeprom->magic);
+
+	if (eeprom->len == 0)
+		return -EINVAL;
+
+	if (eeprom->magic != AX88179_EEPROM_MAGIC)
+		return -EINVAL;
+
+	first_word = eeprom->offset >> 1;
+	last_word = (eeprom->offset + eeprom->len - 1) >> 1;
+
+	eeprom_buff = kmalloc_array(last_word - first_word + 1, sizeof(u16),
+				    GFP_KERNEL);
+	if (!eeprom_buff)
+		return -ENOMEM;
+
+	/* align data to 16 bit boundaries, read the missing data from
+	 * the EEPROM
+	 */
+	if (eeprom->offset & 1) {
+		ret = ax88179_read_cmd(dev, AX_ACCESS_EEPROM, first_word, 1, 2,
+				       &eeprom_buff[0]);
+		if (ret < 0) {
+			netdev_err(net, "Failed to read EEPROM at offset 0x%02x.\n", first_word);
+			goto free;
+		}
+	}
+
+	if ((eeprom->offset + eeprom->len) & 1) {
+		ret = ax88179_read_cmd(dev, AX_ACCESS_EEPROM, last_word, 1, 2,
+				       &eeprom_buff[last_word - first_word]);
+		if (ret < 0) {
+			netdev_err(net, "Failed to read EEPROM at offset 0x%02x.\n", last_word);
+			goto free;
+		}
+	}
+
+	memcpy((u8 *)eeprom_buff + (eeprom->offset & 1), data, eeprom->len);
+
+	for (i = first_word; i <= last_word; i++) {
+		netdev_dbg(net, "write to EEPROM at offset 0x%02x, data 0x%04x\n",
+			   i, eeprom_buff[i - first_word]);
+		ret = ax88179_write_cmd(dev, AX_ACCESS_EEPROM, i, 1, 2,
+					&eeprom_buff[i - first_word]);
+		if (ret < 0) {
+			netdev_err(net, "Failed to write EEPROM at offset 0x%02x.\n", i);
+			goto free;
+		}
+		msleep(20);
+	}
+
+	/* reload EEPROM data */
+	ret = ax88179_write_cmd(dev, AX_RELOAD_EEPROM_EFUSE, 0x0000, 0, 0, NULL);
+	if (ret < 0) {
+		netdev_err(net, "Failed to reload EEPROM data\n");
+		goto free;
+	}
+
+	ret = 0;
+free:
+	kfree(eeprom_buff);
+	return ret;
+}
+
+void ax88179_set_multicast(struct net_device *net)
+{
+	struct usbnet *dev = netdev_priv(net);
+	u8 *m_filter = ((u8 *)dev->data);
+	struct ax88179_data *data;
+
+	data = dev->driver_priv;
+
+	data->rxctl = (AX_RX_CTL_START | AX_RX_CTL_AB | AX_RX_CTL_DROPCRCERR);
+	if (data->ip_align)
+		data->rxctl |= 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 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);
+}
+
+int ax88179_set_features(struct net_device *net, netdev_features_t features)
+{
+	u8 tmp;
+	struct usbnet *dev = netdev_priv(net);
+	struct ax88179_data *data = dev->driver_priv;
+	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) {
+		/* When we disable rx-checksumming, we need to clear the rx_checksum
+		 * flag before disabling the engine
+		 */
+		if (!(features & NETIF_F_RXCSUM))
+			data->rx_checksum = 0;
+
+		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);
+		data->rx_checksum = !!(features & NETIF_F_RXCSUM);
+	}
+
+	return 0;
+}
+
+void ax88179_get_mac_addr(struct usbnet *dev)
+{
+	u8 mac[ETH_ALEN];
+
+	memset(mac, 0, sizeof(mac));
+
+	/* Maybe the boot loader passed the MAC address via device tree */
+	if (!eth_platform_get_mac_address(&dev->udev->dev, mac)) {
+		netif_dbg(dev, ifup, dev->net,
+			  "MAC address read from device tree");
+	} else {
+		ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_NODE_ID, ETH_ALEN,
+				 ETH_ALEN, mac);
+		netif_dbg(dev, ifup, dev->net,
+			  "MAC address read from ASIX chip");
+	}
+
+	if (is_valid_ether_addr(mac)) {
+		eth_hw_addr_set(dev->net, mac);
+		if (!is_local_ether_addr(mac))
+			dev->net->addr_assign_type = NET_ADDR_PERM;
+	} else {
+		netdev_info(dev->net, "invalid MAC address, using random\n");
+	}
+
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_NODE_ID, ETH_ALEN, ETH_ALEN,
+			  dev->net->dev_addr);
+}
+
+int ax88179_change_mtu(struct net_device *net, int new_mtu)
+{
+	struct usbnet *dev = netdev_priv(net);
+	u16 tmp16;
+
+	WRITE_ONCE(net->mtu, new_mtu);
+	dev->hard_mtu = net->mtu + net->hard_header_len;
+
+	rtnl_lock();
+	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);
+	}
+	rtnl_unlock();
+
+	/* max qlen depend on hard_mtu and rx_urb_size */
+	usbnet_update_max_qlen(dev);
+
+	return 0;
+}
+
+int ax88179_set_mac_addr(struct net_device *net, void *p)
+{
+	struct usbnet *dev = netdev_priv(net);
+	struct sockaddr *addr = p;
+	int ret;
+
+	if (netif_running(net))
+		return -EBUSY;
+	if (!is_valid_ether_addr(addr->sa_data))
+		return -EADDRNOTAVAIL;
+
+	eth_hw_addr_set(net, addr->sa_data);
+
+	/* Set the MAC address */
+	ret = ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_NODE_ID, ETH_ALEN,
+				ETH_ALEN, net->dev_addr);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+int ax88179_suspend_wrapper(struct usb_interface *intf, pm_message_t message)
+{
+	struct usbnet *dev = usb_get_intfdata(intf);
+	struct ax88179_data *priv;
+
+	priv = dev->driver_priv;
+
+	return priv->suspend(intf, message);
+}
+
+int ax88179_resume_wrapper(struct usb_interface *intf)
+{
+	struct usbnet *dev = usb_get_intfdata(intf);
+	struct ax88179_data *priv;
+
+	priv = dev->driver_priv;
+	return priv->resume(intf);
+}
+
diff --git a/drivers/net/usb/ax88179_lib.h b/drivers/net/usb/ax88179_lib.h
new file mode 100644
index 0000000000000..a9235ada982b8
--- /dev/null
+++ b/drivers/net/usb/ax88179_lib.h
@@ -0,0 +1,359 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include <linux/usb.h>
+#include <linux/crc32.h>
+#include <linux/phylink.h>
+#include <linux/usb/usbnet.h>
+
+#ifndef __LINUX_USBNET_AX88179_H
+#define __LINUX_USBNET_AX88179_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(29))
+#define AX_RXHDR_DROP_ERR			((u32)BIT(31))
+#define AX_ACCESS_MAC				0x01
+#define AX_ACCESS_PHY				0x02
+#define AX_ACCESS_EEPROM			0x04
+#define AX_ACCESS_EFUS				0x05
+#define AX_RELOAD_EEPROM_EFUSE			0x06
+#define AX88179A_WAKEUP_SETTING			0x07
+#define AX_FW_MODE				0x08
+#define AX_GPHY_CTL				0x0F
+#define AX88179A_FLASH_READ			0x21
+#define AX88179A_FLASH_WEN			0x22
+#define AX88179A_FLASH_WDIS			0x23
+#define AX88179A_FLASH_WRITE			0x24
+#define AX88179A_PHY_CLAUSE45			0x27
+#define AX88179A_FLASH_ERASE_SECTION		0x28
+#define AX88179A_ACCESS_BL			0x2A
+#define AX88179A_PHY_POWER			0x31
+#define AX88179A_AUTODETACH			0xC0
+
+#define AX_PAUSE_WATERLVL_LOW			0x54
+#define AX_PAUSE_WATERLVL_HIGH			0x55
+
+#define AX_FW_MODE_179A				0x01
+#define PHYSICAL_LINK_STATUS			0x02
+	#define	AX_USB_SS		0x04
+	#define	AX_USB_HS		0x02
+	#define AX_USB_FS		0x01
+
+#define GENERAL_STATUS				0x03
+/* Check AX88179 version. UA1:Bit2 = 0,  UA2:Bit2 = 1 */
+	#define	AX_SECLD		0x04
+
+#define AX_CHIP_STATUS				0x05
+
+#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 AX88179A_ETH_TX_GAP			0x0D
+
+#define AX88179A_BFM_DATA			0x0E
+	#define AX_TX_QUEUE_CFG		0x02
+	#define AX_TX_QUEUE_SET		0x08
+	#define AX_TX_Q1_AHB_FC_EN	0x10
+	#define AX_TX_Q2_AHB_FC_EN	0x20
+	#define AX_XGMII_EN		0x80
+
+#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_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 AX88179A_VLAN_ID_ADDRESS		0x2A
+
+#define AX88179A_VLAN_ID_CONTROL		0x2B
+	#define AX_VLAN_CONTROL_WE	0x0001
+	#define AX_VLAN_CONTROL_RD	0x0002
+	#define AX_VLAN_CONTROL_VSO	0x0010
+	#define AX_VLAN_CONTROL_VFE	0x0020
+
+#define AX88179A_VLAN_ID_DATA0			0x2C
+#define AX88179A_VLAN_ID_DATA1			0x2D
+
+#define AX_RX_BULKIN_QCTRL			0x2e
+#define AX_GPHY_EEE_CTRL			0x01
+
+#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 AX88179A_MAC_BM_INT_MASK		0x41
+#define AX88179A_MAC_BM_RX_DMA_CTL		0x43
+#define AX88179A_MAC_BM_TX_DMA_CTL		0x46
+
+#define AX88179A_MAC_RX_STATUS_CDC		0x6D
+	#define AX_LSOFC_WCNT_7_ACCESS	0x03
+	#define AX_GMII_CRC_APPEND	0x10
+
+#define AX_LEDCTRL				0x73
+#define AX88179A_MAC_ARC_CTRL			0x9E
+#define AX88179A_MAC_SWP_CTRL			0xB1
+
+#define AX88179A_MAC_TX_PAUSE			0xB2
+
+#define AX88179A_MAC_CDC_DELAY_TX		0xB5
+
+#define AX88179A_MAC_PATH			0xB7
+	#define AX_MAC_RX_PATH_READY	0x01
+	#define AX_MAC_TX_PATH_READY	0x02
+
+#define AX88179A_NEW_PAUSE_CTRL			0xB8
+	#define AX_NEW_PAUSE_EN		0x01
+
+#define AX88179A_MAC_BULK_OUT_CTRL		0xB9
+	#define AX_MAC_EFF_EN		0x02
+
+#define AX88179A_MAC_RX_DATA_CDC_CNT		0xC0
+	#define AX_MAC_LSO_ERR_EN	0x04
+	#define AX_MAC_MIQFFCTRL_FORMAT	0x10
+	#define AX_MAC_MIQFFCTRL_DROP_CRC 0x20
+
+#define AX88179A_AUTODETACH_DELAY	(5UL << 8)
+#define AX88179A_AUTODETACH_EN		1
+
+#define AX88179A_MAC_LSO_ENHANCE_CTRL		0xC3
+	#define AX_LSO_ENHANCE_EN	0x01
+
+#define AX88179A_MAC_TX_HDR_CKSUM		0xCC
+#define AX88179A_EP5_EHR			0xF9
+
+#define AX_PHY_POWER				0x02
+
+#define EPHY_LOW_POWER_EN			0x01
+#define S5_WOL_EN				0x04
+#define S5_WOL_LOW_POWER			0x20
+
+#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
+	#define GMII_PHY_PGSEL_PAGE3	0x0003
+	#define GMII_PHY_PGSEL_PAGE5	0x0005
+
+#define AX88179A_SW_REVISION		0xFC
+#define AX88179A_SW_VERSION		0xFD
+
+/* TX Descriptor */
+#define AX179A_TX_DESC_LEN_MASK		0x1FFFFF
+#define AX179A_TX_DESC_DROP_PADD	BIT(28)
+#define AX179A_TX_DESC_VLAN		BIT(29)
+#define AX179A_TX_DESC_MSS		GENMASK(46, 32)
+#define AX179A_TX_DESC_VLAN_ID		GENMASK(63, 48)
+
+/* RX Packet Descriptor */
+#define AX179A_RX_PD_L4_ERR		BIT(0)
+#define AX179A_RX_PD_L3_ERR		BIT(1)
+#define AX179A_RX_PD_L4_TYPE_MASK	0x1C
+#define AX179A_RX_PD_L4_UDP		0x04
+#define AX179A_RX_PD_L4_TCP		0x10
+#define AX179A_RX_PD_L3_TYPE_MASK	0x60
+#define AX179A_RX_PD_L3_IP		0x20
+#define AX179A_RX_PD_L3_IP6		0x40
+
+#define AX179A_RX_PD_VLAN		BIT(10)
+#define AX179A_RX_PD_RX_OK		BIT(11)
+#define AX179A_RX_PD_DROP		BIT(31)
+#define AX179A_RX_PD_LEN		GENMASK(30, 16)
+#define AX179A_RX_PD_VLAN_SHIFT		0x20
+
+/* RX Descriptor header */
+#define AX179A_RX_DH_PKT_CNT_MASK	0x1FFF
+#define AX179A_RX_DH_DESC_OFFSET	GENMASK(31, 13)
+
+#define AX179A_RX_HW_PAD		0x02
+
+#define AX_ADVERTISE_2500		0x1000
+
+enum ax_ether_link_speed {
+	ETHER_LINK_NONE = 0,
+	ETHER_LINK_10   = 1,
+	ETHER_LINK_100  = 2,
+	ETHER_LINK_1000 = 3,
+	ETHER_LINK_2500 = 4,
+};
+
+enum ax_chip_version {
+	AX_VERSION_INVALID		= 0x0,
+	AX_VERSION_AX88179		= 0x4,
+	AX_VERSION_AX88179A		= 0x6,	/* Also AX88772D */
+	AX_VERSION_AX88279		= 0x7,
+};
+
+struct ax88179_data {
+	u8  eee_enabled;
+	u8  eee_active;
+	u16 rxctl;
+	u8 in_pm;
+	u32 wol_supported;
+	u32 wolopts;
+	u8 disconnecting;
+	u8 chip_version;
+	u8 fw_version[4];
+	u8 is_ax88772d;
+	u8 ip_align;
+	u8 link;
+	u8 speed;
+	u8 full_duplex;
+	u8 rx_checksum;
+	u8 eeprom_read_cmd;
+	u8 eeprom_write_cmd;
+	u8 eeprom_wen;
+	u16 eeprom_block;
+	struct mii_bus *mdio;
+	struct phy_device *phydev;
+	struct phylink *phylink;
+	struct phylink_config phylink_config;
+	int (*resume)(struct usb_interface *intf);
+	int (*suspend)(struct usb_interface *intf, pm_message_t message);
+};
+
+struct ax88179_int_data {
+	__le32 intdata1;
+	__le32 intdata2;
+};
+
+struct ax_bulkin_settings {
+	unsigned char ctrl, timer_l, timer_h, size, ifg;
+};
+
+void ax88179_set_pm_mode(struct usbnet *dev, bool pm_mode);
+int __ax88179_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index, u16 size, void *data);
+int ax88179_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index, u16 size, void *data);
+int ax88179_write_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index, u16 size,
+		      const void *data);
+void ax88179_write_cmd_async(struct usbnet *dev, u8 cmd, u16 value, u16 index,
+			     u16 size, void *data);
+int ax88179_write_u16(struct usbnet *dev, u8 cmd, u16 value, u16 index, u16 data);
+int ax88179_write_u8(struct usbnet *dev, u8 cmd, u16 value, u16 index, u8 data);
+int ax88179_mdio_read(struct net_device *netdev, int phy_id, int loc);
+void ax88179_mdio_write(struct net_device *netdev, int phy_id, int loc, int val);
+struct ax88179_data *netdev2data(struct net_device *net);
+void ax88179_status(struct usbnet *dev, struct urb *urb);
+void ax88179_get_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo);
+int ax88179_set_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo);
+int ax88179_get_eeprom(struct net_device *net, struct ethtool_eeprom *eeprom, u8 *data);
+int ax88179_set_eeprom(struct net_device *net, struct ethtool_eeprom *eeprom, u8 *data);
+void ax88179_set_multicast(struct net_device *net);
+int ax88179_set_features(struct net_device *net, netdev_features_t features);
+void ax88179_get_mac_addr(struct usbnet *dev);
+int ax88179_change_mtu(struct net_device *net, int new_mtu);
+int ax88179_set_mac_addr(struct net_device *net, void *p);
+int ax88179_suspend_wrapper(struct usb_interface *intf, pm_message_t message);
+int ax88179_resume_wrapper(struct usb_interface *intf);
+
+extern const struct driver_info ax88179a_info;
+extern const struct driver_info ax88772d_info;
+extern const struct driver_info ax88279_info;
+
+#endif /*__LINUX_USBNET_AX88179_H */
diff --git a/drivers/net/usb/ax88179a_devices.c b/drivers/net/usb/ax88179a_devices.c
new file mode 100644
index 0000000000000..f2140e2bb4431
--- /dev/null
+++ b/drivers/net/usb/ax88179a_devices.c
@@ -0,0 +1,1285 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <linux/module.h>
+#include <linux/phylink.h>
+#include <linux/if_vlan.h>
+#include "ax88179_lib.h"
+
+static int ax88179a_reset(struct usbnet *dev);
+
+#define AX88279_EEPROM_LEN			0x4000
+#define AX88179A_EEPROM_LEN			(32 * 20)
+
+enum ax_bulk_in_speeds {
+	BULK_IN_SPEED_2G5 = 0,
+	BULK_IN_SPEED_1G_SS = 1,
+	BULK_IN_SPEED_1G_HS   = 2,
+	BULK_IN_SPEED_100_FULL_SS = 3,
+	BULK_IN_SPEED_100_HALF_SS = 4,
+	BULK_IN_SPEED_100_FULL_HS = 5,
+	BULK_IN_SPEED_100_HALF_HS = 6,
+	BULK_IN_SPEED_FS = 7,
+};
+
+static const struct ax_bulkin_settings AX88179A_BULKIN_SIZE[] = {
+	[BULK_IN_SPEED_1G_SS]		= {5, 0x7B, 0x00, 0x17, 0x0F},
+	[BULK_IN_SPEED_1G_HS]		= {5, 0xC0, 0x02, 0x06, 0x0F},
+	[BULK_IN_SPEED_100_FULL_SS]	= {7, 0xF0, 0x00, 0x0C, 0x0F},
+	[BULK_IN_SPEED_100_HALF_SS]	= {6, 0x00, 0x00, 0x06, 0x0F},
+	[BULK_IN_SPEED_100_FULL_HS]	= {5, 0xC0, 0x04, 0x06, 0x0F},
+	[BULK_IN_SPEED_100_HALF_HS]	= {7, 0xC0, 0x04, 0x06, 0x0F},
+	[BULK_IN_SPEED_FS]		= {7, 0x00, 0x00, 0x03, 0x3F},
+};
+
+static const struct ax_bulkin_settings AX88772D_BULKIN_SIZE[] = {
+	[BULK_IN_SPEED_100_FULL_HS]	= {5, 0xC0, 0x04, 0x06, 0x0F},
+	[BULK_IN_SPEED_100_HALF_HS]	= {7, 0xC0, 0x04, 0x06, 0x0F},
+	[BULK_IN_SPEED_FS]		= {7, 0x00, 0x00, 0x03, 0x3F},
+};
+
+static const struct ax_bulkin_settings AX88279_BULKIN_SIZE[] = {
+	[BULK_IN_SPEED_2G5]		= {5, 0x10, 0x01, 0x11, 0x0F},
+	[BULK_IN_SPEED_1G_SS]		= {7, 0xB3, 0x01, 0x11, 0x0F},
+	[BULK_IN_SPEED_1G_HS]		= {7, 0xC0, 0x02, 0x06, 0x0F},
+	[BULK_IN_SPEED_100_FULL_SS]	= {7, 0x80, 0x01, 0x03, 0x0F},
+	[BULK_IN_SPEED_100_HALF_SS]	= {7, 0x80, 0x01, 0x03, 0x0F},
+	[BULK_IN_SPEED_100_FULL_HS]	= {7, 0x80, 0x01, 0x03, 0x0F},
+	[BULK_IN_SPEED_100_HALF_HS]	= {7, 0x80, 0x01, 0x03, 0x0F},
+	[BULK_IN_SPEED_FS]		= {7, 0x00, 0x00, 0x03, 0x3F},
+};
+
+static int ax88179_mdiobus_read(struct mii_bus *bus, int phy_id, int regnum)
+{
+	struct usbnet *dev = bus->priv;
+	struct ax88179_data *priv;
+	u16 res;
+	int ret;
+
+	priv = dev->driver_priv;
+	/* When reading PHYSID, return unused PHY-IDs from the ASIX vendor range */
+	if (phy_id == AX88179_PHY_ID && regnum == MII_PHYSID1)
+		return 0x003b;
+	if (phy_id == AX88179_PHY_ID && regnum == MII_PHYSID2) {
+		if (priv->chip_version == AX_VERSION_AX88179A && priv->is_ax88772d)
+			return 0x772d;
+		else if (priv->chip_version == AX_VERSION_AX88179A)
+			return 0x179a;
+		else if (priv->chip_version == AX_VERSION_AX88279)
+			return 0x2790;
+	}
+
+	ret = ax88179_read_cmd(dev, AX_ACCESS_PHY, phy_id, (__u16)regnum, 2, &res);
+	if (ret < 0)
+		return ret;
+	return res;
+}
+
+static int ax88179_mdiobus_write(struct mii_bus *bus, int phy_id, int regnum, u16 val)
+{
+	struct usbnet *dev = bus->priv;
+
+	return ax88179_write_u16(dev, AX_ACCESS_PHY, phy_id, (__u16)regnum, val);
+}
+
+static int ax179a_read_mmd(struct usbnet *dev, u16 dev_addr, u16 reg)
+{
+	u16 res;
+	int ret;
+
+	ret = ax88179_read_cmd(dev, AX88179A_PHY_CLAUSE45, dev_addr, reg, 2, &res);
+	if (ret < 0)
+		return ret;
+	return res;
+}
+
+static int ax179a_write_mmd(struct usbnet *dev, u16 dev_addr, u16 reg, u16 data)
+{
+	return ax88179_write_cmd(dev, AX88179A_PHY_CLAUSE45, dev_addr, reg, 2, &data);
+}
+
+static int ax88179_mdiobus_read_c45(struct mii_bus *bus, int addr, int devnum, int regnum)
+{
+	struct usbnet *dev = bus->priv;
+
+	if (addr != AX88179_PHY_ID)
+		return -EINVAL;
+
+	return ax179a_read_mmd(dev, devnum, regnum);
+}
+
+static int ax88179_mdiobus_write_c45(struct mii_bus *bus, int addr, int devnum,
+				     int regnum, u16 val)
+{
+	struct usbnet *dev = bus->priv;
+
+	if (addr != AX88179_PHY_ID)
+		return -EINVAL;
+
+	return ax179a_write_mmd(dev, devnum, regnum, val);
+}
+
+static void ax88179a_status(struct usbnet *dev, struct urb *urb)
+{
+	struct ax88179_data *data = dev->driver_priv;
+
+	if (urb->actual_length < 8)
+		return;
+
+	phylink_mac_interrupt(data->phylink);
+}
+
+static int ax88179a_suspend(struct usb_interface *intf, pm_message_t message)
+{
+	struct usbnet *dev = usb_get_intfdata(intf);
+	struct ax88179_data *priv;
+	u8 tmp8 = 0;
+	u16 tmp16;
+	int ret;
+
+	priv = dev->driver_priv;
+	ax88179_set_pm_mode(dev, true);
+
+	if (netif_running(dev->net)) {
+		rtnl_lock();
+		phylink_suspend(priv->phylink, !!priv->wolopts);
+		rtnl_unlock();
+	}
+
+	ret = usbnet_suspend(intf, message);
+	if (ret) {
+		rtnl_lock();
+		phylink_resume(priv->phylink);
+		rtnl_unlock();
+		return ret;
+	}
+
+	/* Enable WoL */
+	if (priv->wolopts) {
+		ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_MONITOR_MOD, 1, 1, &tmp8);
+		tmp8 &= ~(AX_MONITOR_MODE_RWLC | AX_MONITOR_MODE_RWMP);
+		if (priv->wolopts & WAKE_PHY)
+			tmp8 |= AX_MONITOR_MODE_RWLC;
+		if (priv->wolopts & WAKE_MAGIC)
+			tmp8 |= AX_MONITOR_MODE_RWMP;
+
+		ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MONITOR_MOD, 1, 1, &tmp8);
+
+		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);
+
+		if (priv->chip_version == AX_VERSION_AX88279)
+			ax88179_write_cmd(dev, AX88179A_WAKEUP_SETTING, 8,
+					  EPHY_LOW_POWER_EN | S5_WOL_EN
+					  | S5_WOL_LOW_POWER | 0x8000, 0, NULL);
+		else
+			ax88179_write_cmd(dev, AX88179A_WAKEUP_SETTING, 0,
+					  EPHY_LOW_POWER_EN, 0, NULL);
+
+	} else if (priv->chip_version == AX_VERSION_AX88279) {
+		ax88179_write_cmd(dev, AX88179A_WAKEUP_SETTING, 8, 0x8000, 0, NULL);
+	}
+
+	ax88179_set_pm_mode(dev, false);
+	return 0;
+}
+
+static int ax88179a_auto_detach(struct usbnet *dev)
+{
+	u16 tmp16;
+
+	tmp16 = AX88179A_AUTODETACH_DELAY;
+	ax88179_write_cmd(dev, AX88179A_AUTODETACH, tmp16, 0, 0, NULL);
+	return 0;
+}
+
+static int ax88179a_resume(struct usb_interface *intf)
+{
+	struct usbnet *dev = usb_get_intfdata(intf);
+	struct ax88179_data *ax179_data;
+	u8 reg8 = 0;
+	int ret;
+
+	ax179_data = dev->driver_priv;
+	ax88179_set_pm_mode(dev, true);
+
+	ret = ax88179_read_cmd(dev, AX88179A_PHY_POWER, 0, 0, 1, &reg8);
+	if (ret < 0) {
+		ax88179_set_pm_mode(dev, false);
+		return ret;
+	}
+	if (!(reg8 & AX_PHY_POWER)) {
+		reg8 = AX_PHY_POWER;
+		ax88179_write_cmd(dev, AX88179A_PHY_POWER, 0, 0, 1, &reg8);
+		msleep(250);
+	}
+	ax88179_write_cmd(dev, AX_FW_MODE, AX_FW_MODE_179A, 0, 0, NULL);
+
+	/* Now, that AX_FW_MODE_179A is enabled, the PHY needs a power-cycle.
+	 * PHY-power is re-enabled in ax88179a_reset()
+	 */
+	ax88179_write_u8(dev, AX88179A_PHY_POWER, 0, 0, 0);
+	msleep(250);
+
+	ax88179a_reset(dev);
+
+	if (netif_running(dev->net)) {
+		rtnl_lock();
+		phylink_resume(ax179_data->phylink);
+		rtnl_unlock();
+	}
+
+	ax88179_set_pm_mode(dev, false);
+
+	return usbnet_resume(intf);
+}
+
+static void ax88179a_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *info)
+{
+	struct ax88179_data *priv = netdev2data(net);
+
+	/* Inherit standard device info */
+	usbnet_get_drvinfo(net, info);
+	if (priv->chip_version < AX_VERSION_AX88179A)
+		return;
+
+	snprintf(info->fw_version, sizeof(info->fw_version), "%d.%d.%d.%d",
+		 priv->fw_version[0], priv->fw_version[1],
+		 priv->fw_version[2], priv->fw_version[3]);
+}
+
+static void ax88179a_bulkin_config(struct usbnet *dev, u8 link_sts, u8 speed, bool full_duplex)
+{
+	struct ax88179_data *ax179_data = dev->driver_priv;
+	const struct ax_bulkin_settings *bulkin_data;
+	int index = 0;
+
+	switch (speed) {
+	case ETHER_LINK_2500:	/* AX88279 only */
+		index = BULK_IN_SPEED_2G5;
+		break;
+
+	case ETHER_LINK_1000:	/* AX88279 & AX88178A */
+		if (link_sts & AX_USB_SS)
+			index = BULK_IN_SPEED_1G_SS;
+		else if (link_sts & AX_USB_HS)
+			index = BULK_IN_SPEED_1G_HS;
+		break;
+
+	case ETHER_LINK_100:
+		if (link_sts & AX_USB_SS)
+			index = BULK_IN_SPEED_100_FULL_SS;
+		else if (link_sts & AX_USB_HS)
+			index = BULK_IN_SPEED_100_FULL_HS;
+		if (!full_duplex)
+			index++;
+		break;
+
+	case ETHER_LINK_10:
+		index = BULK_IN_SPEED_FS;
+		break;
+
+	default:	/* No link */
+		index = BULK_IN_SPEED_FS;
+	}
+
+	if (link_sts & AX_USB_FS)
+		index = BULK_IN_SPEED_FS;
+
+	if (ax179_data->chip_version == AX_VERSION_AX88279) {
+		bulkin_data = AX88279_BULKIN_SIZE;
+	} else {
+		if (ax179_data->is_ax88772d)
+			bulkin_data = AX88772D_BULKIN_SIZE;
+		else
+			bulkin_data = AX88179A_BULKIN_SIZE;
+	}
+
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_BULKIN_QCTRL, 5, 5, &bulkin_data[index]);
+}
+
+static void ax88179a_get_pauseparam(struct net_device *net, struct ethtool_pauseparam *pause)
+{
+	struct ax88179_data *data = netdev2data(net);
+
+	phylink_ethtool_get_pauseparam(data->phylink, pause);
+}
+
+static int ax88179a_set_pauseparam(struct net_device *net, struct ethtool_pauseparam *pause)
+{
+	struct ax88179_data *data = netdev2data(net);
+
+	return phylink_ethtool_set_pauseparam(data->phylink, pause);
+}
+
+static int ax88179a_get_link_ksettings(struct net_device *net,
+				       struct ethtool_link_ksettings *cmd)
+{
+	struct ax88179_data *data = netdev2data(net);
+
+	return phylink_ethtool_ksettings_get(data->phylink, cmd);
+}
+
+static int ax88179a_set_link_ksettings(struct net_device *net,
+				       const struct ethtool_link_ksettings *cmd)
+{
+	struct ax88179_data *data = netdev2data(net);
+
+	return phylink_ethtool_ksettings_set(data->phylink, cmd);
+}
+
+static int ax88179a_get_eeprom_len(struct net_device *net)
+{
+	struct ax88179_data *ax179_data = netdev2data(net);
+
+	if (ax179_data->chip_version >= AX_VERSION_AX88279)
+		return AX88279_EEPROM_LEN;
+	else
+		return AX88179A_EEPROM_LEN;
+}
+
+static int ax88179a_get_eee(struct net_device *net, struct ethtool_keee *edata)
+{
+	struct ax88179_data *ax179_data = netdev2data(net);
+
+	return phylink_ethtool_get_eee(ax179_data->phylink, edata);
+}
+
+static int ax88179a_set_eee(struct net_device *net, struct ethtool_keee *edata)
+{
+	struct ax88179_data *ax179_data = netdev2data(net);
+
+	return phylink_ethtool_set_eee(ax179_data->phylink, edata);
+}
+
+static const struct ethtool_ops ax88179a_ethtool_ops = {
+	.get_drvinfo            = ax88179a_get_drvinfo,
+	.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		= ax88179a_get_eeprom_len,
+	.get_eeprom		= ax88179_get_eeprom,
+	.set_eeprom		= ax88179_set_eeprom,
+	.get_eee		= ax88179a_get_eee,
+	.set_eee		= ax88179a_set_eee,
+	.nway_reset		= phy_ethtool_nway_reset,
+	.get_link_ksettings	= ax88179a_get_link_ksettings,
+	.set_link_ksettings	= ax88179a_set_link_ksettings,
+	.get_pauseparam		= ax88179a_get_pauseparam,
+	.set_pauseparam		= ax88179a_set_pauseparam,
+	.get_ts_info		= ethtool_op_get_ts_info,
+};
+
+static int ax88179a_vlan_rx_kill_vid(struct net_device *net, __be16 proto, u16 vid)
+{
+	struct usbnet *dev = netdev_priv(net);
+	u8 vlan_ctrl;
+	int ret = 0;
+	u16 reg16;
+	u8 reg8;
+
+	ret = ax88179_read_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_CONTROL, 1, 1, &reg8);
+	if (ret < 0)
+		return ret;
+	vlan_ctrl = reg8;
+
+	/* Address */
+	reg8 = (vid / 16);
+	ret = ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_ADDRESS, 1, 1, &reg8);
+	if (ret < 0)
+		return ret;
+
+	/* Data */
+	reg8 = vlan_ctrl | AX_VLAN_CONTROL_RD;
+	ret = ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_CONTROL, 1, 1, &reg8);
+	if (ret < 0)
+		return ret;
+
+	ret = ax88179_read_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_DATA0, 2, 2, &reg16);
+	if (ret < 0)
+		return ret;
+	reg16 &= ~(1 << (vid % 16));
+	ret = ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_DATA0, 2, 2, &reg16);
+	if (ret < 0)
+		return ret;
+
+	reg8 = vlan_ctrl | AX_VLAN_CONTROL_WE;
+	ret = ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_CONTROL, 1, 1, &reg8);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static int ax88179a_vlan_rx_add_vid(struct net_device *net, __be16 proto, u16 vid)
+{
+	struct usbnet *dev = netdev_priv(net);
+	u8 vlan_ctrl;
+	int ret = 0;
+	u16 reg16;
+	u8 reg8;
+
+	ret = ax88179_read_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_CONTROL, 1, 1, &reg8);
+	if (ret < 0)
+		return ret;
+	vlan_ctrl = reg8;
+
+	/* Address */
+	reg8 = (vid / 16);
+	ret = ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_ADDRESS, 1, 1, &reg8);
+	if (ret < 0)
+		return ret;
+
+	/* Data */
+	reg8 = vlan_ctrl | AX_VLAN_CONTROL_RD;
+	ret = ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_CONTROL, 1, 1, &reg8);
+	if (ret < 0)
+		return ret;
+
+	ret = ax88179_read_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_DATA0, 2, 2, &reg16);
+	if (ret < 0)
+		return ret;
+	reg16 |= (1 << (vid % 16));
+	ret = ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_DATA0, 2, 2, &reg16);
+	if (ret < 0)
+		return ret;
+
+	reg8 = vlan_ctrl | AX_VLAN_CONTROL_WE;
+	ret = ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_CONTROL, 1, 1, &reg8);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static void ax88179a_mdio_unregister(struct ax88179_data *data)
+{
+	mdiobus_unregister(data->mdio);
+	mdiobus_free(data->mdio);
+}
+
+static int ax88179a_init_phy(struct usbnet *dev)
+{
+	struct ax88179_data *data = dev->driver_priv;
+	int ret;
+
+	data->phydev = mdiobus_get_phy(data->mdio, AX88179_PHY_ID);
+	if (!data->phydev) {
+		netdev_err(dev->net, "Could not find PHY\n");
+		return -ENODEV;
+	}
+
+	data->phydev->irq = PHY_MAC_INTERRUPT;
+	ret = phylink_connect_phy(data->phylink, data->phydev);
+	if (ret) {
+		netdev_err(dev->net, "Could not connect PHY\n");
+		return ret;
+	}
+
+	phy_suspend(data->phydev);
+	data->phydev->mac_managed_pm = true;
+
+	phy_attached_info(data->phydev);
+
+	return 0;
+}
+
+static void ax88179a_mac_config(struct phylink_config *config, unsigned int mode,
+				const struct phylink_link_state *state)
+{
+	/* Nothing to do */
+}
+
+static void ax88179a_mac_link_down(struct phylink_config *config,
+				   unsigned int mode, phy_interface_t interface)
+{
+	/* Nothing to do */
+}
+
+static void ax88179a_mac_link_up(struct phylink_config *config,
+				 struct phy_device *phy,
+				 unsigned int phy_mode, phy_interface_t interface,
+				 int speed, int duplex,
+				 bool tx_pause, bool rx_pause)
+{
+	struct usbnet *dev = netdev_priv(to_net_dev(config->dev));
+	struct ax88179_data *ax179_data = dev->driver_priv;
+	u8 link_sts = 0, reg8[3];
+	u8 bulk_config_speed = 0;
+	u16 mode;
+
+	/* Stop RX/TX for link configuration */
+	ax88179_write_u16(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, AX_RX_CTL_STOP);
+	ax88179_write_u8(dev, AX_ACCESS_MAC, AX88179A_MAC_PATH, 1, 0);
+
+	ax88179_write_u8(dev, AX_ACCESS_MAC, AX88179A_MAC_CDC_DELAY_TX, 1, 0xa5);
+
+	ax88179_write_u16(dev, AX_ACCESS_MAC, AX_PAUSE_WATERLVL_LOW, 2, 0x0410);
+
+	ax88179_write_u8(dev, AX_ACCESS_MAC, AX88179A_ETH_TX_GAP, 1, 0);
+
+	ax88179_write_u8(dev, AX_ACCESS_MAC, AX88179A_EP5_EHR, 1, 0x07);
+
+	ax88179_write_u8(dev, AX_ACCESS_MAC, AX88179A_NEW_PAUSE_CTRL, 1,
+			 0x28 | AX_NEW_PAUSE_EN);
+
+	mode = AX_MEDIUM_RECEIVE_EN;
+	if (tx_pause)
+		mode |= AX_MEDIUM_TXFLOW_CTRLEN;
+	if (rx_pause)
+		mode |= AX_MEDIUM_RXFLOW_CTRLEN;
+
+	switch (speed) {
+	case SPEED_2500:
+		reg8[0] = 0x00;
+		reg8[1] = 0xF8;
+		reg8[2] = 0x07;
+		ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_MAC_TX_PAUSE, 3, 3, reg8);
+
+		reg8[0] = 0x78;
+		reg8[1] = (AX_LSOFC_WCNT_7_ACCESS << 5);
+		reg8[2] = 0;
+		ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_MAC_RX_STATUS_CDC, 3, 3, reg8);
+
+		ax88179_write_u8(dev, AX_ACCESS_MAC, AX88179A_MAC_RX_DATA_CDC_CNT, 1, 0x40);
+		ax88179_write_u8(dev, AX_ACCESS_MAC, AX88179A_MAC_RX_DATA_CDC_CNT + 1, 1,
+				 AX_MAC_MIQFFCTRL_FORMAT | AX_MAC_MIQFFCTRL_DROP_CRC |
+				 AX_MAC_LSO_ERR_EN);
+
+		ax88179_write_u8(dev, AX_ACCESS_MAC, AX88179A_BFM_DATA, 1, AX_XGMII_EN);
+
+		ax88179_write_u8(dev, AX_ACCESS_MAC, AX88179A_MAC_LSO_ENHANCE_CTRL, 1,
+				 0x1C | AX_LSO_ENHANCE_EN);
+
+		mode |= AX_MEDIUM_GIGAMODE | AX_MEDIUM_FULL_DUPLEX;
+		bulk_config_speed = ETHER_LINK_2500;
+
+		break;
+
+	case SPEED_1000:
+		mode |= AX_MEDIUM_GIGAMODE;
+		bulk_config_speed = ETHER_LINK_1000;
+		fallthrough;
+
+	case SPEED_100:
+		reg8[0] = 0x78;
+		reg8[1] = (AX_LSOFC_WCNT_7_ACCESS << 5) | AX_GMII_CRC_APPEND;
+		reg8[2] = 0;
+		ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_MAC_RX_STATUS_CDC, 3, 3, reg8);
+
+		ax88179_write_u8(dev, AX_ACCESS_MAC, AX88179A_MAC_RX_DATA_CDC_CNT, 1, 0x40);
+		if (!bulk_config_speed)
+			bulk_config_speed = ETHER_LINK_100;
+		break;
+
+	case SPEED_10:
+		reg8[0] = 0xFA;
+		reg8[1] = (AX_LSOFC_WCNT_7_ACCESS << 5) | AX_GMII_CRC_APPEND;
+		reg8[2] = 0xFF;
+		ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_MAC_RX_STATUS_CDC, 3, 3, reg8);
+
+		ax88179_write_u8(dev, AX_ACCESS_MAC, AX88179A_MAC_RX_DATA_CDC_CNT, 1, 0xFA);
+
+		bulk_config_speed = ETHER_LINK_10;
+		break;
+	}
+
+	ax88179_read_cmd(dev, AX_ACCESS_MAC, PHYSICAL_LINK_STATUS, 1, 1, &link_sts);
+	ax88179a_bulkin_config(dev, link_sts, bulk_config_speed, !!duplex);
+
+	if (speed != SPEED_2500)
+		ax88179_write_u8(dev, AX_ACCESS_MAC, AX88179A_BFM_DATA, 1, 0);
+
+	if (duplex)
+		mode |= AX_MEDIUM_FULL_DUPLEX;
+
+	rtnl_lock();
+	if (dev->net->mtu > 1500)
+		mode |= AX_MEDIUM_JUMBO_EN;
+	ax88179_write_u16(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE, 2, mode);
+
+	ax88179_write_u16(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, READ_ONCE(ax179_data->rxctl));
+	rtnl_unlock();
+
+	ax88179_write_u8(dev, AX_ACCESS_MAC, AX88179A_MAC_PATH, 1,
+			 AX_MAC_RX_PATH_READY | AX_MAC_TX_PATH_READY);
+}
+
+static void ax88179a_mac_disable_tx_lpi(struct phylink_config *config)
+{
+	struct usbnet *dev = netdev_priv(to_net_dev(config->dev));
+
+	ax88179_write_cmd(dev, AX_GPHY_CTL, AX_GPHY_EEE_CTRL, false, 0, NULL);
+}
+
+static int ax88179a_mac_enable_tx_lpi(struct phylink_config *config, u32 timer, bool tx_clk_stop)
+{
+	struct usbnet *dev = netdev_priv(to_net_dev(config->dev));
+
+	/* AX88179A does not provide LPI timer registers */
+	return ax88179_write_cmd(dev, AX_GPHY_CTL, AX_GPHY_EEE_CTRL, true, 0, NULL);
+}
+
+static const struct phylink_mac_ops ax88179a_phylink_mac_ops = {
+	.mac_config = ax88179a_mac_config,
+	.mac_link_down = ax88179a_mac_link_down,
+	.mac_link_up = ax88179a_mac_link_up,
+	.mac_disable_tx_lpi = ax88179a_mac_disable_tx_lpi,
+	.mac_enable_tx_lpi = ax88179a_mac_enable_tx_lpi,
+};
+
+static int ax88179a_phylink_setup(struct usbnet *dev)
+{
+	struct ax88179_data *data = dev->driver_priv;
+	phy_interface_t phy_if_mode;
+	struct phylink *phylink;
+
+	data->phylink_config.dev = &dev->net->dev;
+	data->phylink_config.type = PHYLINK_NETDEV;
+	data->phylink_config.mac_capabilities = MAC_SYM_PAUSE | MAC_ASYM_PAUSE | MAC_100;
+	if (data->is_ax88772d)
+		data->phylink_config.mac_capabilities |= MAC_10;
+	else if (data->chip_version < AX_VERSION_AX88279)
+		data->phylink_config.mac_capabilities |= MAC_10 | MAC_1000;
+	else
+		data->phylink_config.mac_capabilities |= MAC_1000 | MAC_2500FD;
+
+	if (!data->is_ax88772d) {
+		data->phylink_config.lpi_capabilities = MAC_100FD | MAC_1000FD;
+		data->phylink_config.eee_enabled_default = false;
+	}
+
+	if (data->chip_version == AX_VERSION_AX88279) {
+		__set_bit(PHY_INTERFACE_MODE_2500BASEX,
+			  data->phylink_config.supported_interfaces);
+		__set_bit(PHY_INTERFACE_MODE_SGMII,
+			  data->phylink_config.supported_interfaces);
+		phy_if_mode = PHY_INTERFACE_MODE_2500BASEX;
+	} else {
+		__set_bit(PHY_INTERFACE_MODE_SGMII,
+			  data->phylink_config.supported_interfaces);
+		phy_if_mode = PHY_INTERFACE_MODE_SGMII;
+	}
+
+	memcpy(data->phylink_config.lpi_interfaces,
+	       data->phylink_config.supported_interfaces,
+	       sizeof(data->phylink_config.lpi_interfaces));
+
+	phylink = phylink_create(&data->phylink_config, dev->net->dev.fwnode,
+				 phy_if_mode, &ax88179a_phylink_mac_ops);
+	if (IS_ERR(phylink))
+		return PTR_ERR(phylink);
+
+	data->phylink = phylink;
+	return 0;
+}
+
+static int ax88179a_init_mdio(struct usbnet *dev)
+{
+	struct ax88179_data *data = dev->driver_priv;
+	int ret;
+
+	data->mdio = mdiobus_alloc();
+	if (!data->mdio)
+		return -ENOMEM;
+
+	data->mdio->priv = dev;
+	data->mdio->read = ax88179_mdiobus_read;
+	data->mdio->write = ax88179_mdiobus_write;
+	data->mdio->read_c45 = ax88179_mdiobus_read_c45;
+	data->mdio->write_c45 = ax88179_mdiobus_write_c45;
+	data->mdio->name = "AX88179A MDIO Bus";
+	data->mdio->phy_mask = ~(1 << AX88179_PHY_ID);
+	/* mii bus name is usb-<usb bus number>-<usb device number> */
+	snprintf(data->mdio->id, MII_BUS_ID_SIZE, "usb-%03d:%03d",
+		 dev->udev->bus->busnum, dev->udev->devnum);
+
+	ret = mdiobus_register(data->mdio);
+	if (ret) {
+		netdev_err(dev->net, "Could not register MDIO bus (err %d)\n", ret);
+		mdiobus_free(data->mdio);
+		data->mdio = NULL;
+	}
+
+	return ret;
+}
+
+static int ax88179a_mii_ioctl(struct net_device *net, struct ifreq *rq, int cmd)
+{
+	struct ax88179_data *data = netdev2data(net);
+
+	return phylink_mii_ioctl(data->phylink, rq, cmd);
+}
+
+static int ax88179a_set_features(struct net_device *net, netdev_features_t features)
+{
+	struct usbnet *dev = netdev_priv(net);
+	netdev_features_t changed;
+	int ret;
+	u8 tmp;
+
+	changed = net->features ^ features;
+
+	ret = ax88179_set_features(net, features);
+	if (ret)
+		return ret;
+
+	if (changed & NETIF_F_HW_VLAN_CTAG_FILTER) {
+		if (features & NETIF_F_HW_VLAN_CTAG_FILTER) {
+			for (int i = 0; i < 256; i++) {
+				/* Address */
+				tmp = i;
+				ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_ADDRESS,
+						  1, 1, &tmp);
+				/* Data */
+				ax88179_write_u16(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_DATA0,
+						  2, 0);
+				ax88179_write_u8(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_CONTROL,
+						 1, AX_VLAN_CONTROL_WE);
+			}
+			ret = ax88179_read_cmd(dev, AX_ACCESS_MAC,
+					       AX88179A_VLAN_ID_CONTROL, 1, 1, &tmp);
+			if (ret < 0)
+				return ret;
+			tmp ^= AX_VLAN_CONTROL_VFE;
+			ret = ax88179_write_cmd(dev, AX_ACCESS_MAC,
+						AX88179A_VLAN_ID_CONTROL, 1, 1, &tmp);
+			if (ret < 0)
+				return ret;
+		}
+	}
+
+	if (changed & NETIF_F_HW_VLAN_CTAG_RX) {
+		ret = ax88179_read_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_CONTROL,
+				       1, 1, &tmp);
+		if (ret < 0)
+			return ret;
+		tmp ^= AX_VLAN_CONTROL_VSO;
+		ret = ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_CONTROL,
+					1, 1, &tmp);
+		if (ret < 0)
+			return ret;
+	}
+	return 0;
+}
+
+static const struct net_device_ops ax88179a_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	= dev_get_tstats64,
+	.ndo_change_mtu		= ax88179_change_mtu,
+	.ndo_set_mac_address	= ax88179_set_mac_addr,
+	.ndo_validate_addr	= eth_validate_addr,
+	.ndo_eth_ioctl		= ax88179a_mii_ioctl,
+	.ndo_set_rx_mode	= ax88179_set_multicast,
+	.ndo_set_features	= ax88179a_set_features,
+	.ndo_vlan_rx_add_vid	= ax88179a_vlan_rx_add_vid,
+	.ndo_vlan_rx_kill_vid	= ax88179a_vlan_rx_kill_vid,
+};
+
+static int ax88179a_bind(struct usbnet *dev, struct usb_interface *intf)
+{
+	struct usb_device *udev = interface_to_usbdev(intf);
+	struct ax88179_data *ax179_data;
+	int ret;
+
+	/* Check if vendor configuration */
+	if (udev->actconfig->desc.bConfigurationValue != 1) {
+		netdev_info(dev->net, "Switching to vendor mode\n");
+		usb_driver_set_configuration(udev, 1);
+		return -ENODEV;
+	}
+
+	ret = usbnet_get_endpoints(dev, intf);
+	if (ret < 0)
+		return ret;
+
+	ax179_data = kzalloc_obj(*ax179_data);
+	if (!ax179_data)
+		return -ENOMEM;
+
+	dev->driver_priv = ax179_data;
+
+	ret = ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_CHIP_STATUS,
+			       1, 1, &ax179_data->chip_version);
+	if (ret < 0)
+		goto err_nodev;
+
+	ax179_data->chip_version = (ax179_data->chip_version & 0xf0) >> 4;
+	ax179_data->is_ax88772d = 0;
+	if (ax179_data->chip_version == AX_VERSION_AX88179A) {
+		if (le16_to_cpu(udev->descriptor.bcdDevice) == 0x300)
+			ax179_data->is_ax88772d = 1;
+	}
+
+	for (int i = 0; i < 3; i++) {
+		ret = ax88179_read_cmd(dev, AX88179A_ACCESS_BL, AX88179A_SW_VERSION + i,
+				       1, 1, &ax179_data->fw_version[i]);
+		if (ret < 0)
+			ax179_data->fw_version[i] = 0xff;
+	}
+	ret = ax88179_read_cmd(dev, AX88179A_ACCESS_BL, AX88179A_SW_REVISION,
+			       1, 1, &ax179_data->fw_version[3]);
+	if (ret < 0)
+		ax179_data->fw_version[3] = 0xff;
+
+	netdev_info(dev->net, "AX88179A/279/772D Chip Version: %x, FW: %d.%d.%d.%d\n",
+		    ax179_data->chip_version,
+		    ax179_data->fw_version[0], ax179_data->fw_version[1],
+		    ax179_data->fw_version[2], ax179_data->fw_version[3]);
+
+	/* The AX88279 requires both the AX_RX_CTL_IPE and AX_RX_CTL_DROPCRCERR
+	 * bits set in AX_RX_CTL for creating correct RX-URBs. AX_RX_CTL_DROPCRCERR
+	 * is anyway set for all chips, make sure AX_RX_CTL_IPE is set via ip_align.
+	 * Also configure eeprom access parameters.
+	 */
+	if (ax179_data->chip_version == AX_VERSION_AX88279) {
+		ax179_data->ip_align = 1;
+		ax179_data->eeprom_read_cmd = AX88179A_FLASH_READ;
+		ax179_data->eeprom_write_cmd = AX88179A_FLASH_WRITE;
+		ax179_data->eeprom_block = 256;
+		ax179_data->eeprom_wen = 1;
+	} else {
+		ax179_data->ip_align = 0;
+		ax179_data->eeprom_read_cmd = AX_ACCESS_EFUS;
+		ax179_data->eeprom_write_cmd = AX_ACCESS_EFUS;
+		ax179_data->eeprom_block = 20;
+		ax179_data->eeprom_wen = 0;
+	}
+
+	ax179_data->resume = ax88179a_resume;
+	ax179_data->suspend = ax88179a_suspend;
+
+	dev->net->netdev_ops = &ax88179a_netdev_ops;
+	dev->net->ethtool_ops = &ax88179a_ethtool_ops;
+	dev->net->needed_headroom = 8;
+	dev->net->needed_tailroom = 8;
+	dev->net->min_mtu = ETH_MIN_MTU;
+	dev->hard_mtu = 9 * 1024;
+	dev->net->max_mtu = dev->hard_mtu - dev->net->hard_header_len;
+
+	if (!ax179_data->is_ax88772d)
+		dev->mii.supports_gmii = 1;
+
+	dev->net->features |= NETIF_F_SG | NETIF_F_IP_CSUM |
+			      NETIF_F_IPV6_CSUM | NETIF_F_RXCSUM | NETIF_F_TSO |
+			      NETIF_F_HW_VLAN_CTAG_TX;
+
+	dev->net->hw_features |= dev->net->features;
+	dev->net->hw_features |= NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_HW_VLAN_CTAG_FILTER;
+
+	dev->net->vlan_features = NETIF_F_SG | NETIF_F_IP_CSUM |
+				  NETIF_F_IPV6_CSUM | NETIF_F_RXCSUM | NETIF_F_TSO;
+
+	netif_set_tso_max_size(dev->net, 16384);
+
+	/* Enable Transmission of Link Speed byte in interrupt URB */
+	ax88179_write_cmd(dev, AX_FW_MODE, AX_FW_MODE_179A, 0, 0, NULL);
+	ax88179_write_cmd(dev, AX_RELOAD_EEPROM_EFUSE, 0, 0, 0, NULL);
+
+	/* Read MAC address from DTB or ASIX chip */
+	ax88179_get_mac_addr(dev);
+	memcpy(dev->net->perm_addr, dev->net->dev_addr, ETH_ALEN);
+
+	/* Power PHY for probing */
+	ax88179_write_u8(dev, AX88179A_PHY_POWER, 0, 0, AX_PHY_POWER);
+	msleep(250);
+
+	ret = ax88179a_init_mdio(dev);
+	if (ret)
+		goto err_nodev;
+
+	ret = ax88179a_phylink_setup(dev);
+	if (ret)
+		goto phylink_err;
+
+	ret = ax88179a_init_phy(dev);
+	if (ret)
+		goto initphy_err;
+
+	/* Keep this interface runtime-PM active by taking a usage ref.
+	 * Prevents runtime suspend while bound and avoids resume paths
+	 * that could deadlock (autoresume under RTNL while USB PM lock
+	 * is held, phylink/MDIO wants RTNL).
+	 */
+	pm_runtime_get_noresume(&intf->dev);
+
+	return 0;
+
+initphy_err:
+	phylink_destroy(ax179_data->phylink);
+phylink_err:
+	ax88179a_mdio_unregister(ax179_data);
+err_nodev:
+	kfree(ax179_data);
+	ax179_data = NULL;
+
+	return ret;
+}
+
+static void ax88179a_unbind(struct usbnet *dev, struct usb_interface *intf)
+{
+	struct ax88179_data *ax179_data = dev->driver_priv;
+
+	/* Configure RX control register => stop operation */
+	ax88179_write_u16(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, AX_RX_CTL_STOP);
+
+	rtnl_lock();
+	phylink_disconnect_phy(ax179_data->phylink);
+	rtnl_unlock();
+	phylink_destroy(ax179_data->phylink);
+	ax88179a_mdio_unregister(ax179_data);
+
+	ax88179_write_u8(dev, AX88179A_PHY_POWER, 0, 0, 0);
+
+	kfree(ax179_data);
+}
+
+static void ax88179a_rx_checksum(struct sk_buff *skb, u64 pkt_desc)
+{
+	u32 pkt_type;
+
+	skb->ip_summed = CHECKSUM_NONE;
+	/* checksum error bit is set */
+	if (pkt_desc & AX179A_RX_PD_L4_ERR || pkt_desc & AX179A_RX_PD_L3_ERR)
+		return;
+
+	pkt_type = pkt_desc & AX179A_RX_PD_L4_TYPE_MASK;
+	/* It must be a TCP or UDP packet with a valid checksum */
+	if (pkt_type == AX179A_RX_PD_L4_TCP || pkt_type == AX179A_RX_PD_L4_UDP)
+		skb->ip_summed = CHECKSUM_UNNECESSARY;
+}
+
+static int ax88179a_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
+{
+	struct ax88179_data *ax179_data = dev->driver_priv;
+	struct sk_buff *ax_skb;
+	u32 hdr_off, pkt_end;
+	u64 *pkt_desc_ptr;
+	u16 vlan_tag;
+	u16 pkt_cnt;
+	u64 rx_hdr;
+
+	/* SKB contents for AX179A-based chips:
+	 *   <packet 1>
+	 *   ...
+	 *   <packet N>
+	 *   <per-packet metadata entry 1>
+	 *   ...
+	 *   <per-packet metadata entry N>
+	 *   <rx_hdr>
+	 *
+	 * where:
+	 *   <packet N> contains pkt_len data bytes and padding:
+	 *		2 bytes of IP alignment (optional, depends on AX_RX_CTL_IPE flag)
+	 *		packet data received
+	 *		optional padding to 8-bytes boundary
+	 *   <per-packet metadata entry N> contains 8 bytes:
+	 *		pkt_len and fields AX_RXHDR_*
+	 *   <rx-hdr>	contains 8 bytes:
+	 *		pkt_cnt and hdr_off (offset of <per-packet metadata entry 1>)
+	 *
+	 * pkt_cnt is number of entries in the per-packet metadata array.
+	 */
+
+	if (!skb || skb->len < sizeof(rx_hdr))
+		goto err;
+
+	/* RX Descriptor Header */
+	skb_trim(skb, skb->len - sizeof(rx_hdr));
+	rx_hdr = *(u64 *)skb_tail_pointer(skb);
+	le64_to_cpus(rx_hdr);
+
+	/* Check these packets */
+	hdr_off = FIELD_GET(AX179A_RX_DH_DESC_OFFSET, rx_hdr);
+	pkt_cnt = rx_hdr & AX179A_RX_DH_PKT_CNT_MASK;
+
+	/* Consistency check header position */
+	if (hdr_off != skb->len - (pkt_cnt * sizeof(rx_hdr)))
+		goto err;
+
+	/* Make sure that the bounds of the metadata array are inside the SKB
+	 * (and in front of the counter at the end).
+	 */
+	if (pkt_cnt * 8 + hdr_off > skb->len)
+		goto err;
+
+	/* Packets must not overlap the metadata array */
+	skb_trim(skb, hdr_off);
+
+	if (!pkt_cnt)
+		goto err;
+
+	/* Get the first RX packet descriptor */
+	pkt_desc_ptr = (u64 *)(skb->data + hdr_off);
+
+	pkt_end = 0;
+	while (pkt_cnt--) {
+		u64 pkt_desc = le64_to_cpu(*pkt_desc_ptr);
+		u32 pkt_len_plus_padd;
+		u32 pkt_len;
+
+		pkt_len = FIELD_GET(AX179A_RX_PD_LEN, pkt_desc);
+		pkt_len -= (ax179_data->ip_align ? 2 : 0);
+		pkt_len_plus_padd = ((pkt_len + 7 + (ax179_data->ip_align ? 2 : 0)) & 0x7FFF8);
+
+		pkt_end += pkt_len_plus_padd;
+		if (pkt_end > hdr_off || (pkt_cnt == 0 && pkt_end != hdr_off))
+			goto err;
+
+		if (pkt_desc & AX179A_RX_PD_DROP || !(pkt_desc & AX179A_RX_PD_RX_OK) ||
+		    pkt_len > (dev->hard_mtu + AX179A_RX_HW_PAD) || pkt_len < 2 + ETH_HLEN) {
+			skb_pull(skb, pkt_len_plus_padd);
+
+			/* Next RX Packet Descriptor */
+			pkt_desc_ptr++;
+			continue;
+		}
+
+		ax_skb = netdev_alloc_skb_ip_align(dev->net, pkt_len);
+		if (!ax_skb)
+			goto err;
+
+		skb_put(ax_skb, pkt_len);
+		memcpy(ax_skb->data, skb->data + (ax179_data->ip_align ? AX179A_RX_HW_PAD : 0),
+		       pkt_len);
+
+		if (ax179_data->rx_checksum)
+			ax88179a_rx_checksum(ax_skb, pkt_desc);
+
+		if (pkt_desc & AX179A_RX_PD_VLAN) {
+			vlan_tag = pkt_desc >> AX179A_RX_PD_VLAN_SHIFT;
+			__vlan_hwaccel_put_tag(ax_skb, htons(ETH_P_8021Q), vlan_tag);
+		}
+
+		usbnet_skb_return(dev, ax_skb);
+		skb_pull(skb, pkt_len_plus_padd);
+
+		/* Next RX Packet Header */
+		pkt_desc_ptr++;
+	}
+
+	return 1;
+
+err:
+	return 0;
+}
+
+static struct sk_buff *ax88179a_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags)
+{
+	u64 tx_desc = skb->len & AX179A_TX_DESC_LEN_MASK;
+	int frame_size = dev->maxpacket;
+	struct sk_buff *ax_skb;
+	u64 *tx_desc_ptr;
+	int padding_size;
+	int headroom;
+	int tailroom;
+	u16 tci = 0;
+
+	/* TSO MSS */
+	tx_desc |= FIELD_PREP(AX179A_TX_DESC_MSS, (u64)skb_shinfo(skb)->gso_size);
+
+	headroom = (skb->len + sizeof(tx_desc)) % 8;
+	padding_size = headroom ? 8 - headroom : 0;
+
+	if (((skb->len + sizeof(tx_desc) + padding_size) % frame_size) == 0) {
+		padding_size += 8;
+		tx_desc |= AX179A_TX_DESC_DROP_PADD;
+	}
+
+	if ((dev->net->features & NETIF_F_HW_VLAN_CTAG_TX) && (vlan_get_tag(skb, &tci) >= 0)) {
+		tx_desc |= AX179A_TX_DESC_VLAN;
+		tx_desc |= FIELD_PREP(AX179A_TX_DESC_VLAN_ID, (u64)tci);
+	}
+
+	if (!dev->can_dma_sg && (dev->net->features & NETIF_F_SG) && skb_linearize(skb)) {
+		dev_kfree_skb_any(skb);
+		return NULL;
+	}
+
+	headroom = skb_headroom(skb);
+	tailroom = skb_tailroom(skb);
+
+	if (!(headroom >= sizeof(tx_desc) && tailroom >= padding_size)) {
+		ax_skb = skb_copy_expand(skb, sizeof(tx_desc), padding_size, flags);
+		dev_kfree_skb_any(skb);
+		skb = ax_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));
+	put_unaligned_le64(tx_desc, tx_desc_ptr);
+
+	usbnet_set_skb_tx_stats(skb, 1, 0);
+
+	return skb;
+}
+
+static int ax88179a_reset(struct usbnet *dev)
+{
+	struct ax88179_data *ax179_data = dev->driver_priv;
+	u16 *tmp16;
+	u8 buf[5];
+	u16 rxctl;
+	u8 *tmp;
+
+	tmp16 = (u16 *)buf;
+	tmp = (u8 *)buf;
+
+	/* Power up ethernet PHY */
+	ax88179_write_u8(dev, AX88179A_PHY_POWER, 0, 0, AX_PHY_POWER);
+	msleep(250);
+
+	/* Ethernet PHY Auto Detach*/
+	ax88179a_auto_detach(dev);
+
+	ax88179_write_u8(dev, AX_ACCESS_MAC, AX88179A_MAC_BULK_OUT_CTRL, 1, AX_MAC_EFF_EN);
+
+	ax88179_write_u16(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 0);
+
+	ax88179_write_u8(dev, AX_ACCESS_MAC, AX_PAUSE_WATERLVL_LOW, 1, 0x04);
+	ax88179_write_u8(dev, AX_ACCESS_MAC, AX_PAUSE_WATERLVL_HIGH, 1, 0x10);
+
+	*tmp = 0;
+	if (dev->net->features & NETIF_F_HW_VLAN_CTAG_FILTER)
+		*tmp |= AX_VLAN_CONTROL_VFE;
+	if (dev->net->features & NETIF_F_HW_VLAN_CTAG_RX)
+		*tmp |= AX_VLAN_CONTROL_VSO;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_CONTROL, 1, 1, tmp);
+
+	ax88179_write_u8(dev, AX_ACCESS_MAC, AX88179A_MAC_BM_INT_MASK, 1, 0xff);
+
+	ax88179_write_u8(dev, AX_ACCESS_MAC, AX88179A_MAC_BM_RX_DMA_CTL, 1, 0);
+	ax88179_write_u8(dev, AX_ACCESS_MAC, AX88179A_MAC_BM_TX_DMA_CTL, 1, 0);
+	ax88179_write_u8(dev, AX_ACCESS_MAC, AX88179A_MAC_ARC_CTRL, 1, 0);
+	ax88179_write_u8(dev, AX_ACCESS_MAC, AX88179A_MAC_SWP_CTRL, 1, 0);
+	ax88179_write_u8(dev, AX_ACCESS_MAC, AX88179A_MAC_TX_HDR_CKSUM, 1, 0);
+
+	/* Read MAC address from DTB or asix chip */
+	ax88179_get_mac_addr(dev);
+	memcpy(dev->net->perm_addr, dev->net->dev_addr, ETH_ALEN);
+
+	/* The Bulk-Register configuration for the AX88179A is done in
+	 * ax88179a_mac_link_up(), once the link is up for a given link and USB-speed.
+	 */
+	if (ax179_data->is_ax88772d)
+		dev->rx_urb_size = 1024 * 24;
+	else
+		dev->rx_urb_size = 1024 * 48;
+
+	/* Enable checksum offload */
+	if (dev->net->features & NETIF_F_RXCSUM) {
+		ax88179_write_u8(dev, AX_ACCESS_MAC, AX_RXCOE_CTL, 1,
+				 AX_RXCOE_IP | AX_RXCOE_TCP | AX_RXCOE_UDP |
+				 AX_RXCOE_TCPV6 | AX_RXCOE_UDPV6);
+		ax179_data->rx_checksum = 1;
+	} else {
+		ax179_data->rx_checksum = 0;
+		ax88179_write_u8(dev, AX_ACCESS_MAC, AX_RXCOE_CTL, 1, 0);
+	}
+
+	ax88179_write_u8(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, 1,
+			 AX_TXCOE_IP | AX_TXCOE_TCP | AX_TXCOE_UDP |
+			 AX_TXCOE_TCPV6 | AX_TXCOE_UDPV6);
+
+	/* Configure RX control register => start operation */
+	rxctl = AX_RX_CTL_DROPCRCERR | AX_RX_CTL_START | AX_RX_CTL_AP |
+		AX_RX_CTL_AMALL | AX_RX_CTL_AB;
+	if (ax179_data->ip_align)
+		rxctl |= AX_RX_CTL_IPE;
+	if (dev->net->flags & IFF_PROMISC)
+		rxctl |= AX_RX_CTL_PRO;
+	WRITE_ONCE(ax179_data->rxctl, rxctl);
+	ax88179_write_u16(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, rxctl);
+
+	if (ax179_data->chip_version < AX_VERSION_AX88179A)
+		*tmp = AX_MONITOR_MODE_PMETYPE | AX_MONITOR_MODE_PMEPOL | AX_MONITOR_MODE_RWMP;
+	else
+		*tmp = 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_FULL_DUPLEX;
+	if (!ax179_data->is_ax88772d)
+		*tmp16 |= AX_MEDIUM_GIGAMODE;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE, 2, 2, tmp16);
+
+	/* Check if WoL is supported */
+	ax179_data->wol_supported = 0;
+	if (ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_MONITOR_MOD,
+			     1, 1, tmp) > 0)
+		ax179_data->wol_supported = WAKE_MAGIC | WAKE_PHY;
+
+	/* Ensure EEE state is off, consistent with phylink setup */
+	ax88179_write_cmd(dev, AX_GPHY_CTL, AX_GPHY_EEE_CTRL, false, 0, NULL);
+
+	/* ax88179a_reset() may also be called from resume context,
+	 * phylink_resume() will start phylink, then.
+	 */
+	if (!ax179_data->in_pm)
+		phylink_start(ax179_data->phylink);
+
+	return 0;
+}
+
+static int ax88179a_stop(struct usbnet *dev)
+{
+	struct ax88179_data *ax179_data = dev->driver_priv;
+	u16 reg16;
+
+	phylink_stop(ax179_data->phylink);
+
+	ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE, 2, 2, &reg16);
+	reg16 &= ~AX_MEDIUM_RECEIVE_EN;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE, 2, 2, &reg16);
+
+	ax88179_write_u16(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 0);
+
+	ax88179_write_u8(dev, AX88179A_PHY_POWER, 0, 0, 0);
+
+	return 0;
+}
+
+const struct driver_info ax88179a_info = {
+	.description = "ASIX AX88179A USB 3.2 Gigabit Ethernet",
+	.bind = ax88179a_bind,
+	.unbind = ax88179a_unbind,
+	.status = ax88179a_status,
+	.reset = ax88179a_reset,
+	.stop = ax88179a_stop,
+	.flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_MULTI_PACKET | FLAG_AVOID_UNLINK_URBS,
+	.rx_fixup = ax88179a_rx_fixup,
+	.tx_fixup = ax88179a_tx_fixup,
+};
+
+const struct driver_info ax88772d_info = {
+	.description = "ASIX AX88772D/E USB 2.0 Fast Ethernet",
+	.bind = ax88179a_bind,
+	.unbind = ax88179a_unbind,
+	.status = ax88179a_status,
+	.reset = ax88179a_reset,
+	.stop = ax88179a_stop,
+	.flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_MULTI_PACKET | FLAG_AVOID_UNLINK_URBS,
+	.rx_fixup = ax88179a_rx_fixup,
+	.tx_fixup = ax88179a_tx_fixup,
+};
+
+const struct driver_info ax88279_info = {
+	.description = "ASIX AX88279 USB 3.2 2.5Gigabit Ethernet",
+	.bind = ax88179a_bind,
+	.unbind = ax88179a_unbind,
+	.status = ax88179a_status,
+	.reset = ax88179a_reset,
+	.stop = ax88179a_stop,
+	.flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_MULTI_PACKET | FLAG_AVOID_UNLINK_URBS,
+	.rx_fixup = ax88179a_rx_fixup,
+	.tx_fixup = ax88179a_tx_fixup,
+};
diff --git a/include/linux/phy.h b/include/linux/phy.h
index 7c5098a0dd6cd..4143bffdcdb9b 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -64,6 +64,7 @@ extern const int phy_basic_ports_array[3];
 #define PHY_RST_AFTER_CLK_EN	0x00000002
 #define PHY_POLL_CABLE_TEST	0x00000004
 #define PHY_ALWAYS_CALL_SUSPEND	0x00000008
+#define PHY_BROKEN_FORCED	0x00000010
 #define MDIO_DEVICE_IS_PHY	0x80000000
 
 /**
diff --git a/include/linux/phylink.h b/include/linux/phylink.h
index 3a88a69882a61..a7b6294cc4a07 100644
--- a/include/linux/phylink.h
+++ b/include/linux/phylink.h
@@ -768,6 +768,7 @@ int phylink_ethtool_set_eee(struct phylink *link, struct ethtool_keee *eee);
 int phylink_mii_ioctl(struct phylink *, struct ifreq *, int);
 int phylink_speed_down(struct phylink *pl, bool sync);
 int phylink_speed_up(struct phylink *pl);
+void phylink_mac_interrupt(struct phylink *pl);
 
 #define phylink_zero(bm) \
 	bitmap_zero(bm, __ETHTOOL_LINK_MODE_MASK_NBITS)

3/2 2026/10/04 05:43 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 50700
							output: 39
							thoughts: 1721
						

Thoughts:
**Fuzzing Worthiness of USB Ethernet Driver Updates**

Okay, here's what I'm thinking. This patch looks like it's adding support for several new ASIX USB Ethernet devices, namely the AX88772D, AX88179A, and AX88279. The changes touch a lot of areas: configuration, driver code, PHY handling, and shared utility functions. It's a fairly extensive refactor, moving common code into libraries and updating probe tables for the new devices. My initial instinct is to dig in and understand the potential attack surface.

My first thought is always reachability. Can syzkaller *actually* hit this code? Thankfully, syzkaller *does* have robust USB fuzzing capabilities via the raw-gadget/dummy HCD setup. It emulates USB devices, and these USB net devices (like the `ax88179` family) are standard drivers. Perfect! We can definitely fuzz USB drivers. I need to confirm that this specific device ID (`0x0b95, 0x1790`, etc.) can be matched when descriptors match those numbers.

The code modifications themselves are also intriguing. There's a lot of focus on PHY and Phylink integration, like `ax88179a_init_mdio`, `ax88179a_init_phy`, `ax88179a_phylink_setup`, and modifications to `phy_ethtool_ksettings_set` and `phylink_mac_interrupt`. That implies interactions with the PHY layer and interrupt handling, which are always good targets. I'm also noticing the changes in `ax88179a_rx_fixup` and `ax88179a_tx_fixup` functions, they are looking at the packet buffers in receive and transmit paths, which is always interesting.

Considering the "REACHABILITY IS THE PRIMARY GATE" principle, and the explicit mention of virtual buses like USB gadget in the positive criteria for fuzzing worthiness, I'm leaning heavily towards "WorthFuzzing=true". The patch is making changes to executable code that is accessible through standard syzkaller configurations, specifically the USB gadget emulation.

To be absolutely sure, I'll quickly check with `grepper` to confirm that `USB_NET_AX88179` (and `USB_NET_DRIVERS`) are either enabled by default in syzkaller or are easily configurable. If so, it's a definite go!



4/2 2026/10/04 05:43 tool grepper
0m Args:
{
  "Expression": "config USB_NET_AX88179",
  "PathPrefix": "drivers/net/usb/Kconfig"
}

Results:
{
  "Output": "drivers/net/usb/Kconfig=166=config USB_NET_AX8817X\n--\ndrivers/net/usb/Kconfig-197-\ndrivers/net/usb/Kconfig:198:config USB_NET_AX88179\ndrivers/net/usb/Kconfig-199-\ttristate \"ASIX AX88179/179A/178A USB 2.0/3.0 to 100M/1G/2.5GbE Adapters\"\n"
}

5/2 2026/10/04 05:44 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 53148
							output: 36
							thoughts: 29
						

Thoughts:
Here's a summary of my thought process:

**Diving into the USB Networking Configuration**

Alright, let's get down to brass tacks. I need to understand the dependencies and selections within the USB networking configuration. My first step is to focus on `drivers/net/usb/Kconfig`, specifically around line 198. That's the likely location where the core options and their interactions are defined. I'll need to carefully examine what that section depends on – are there prerequisite modules or features that need to be enabled before these USB networking components become available? And just as importantly, what other options does this section *select*? Are there downstream modules or drivers that are automatically brought in or configured based on this setting? Understanding these relationships is crucial for correctly configuring the USB networking subsystem and avoiding any unexpected conflicts or missing functionality.



6/2 2026/10/04 05:44 tool read-file
0m Args:
{
  "File": "drivers/net/usb/Kconfig",
  "FirstLine": 195,
  "LineCount": 35
}

Results:
{
  "Contents": " 195:\t\t  This driver creates an interface named \"ethX\", where X depends on\n 196:\t\t  what other networking devices you have in use.\n 197:\t\n 198:\tconfig USB_NET_AX88179\n 199:\t\ttristate \"ASIX AX88179/179A/178A USB 2.0/3.0 to 100M/1G/2.5GbE Adapters\"\n 200:\t\tdepends on USB_USBNET\n 201:\t\tselect PHYLINK\n 202:\t\tselect AX88796B_PHY\n 203:\t\tselect CRC32\n 204:\t\tdefault y\n 205:\t\thelp\n 206:\t\t  This option adds support for ASIX AX88179 based USB 3.0/2.0\n 207:\t\t  to Gigabit Ethernet adapters.\n 208:\t\n 209:\t\t  This driver should work with at least the following devices:\n 210:\t\t    * ASIX AX88179\n 211:\t\t    * ASIX AX88179A/B\n 212:\t\t    * ASIX AX88279\n 213:\t\t    * ASIX AX88772D/E\n 214:\t\t    * ASIX AX88178A\n 215:\t\t    * Sitcomm LN-032\n 216:\t\n 217:\t\t  This driver creates an interface named \"ethX\", where X depends on\n 218:\t\t  what other networking devices you have in use.\n 219:\t\n 220:\tconfig USB_NET_CDCETHER\n 221:\t\ttristate \"CDC Ethernet support (smart devices such as cable modems)\"\n 222:\t\tdepends on USB_USBNET\n 223:\t\tdefault y\n 224:\t\thelp\n 225:\t\t  This option supports devices conforming to the Communication Device\n 226:\t\t  Class (CDC) Ethernet Control Model, a specification that's easy to\n 227:\t\t  implement in device firmware.  The CDC specifications are available\n 228:\t\t  from \u003chttp://www.usb.org/\u003e.\n 229:\t\n"
}

7/2 2026/10/04 05:44 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 53975
							output: 34
							thoughts: 23
						

Thoughts:
Here's a summary of my thought process:

**USB Network Driver Dependency Check**

Okay, time to dive into this. My initial thought is to zero in on the `USB_USBNET` configuration option. To understand its dependencies, I need to open up `drivers/net/usb/Kconfig`. That's where I'll find the dependencies defined. This should give me a clear picture of what other kernel features, drivers, or modules need to be enabled or are required for `USB_USBNET` to function correctly. This is standard procedure to confirm its availability. I expect to see the standard dependencies around USB subsystem, network subsystem, and any specific USB network controller support. Let's see what the Kconfig file reveals.



8/2 2026/10/04 05:44 tool grepper
0m Args:
{
  "Expression": "config USB_USBNET",
  "PathPrefix": "drivers/net/usb/Kconfig"
}

Results:
{
  "Output": "drivers/net/usb/Kconfig=113=config USB_LAN78XX\n--\ndrivers/net/usb/Kconfig-131-\ndrivers/net/usb/Kconfig:132:config USB_USBNET\ndrivers/net/usb/Kconfig-133-\ttristate \"Multi-purpose USB Networking Framework\"\n"
}

9/2 2026/10/04 05:44 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 54284
							output: 36
							thoughts: 24
						

Thoughts:
Here's a summary of my thought process, as requested:

**Navigating the USB Network Driver Configuration**

Okay, let's dive into this. To get a handle on this USB network driver configuration, I should immediately look at lines 125-145 of the `drivers/net/usb/Kconfig` file. That specific section usually contains the core configuration options for a particular USB network driver or feature. I'm expecting to find the standard `config` statements, `tristate` or `bool` options, likely with dependencies specified using `depends on` clauses. I'll need to pay close attention to the descriptions associated with each option to understand the underlying functionality. I'm also ready for any unusual dependencies or tricky interactions between the different options. I anticipate that this section will provide the key pieces of information to understand the USB networking setup in this part of the codebase.



10/2 2026/10/04 05:44 tool read-file
0m Args:
{
  "File": "drivers/net/usb/Kconfig",
  "FirstLine": 125,
  "LineCount": 25
}

Results:
{
  "Contents": " 125:\t\t  LAN7801 : USB 3 to 10/100/1000 Ethernet adapter (MAC only)\n 126:\t\n 127:\t\t  Proper PHY driver is required for LAN7801.\n 128:\t\n 129:\t\t  To compile this driver as a module, choose M here: the\n 130:\t\t  module will be called lan78xx.\n 131:\t\n 132:\tconfig USB_USBNET\n 133:\t\ttristate \"Multi-purpose USB Networking Framework\"\n 134:\t\tselect MII\n 135:\t\thelp\n 136:\t\t  This driver supports several kinds of network links over USB,\n 137:\t\t  with \"minidrivers\" built around a common network driver core\n 138:\t\t  that supports deep queues for efficient transfers.  (This gives\n 139:\t\t  better performance with small packets and at high speeds).\n 140:\t\n 141:\t\t  The USB host runs \"usbnet\", and the other end of the link might be:\n 142:\t\n 143:\t\t  - Another USB host, when using USB \"network\" or \"data transfer\"\n 144:\t\t    cables.  These are often used to network laptops to PCs, like\n 145:\t\t    \"Laplink\" parallel cables or some motherboards.  These rely\n 146:\t\t    on specialized chips from many suppliers.\n 147:\t\n 148:\t\t  - An intelligent USB gadget, perhaps embedding a Linux system.\n 149:\t\t    These include PDAs running Linux (iPaq, Yopy, Zaurus, and\n"
}

11/2 2026/10/04 05:44 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 54965
							output: 34
							thoughts: 25
						

Thoughts:
Okay, here's my take on those thoughts, tailored for an expert audience, and written as if they were my own:

**Investigating USB Network Driver Dependencies**

Alright, so `USB_USBNET` is acting up, and I need to figure out the root cause. My first instinct, given my experience, is to immediately check the configuration. This means I need to quickly establish the dependencies of `USB_USBNET`. Let's confirm `menuconfig` and the status of `USB_NET_DRIVERS`. I'm mentally filing this under the broader category of debugging network drivers, but this initial step will reveal the basic enablement status. Is the underlying support even *there*? Gotta start with the fundamentals. Then I can go from there.



12/2 2026/10/04 05:44 tool read-file
0m Args:
{
  "File": "drivers/net/usb/Kconfig",
  "FirstLine": 1,
  "LineCount": 40
}

Results:
{
  "Contents": "   1:\t# SPDX-License-Identifier: GPL-2.0-only\n   2:\t#\n   3:\t# USB Network devices configuration\n   4:\t#\n   5:\tcomment \"Host-side USB support is needed for USB Network Adapter support\"\n   6:\t\tdepends on !USB \u0026\u0026 NET\n   7:\t\n   8:\tmenuconfig USB_NET_DRIVERS\n   9:\t\ttristate \"USB Network Adapters\"\n  10:\t\tdefault USB if USB\n  11:\t\tdepends on USB \u0026\u0026 NET\n  12:\t\n  13:\tif USB_NET_DRIVERS\n  14:\t\n  15:\tconfig USB_CATC\n  16:\t\ttristate \"USB CATC NetMate-based Ethernet device support\"\n  17:\t\tselect CRC32\n  18:\t\thelp\n  19:\t\t  Say Y if you want to use one of the following 10Mbps USB Ethernet\n  20:\t\t  device based on the EL1210A chip. Supported devices are:\n  21:\t\t  Belkin F5U011\n  22:\t\t  Belkin F5U111\n  23:\t\t  CATC NetMate\n  24:\t\t  CATC NetMate II\n  25:\t\t  smartBridges smartNIC\n  26:\t\n  27:\t\t  This driver makes the adapter appear as a normal Ethernet interface,\n  28:\t\t  typically on eth0, if it is the only ethernet device, or perhaps on\n  29:\t\t  eth1, if you have a PCI or ISA ethernet card installed.\n  30:\t\n  31:\t\t  To compile this driver as a module, choose M here: the\n  32:\t\t  module will be called catc.\n  33:\t\n  34:\tconfig USB_KAWETH\n  35:\t\ttristate \"USB KLSI KL5USB101-based ethernet device support\"\n  36:\t\thelp\n  37:\t\t  Say Y here if you want to use one of the following 10Mbps only\n  38:\t\t  USB Ethernet adapters based on the KLSI KL5KUSB101B chipset:\n  39:\t\t  3Com 3C19250\n  40:\t\t  ADS USB-10BT\n"
}

13/2 2026/10/04 05:44 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 55747
							output: 38
							thoughts: 105
						

Thoughts:
**Analyzing USB Network Driver Dependencies**

Okay, so I'm looking at these kernel configuration options and how they relate to each other. It's pretty straightforward, but I want to make sure I'm not missing any implications.

First, `USB_NET_DRIVERS` is clearly contingent on both `USB` and `NET` being enabled. Makes perfect sense, it's a foundation.

Then, `USB_USBNET` depends on `USB_NET_DRIVERS`. Again, logical. This is the specific USB network driver functionality that leverages the generic USB net infrastructure.

Finally, `USB_NET_AX88179` relies on `USB_USBNET`. Now, this is the driver for a specific USB to Ethernet adapter, the AX88179. It also *selects* `PHYLINK`, `AX88796B_PHY`, and `CRC32`. That selection is what I want to verify, let me make sure that those PHY drivers, `PHYLINK` and `AX88796B_PHY`, are compatible or supported as expected. Time to jump over to `drivers/net/phy/Kconfig` to confirm that the `AX88796B_PHY` option is valid, and look at any associated dependencies.



14/2 2026/10/04 05:44 tool grepper
0m Args:
{
  "Expression": "config AX88796B_PHY",
  "PathPrefix": "drivers/net/phy/Kconfig"
}

Results:
{
  "Output": "drivers/net/phy/Kconfig=152=source \"drivers/net/phy/aquantia/Kconfig\"\ndrivers/net/phy/Kconfig-153-\ndrivers/net/phy/Kconfig:154:config AX88796B_PHY\ndrivers/net/phy/Kconfig-155-\ttristate \"Asix PHYs\"\n"
}

15/2 2026/10/04 05:44 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 56275
							output: 36
							thoughts: 24
						

Thoughts:
Okay, here's my take on those thoughts, tailored for an expert audience:

**Diving into `drivers/net/phy/Kconfig`**

Alright, time to get to the heart of the matter. My mental checklist says to immediately look at lines 154-165 within the `drivers/net/phy/Kconfig` file. I'm assuming that range probably contains the key configurations I'm interested in for the current issue or feature I'm examining. I expect it to either be defining a new PHY or modifying the existing ones. This section almost certainly concerns a specific PHY driver, or a group of drivers, based on the file and line number. I will probably find some of the important selection of options there, maybe conditional dependencies, or even platform-specific settings that I can quickly glean to understand the overall picture. I'm hoping it's well-structured – the way it's laid out will give me insights into how the maintainers thought about modularity, dependencies, and potential conflicts. Let's see what gems are hidden there.



16/2 2026/10/04 05:44 tool read-file
0m Args:
{
  "File": "drivers/net/phy/Kconfig",
  "FirstLine": 154,
  "LineCount": 15
}

Results:
{
  "Contents": " 154:\tconfig AX88796B_PHY\n 155:\t\ttristate \"Asix PHYs\"\n 156:\t\thelp\n 157:\t\t  Currently supports the Asix Electronics PHY found in the X-Surf 100\n 158:\t\t  AX88796B package.\n 159:\t\n 160:\tconfig AX88796B_RUST_PHY\n 161:\t\tbool \"Rust reference driver for Asix PHYs\"\n 162:\t\tdepends on RUST_PHYLIB_ABSTRACTIONS \u0026\u0026 AX88796B_PHY\n 163:\t\thelp\n 164:\t\t  Uses the Rust reference driver for Asix PHYs (ax88796b_rust.ko).\n 165:\t\t  The features are equivalent. It supports the Asix Electronics PHY\n 166:\t\t  found in the X-Surf 100 AX88796B package.\n 167:\t\n 168:\tconfig BROADCOM_PHY\n"
}

17/2 2026/10/04 05:44 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 56837
							output: 34
							thoughts: 33
						

Thoughts:
Okay, here's my take on those thoughts, tailored for an expert audience:

**Analyzing the AX88796B PHY Driver Build Process**

Alright, let's dive into the specifics of how the `ax88796b.c` driver gets built. I need to figure out the build process. My first stop is going to be `drivers/net/phy/Makefile`. That's where I expect to find the rules that govern the inclusion and compilation of this particular PHY driver. I need to understand any conditional compilation, dependencies on other modules, and build flags that might be in play. This will give me a strong foundation for debugging potential issues, modifying the driver, or simply understanding its integration within the larger network subsystem. Understanding the makefile is crucial to understanding the driver's place in the broader kernel ecosystem.



18/2 2026/10/04 05:44 tool grepper
0m Args:
{
  "Expression": "ax88796b",
  "PathPrefix": "drivers/net/phy/Makefile"
}

Results:
{
  "Output": "drivers/net/phy/Makefile=40=ifdef CONFIG_AX88796B_RUST_PHY\ndrivers/net/phy/Makefile:41:  obj-$(CONFIG_AX88796B_PHY)\t+= ax88796b_rust.o\ndrivers/net/phy/Makefile-42-else\ndrivers/net/phy/Makefile:43:  obj-$(CONFIG_AX88796B_PHY)\t+= ax88796b.o\ndrivers/net/phy/Makefile-44-endif\n"
}

19/2 2026/10/04 05:44 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 57226
							output: 31
							thoughts: 107
						

Thoughts:
**Examining the Network PHY Configuration**

Okay, so `CONFIG_AX88796B_PHY` compiles `ax88796b.o`. That much is clear. And, as I suspected, `USB_NET_AX88179` pulls in `AX88796B_PHY`. Now, I need to understand the larger context. My immediate next step is to examine the PHY-related code. Let's delve into `drivers/net/phy/phy.c` and `drivers/net/phy/phylink.c`. I need to figure out which build configuration controls these critical files. Time to check `drivers/net/phy/Makefile`. What are the dependencies that build those? Let's trace it back and see how these pieces fit together. I want to understand how these PHY components are integrated into the overall network stack.



20/2 2026/10/04 05:44 tool grepper
0m Args:
{
  "Expression": "phylink\\.o",
  "PathPrefix": "drivers/net/phy/Makefile"
}

Results:
{
  "Output": "drivers/net/phy/Makefile=18=obj-$(CONFIG_PHYLIB)\t\t+= mdio_bus.o\ndrivers/net/phy/Makefile:19:obj-$(CONFIG_PHYLINK)\t\t+= phylink.o\ndrivers/net/phy/Makefile-20-obj-$(CONFIG_PHYLIB)\t\t+= libphy.o\n"
}

21/2 2026/10/04 05:44 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 57646
							output: 36
							thoughts: 4195
						

Thoughts:
**Analysis of ASIX USB Ethernet Driver Patch for Fuzzing**

Alright, let's break this down. This patch is adding support for a handful of new ASIX USB Ethernet chips (AX88179A, AX88772D, AX88279). My initial thought is to identify if this is reachable code and if it's worth fuzzing. The key here is USB, which is a virtual bus. Virtual buses are prime targets for fuzzing, and this patch introduces new packet handling logic and USBnet binding ops, including complex functions like `ax88179a_rx_fixup` and `ax88179a_tx_fixup`. Bingo. That's a red flag!

Looking at `ax88179a_rx_fixup`, my expert intuition is screaming "possible memory corruption!" There's a lot of manual pointer arithmetic and structure offsets based on packet lengths and metadata. The interaction between `skb_trim` and subsequent pointer dereferences is a classic setup for an out-of-bounds read or write. Specifically, I see `skb_trim(skb, hdr_off)` followed by `pkt_desc_ptr = (u64 *)(skb->data + hdr_off)` and then a `skb_pull` operation within a loop that can easily get the indexing wrong. The integer underflow when calculating packet lengths is another potential vulnerability. All of this can be exploited.

Now, my experience says that USB network drivers are perfect targets for syzkaller. Syzkaller's USB pseudo-syscalls combined with the raw-gadget support is ideal for generating the fuzzing campaigns for it. The presence of functions like `syz_usb_connect` and `syz_usb_ep_write` makes fuzzing this code trivial. I have seen hundreds of CVEs in usbnet drivers found by syzkaller. The code is clearly reachable, executable, and involves drivers accessible via a virtual bus.

Therefore, `WorthFuzzing = true`.

For FocusSymbols, let's pinpoint the functions that implement the *core* logic and are entry points. I'm focusing on those specific to the new chips and functionalities: `ax88179a_rx_fixup`, `ax88179a_tx_fixup`, which directly deal with packet handling. `ax88179a_bind`, `ax88179a_reset`, `ax88179a_mac_link_up` are good entry points, too, since they control the overall driver operations. Also, `phylink_mac_interrupt` and `asix_ax88279_read_status` and `asix_ax88279_config_aneg` as they handle lower-level PHY interactions. I will include `ax88179_get_eeprom`, `ax88179_set_eeprom`, `ax88179_set_multicast`, `ax88179_set_features`, `ax88179_change_mtu`, `ax88179_set_mac_addr` since they are non-static calls across files.

These functions should be directly targeted because they represent the *new* core functionality and would most likely expose new bugs.

Finally, for EnableConfigs, the Kconfig dependencies are pretty straightforward. Enabling `USB_NET_AX88179` automatically selects `PHYLINK`, `AX88796B_PHY`, and `CRC32`, so we need: `USB_NET_AX88179`, which also selects: `USB_USBNET`, `USB_NET_DRIVERS`, `USB`, `NET`.



22/2 2026/10/04 05:44 tool grepper
0m Args:
{
  "Expression": "config AX88796B_PHY",
  "PathPrefix": "drivers/net/phy/"
}

Results:
{
  "Output": "drivers/net/phy/Kconfig=152=source \"drivers/net/phy/aquantia/Kconfig\"\ndrivers/net/phy/Kconfig-153-\ndrivers/net/phy/Kconfig:154:config AX88796B_PHY\ndrivers/net/phy/Kconfig-155-\ttristate \"Asix PHYs\"\n"
}

23/2 2026/10/04 05:44 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 62804
							output: 34
							thoughts: 47
						

Thoughts:
Okay, here's my take on those thoughts, tailored for an expert audience:

**Investigating Conditional Compilation in `drivers/net/phy/Kconfig`**

Alright, let's dive into this. My initial instinct is to pinpoint the exact conditional compilation directives affecting `config AX88796B_PHY` within `drivers/net/phy/Kconfig`. Specifically, I need to understand whether this PHY is directly selectable or subject to an enclosing `if` or `menu` block. The goal is to determine the preconditions or dependencies that govern its inclusion in the build. Thus, I'll be meticulously examining the lines *preceding* line 152 in that Kconfig file. I'm looking for the defining logic. This process is fairly routine - it is a common debugging practice when investigating kernel configuration interactions. Ultimately, I am trying to determine what other options need to be enabled in order for `AX88796B_PHY` to also be enabled.



24/2 2026/10/04 05:44 tool read-file
0m Args:
{
  "File": "drivers/net/phy/Kconfig",
  "FirstLine": 1,
  "LineCount": 40
}

Results:
{
  "Contents": "   1:\t# SPDX-License-Identifier: GPL-2.0-only\n   2:\t#\n   3:\t# PHY Layer Configuration\n   4:\t#\n   5:\t\n   6:\tconfig PHYLINK\n   7:\t\ttristate\n   8:\t\tselect PHYLIB\n   9:\t\tselect SWPHY\n  10:\t\thelp\n  11:\t\t  PHYlink models the link between the PHY and MAC, allowing fixed\n  12:\t\t  configuration links, PHYs, and Serdes links with MAC level\n  13:\t\t  autonegotiation modes.\n  14:\t\n  15:\tmenuconfig PHYLIB\n  16:\t\ttristate \"PHY Device support and infrastructure\"\n  17:\t\thelp\n  18:\t\t  Ethernet controllers are usually attached to PHY\n  19:\t\t  devices.  This option provides infrastructure for\n  20:\t\t  managing PHY devices.\n  21:\t\n  22:\tif PHYLIB\n  23:\t\n  24:\tconfig SWPHY\n  25:\t\tbool\n  26:\t\n  27:\tconfig PHY_PACKAGE\n  28:\t\t tristate\n  29:\t\n  30:\tconfig LED_TRIGGER_PHY\n  31:\t\tbool \"Support LED triggers for tracking link state\"\n  32:\t\tdepends on LEDS_TRIGGERS\n  33:\t\thelp\n  34:\t\t  Adds support for a set of LED trigger events per-PHY.  Link\n  35:\t\t  state change will trigger the events, for consumption by an\n  36:\t\t  LED class driver.  There are triggers for each link speed currently\n  37:\t\t  supported by the PHY and also a one common \"link\" trigger as a\n  38:\t\t  logical-or of all the link speed ones.\n  39:\t\t  All these triggers are named according to the following pattern:\n  40:\t\t      \u003cmii bus id\u003e:\u003cphy\u003e:\u003cspeed\u003e\n"
}

25/2 2026/10/04 05:44 llm patch-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 63576
							output: 408
							thoughts: 1156
						

Thoughts:
**Configuration Analysis for USB Ethernet Controller Fuzzing**

Okay, let's break this down. First, I need to confirm the correct configuration options to enable the target USB Ethernet controller and associated PHYs for fuzzing purposes. It seems that `AX88796B_PHY` is nested under `if PHYLIB`, but `PHYLINK` (which selects `PHYLIB`) is defined outside that conditional in the Kconfig files. This is important to note to make sure all related modules are included. I need to be sure the dependencies are met. Specifically, I'm verifying that enabling `USB_NET_AX88179` *should* indirectly enable `PHYLINK`, which in turn enables `PHYLIB` and `AX88796B_PHY`, and I'm confident that's the correct path. I've double-checked that `PHYLINK` does indeed select `PHYLIB`, which is key.

To be extra certain, I need to list the required configurations for syzkaller, ensuring they're spelled correctly and that nothing crucial is missing: `USB_NET_AX88179`, `USB_USBNET`, `USB_NET_DRIVERS`, `AX88796B_PHY`, `PHYLINK`, and `PHYLIB`. I've made sure to verify the correct spelling to avoid any wasted effort.

Now, let's explore for other options. From the Makefiles, I see how these configurations translate into compiled objects. Critically, for `AX88796B_PHY`, I need to make sure `CONFIG_AX88796B_RUST_PHY` is *not* enabled to get the correct object file (`ax88796b.o`). This will matter for what code is actually executed.

Now, the focus here is to define the reachability of the symbols. Let's make sure I've identified the key symbols within this driver that are good fuzzing targets: `ax88179a_rx_fixup`, `ax88179a_tx_fixup`, `ax88179a_bind`, `ax88179a_reset`, `ax88179a_mac_link_up`, `ax88179_set_features`, `phylink_mac_interrupt`, `asix_ax88279_read_status`, and `asix_ax88279_config_aneg`. I'm confirming that these are specific to the relevant hardware and driver, not some generic networking function. I need to check to see what each of these calls is capable of.

Finally, I'm solidifying my rationale. This patch adds support for the newer ASIX controllers, refactoring code, and importantly, adds PHY support.  I need to emphasize that USB Ethernet drivers are reachable in virtualized environments like QEMU with syzkaller using `USB_RAW_GADGET` or `USB_DUMMY_HCD`. This will give syzkaller the capability to send packets, use `ethtool`, and trigger the relevant `netdev` operations, specifically on the `rx/tx` paths. This is important because the `rx` and `tx` fixup routines, such as `ax88179a_rx_fixup` and `ax88179a_tx_fixup`, involve parsing untrusted USB input.  This packet parsing and descriptor manipulation makes them excellent fuzzing targets.

Therefore, because this code is reachable and involves parsing potentially malicious input, it’s worth fuzzing (`WorthFuzzing=true`). I am now confident in my assessment and ready to proceed.



26/2 2026/10/04 05:45 tool set-results
0m Args:
{
  "EnableConfigs": [
    "USB_NET_AX88179",
    "USB_USBNET",
    "USB_NET_DRIVERS",
    "AX88796B_PHY",
    "PHYLINK",
    "PHYLIB"
  ],
  "FocusSymbols": [
    "ax88179a_rx_fixup",
    "ax88179a_tx_fixup",
    "ax88179a_bind",
    "ax88179a_reset",
    "ax88179a_mac_link_up",
    "ax88179_set_features",
    "phylink_mac_interrupt",
    "asix_ax88279_read_status",
    "asix_ax88279_config_aneg"
  ],
  "Reasoning": "The patch series introduces support for ASIX AX88179A, AX88772D, and AX88279 USB Ethernet adapters. It adds new device driver logic in drivers/net/usb/ax88179a_devices.c, refactors shared functionality into ax88179_lib.c, adds new PHY drivers in drivers/net/phy/ax88796b.c, and adds phylink_mac_interrupt(). These USB network devices are reachable in virtualized environments through syzkaller's USB emulation (raw-gadget/dummy_hcd). The new driver logic includes complex packet descriptor processing and skb manipulation in ax88179a_rx_fixup() and ax88179a_tx_fixup(), as well as netdev, ethtool, and MDIO/phylink callbacks, making it prime for fuzzing.",
  "WorthFuzzing": true
}

Results:
{
  "EnableConfigs": [
    "USB_NET_AX88179",
    "USB_USBNET",
    "USB_NET_DRIVERS",
    "AX88796B_PHY",
    "PHYLINK",
    "PHYLIB"
  ],
  "FocusSymbols": [
    "ax88179a_rx_fixup",
    "ax88179a_tx_fixup",
    "ax88179a_bind",
    "ax88179a_reset",
    "ax88179a_mac_link_up",
    "ax88179_set_features",
    "phylink_mac_interrupt",
    "asix_ax88279_read_status",
    "asix_ax88279_config_aneg"
  ],
  "Reasoning": "The patch series introduces support for ASIX AX88179A, AX88772D, and AX88279 USB Ethernet adapters. It adds new device driver logic in drivers/net/usb/ax88179a_devices.c, refactors shared functionality into ax88179_lib.c, adds new PHY drivers in drivers/net/phy/ax88796b.c, and adds phylink_mac_interrupt(). These USB network devices are reachable in virtualized environments through syzkaller's USB emulation (raw-gadget/dummy_hcd). The new driver logic includes complex packet descriptor processing and skb manipulation in ax88179a_rx_fixup() and ax88179a_tx_fixup(), as well as netdev, ethtool, and MDIO/phylink callbacks, making it prime for fuzzing.",
  "WorthFuzzing": true
}

27/1 2026/10/04 05:45 action If
1m Args:
{
  "WorthFuzzing": true
}

Results:
null

28/2 2026/10/04 05:45 agent kmsan-evaluator
1m Model:
core

Args:
null

Results:
{
  "KMSANReasoning": "The patch series refactors the ASIX AX88179/178A USB Ethernet driver into shared library components (`ax88179_lib.c`, `ax88179_lib.h`) and adds support for newer ASIX chip variants (AX88179A, AX88772D, AX88279) with phylink integration in `ax88179a_devices.c` and `ax88796b.c`.\n\nKMSAN vs KASAN / LOCKDEP analysis:\n1. Memory allocations: The private driver context `struct ax88179_data` is allocated using `kzalloc_obj` in `ax88179a_bind()` and `kzalloc` in `ax88179_bind()`. The MDIO bus is allocated via `mdiobus_alloc()` (zero-initialized). No struct members are left uninitialized.\n2. RX / TX path: In `ax88179a_rx_fixup()`, newly allocated skbs are populated via `skb_put()` and `memcpy()` for the exact packet length, avoiding uninitialized payload bytes. In `ax88179a_tx_fixup()`, any padding bytes are zeroed with `skb_put_zero()` and the 64-bit descriptor is initialized via `put_unaligned_le64()`.\n3. User-space copies / info-leaks: Ethtool handlers like `ax88179_get_eeprom()` allocate buffers using `kzalloc()` and strictly check that the read length matches before copying to the caller buffer; `ax88179a_get_drvinfo()` formats strings using `snprintf()`. No kernel stack or uninitialized heap padding is exposed to user space.\n4. Other bug detectors: Potential concurrency or locking issues (e.g. `rtnl_lock()` in `ax88179_change_mtu()`) are covered by LOCKDEP. Buffer boundary issues, packet parsing overflows, and use-after-free risks are covered by standard KASAN.\n\nThere are no uninitialized memory reads or information leak risks that would uniquely warrant a KMSAN fuzzing session.",
  "NeedsKMSAN": false
}

Instruction:
You are an expert Linux kernel security engineer specializing in kernel memory
error detectors (KASAN and KMSAN). Your job is to review the provided patch series and
determine if the code changes justify spawning a dedicated KMSAN (KernelMemorySanitizer)
fuzzing session in addition to standard KASAN fuzzing.

CRITICAL DISTINCTION BETWEEN KASAN AND KMSAN:
- Standard KASAN kernel builds (upstream-apparmor-kasan.config) already enable
  a comprehensive suite of debugging tools and sanitizers, including KASAN
  (out-of-bounds accesses, use-after-free, double free, invalid free), LOCKDEP
  (locking bugs and deadlocks), UB-sanitizers, and memory corruption checks.
- KMSAN (KernelMemorySanitizer) detects reads of UNINITIALIZED memory (stack, heap,
  or page allocations) and kernel-to-user memory info-leaks.

Rule: THERE IS NO SENSE IN RUNNING A KMSAN SESSION IF A BUG CAN BE CAUGHT BY KASAN,
LOCKDEP, OR OTHER STANDARD BUG DETECTORS.
A dedicated KMSAN fuzzing session incurs significant resource costs. You must ONLY
set NeedsKMSAN=true if the code changes introduce or expose UNINITIALIZED MEMORY risks
that are detected ONLY by KMSAN.

Look holistically at the patch series and surrounding code. Even if no direct
uninitialized field accesses or new buffer allocations are added in the diff itself,
a patch may alter control flow, bounds checking, or data length calculations in ways
that change how the rest of the code operates on existing buffers (e.g. allowing
uninitialized stack/heap memory to be read, copied to user space, or used in control
flow). Do not hesitate to use your code access tools to inspect the surrounding code,
called functions, and callers.

Set NeedsKMSAN=true ONLY IF the patch introduces or modifies:
1. Kernel structures sent to user space (via copy_to_user, put_user, netlink skb
   attributes, ioctl output arguments, socket options, or BPF buffers) where fields
   or structure padding might not be fully initialized/zeroed.
2. Conditional logic or branching that depends on potentially uninitialized variables
   or struct fields.
3. Allocation or initialization of complex data structures where uninitialized fields
   could be read later in reachable code paths.
4. Bounds checks, lengths, or logic in a way that allows surrounding code to access
   uninitialized bytes of existing buffers.

Set NeedsKMSAN=false IF:
- The code changes primarily risk out-of-bounds access, array overflows, NULL pointer
  dereferences, locking deadlocks, or use-after-free bugs (these are already caught
  by KASAN, LOCKDEP, or standard bug detectors).
- All stack/heap structures touched or introduced by the patch are fully zeroed
  or initialized (e.g. using = {0}, memset, kzalloc) before being read or copied.
- The patch does not introduce any risk of uninitialized memory usage or info-leaks.

Use your code access tools to inspect the surrounding code if necessary, then provide
detailed KMSANReasoning contrasting KASAN vs KMSAN applicability for this patch.
Prefer calling several tools at the same time to save round-trips.


Use set-results tool to provide results of the analysis.
It must be called exactly once before the final reply.
Ignore results of this tool.

Prompt:
Target architecture: amd64

For your convenience, here is the diff of the changes:
commit aaa4065a354c1790e97c05295c45e12c578b32d1
Author: syz-cluster <triage@syzkaller.com>
Date:   Sun Oct 4 05:43:49 2026 +0000

    syz-cluster: applied patch under review

diff --git a/MAINTAINERS b/MAINTAINERS
index 3011f995437fd..7464e87d41ef8 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4020,6 +4020,12 @@ S:	Maintained
 F:	Documentation/devicetree/bindings/net/asix,ax88796c.yaml
 F:	drivers/net/ethernet/asix/ax88796c_*
 
+ASIX AX88179 USB ETHERNET DRIVER
+M:	Birger Koblitz <mail@birger-koblitz.de>
+L:	netdev@vger.kernel.org
+S:	Maintained
+F:	drivers/net/usb/ax88179*
+
 ASIX PHY DRIVER [RUST]
 M:	FUJITA Tomonori <fujita.tomonori@gmail.com>
 R:	Trevor Gross <tmgross@umich.edu>
diff --git a/arch/loongarch/configs/loongson32_defconfig b/arch/loongarch/configs/loongson32_defconfig
index dbef879935f32..a039b8e8d2420 100644
--- a/arch/loongarch/configs/loongson32_defconfig
+++ b/arch/loongarch/configs/loongson32_defconfig
@@ -641,7 +641,7 @@ CONFIG_USB_RTL8150=m
 CONFIG_USB_RTL8152=m
 CONFIG_USB_USBNET=m
 # CONFIG_USB_NET_AX8817X is not set
-# CONFIG_USB_NET_AX88179_178A is not set
+# CONFIG_USB_NET_AX88179 is not set
 CONFIG_USB_NET_CDC_EEM=m
 CONFIG_USB_NET_HUAWEI_CDC_NCM=m
 CONFIG_USB_NET_CDC_MBIM=m
diff --git a/arch/loongarch/configs/loongson64_defconfig b/arch/loongarch/configs/loongson64_defconfig
index 4f8bb77b8a1e6..b30415c03fa18 100644
--- a/arch/loongarch/configs/loongson64_defconfig
+++ b/arch/loongarch/configs/loongson64_defconfig
@@ -657,7 +657,7 @@ CONFIG_USB_RTL8150=m
 CONFIG_USB_RTL8152=m
 CONFIG_USB_USBNET=m
 # CONFIG_USB_NET_AX8817X is not set
-# CONFIG_USB_NET_AX88179_178A is not set
+# CONFIG_USB_NET_AX88179 is not set
 CONFIG_USB_NET_CDC_EEM=m
 CONFIG_USB_NET_HUAWEI_CDC_NCM=m
 CONFIG_USB_NET_CDC_MBIM=m
diff --git a/drivers/net/phy/ax88796b.c b/drivers/net/phy/ax88796b.c
index f20ddf6491499..0f1437d1e3e9b 100644
--- a/drivers/net/phy/ax88796b.c
+++ b/drivers/net/phy/ax88796b.c
@@ -13,6 +13,19 @@
 #define PHY_ID_ASIX_AX88772A		0x003b1861
 #define PHY_ID_ASIX_AX88772C		0x003b1881
 #define PHY_ID_ASIX_AX88796B		0x003b1841
+#define PHY_ID_ASIX_AX88772D		0x003b772d
+#define PHY_ID_ASIX_AX88179A		0x003b179a
+#define PHY_ID_ASIX_AX88279		0x003b2790
+
+#define AX_ADVERTISE_2500		0x1000
+
+/* MII Vendor registers */
+#define AX_CTRL_STATUS			0x1d
+#define AX_CTRL_STATUS_SPEED_MASK	0x0c
+#define   AX_CTRL_STATUS_SPEED_10	0x0
+#define   AX_CTRL_STATUS_SPEED_100	0x4
+#define   AX_CTRL_STATUS_SPEED_1000	0x8
+#define   AX_CTRL_STATUS_SPEED_2500	0xc
 
 MODULE_DESCRIPTION("Asix PHY driver");
 MODULE_AUTHOR("Michael Schmitz <schmitzmic@gmail.com>");
@@ -83,6 +96,110 @@ static int asix_ax88772a_read_status(struct phy_device *phydev)
 	return 0;
 }
 
+static int asix_ax88279_read_status(struct phy_device *phydev)
+{
+	int ret, val;
+
+	ret = genphy_read_status(phydev);
+	if (ret)
+		return ret;
+
+	if (!phydev->link)
+		return 0;
+
+	/* Read actual speed from vendor register */
+	val = phy_read(phydev, AX_CTRL_STATUS);
+	if (val < 0)
+		return val;
+
+	switch (val & AX_CTRL_STATUS_SPEED_MASK) {
+	case AX_CTRL_STATUS_SPEED_2500:
+		phydev->speed = SPEED_2500;
+		break;
+	case AX_CTRL_STATUS_SPEED_1000:
+		phydev->speed = SPEED_1000;
+		break;
+	case AX_CTRL_STATUS_SPEED_100:
+		phydev->speed = SPEED_100;
+		break;
+	case AX_CTRL_STATUS_SPEED_10:
+		phydev->speed = SPEED_10;
+	}
+
+	/* MDIO_AN_10GBT_STAT_LP2_5G is broken, but we can deduce that
+	 * the link-partner advertised 2500M if remotely AN succceded
+	 * for link speed > 1000M and we locally have a link speed of
+	 * 2500M
+	 */
+	linkmode_clear_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->lp_advertising);
+	val = phy_read_mmd(phydev, MDIO_MMD_AN, MDIO_AN_10GBT_STAT);
+	if (val >= 0 && val & MDIO_AN_10GBT_STAT_REMOK) {
+		linkmode_mod_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT,
+				 phydev->lp_advertising,
+				 phydev->speed == SPEED_2500);
+	}
+	/* Only supports full duplex */
+	phydev->duplex = DUPLEX_FULL;
+
+	/* PHY switches interface between 2.5GBit and slower modes */
+	if (phydev->speed == SPEED_2500)
+		phydev->interface = PHY_INTERFACE_MODE_2500BASEX;
+	else
+		phydev->interface = PHY_INTERFACE_MODE_SGMII;
+
+	return 0;
+}
+
+static int asix_ax88279_config_aneg(struct phy_device *phydev)
+{
+	bool adv_2500;
+	int ret;
+
+	if (phydev->autoneg == AUTONEG_DISABLE) {
+		phydev_warn(phydev, "Disabling autoneg is not supported\n");
+		return -EOPNOTSUPP;
+	}
+
+	ret = genphy_config_aneg(phydev);
+
+	if (ret < 0)
+		return ret;
+
+	adv_2500 = linkmode_test_bit(ETHTOOL_LINK_MODE_2500baseT_Full_BIT, phydev->advertising);
+	ret = phy_modify(phydev, MII_ADVERTISE, AX_ADVERTISE_2500,
+			 adv_2500 ? AX_ADVERTISE_2500 : 0);
+
+	return ret;
+}
+
+static int asix_ax88279_get_features(struct phy_device *phydev)
+{
+	int ret;
+
+	/* MDIO_DEVS1/2 empty, so set mmds_present bits to allow reading abilities */
+	phydev->c45_ids.mmds_present |= MDIO_DEVS_PMAPMD | MDIO_DEVS_AN;
+
+	linkmode_set_bit_array(phy_basic_ports_array, ARRAY_SIZE(phy_basic_ports_array),
+			       phydev->supported);
+
+	ret = genphy_c45_pma_read_abilities(phydev);
+	if (ret < 0)
+		return ret;
+
+	/* AX88279 does not support reported 100baseT-half duplex mode */
+	linkmode_clear_bit(ETHTOOL_LINK_MODE_100baseT_Half_BIT, phydev->supported);
+
+	return 0;
+}
+
+static int asix_ax88279_config_init(struct phy_device *phydev)
+{
+	__set_bit(PHY_INTERFACE_MODE_2500BASEX, phydev->possible_interfaces);
+	__set_bit(PHY_INTERFACE_MODE_SGMII, phydev->possible_interfaces);
+
+	return 0;
+}
+
 static void asix_ax88772a_link_change_notify(struct phy_device *phydev)
 {
 	/* Reset PHY, otherwise MII_LPA will provide outdated information.
@@ -94,6 +211,24 @@ static void asix_ax88772a_link_change_notify(struct phy_device *phydev)
 	}
 }
 
+static int asix_ax88772D_get_features(struct phy_device *phydev)
+{
+	int ret;
+	__ETHTOOL_DECLARE_LINK_MODE_MASK(mask) = {};
+
+	/* MDIO_DEVS1/2 empty, so set mmds_present bits to allow reading abilities */
+	phydev->c45_ids.mmds_present |= MDIO_DEVS_PMAPMD | MDIO_DEVS_AN;
+
+	ret = genphy_read_abilities(phydev);
+	if (ret < 0)
+		return ret;
+
+	/* AX88772D does not support reported 1000baseT mode */
+	linkmode_clear_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, phydev->supported);
+
+	return 0;
+}
+
 static struct phy_driver asix_driver[] = {
 {
 	PHY_ID_MATCH_EXACT(PHY_ID_ASIX_AX88772A),
@@ -111,6 +246,35 @@ static struct phy_driver asix_driver[] = {
 	.suspend	= genphy_suspend,
 	.resume		= genphy_resume,
 	.soft_reset	= asix_soft_reset,
+}, {
+	PHY_ID_MATCH_EXACT(PHY_ID_ASIX_AX88772D),
+	.name		= "Asix Electronics AX88772D",
+	.flags		= PHY_IS_INTERNAL,
+	.get_features	= asix_ax88772D_get_features,
+	.read_mmd	= genphy_read_mmd_c45,
+	.write_mmd	= genphy_write_mmd_c45,
+	.suspend	= genphy_suspend,
+	.resume		= genphy_resume,
+}, {
+	PHY_ID_MATCH_EXACT(PHY_ID_ASIX_AX88179A),
+	.name		= "Asix Electronics AX88179A",
+	.flags		= PHY_IS_INTERNAL,
+	.suspend	= genphy_suspend,
+	.resume		= genphy_resume,
+	.read_mmd	= genphy_read_mmd_c45,
+	.write_mmd	= genphy_write_mmd_c45,
+}, {
+	PHY_ID_MATCH_EXACT(PHY_ID_ASIX_AX88279),
+	.name		= "Asix Electronics AX88279",
+	.flags		= PHY_IS_INTERNAL | PHY_BROKEN_FORCED,
+	.get_features	= asix_ax88279_get_features,
+	.read_status	= asix_ax88279_read_status,
+	.config_aneg	= asix_ax88279_config_aneg,
+	.config_init	= asix_ax88279_config_init,
+	.read_mmd	= genphy_read_mmd_c45,
+	.write_mmd	= genphy_write_mmd_c45,
+	.suspend	= genphy_suspend,
+	.resume		= genphy_resume,
 }, {
 	PHY_ID_MATCH_MODEL(PHY_ID_ASIX_AX88796B),
 	.name		= "Asix Electronics AX88796B",
@@ -124,6 +288,9 @@ static const struct mdio_device_id __maybe_unused asix_tbl[] = {
 	{ PHY_ID_MATCH_EXACT(PHY_ID_ASIX_AX88772A) },
 	{ PHY_ID_MATCH_EXACT(PHY_ID_ASIX_AX88772C) },
 	{ PHY_ID_MATCH_MODEL(PHY_ID_ASIX_AX88796B) },
+	{ PHY_ID_MATCH_EXACT(PHY_ID_ASIX_AX88772D) },
+	{ PHY_ID_MATCH_EXACT(PHY_ID_ASIX_AX88179A) },
+	{ PHY_ID_MATCH_EXACT(PHY_ID_ASIX_AX88279) },
 	{ }
 };
 
diff --git a/drivers/net/phy/phy.c b/drivers/net/phy/phy.c
index fce9bc7be3304..b83f938fe7c3b 100644
--- a/drivers/net/phy/phy.c
+++ b/drivers/net/phy/phy.c
@@ -1190,6 +1190,10 @@ int phy_ethtool_ksettings_set(struct phy_device *phydev,
 	      duplex != DUPLEX_FULL)))
 		return -EINVAL;
 
+	if (autoneg == AUTONEG_DISABLE &&
+	    (phydev->drv->flags & PHY_BROKEN_FORCED))
+		return -EOPNOTSUPP;
+
 	mutex_lock(&phydev->lock);
 	phydev->autoneg = autoneg;
 
diff --git a/drivers/net/phy/phylink.c b/drivers/net/phy/phylink.c
index a7d086cdc9b25..a3ca7993eb7e0 100644
--- a/drivers/net/phy/phylink.c
+++ b/drivers/net/phy/phylink.c
@@ -1612,6 +1612,26 @@ static bool phylink_link_is_up(struct phylink *pl)
 	return pl->netdev ? netif_carrier_ok(pl->netdev) : pl->old_link_state;
 }
 
+/**
+ * phylink_mac_interrupt() - wrapper for phy_mac_interrupt()
+ * @pl: a pointer to a &struct phylink returned from phylink_create()
+ *
+ * Passes a link-change interrupt received by the MAC to phylib.
+ * This function may be called in interrupt context, and therefore cannot take
+ * a mutex. However, it is safe to assume that the reference to phy does not
+ * disappear during the call, as only an SFP module may be unplugged to make a
+ * PHY disappear. SFP modules however do not support interrupts.
+ */
+void phylink_mac_interrupt(struct phylink *pl)
+{
+	struct phy_device *phy;
+
+	phy = pl->phydev;
+	if (phy)
+		phy_mac_interrupt(phy);
+}
+EXPORT_SYMBOL_GPL(phylink_mac_interrupt);
+
 static void phylink_resolve(struct work_struct *w)
 {
 	struct phylink *pl = container_of(w, struct phylink, resolve);
diff --git a/drivers/net/usb/Kconfig b/drivers/net/usb/Kconfig
index 52a5c0922c79f..27862e380bdb4 100644
--- a/drivers/net/usb/Kconfig
+++ b/drivers/net/usb/Kconfig
@@ -195,11 +195,12 @@ 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"
+config USB_NET_AX88179
+	tristate "ASIX AX88179/179A/178A USB 2.0/3.0 to 100M/1G/2.5GbE Adapters"
 	depends on USB_USBNET
+	select PHYLINK
+	select AX88796B_PHY
 	select CRC32
-	select PHYLIB
 	default y
 	help
 	  This option adds support for ASIX AX88179 based USB 3.0/2.0
@@ -207,6 +208,9 @@ config USB_NET_AX88179_178A
 
 	  This driver should work with at least the following devices:
 	    * ASIX AX88179
+	    * ASIX AX88179A/B
+	    * ASIX AX88279
+	    * ASIX AX88772D/E
 	    * ASIX AX88178A
 	    * Sitcomm LN-032
 
diff --git a/drivers/net/usb/Makefile b/drivers/net/usb/Makefile
index 4964f7b326fbc..4026f19ecb826 100644
--- a/drivers/net/usb/Makefile
+++ b/drivers/net/usb/Makefile
@@ -12,7 +12,8 @@ obj-$(CONFIG_USB_HSO)		+= hso.o
 obj-$(CONFIG_USB_LAN78XX)	+= lan78xx.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_AX88179)      += ax88179.o
+ax88179-y := ax88179_178a.o ax88179a_devices.o ax88179_lib.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
index 2ed46ab62c006..7ec7143805484 100644
--- a/drivers/net/usb/ax88179_178a.c
+++ b/drivers/net/usb/ax88179_178a.c
@@ -8,179 +8,12 @@
 #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>
 #include <uapi/linux/mdio.h>
 #include <linux/mdio.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(29))
-#define AX_RXHDR_DROP_ERR			((u32)BIT(31))
-#define AX_ACCESS_MAC				0x01
-#define AX_ACCESS_PHY				0x02
-#define AX_ACCESS_EEPROM			0x04
-#define AX_ACCESS_EFUS				0x05
-#define AX_RELOAD_EEPROM_EFUSE			0x06
-#define AX_PAUSE_WATERLVL_LOW			0x54
-#define AX_PAUSE_WATERLVL_HIGH			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_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
-	#define GMII_PHY_PGSEL_PAGE3	0x0003
-	#define GMII_PHY_PGSEL_PAGE5	0x0005
+#include "ax88179_lib.h"
 
 static int ax88179_reset(struct usbnet *dev);
 
-struct ax88179_data {
-	u8  eee_enabled;
-	u8  eee_active;
-	u16 rxctl;
-	u8 in_pm;
-	u32 wol_supported;
-	u32 wolopts;
-	u8 disconnecting;
-};
-
-struct ax88179_int_data {
-	__le32 intdata1;
-	__le32 intdata2;
-};
-
 static const struct {
 	unsigned char ctrl, timer_l, timer_h, size, ifg;
 } AX88179_BULKIN_SIZE[] =	{
@@ -190,165 +23,6 @@ static const struct {
 	{7, 0xcc, 0x4c, 0x18, 8},
 };
 
-static void ax88179_set_pm_mode(struct usbnet *dev, bool pm_mode)
-{
-	struct ax88179_data *ax179_data = dev->driver_priv;
-
-	ax179_data->in_pm = pm_mode;
-}
-
-static int ax88179_in_pm(struct usbnet *dev)
-{
-	struct ax88179_data *ax179_data = dev->driver_priv;
-
-	return ax179_data->in_pm;
-}
-
-static int __ax88179_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,
-			      u16 size, void *data)
-{
-	int ret;
-	int (*fn)(struct usbnet *, u8, u8, u16, u16, void *, u16);
-	struct ax88179_data *ax179_data = dev->driver_priv;
-
-	BUG_ON(!dev);
-
-	if (!ax88179_in_pm(dev))
-		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) && !(ret == -ENODEV && ax179_data->disconnecting)))
-		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, const void *data)
-{
-	int ret;
-	int (*fn)(struct usbnet *, u8, u8, u16, u16, const void *, u16);
-	struct ax88179_data *ax179_data = dev->driver_priv;
-
-	BUG_ON(!dev);
-
-	if (!ax88179_in_pm(dev))
-		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) && !(ret == -ENODEV && ax179_data->disconnecting)))
-		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(struct usbnet *dev, u8 cmd, u16 value, u16 index,
-			    u16 size, void *data)
-{
-	int ret;
-
-	if (2 == size) {
-		u16 buf = 0;
-		ret = __ax88179_read_cmd(dev, cmd, value, index, size, &buf);
-		le16_to_cpus(&buf);
-		*((u16 *)data) = buf;
-	} else if (4 == size) {
-		u32 buf = 0;
-		ret = __ax88179_read_cmd(dev, cmd, value, index, size, &buf);
-		le32_to_cpus(&buf);
-		*((u32 *)data) = buf;
-	} else {
-		ret = __ax88179_read_cmd(dev, cmd, value, index, size, data);
-	}
-
-	return ret;
-}
-
-static int ax88179_write_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,
-			     u16 size, const 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);
-	} else {
-		ret = __ax88179_write_cmd(dev, cmd, value, index,
-					  size, data);
-	}
-
-	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) {
-		usbnet_link_change(dev, link, 1);
-		if (!link)
-			netdev_info(dev->net, "ax88179 - Link status is: 0\n");
-	}
-}
-
-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 inline int ax88179_phy_mmd_indirect(struct usbnet *dev, u16 prtad,
 					   u16 devad)
 {
@@ -510,146 +184,11 @@ static void ax88179_disconnect(struct usb_interface *intf)
 	usbnet_disconnect(intf);
 }
 
-static void
-ax88179_get_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo)
-{
-	struct usbnet *dev = netdev_priv(net);
-	struct ax88179_data *priv = dev->driver_priv;
-
-	wolinfo->supported = priv->wol_supported;
-	wolinfo->wolopts = priv->wolopts;
-}
-
-static int
-ax88179_set_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo)
-{
-	struct usbnet *dev = netdev_priv(net);
-	struct ax88179_data *priv = dev->driver_priv;
-
-	if (wolinfo->wolopts & ~(priv->wol_supported))
-		return -EINVAL;
-
-	priv->wolopts = wolinfo->wolopts;
-
-	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_array(last_word - first_word + 1, sizeof(u16),
-				    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]);
-		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_set_eeprom(struct net_device *net, struct ethtool_eeprom *eeprom,
-		   u8 *data)
-{
-	struct usbnet *dev = netdev_priv(net);
-	u16 *eeprom_buff;
-	int first_word;
-	int last_word;
-	int ret;
-	int i;
-
-	netdev_dbg(net, "write EEPROM len %d, offset %d, magic 0x%x\n",
-		   eeprom->len, eeprom->offset, eeprom->magic);
-
-	if (eeprom->len == 0)
-		return -EINVAL;
-
-	if (eeprom->magic != AX88179_EEPROM_MAGIC)
-		return -EINVAL;
-
-	first_word = eeprom->offset >> 1;
-	last_word = (eeprom->offset + eeprom->len - 1) >> 1;
-
-	eeprom_buff = kmalloc_array(last_word - first_word + 1, sizeof(u16),
-				    GFP_KERNEL);
-	if (!eeprom_buff)
-		return -ENOMEM;
-
-	/* align data to 16 bit boundaries, read the missing data from
-	   the EEPROM */
-	if (eeprom->offset & 1) {
-		ret = ax88179_read_cmd(dev, AX_ACCESS_EEPROM, first_word, 1, 2,
-				       &eeprom_buff[0]);
-		if (ret < 0) {
-			netdev_err(net, "Failed to read EEPROM at offset 0x%02x.\n", first_word);
-			goto free;
-		}
-	}
-
-	if ((eeprom->offset + eeprom->len) & 1) {
-		ret = ax88179_read_cmd(dev, AX_ACCESS_EEPROM, last_word, 1, 2,
-				       &eeprom_buff[last_word - first_word]);
-		if (ret < 0) {
-			netdev_err(net, "Failed to read EEPROM at offset 0x%02x.\n", last_word);
-			goto free;
-		}
-	}
-
-	memcpy((u8 *)eeprom_buff + (eeprom->offset & 1), data, eeprom->len);
-
-	for (i = first_word; i <= last_word; i++) {
-		netdev_dbg(net, "write to EEPROM at offset 0x%02x, data 0x%04x\n",
-			   i, eeprom_buff[i - first_word]);
-		ret = ax88179_write_cmd(dev, AX_ACCESS_EEPROM, i, 1, 2,
-					&eeprom_buff[i - first_word]);
-		if (ret < 0) {
-			netdev_err(net, "Failed to write EEPROM at offset 0x%02x.\n", i);
-			goto free;
-		}
-		msleep(20);
-	}
-
-	/* reload EEPROM data */
-	ret = ax88179_write_cmd(dev, AX_RELOAD_EEPROM_EFUSE, 0x0000, 0, 0, NULL);
-	if (ret < 0) {
-		netdev_err(net, "Failed to reload EEPROM data\n");
-		goto free;
-	}
-
-	ret = 0;
-free:
-	kfree(eeprom_buff);
-	return ret;
-}
-
 static int ax88179_get_link_ksettings(struct net_device *net,
 				      struct ethtool_link_ksettings *cmd)
 {
@@ -864,125 +403,6 @@ static const struct ethtool_ops ax88179_ethtool_ops = {
 	.get_ts_info		= ethtool_op_get_ts_info,
 };
 
-static void ax88179_set_multicast(struct net_device *net)
-{
-	struct usbnet *dev = netdev_priv(net);
-	struct ax88179_data *data = dev->driver_priv;
-	u8 *m_filter = ((u8 *)dev->data);
-
-	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 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;
-
-	WRITE_ONCE(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);
-	}
-
-	/* max qlen depend on hard_mtu and rx_urb_size */
-	usbnet_update_max_qlen(dev);
-
-	return 0;
-}
-
-static int ax88179_set_mac_addr(struct net_device *net, void *p)
-{
-	struct usbnet *dev = netdev_priv(net);
-	struct sockaddr *addr = p;
-	int ret;
-
-	if (netif_running(net))
-		return -EBUSY;
-	if (!is_valid_ether_addr(addr->sa_data))
-		return -EADDRNOTAVAIL;
-
-	eth_hw_addr_set(net, addr->sa_data);
-
-	/* Set the MAC address */
-	ret = ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_NODE_ID, ETH_ALEN,
-				 ETH_ALEN, net->dev_addr);
-	if (ret < 0)
-		return ret;
-
-	return 0;
-}
-
 static const struct net_device_ops ax88179_netdev_ops = {
 	.ndo_open		= usbnet_open,
 	.ndo_stop		= usbnet_stop,
@@ -1249,35 +669,6 @@ static int ax88179_led_setting(struct usbnet *dev)
 	return 0;
 }
 
-static void ax88179_get_mac_addr(struct usbnet *dev)
-{
-	u8 mac[ETH_ALEN];
-
-	memset(mac, 0, sizeof(mac));
-
-	/* Maybe the boot loader passed the MAC address via device tree */
-	if (!eth_platform_get_mac_address(&dev->udev->dev, mac)) {
-		netif_dbg(dev, ifup, dev->net,
-			  "MAC address read from device tree");
-	} else {
-		ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_NODE_ID, ETH_ALEN,
-				 ETH_ALEN, mac);
-		netif_dbg(dev, ifup, dev->net,
-			  "MAC address read from ASIX chip");
-	}
-
-	if (is_valid_ether_addr(mac)) {
-		eth_hw_addr_set(dev->net, mac);
-		if (!is_local_ether_addr(mac))
-			dev->net->addr_assign_type = NET_ADDR_PERM;
-	} else {
-		netdev_info(dev->net, "invalid MAC address, using random\n");
-	}
-
-	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_NODE_ID, ETH_ALEN, ETH_ALEN,
-			  dev->net->dev_addr);
-}
-
 static int ax88179_bind(struct usbnet *dev, struct usb_interface *intf)
 {
 	struct ax88179_data *ax179_data;
@@ -1293,6 +684,22 @@ static int ax88179_bind(struct usbnet *dev, struct usb_interface *intf)
 
 	dev->driver_priv = ax179_data;
 
+	ax179_data->resume = ax88179_resume;
+	ax179_data->suspend = ax88179_suspend;
+
+	ret = ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_CHIP_STATUS,
+			       1, 1, &ax179_data->chip_version);
+	if (ret < 0)
+		goto err_nodev;
+
+	ax179_data->chip_version = (ax179_data->chip_version & 0xf0) >> 4;
+	ax179_data->is_ax88772d = 0;
+	ax179_data->ip_align = 1;
+	ax179_data->eeprom_read_cmd = AX_ACCESS_EEPROM;
+	ax179_data->eeprom_write_cmd = AX_ACCESS_EEPROM;
+	ax179_data->eeprom_block = 2;
+	ax179_data->eeprom_wen = 0;
+
 	dev->net->netdev_ops = &ax88179_netdev_ops;
 	dev->net->ethtool_ops = &ax88179_ethtool_ops;
 	dev->net->needed_headroom = 8;
@@ -1317,6 +724,12 @@ static int ax88179_bind(struct usbnet *dev, struct usb_interface *intf)
 	ax88179_reset(dev);
 
 	return 0;
+
+err_nodev:
+	kfree(ax179_data);
+	ax179_data = NULL;
+
+	return ret;
 }
 
 static void ax88179_unbind(struct usbnet *dev, struct usb_interface *intf)
@@ -1879,6 +1292,18 @@ static const struct driver_info at_umc2000sp_info = {
 
 static const struct usb_device_id products[] = {
 {
+	/* ASIX AX88179A USB 3.2 1000Mbit Ethernet */
+	USB_DEVICE_VER(0x0b95, 0x1790, 0x0200, 0x0200),
+	.driver_info = (unsigned long)&ax88179a_info,
+}, {
+	/* ASIX AX88772D USB 2.0 100Mbit Ethernet */
+	USB_DEVICE_VER(0x0b95, 0x1790, 0x0300, 0x0300),
+	.driver_info = (unsigned long)&ax88772d_info,
+}, {
+	/* ASIX AX88279 USB 3.2 2500Mbit Ethernet */
+	USB_DEVICE_VER(0x0b95, 0x1790, 0x0400, 0x0400),
+	.driver_info = (unsigned long)&ax88279_info,
+}, {
 	/* ASIX AX88179 10/100/1000 */
 	USB_DEVICE_AND_INTERFACE_INFO(0x0b95, 0x1790, 0xff, 0xff, 0),
 	.driver_info = (unsigned long)&ax88179_info,
@@ -1935,19 +1360,20 @@ static const struct usb_device_id products[] = {
 };
 MODULE_DEVICE_TABLE(usb, products);
 
-static struct usb_driver ax88179_178a_driver = {
-	.name =		"ax88179_178a",
+static struct usb_driver ax88179_driver = {
+	.name =		"ax88179",
 	.id_table =	products,
 	.probe =	usbnet_probe,
-	.suspend =	ax88179_suspend,
-	.resume =	ax88179_resume,
-	.reset_resume =	ax88179_resume,
+	.suspend =	ax88179_suspend_wrapper,
+	.resume =	ax88179_resume_wrapper,
+	.reset_resume =	ax88179_resume_wrapper,
 	.disconnect =	ax88179_disconnect,
 	.supports_autosuspend = 1,
 	.disable_hub_initiated_lpm = 1,
 };
 
-module_usb_driver(ax88179_178a_driver);
+module_usb_driver(ax88179_driver);
 
-MODULE_DESCRIPTION("ASIX AX88179/178A based USB 3.0/2.0 Gigabit Ethernet Devices");
+MODULE_DESCRIPTION("ASIX AX88179/179A/178A/279 based USB 3.0/2.0 100M/1G/2.5GbE Controllers");
 MODULE_LICENSE("GPL");
+MODULE_ALIAS("ax88179_178a");
diff --git a/drivers/net/usb/ax88179_lib.c b/drivers/net/usb/ax88179_lib.c
new file mode 100644
index 0000000000000..de849650f7871
--- /dev/null
+++ b/drivers/net/usb/ax88179_lib.c
@@ -0,0 +1,530 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * ASIX AX88179/178A USB 3.0/2.0 to Gigabit Ethernet Devices
+ *
+ * Copyright (C) 2011-2013 ASIX
+ */
+
+#include <linux/etherdevice.h>
+#include <linux/rtnetlink.h>
+#include "ax88179_lib.h"
+
+void ax88179_set_pm_mode(struct usbnet *dev, bool pm_mode)
+{
+	struct ax88179_data *ax179_data = dev->driver_priv;
+
+	ax179_data->in_pm = pm_mode;
+}
+
+static int ax88179_in_pm(struct usbnet *dev)
+{
+	struct ax88179_data *ax179_data = dev->driver_priv;
+
+	return ax179_data->in_pm;
+}
+
+int __ax88179_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,
+		       u16 size, void *data)
+{
+	int (*fn)(struct usbnet *dev, u8 cmd, u8 rtype, u16 val, u16 i, void *d, u16 size);
+	struct ax88179_data *ax179_data = dev->driver_priv;
+	int ret;
+
+	if (!ax88179_in_pm(dev))
+		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 && !(ret == -ENODEV && ax179_data->disconnecting)))
+		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, const void *data)
+{
+	int (*fn)(struct usbnet *dev, u8 cmd, u8 rtype, u16 val, u16 i, const void *d, u16 size);
+	struct ax88179_data *ax179_data = dev->driver_priv;
+	int ret;
+
+	if (!ax88179_in_pm(dev))
+		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 && !(ret == -ENODEV && ax179_data->disconnecting)))
+		netdev_warn(dev->net, "Failed to write reg index 0x%04x: %d\n",
+			    index, ret);
+
+	return ret;
+}
+
+void ax88179_write_cmd_async(struct usbnet *dev, u8 cmd, u16 value,
+			     u16 index, u16 size, void *data)
+{
+	u16 buf;
+
+	if (size == 2) {
+		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);
+	}
+}
+
+int ax88179_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,
+		     u16 size, void *data)
+{
+	int ret;
+
+	if (size == 2) {
+		u16 buf = 0;
+
+		ret = __ax88179_read_cmd(dev, cmd, value, index, size, &buf);
+		le16_to_cpus(&buf);
+		*((u16 *)data) = buf;
+	} else if (size == 4) {
+		u32 buf = 0;
+
+		ret = __ax88179_read_cmd(dev, cmd, value, index, size, &buf);
+		le32_to_cpus(&buf);
+		*((u32 *)data) = buf;
+	} else {
+		ret = __ax88179_read_cmd(dev, cmd, value, index, size, data);
+	}
+
+	return ret;
+}
+
+int ax88179_write_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index,
+		      u16 size, const void *data)
+{
+	int ret;
+
+	if (size == 2) {
+		u16 buf;
+
+		buf = *((u16 *)data);
+		cpu_to_le16s(&buf);
+		ret = __ax88179_write_cmd(dev, cmd, value, index,
+					  size, &buf);
+	} else {
+		ret = __ax88179_write_cmd(dev, cmd, value, index,
+					  size, data);
+	}
+
+	return ret;
+}
+
+int ax88179_write_u16(struct usbnet *dev, u8 cmd, u16 value, u16 index, u16 data)
+{
+	u16 buf = data;
+
+	cpu_to_le16s(&buf);
+	return __ax88179_write_cmd(dev, cmd, value, index, 2, &buf);
+}
+
+int ax88179_write_u8(struct usbnet *dev, u8 cmd, u16 value, u16 index, u8 data)
+{
+	u8 buf = data;
+
+	return __ax88179_write_cmd(dev, cmd, value, index, 1, &buf);
+}
+
+struct ax88179_data *netdev2data(struct net_device *net)
+{
+	struct usbnet *dev = netdev_priv(net);
+
+	return dev->driver_priv;
+}
+
+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) {
+		usbnet_link_change(dev, link, 1);
+		if (!link)
+			netdev_info(dev->net, "ax88179 - Link status is: 0\n");
+	}
+}
+
+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;
+}
+
+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);
+}
+
+void ax88179_get_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo)
+{
+	struct usbnet *dev = netdev_priv(net);
+	struct ax88179_data *priv = dev->driver_priv;
+
+	wolinfo->supported = priv->wol_supported;
+	wolinfo->wolopts = priv->wolopts;
+}
+
+int ax88179_set_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo)
+{
+	struct usbnet *dev = netdev_priv(net);
+	struct ax88179_data *priv = dev->driver_priv;
+
+	if (wolinfo->wolopts & ~(priv->wol_supported))
+		return -EINVAL;
+
+	priv->wolopts = wolinfo->wolopts;
+
+	return 0;
+}
+
+static void
+ax88179_eeprom_access_params(struct ax88179_data *ax179_data, int i, u16 *value, u16 *idx)
+{
+	/* AX88179 has a word-addressable EEPROM
+	 * AX88179A uses EFUSES with 20 bytes length
+	 * AX88279 has an EEPROM addressable in 256 byte blocks
+	 */
+	if (ax179_data->chip_version < AX_VERSION_AX88179A) {
+		*value = i;
+		*idx = 1;
+	} else if (ax179_data->chip_version == AX_VERSION_AX88279) {
+		*value = (i * ax179_data->eeprom_block) >> 16;
+		*idx = (i * ax179_data->eeprom_block) & 0xffff;
+	} else {
+		*value = i << 4;
+		*idx = 0;
+	}
+}
+
+int ax88179_get_eeprom(struct net_device *net, struct ethtool_eeprom *eeprom, u8 *data)
+{
+	struct usbnet *dev = netdev_priv(net);
+	struct ax88179_data *ax179_data;
+	int first, last, i, ret;
+	u8 *eeprom_buff;
+
+	ax179_data = dev->driver_priv;
+
+	if (eeprom->len == 0)
+		return -EINVAL;
+
+	eeprom->magic = AX88179_EEPROM_MAGIC;
+
+	first = eeprom->offset / ax179_data->eeprom_block;
+	last = (eeprom->offset + eeprom->len - 1) / ax179_data->eeprom_block;
+
+	eeprom_buff = kzalloc((last - first + 1) * ax179_data->eeprom_block, GFP_KERNEL);
+	if (!eeprom_buff)
+		return -ENOMEM;
+
+	for (i = first; i <= last; i++) {
+		u16 value, idx;
+
+		ax88179_eeprom_access_params(ax179_data, i, &value, &idx);
+		ret = __ax88179_read_cmd(dev, ax179_data->eeprom_read_cmd,
+					 value, idx, ax179_data->eeprom_block,
+					 eeprom_buff + (i - first) * ax179_data->eeprom_block);
+
+		if (ret != ax179_data->eeprom_block) {
+			kfree(eeprom_buff);
+			return -EIO;
+		}
+	}
+
+	memcpy(data, eeprom_buff + eeprom->offset % ax179_data->eeprom_block, eeprom->len);
+	kfree(eeprom_buff);
+	return 0;
+}
+
+int ax88179_set_eeprom(struct net_device *net, struct ethtool_eeprom *eeprom, u8 *data)
+{
+	struct usbnet *dev = netdev_priv(net);
+	struct ax88179_data *ax179_data;
+	u16 *eeprom_buff;
+	int first_word;
+	int last_word;
+	int ret;
+	int i;
+
+	ax179_data = dev->driver_priv;
+	if (ax179_data->chip_version >= AX_VERSION_AX88179A)
+		return -EOPNOTSUPP;
+
+	netdev_dbg(net, "write EEPROM len %d, offset %d, magic 0x%x\n",
+		   eeprom->len, eeprom->offset, eeprom->magic);
+
+	if (eeprom->len == 0)
+		return -EINVAL;
+
+	if (eeprom->magic != AX88179_EEPROM_MAGIC)
+		return -EINVAL;
+
+	first_word = eeprom->offset >> 1;
+	last_word = (eeprom->offset + eeprom->len - 1) >> 1;
+
+	eeprom_buff = kmalloc_array(last_word - first_word + 1, sizeof(u16),
+				    GFP_KERNEL);
+	if (!eeprom_buff)
+		return -ENOMEM;
+
+	/* align data to 16 bit boundaries, read the missing data from
+	 * the EEPROM
+	 */
+	if (eeprom->offset & 1) {
+		ret = ax88179_read_cmd(dev, AX_ACCESS_EEPROM, first_word, 1, 2,
+				       &eeprom_buff[0]);
+		if (ret < 0) {
+			netdev_err(net, "Failed to read EEPROM at offset 0x%02x.\n", first_word);
+			goto free;
+		}
+	}
+
+	if ((eeprom->offset + eeprom->len) & 1) {
+		ret = ax88179_read_cmd(dev, AX_ACCESS_EEPROM, last_word, 1, 2,
+				       &eeprom_buff[last_word - first_word]);
+		if (ret < 0) {
+			netdev_err(net, "Failed to read EEPROM at offset 0x%02x.\n", last_word);
+			goto free;
+		}
+	}
+
+	memcpy((u8 *)eeprom_buff + (eeprom->offset & 1), data, eeprom->len);
+
+	for (i = first_word; i <= last_word; i++) {
+		netdev_dbg(net, "write to EEPROM at offset 0x%02x, data 0x%04x\n",
+			   i, eeprom_buff[i - first_word]);
+		ret = ax88179_write_cmd(dev, AX_ACCESS_EEPROM, i, 1, 2,
+					&eeprom_buff[i - first_word]);
+		if (ret < 0) {
+			netdev_err(net, "Failed to write EEPROM at offset 0x%02x.\n", i);
+			goto free;
+		}
+		msleep(20);
+	}
+
+	/* reload EEPROM data */
+	ret = ax88179_write_cmd(dev, AX_RELOAD_EEPROM_EFUSE, 0x0000, 0, 0, NULL);
+	if (ret < 0) {
+		netdev_err(net, "Failed to reload EEPROM data\n");
+		goto free;
+	}
+
+	ret = 0;
+free:
+	kfree(eeprom_buff);
+	return ret;
+}
+
+void ax88179_set_multicast(struct net_device *net)
+{
+	struct usbnet *dev = netdev_priv(net);
+	u8 *m_filter = ((u8 *)dev->data);
+	struct ax88179_data *data;
+
+	data = dev->driver_priv;
+
+	data->rxctl = (AX_RX_CTL_START | AX_RX_CTL_AB | AX_RX_CTL_DROPCRCERR);
+	if (data->ip_align)
+		data->rxctl |= 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 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);
+}
+
+int ax88179_set_features(struct net_device *net, netdev_features_t features)
+{
+	u8 tmp;
+	struct usbnet *dev = netdev_priv(net);
+	struct ax88179_data *data = dev->driver_priv;
+	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) {
+		/* When we disable rx-checksumming, we need to clear the rx_checksum
+		 * flag before disabling the engine
+		 */
+		if (!(features & NETIF_F_RXCSUM))
+			data->rx_checksum = 0;
+
+		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);
+		data->rx_checksum = !!(features & NETIF_F_RXCSUM);
+	}
+
+	return 0;
+}
+
+void ax88179_get_mac_addr(struct usbnet *dev)
+{
+	u8 mac[ETH_ALEN];
+
+	memset(mac, 0, sizeof(mac));
+
+	/* Maybe the boot loader passed the MAC address via device tree */
+	if (!eth_platform_get_mac_address(&dev->udev->dev, mac)) {
+		netif_dbg(dev, ifup, dev->net,
+			  "MAC address read from device tree");
+	} else {
+		ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_NODE_ID, ETH_ALEN,
+				 ETH_ALEN, mac);
+		netif_dbg(dev, ifup, dev->net,
+			  "MAC address read from ASIX chip");
+	}
+
+	if (is_valid_ether_addr(mac)) {
+		eth_hw_addr_set(dev->net, mac);
+		if (!is_local_ether_addr(mac))
+			dev->net->addr_assign_type = NET_ADDR_PERM;
+	} else {
+		netdev_info(dev->net, "invalid MAC address, using random\n");
+	}
+
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_NODE_ID, ETH_ALEN, ETH_ALEN,
+			  dev->net->dev_addr);
+}
+
+int ax88179_change_mtu(struct net_device *net, int new_mtu)
+{
+	struct usbnet *dev = netdev_priv(net);
+	u16 tmp16;
+
+	WRITE_ONCE(net->mtu, new_mtu);
+	dev->hard_mtu = net->mtu + net->hard_header_len;
+
+	rtnl_lock();
+	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);
+	}
+	rtnl_unlock();
+
+	/* max qlen depend on hard_mtu and rx_urb_size */
+	usbnet_update_max_qlen(dev);
+
+	return 0;
+}
+
+int ax88179_set_mac_addr(struct net_device *net, void *p)
+{
+	struct usbnet *dev = netdev_priv(net);
+	struct sockaddr *addr = p;
+	int ret;
+
+	if (netif_running(net))
+		return -EBUSY;
+	if (!is_valid_ether_addr(addr->sa_data))
+		return -EADDRNOTAVAIL;
+
+	eth_hw_addr_set(net, addr->sa_data);
+
+	/* Set the MAC address */
+	ret = ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_NODE_ID, ETH_ALEN,
+				ETH_ALEN, net->dev_addr);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+int ax88179_suspend_wrapper(struct usb_interface *intf, pm_message_t message)
+{
+	struct usbnet *dev = usb_get_intfdata(intf);
+	struct ax88179_data *priv;
+
+	priv = dev->driver_priv;
+
+	return priv->suspend(intf, message);
+}
+
+int ax88179_resume_wrapper(struct usb_interface *intf)
+{
+	struct usbnet *dev = usb_get_intfdata(intf);
+	struct ax88179_data *priv;
+
+	priv = dev->driver_priv;
+	return priv->resume(intf);
+}
+
diff --git a/drivers/net/usb/ax88179_lib.h b/drivers/net/usb/ax88179_lib.h
new file mode 100644
index 0000000000000..a9235ada982b8
--- /dev/null
+++ b/drivers/net/usb/ax88179_lib.h
@@ -0,0 +1,359 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include <linux/usb.h>
+#include <linux/crc32.h>
+#include <linux/phylink.h>
+#include <linux/usb/usbnet.h>
+
+#ifndef __LINUX_USBNET_AX88179_H
+#define __LINUX_USBNET_AX88179_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(29))
+#define AX_RXHDR_DROP_ERR			((u32)BIT(31))
+#define AX_ACCESS_MAC				0x01
+#define AX_ACCESS_PHY				0x02
+#define AX_ACCESS_EEPROM			0x04
+#define AX_ACCESS_EFUS				0x05
+#define AX_RELOAD_EEPROM_EFUSE			0x06
+#define AX88179A_WAKEUP_SETTING			0x07
+#define AX_FW_MODE				0x08
+#define AX_GPHY_CTL				0x0F
+#define AX88179A_FLASH_READ			0x21
+#define AX88179A_FLASH_WEN			0x22
+#define AX88179A_FLASH_WDIS			0x23
+#define AX88179A_FLASH_WRITE			0x24
+#define AX88179A_PHY_CLAUSE45			0x27
+#define AX88179A_FLASH_ERASE_SECTION		0x28
+#define AX88179A_ACCESS_BL			0x2A
+#define AX88179A_PHY_POWER			0x31
+#define AX88179A_AUTODETACH			0xC0
+
+#define AX_PAUSE_WATERLVL_LOW			0x54
+#define AX_PAUSE_WATERLVL_HIGH			0x55
+
+#define AX_FW_MODE_179A				0x01
+#define PHYSICAL_LINK_STATUS			0x02
+	#define	AX_USB_SS		0x04
+	#define	AX_USB_HS		0x02
+	#define AX_USB_FS		0x01
+
+#define GENERAL_STATUS				0x03
+/* Check AX88179 version. UA1:Bit2 = 0,  UA2:Bit2 = 1 */
+	#define	AX_SECLD		0x04
+
+#define AX_CHIP_STATUS				0x05
+
+#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 AX88179A_ETH_TX_GAP			0x0D
+
+#define AX88179A_BFM_DATA			0x0E
+	#define AX_TX_QUEUE_CFG		0x02
+	#define AX_TX_QUEUE_SET		0x08
+	#define AX_TX_Q1_AHB_FC_EN	0x10
+	#define AX_TX_Q2_AHB_FC_EN	0x20
+	#define AX_XGMII_EN		0x80
+
+#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_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 AX88179A_VLAN_ID_ADDRESS		0x2A
+
+#define AX88179A_VLAN_ID_CONTROL		0x2B
+	#define AX_VLAN_CONTROL_WE	0x0001
+	#define AX_VLAN_CONTROL_RD	0x0002
+	#define AX_VLAN_CONTROL_VSO	0x0010
+	#define AX_VLAN_CONTROL_VFE	0x0020
+
+#define AX88179A_VLAN_ID_DATA0			0x2C
+#define AX88179A_VLAN_ID_DATA1			0x2D
+
+#define AX_RX_BULKIN_QCTRL			0x2e
+#define AX_GPHY_EEE_CTRL			0x01
+
+#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 AX88179A_MAC_BM_INT_MASK		0x41
+#define AX88179A_MAC_BM_RX_DMA_CTL		0x43
+#define AX88179A_MAC_BM_TX_DMA_CTL		0x46
+
+#define AX88179A_MAC_RX_STATUS_CDC		0x6D
+	#define AX_LSOFC_WCNT_7_ACCESS	0x03
+	#define AX_GMII_CRC_APPEND	0x10
+
+#define AX_LEDCTRL				0x73
+#define AX88179A_MAC_ARC_CTRL			0x9E
+#define AX88179A_MAC_SWP_CTRL			0xB1
+
+#define AX88179A_MAC_TX_PAUSE			0xB2
+
+#define AX88179A_MAC_CDC_DELAY_TX		0xB5
+
+#define AX88179A_MAC_PATH			0xB7
+	#define AX_MAC_RX_PATH_READY	0x01
+	#define AX_MAC_TX_PATH_READY	0x02
+
+#define AX88179A_NEW_PAUSE_CTRL			0xB8
+	#define AX_NEW_PAUSE_EN		0x01
+
+#define AX88179A_MAC_BULK_OUT_CTRL		0xB9
+	#define AX_MAC_EFF_EN		0x02
+
+#define AX88179A_MAC_RX_DATA_CDC_CNT		0xC0
+	#define AX_MAC_LSO_ERR_EN	0x04
+	#define AX_MAC_MIQFFCTRL_FORMAT	0x10
+	#define AX_MAC_MIQFFCTRL_DROP_CRC 0x20
+
+#define AX88179A_AUTODETACH_DELAY	(5UL << 8)
+#define AX88179A_AUTODETACH_EN		1
+
+#define AX88179A_MAC_LSO_ENHANCE_CTRL		0xC3
+	#define AX_LSO_ENHANCE_EN	0x01
+
+#define AX88179A_MAC_TX_HDR_CKSUM		0xCC
+#define AX88179A_EP5_EHR			0xF9
+
+#define AX_PHY_POWER				0x02
+
+#define EPHY_LOW_POWER_EN			0x01
+#define S5_WOL_EN				0x04
+#define S5_WOL_LOW_POWER			0x20
+
+#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
+	#define GMII_PHY_PGSEL_PAGE3	0x0003
+	#define GMII_PHY_PGSEL_PAGE5	0x0005
+
+#define AX88179A_SW_REVISION		0xFC
+#define AX88179A_SW_VERSION		0xFD
+
+/* TX Descriptor */
+#define AX179A_TX_DESC_LEN_MASK		0x1FFFFF
+#define AX179A_TX_DESC_DROP_PADD	BIT(28)
+#define AX179A_TX_DESC_VLAN		BIT(29)
+#define AX179A_TX_DESC_MSS		GENMASK(46, 32)
+#define AX179A_TX_DESC_VLAN_ID		GENMASK(63, 48)
+
+/* RX Packet Descriptor */
+#define AX179A_RX_PD_L4_ERR		BIT(0)
+#define AX179A_RX_PD_L3_ERR		BIT(1)
+#define AX179A_RX_PD_L4_TYPE_MASK	0x1C
+#define AX179A_RX_PD_L4_UDP		0x04
+#define AX179A_RX_PD_L4_TCP		0x10
+#define AX179A_RX_PD_L3_TYPE_MASK	0x60
+#define AX179A_RX_PD_L3_IP		0x20
+#define AX179A_RX_PD_L3_IP6		0x40
+
+#define AX179A_RX_PD_VLAN		BIT(10)
+#define AX179A_RX_PD_RX_OK		BIT(11)
+#define AX179A_RX_PD_DROP		BIT(31)
+#define AX179A_RX_PD_LEN		GENMASK(30, 16)
+#define AX179A_RX_PD_VLAN_SHIFT		0x20
+
+/* RX Descriptor header */
+#define AX179A_RX_DH_PKT_CNT_MASK	0x1FFF
+#define AX179A_RX_DH_DESC_OFFSET	GENMASK(31, 13)
+
+#define AX179A_RX_HW_PAD		0x02
+
+#define AX_ADVERTISE_2500		0x1000
+
+enum ax_ether_link_speed {
+	ETHER_LINK_NONE = 0,
+	ETHER_LINK_10   = 1,
+	ETHER_LINK_100  = 2,
+	ETHER_LINK_1000 = 3,
+	ETHER_LINK_2500 = 4,
+};
+
+enum ax_chip_version {
+	AX_VERSION_INVALID		= 0x0,
+	AX_VERSION_AX88179		= 0x4,
+	AX_VERSION_AX88179A		= 0x6,	/* Also AX88772D */
+	AX_VERSION_AX88279		= 0x7,
+};
+
+struct ax88179_data {
+	u8  eee_enabled;
+	u8  eee_active;
+	u16 rxctl;
+	u8 in_pm;
+	u32 wol_supported;
+	u32 wolopts;
+	u8 disconnecting;
+	u8 chip_version;
+	u8 fw_version[4];
+	u8 is_ax88772d;
+	u8 ip_align;
+	u8 link;
+	u8 speed;
+	u8 full_duplex;
+	u8 rx_checksum;
+	u8 eeprom_read_cmd;
+	u8 eeprom_write_cmd;
+	u8 eeprom_wen;
+	u16 eeprom_block;
+	struct mii_bus *mdio;
+	struct phy_device *phydev;
+	struct phylink *phylink;
+	struct phylink_config phylink_config;
+	int (*resume)(struct usb_interface *intf);
+	int (*suspend)(struct usb_interface *intf, pm_message_t message);
+};
+
+struct ax88179_int_data {
+	__le32 intdata1;
+	__le32 intdata2;
+};
+
+struct ax_bulkin_settings {
+	unsigned char ctrl, timer_l, timer_h, size, ifg;
+};
+
+void ax88179_set_pm_mode(struct usbnet *dev, bool pm_mode);
+int __ax88179_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index, u16 size, void *data);
+int ax88179_read_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index, u16 size, void *data);
+int ax88179_write_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index, u16 size,
+		      const void *data);
+void ax88179_write_cmd_async(struct usbnet *dev, u8 cmd, u16 value, u16 index,
+			     u16 size, void *data);
+int ax88179_write_u16(struct usbnet *dev, u8 cmd, u16 value, u16 index, u16 data);
+int ax88179_write_u8(struct usbnet *dev, u8 cmd, u16 value, u16 index, u8 data);
+int ax88179_mdio_read(struct net_device *netdev, int phy_id, int loc);
+void ax88179_mdio_write(struct net_device *netdev, int phy_id, int loc, int val);
+struct ax88179_data *netdev2data(struct net_device *net);
+void ax88179_status(struct usbnet *dev, struct urb *urb);
+void ax88179_get_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo);
+int ax88179_set_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo);
+int ax88179_get_eeprom(struct net_device *net, struct ethtool_eeprom *eeprom, u8 *data);
+int ax88179_set_eeprom(struct net_device *net, struct ethtool_eeprom *eeprom, u8 *data);
+void ax88179_set_multicast(struct net_device *net);
+int ax88179_set_features(struct net_device *net, netdev_features_t features);
+void ax88179_get_mac_addr(struct usbnet *dev);
+int ax88179_change_mtu(struct net_device *net, int new_mtu);
+int ax88179_set_mac_addr(struct net_device *net, void *p);
+int ax88179_suspend_wrapper(struct usb_interface *intf, pm_message_t message);
+int ax88179_resume_wrapper(struct usb_interface *intf);
+
+extern const struct driver_info ax88179a_info;
+extern const struct driver_info ax88772d_info;
+extern const struct driver_info ax88279_info;
+
+#endif /*__LINUX_USBNET_AX88179_H */
diff --git a/drivers/net/usb/ax88179a_devices.c b/drivers/net/usb/ax88179a_devices.c
new file mode 100644
index 0000000000000..f2140e2bb4431
--- /dev/null
+++ b/drivers/net/usb/ax88179a_devices.c
@@ -0,0 +1,1285 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include <linux/module.h>
+#include <linux/phylink.h>
+#include <linux/if_vlan.h>
+#include "ax88179_lib.h"
+
+static int ax88179a_reset(struct usbnet *dev);
+
+#define AX88279_EEPROM_LEN			0x4000
+#define AX88179A_EEPROM_LEN			(32 * 20)
+
+enum ax_bulk_in_speeds {
+	BULK_IN_SPEED_2G5 = 0,
+	BULK_IN_SPEED_1G_SS = 1,
+	BULK_IN_SPEED_1G_HS   = 2,
+	BULK_IN_SPEED_100_FULL_SS = 3,
+	BULK_IN_SPEED_100_HALF_SS = 4,
+	BULK_IN_SPEED_100_FULL_HS = 5,
+	BULK_IN_SPEED_100_HALF_HS = 6,
+	BULK_IN_SPEED_FS = 7,
+};
+
+static const struct ax_bulkin_settings AX88179A_BULKIN_SIZE[] = {
+	[BULK_IN_SPEED_1G_SS]		= {5, 0x7B, 0x00, 0x17, 0x0F},
+	[BULK_IN_SPEED_1G_HS]		= {5, 0xC0, 0x02, 0x06, 0x0F},
+	[BULK_IN_SPEED_100_FULL_SS]	= {7, 0xF0, 0x00, 0x0C, 0x0F},
+	[BULK_IN_SPEED_100_HALF_SS]	= {6, 0x00, 0x00, 0x06, 0x0F},
+	[BULK_IN_SPEED_100_FULL_HS]	= {5, 0xC0, 0x04, 0x06, 0x0F},
+	[BULK_IN_SPEED_100_HALF_HS]	= {7, 0xC0, 0x04, 0x06, 0x0F},
+	[BULK_IN_SPEED_FS]		= {7, 0x00, 0x00, 0x03, 0x3F},
+};
+
+static const struct ax_bulkin_settings AX88772D_BULKIN_SIZE[] = {
+	[BULK_IN_SPEED_100_FULL_HS]	= {5, 0xC0, 0x04, 0x06, 0x0F},
+	[BULK_IN_SPEED_100_HALF_HS]	= {7, 0xC0, 0x04, 0x06, 0x0F},
+	[BULK_IN_SPEED_FS]		= {7, 0x00, 0x00, 0x03, 0x3F},
+};
+
+static const struct ax_bulkin_settings AX88279_BULKIN_SIZE[] = {
+	[BULK_IN_SPEED_2G5]		= {5, 0x10, 0x01, 0x11, 0x0F},
+	[BULK_IN_SPEED_1G_SS]		= {7, 0xB3, 0x01, 0x11, 0x0F},
+	[BULK_IN_SPEED_1G_HS]		= {7, 0xC0, 0x02, 0x06, 0x0F},
+	[BULK_IN_SPEED_100_FULL_SS]	= {7, 0x80, 0x01, 0x03, 0x0F},
+	[BULK_IN_SPEED_100_HALF_SS]	= {7, 0x80, 0x01, 0x03, 0x0F},
+	[BULK_IN_SPEED_100_FULL_HS]	= {7, 0x80, 0x01, 0x03, 0x0F},
+	[BULK_IN_SPEED_100_HALF_HS]	= {7, 0x80, 0x01, 0x03, 0x0F},
+	[BULK_IN_SPEED_FS]		= {7, 0x00, 0x00, 0x03, 0x3F},
+};
+
+static int ax88179_mdiobus_read(struct mii_bus *bus, int phy_id, int regnum)
+{
+	struct usbnet *dev = bus->priv;
+	struct ax88179_data *priv;
+	u16 res;
+	int ret;
+
+	priv = dev->driver_priv;
+	/* When reading PHYSID, return unused PHY-IDs from the ASIX vendor range */
+	if (phy_id == AX88179_PHY_ID && regnum == MII_PHYSID1)
+		return 0x003b;
+	if (phy_id == AX88179_PHY_ID && regnum == MII_PHYSID2) {
+		if (priv->chip_version == AX_VERSION_AX88179A && priv->is_ax88772d)
+			return 0x772d;
+		else if (priv->chip_version == AX_VERSION_AX88179A)
+			return 0x179a;
+		else if (priv->chip_version == AX_VERSION_AX88279)
+			return 0x2790;
+	}
+
+	ret = ax88179_read_cmd(dev, AX_ACCESS_PHY, phy_id, (__u16)regnum, 2, &res);
+	if (ret < 0)
+		return ret;
+	return res;
+}
+
+static int ax88179_mdiobus_write(struct mii_bus *bus, int phy_id, int regnum, u16 val)
+{
+	struct usbnet *dev = bus->priv;
+
+	return ax88179_write_u16(dev, AX_ACCESS_PHY, phy_id, (__u16)regnum, val);
+}
+
+static int ax179a_read_mmd(struct usbnet *dev, u16 dev_addr, u16 reg)
+{
+	u16 res;
+	int ret;
+
+	ret = ax88179_read_cmd(dev, AX88179A_PHY_CLAUSE45, dev_addr, reg, 2, &res);
+	if (ret < 0)
+		return ret;
+	return res;
+}
+
+static int ax179a_write_mmd(struct usbnet *dev, u16 dev_addr, u16 reg, u16 data)
+{
+	return ax88179_write_cmd(dev, AX88179A_PHY_CLAUSE45, dev_addr, reg, 2, &data);
+}
+
+static int ax88179_mdiobus_read_c45(struct mii_bus *bus, int addr, int devnum, int regnum)
+{
+	struct usbnet *dev = bus->priv;
+
+	if (addr != AX88179_PHY_ID)
+		return -EINVAL;
+
+	return ax179a_read_mmd(dev, devnum, regnum);
+}
+
+static int ax88179_mdiobus_write_c45(struct mii_bus *bus, int addr, int devnum,
+				     int regnum, u16 val)
+{
+	struct usbnet *dev = bus->priv;
+
+	if (addr != AX88179_PHY_ID)
+		return -EINVAL;
+
+	return ax179a_write_mmd(dev, devnum, regnum, val);
+}
+
+static void ax88179a_status(struct usbnet *dev, struct urb *urb)
+{
+	struct ax88179_data *data = dev->driver_priv;
+
+	if (urb->actual_length < 8)
+		return;
+
+	phylink_mac_interrupt(data->phylink);
+}
+
+static int ax88179a_suspend(struct usb_interface *intf, pm_message_t message)
+{
+	struct usbnet *dev = usb_get_intfdata(intf);
+	struct ax88179_data *priv;
+	u8 tmp8 = 0;
+	u16 tmp16;
+	int ret;
+
+	priv = dev->driver_priv;
+	ax88179_set_pm_mode(dev, true);
+
+	if (netif_running(dev->net)) {
+		rtnl_lock();
+		phylink_suspend(priv->phylink, !!priv->wolopts);
+		rtnl_unlock();
+	}
+
+	ret = usbnet_suspend(intf, message);
+	if (ret) {
+		rtnl_lock();
+		phylink_resume(priv->phylink);
+		rtnl_unlock();
+		return ret;
+	}
+
+	/* Enable WoL */
+	if (priv->wolopts) {
+		ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_MONITOR_MOD, 1, 1, &tmp8);
+		tmp8 &= ~(AX_MONITOR_MODE_RWLC | AX_MONITOR_MODE_RWMP);
+		if (priv->wolopts & WAKE_PHY)
+			tmp8 |= AX_MONITOR_MODE_RWLC;
+		if (priv->wolopts & WAKE_MAGIC)
+			tmp8 |= AX_MONITOR_MODE_RWMP;
+
+		ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MONITOR_MOD, 1, 1, &tmp8);
+
+		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);
+
+		if (priv->chip_version == AX_VERSION_AX88279)
+			ax88179_write_cmd(dev, AX88179A_WAKEUP_SETTING, 8,
+					  EPHY_LOW_POWER_EN | S5_WOL_EN
+					  | S5_WOL_LOW_POWER | 0x8000, 0, NULL);
+		else
+			ax88179_write_cmd(dev, AX88179A_WAKEUP_SETTING, 0,
+					  EPHY_LOW_POWER_EN, 0, NULL);
+
+	} else if (priv->chip_version == AX_VERSION_AX88279) {
+		ax88179_write_cmd(dev, AX88179A_WAKEUP_SETTING, 8, 0x8000, 0, NULL);
+	}
+
+	ax88179_set_pm_mode(dev, false);
+	return 0;
+}
+
+static int ax88179a_auto_detach(struct usbnet *dev)
+{
+	u16 tmp16;
+
+	tmp16 = AX88179A_AUTODETACH_DELAY;
+	ax88179_write_cmd(dev, AX88179A_AUTODETACH, tmp16, 0, 0, NULL);
+	return 0;
+}
+
+static int ax88179a_resume(struct usb_interface *intf)
+{
+	struct usbnet *dev = usb_get_intfdata(intf);
+	struct ax88179_data *ax179_data;
+	u8 reg8 = 0;
+	int ret;
+
+	ax179_data = dev->driver_priv;
+	ax88179_set_pm_mode(dev, true);
+
+	ret = ax88179_read_cmd(dev, AX88179A_PHY_POWER, 0, 0, 1, &reg8);
+	if (ret < 0) {
+		ax88179_set_pm_mode(dev, false);
+		return ret;
+	}
+	if (!(reg8 & AX_PHY_POWER)) {
+		reg8 = AX_PHY_POWER;
+		ax88179_write_cmd(dev, AX88179A_PHY_POWER, 0, 0, 1, &reg8);
+		msleep(250);
+	}
+	ax88179_write_cmd(dev, AX_FW_MODE, AX_FW_MODE_179A, 0, 0, NULL);
+
+	/* Now, that AX_FW_MODE_179A is enabled, the PHY needs a power-cycle.
+	 * PHY-power is re-enabled in ax88179a_reset()
+	 */
+	ax88179_write_u8(dev, AX88179A_PHY_POWER, 0, 0, 0);
+	msleep(250);
+
+	ax88179a_reset(dev);
+
+	if (netif_running(dev->net)) {
+		rtnl_lock();
+		phylink_resume(ax179_data->phylink);
+		rtnl_unlock();
+	}
+
+	ax88179_set_pm_mode(dev, false);
+
+	return usbnet_resume(intf);
+}
+
+static void ax88179a_get_drvinfo(struct net_device *net, struct ethtool_drvinfo *info)
+{
+	struct ax88179_data *priv = netdev2data(net);
+
+	/* Inherit standard device info */
+	usbnet_get_drvinfo(net, info);
+	if (priv->chip_version < AX_VERSION_AX88179A)
+		return;
+
+	snprintf(info->fw_version, sizeof(info->fw_version), "%d.%d.%d.%d",
+		 priv->fw_version[0], priv->fw_version[1],
+		 priv->fw_version[2], priv->fw_version[3]);
+}
+
+static void ax88179a_bulkin_config(struct usbnet *dev, u8 link_sts, u8 speed, bool full_duplex)
+{
+	struct ax88179_data *ax179_data = dev->driver_priv;
+	const struct ax_bulkin_settings *bulkin_data;
+	int index = 0;
+
+	switch (speed) {
+	case ETHER_LINK_2500:	/* AX88279 only */
+		index = BULK_IN_SPEED_2G5;
+		break;
+
+	case ETHER_LINK_1000:	/* AX88279 & AX88178A */
+		if (link_sts & AX_USB_SS)
+			index = BULK_IN_SPEED_1G_SS;
+		else if (link_sts & AX_USB_HS)
+			index = BULK_IN_SPEED_1G_HS;
+		break;
+
+	case ETHER_LINK_100:
+		if (link_sts & AX_USB_SS)
+			index = BULK_IN_SPEED_100_FULL_SS;
+		else if (link_sts & AX_USB_HS)
+			index = BULK_IN_SPEED_100_FULL_HS;
+		if (!full_duplex)
+			index++;
+		break;
+
+	case ETHER_LINK_10:
+		index = BULK_IN_SPEED_FS;
+		break;
+
+	default:	/* No link */
+		index = BULK_IN_SPEED_FS;
+	}
+
+	if (link_sts & AX_USB_FS)
+		index = BULK_IN_SPEED_FS;
+
+	if (ax179_data->chip_version == AX_VERSION_AX88279) {
+		bulkin_data = AX88279_BULKIN_SIZE;
+	} else {
+		if (ax179_data->is_ax88772d)
+			bulkin_data = AX88772D_BULKIN_SIZE;
+		else
+			bulkin_data = AX88179A_BULKIN_SIZE;
+	}
+
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_BULKIN_QCTRL, 5, 5, &bulkin_data[index]);
+}
+
+static void ax88179a_get_pauseparam(struct net_device *net, struct ethtool_pauseparam *pause)
+{
+	struct ax88179_data *data = netdev2data(net);
+
+	phylink_ethtool_get_pauseparam(data->phylink, pause);
+}
+
+static int ax88179a_set_pauseparam(struct net_device *net, struct ethtool_pauseparam *pause)
+{
+	struct ax88179_data *data = netdev2data(net);
+
+	return phylink_ethtool_set_pauseparam(data->phylink, pause);
+}
+
+static int ax88179a_get_link_ksettings(struct net_device *net,
+				       struct ethtool_link_ksettings *cmd)
+{
+	struct ax88179_data *data = netdev2data(net);
+
+	return phylink_ethtool_ksettings_get(data->phylink, cmd);
+}
+
+static int ax88179a_set_link_ksettings(struct net_device *net,
+				       const struct ethtool_link_ksettings *cmd)
+{
+	struct ax88179_data *data = netdev2data(net);
+
+	return phylink_ethtool_ksettings_set(data->phylink, cmd);
+}
+
+static int ax88179a_get_eeprom_len(struct net_device *net)
+{
+	struct ax88179_data *ax179_data = netdev2data(net);
+
+	if (ax179_data->chip_version >= AX_VERSION_AX88279)
+		return AX88279_EEPROM_LEN;
+	else
+		return AX88179A_EEPROM_LEN;
+}
+
+static int ax88179a_get_eee(struct net_device *net, struct ethtool_keee *edata)
+{
+	struct ax88179_data *ax179_data = netdev2data(net);
+
+	return phylink_ethtool_get_eee(ax179_data->phylink, edata);
+}
+
+static int ax88179a_set_eee(struct net_device *net, struct ethtool_keee *edata)
+{
+	struct ax88179_data *ax179_data = netdev2data(net);
+
+	return phylink_ethtool_set_eee(ax179_data->phylink, edata);
+}
+
+static const struct ethtool_ops ax88179a_ethtool_ops = {
+	.get_drvinfo            = ax88179a_get_drvinfo,
+	.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		= ax88179a_get_eeprom_len,
+	.get_eeprom		= ax88179_get_eeprom,
+	.set_eeprom		= ax88179_set_eeprom,
+	.get_eee		= ax88179a_get_eee,
+	.set_eee		= ax88179a_set_eee,
+	.nway_reset		= phy_ethtool_nway_reset,
+	.get_link_ksettings	= ax88179a_get_link_ksettings,
+	.set_link_ksettings	= ax88179a_set_link_ksettings,
+	.get_pauseparam		= ax88179a_get_pauseparam,
+	.set_pauseparam		= ax88179a_set_pauseparam,
+	.get_ts_info		= ethtool_op_get_ts_info,
+};
+
+static int ax88179a_vlan_rx_kill_vid(struct net_device *net, __be16 proto, u16 vid)
+{
+	struct usbnet *dev = netdev_priv(net);
+	u8 vlan_ctrl;
+	int ret = 0;
+	u16 reg16;
+	u8 reg8;
+
+	ret = ax88179_read_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_CONTROL, 1, 1, &reg8);
+	if (ret < 0)
+		return ret;
+	vlan_ctrl = reg8;
+
+	/* Address */
+	reg8 = (vid / 16);
+	ret = ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_ADDRESS, 1, 1, &reg8);
+	if (ret < 0)
+		return ret;
+
+	/* Data */
+	reg8 = vlan_ctrl | AX_VLAN_CONTROL_RD;
+	ret = ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_CONTROL, 1, 1, &reg8);
+	if (ret < 0)
+		return ret;
+
+	ret = ax88179_read_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_DATA0, 2, 2, &reg16);
+	if (ret < 0)
+		return ret;
+	reg16 &= ~(1 << (vid % 16));
+	ret = ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_DATA0, 2, 2, &reg16);
+	if (ret < 0)
+		return ret;
+
+	reg8 = vlan_ctrl | AX_VLAN_CONTROL_WE;
+	ret = ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_CONTROL, 1, 1, &reg8);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static int ax88179a_vlan_rx_add_vid(struct net_device *net, __be16 proto, u16 vid)
+{
+	struct usbnet *dev = netdev_priv(net);
+	u8 vlan_ctrl;
+	int ret = 0;
+	u16 reg16;
+	u8 reg8;
+
+	ret = ax88179_read_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_CONTROL, 1, 1, &reg8);
+	if (ret < 0)
+		return ret;
+	vlan_ctrl = reg8;
+
+	/* Address */
+	reg8 = (vid / 16);
+	ret = ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_ADDRESS, 1, 1, &reg8);
+	if (ret < 0)
+		return ret;
+
+	/* Data */
+	reg8 = vlan_ctrl | AX_VLAN_CONTROL_RD;
+	ret = ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_CONTROL, 1, 1, &reg8);
+	if (ret < 0)
+		return ret;
+
+	ret = ax88179_read_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_DATA0, 2, 2, &reg16);
+	if (ret < 0)
+		return ret;
+	reg16 |= (1 << (vid % 16));
+	ret = ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_DATA0, 2, 2, &reg16);
+	if (ret < 0)
+		return ret;
+
+	reg8 = vlan_ctrl | AX_VLAN_CONTROL_WE;
+	ret = ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_CONTROL, 1, 1, &reg8);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static void ax88179a_mdio_unregister(struct ax88179_data *data)
+{
+	mdiobus_unregister(data->mdio);
+	mdiobus_free(data->mdio);
+}
+
+static int ax88179a_init_phy(struct usbnet *dev)
+{
+	struct ax88179_data *data = dev->driver_priv;
+	int ret;
+
+	data->phydev = mdiobus_get_phy(data->mdio, AX88179_PHY_ID);
+	if (!data->phydev) {
+		netdev_err(dev->net, "Could not find PHY\n");
+		return -ENODEV;
+	}
+
+	data->phydev->irq = PHY_MAC_INTERRUPT;
+	ret = phylink_connect_phy(data->phylink, data->phydev);
+	if (ret) {
+		netdev_err(dev->net, "Could not connect PHY\n");
+		return ret;
+	}
+
+	phy_suspend(data->phydev);
+	data->phydev->mac_managed_pm = true;
+
+	phy_attached_info(data->phydev);
+
+	return 0;
+}
+
+static void ax88179a_mac_config(struct phylink_config *config, unsigned int mode,
+				const struct phylink_link_state *state)
+{
+	/* Nothing to do */
+}
+
+static void ax88179a_mac_link_down(struct phylink_config *config,
+				   unsigned int mode, phy_interface_t interface)
+{
+	/* Nothing to do */
+}
+
+static void ax88179a_mac_link_up(struct phylink_config *config,
+				 struct phy_device *phy,
+				 unsigned int phy_mode, phy_interface_t interface,
+				 int speed, int duplex,
+				 bool tx_pause, bool rx_pause)
+{
+	struct usbnet *dev = netdev_priv(to_net_dev(config->dev));
+	struct ax88179_data *ax179_data = dev->driver_priv;
+	u8 link_sts = 0, reg8[3];
+	u8 bulk_config_speed = 0;
+	u16 mode;
+
+	/* Stop RX/TX for link configuration */
+	ax88179_write_u16(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, AX_RX_CTL_STOP);
+	ax88179_write_u8(dev, AX_ACCESS_MAC, AX88179A_MAC_PATH, 1, 0);
+
+	ax88179_write_u8(dev, AX_ACCESS_MAC, AX88179A_MAC_CDC_DELAY_TX, 1, 0xa5);
+
+	ax88179_write_u16(dev, AX_ACCESS_MAC, AX_PAUSE_WATERLVL_LOW, 2, 0x0410);
+
+	ax88179_write_u8(dev, AX_ACCESS_MAC, AX88179A_ETH_TX_GAP, 1, 0);
+
+	ax88179_write_u8(dev, AX_ACCESS_MAC, AX88179A_EP5_EHR, 1, 0x07);
+
+	ax88179_write_u8(dev, AX_ACCESS_MAC, AX88179A_NEW_PAUSE_CTRL, 1,
+			 0x28 | AX_NEW_PAUSE_EN);
+
+	mode = AX_MEDIUM_RECEIVE_EN;
+	if (tx_pause)
+		mode |= AX_MEDIUM_TXFLOW_CTRLEN;
+	if (rx_pause)
+		mode |= AX_MEDIUM_RXFLOW_CTRLEN;
+
+	switch (speed) {
+	case SPEED_2500:
+		reg8[0] = 0x00;
+		reg8[1] = 0xF8;
+		reg8[2] = 0x07;
+		ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_MAC_TX_PAUSE, 3, 3, reg8);
+
+		reg8[0] = 0x78;
+		reg8[1] = (AX_LSOFC_WCNT_7_ACCESS << 5);
+		reg8[2] = 0;
+		ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_MAC_RX_STATUS_CDC, 3, 3, reg8);
+
+		ax88179_write_u8(dev, AX_ACCESS_MAC, AX88179A_MAC_RX_DATA_CDC_CNT, 1, 0x40);
+		ax88179_write_u8(dev, AX_ACCESS_MAC, AX88179A_MAC_RX_DATA_CDC_CNT + 1, 1,
+				 AX_MAC_MIQFFCTRL_FORMAT | AX_MAC_MIQFFCTRL_DROP_CRC |
+				 AX_MAC_LSO_ERR_EN);
+
+		ax88179_write_u8(dev, AX_ACCESS_MAC, AX88179A_BFM_DATA, 1, AX_XGMII_EN);
+
+		ax88179_write_u8(dev, AX_ACCESS_MAC, AX88179A_MAC_LSO_ENHANCE_CTRL, 1,
+				 0x1C | AX_LSO_ENHANCE_EN);
+
+		mode |= AX_MEDIUM_GIGAMODE | AX_MEDIUM_FULL_DUPLEX;
+		bulk_config_speed = ETHER_LINK_2500;
+
+		break;
+
+	case SPEED_1000:
+		mode |= AX_MEDIUM_GIGAMODE;
+		bulk_config_speed = ETHER_LINK_1000;
+		fallthrough;
+
+	case SPEED_100:
+		reg8[0] = 0x78;
+		reg8[1] = (AX_LSOFC_WCNT_7_ACCESS << 5) | AX_GMII_CRC_APPEND;
+		reg8[2] = 0;
+		ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_MAC_RX_STATUS_CDC, 3, 3, reg8);
+
+		ax88179_write_u8(dev, AX_ACCESS_MAC, AX88179A_MAC_RX_DATA_CDC_CNT, 1, 0x40);
+		if (!bulk_config_speed)
+			bulk_config_speed = ETHER_LINK_100;
+		break;
+
+	case SPEED_10:
+		reg8[0] = 0xFA;
+		reg8[1] = (AX_LSOFC_WCNT_7_ACCESS << 5) | AX_GMII_CRC_APPEND;
+		reg8[2] = 0xFF;
+		ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_MAC_RX_STATUS_CDC, 3, 3, reg8);
+
+		ax88179_write_u8(dev, AX_ACCESS_MAC, AX88179A_MAC_RX_DATA_CDC_CNT, 1, 0xFA);
+
+		bulk_config_speed = ETHER_LINK_10;
+		break;
+	}
+
+	ax88179_read_cmd(dev, AX_ACCESS_MAC, PHYSICAL_LINK_STATUS, 1, 1, &link_sts);
+	ax88179a_bulkin_config(dev, link_sts, bulk_config_speed, !!duplex);
+
+	if (speed != SPEED_2500)
+		ax88179_write_u8(dev, AX_ACCESS_MAC, AX88179A_BFM_DATA, 1, 0);
+
+	if (duplex)
+		mode |= AX_MEDIUM_FULL_DUPLEX;
+
+	rtnl_lock();
+	if (dev->net->mtu > 1500)
+		mode |= AX_MEDIUM_JUMBO_EN;
+	ax88179_write_u16(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE, 2, mode);
+
+	ax88179_write_u16(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, READ_ONCE(ax179_data->rxctl));
+	rtnl_unlock();
+
+	ax88179_write_u8(dev, AX_ACCESS_MAC, AX88179A_MAC_PATH, 1,
+			 AX_MAC_RX_PATH_READY | AX_MAC_TX_PATH_READY);
+}
+
+static void ax88179a_mac_disable_tx_lpi(struct phylink_config *config)
+{
+	struct usbnet *dev = netdev_priv(to_net_dev(config->dev));
+
+	ax88179_write_cmd(dev, AX_GPHY_CTL, AX_GPHY_EEE_CTRL, false, 0, NULL);
+}
+
+static int ax88179a_mac_enable_tx_lpi(struct phylink_config *config, u32 timer, bool tx_clk_stop)
+{
+	struct usbnet *dev = netdev_priv(to_net_dev(config->dev));
+
+	/* AX88179A does not provide LPI timer registers */
+	return ax88179_write_cmd(dev, AX_GPHY_CTL, AX_GPHY_EEE_CTRL, true, 0, NULL);
+}
+
+static const struct phylink_mac_ops ax88179a_phylink_mac_ops = {
+	.mac_config = ax88179a_mac_config,
+	.mac_link_down = ax88179a_mac_link_down,
+	.mac_link_up = ax88179a_mac_link_up,
+	.mac_disable_tx_lpi = ax88179a_mac_disable_tx_lpi,
+	.mac_enable_tx_lpi = ax88179a_mac_enable_tx_lpi,
+};
+
+static int ax88179a_phylink_setup(struct usbnet *dev)
+{
+	struct ax88179_data *data = dev->driver_priv;
+	phy_interface_t phy_if_mode;
+	struct phylink *phylink;
+
+	data->phylink_config.dev = &dev->net->dev;
+	data->phylink_config.type = PHYLINK_NETDEV;
+	data->phylink_config.mac_capabilities = MAC_SYM_PAUSE | MAC_ASYM_PAUSE | MAC_100;
+	if (data->is_ax88772d)
+		data->phylink_config.mac_capabilities |= MAC_10;
+	else if (data->chip_version < AX_VERSION_AX88279)
+		data->phylink_config.mac_capabilities |= MAC_10 | MAC_1000;
+	else
+		data->phylink_config.mac_capabilities |= MAC_1000 | MAC_2500FD;
+
+	if (!data->is_ax88772d) {
+		data->phylink_config.lpi_capabilities = MAC_100FD | MAC_1000FD;
+		data->phylink_config.eee_enabled_default = false;
+	}
+
+	if (data->chip_version == AX_VERSION_AX88279) {
+		__set_bit(PHY_INTERFACE_MODE_2500BASEX,
+			  data->phylink_config.supported_interfaces);
+		__set_bit(PHY_INTERFACE_MODE_SGMII,
+			  data->phylink_config.supported_interfaces);
+		phy_if_mode = PHY_INTERFACE_MODE_2500BASEX;
+	} else {
+		__set_bit(PHY_INTERFACE_MODE_SGMII,
+			  data->phylink_config.supported_interfaces);
+		phy_if_mode = PHY_INTERFACE_MODE_SGMII;
+	}
+
+	memcpy(data->phylink_config.lpi_interfaces,
+	       data->phylink_config.supported_interfaces,
+	       sizeof(data->phylink_config.lpi_interfaces));
+
+	phylink = phylink_create(&data->phylink_config, dev->net->dev.fwnode,
+				 phy_if_mode, &ax88179a_phylink_mac_ops);
+	if (IS_ERR(phylink))
+		return PTR_ERR(phylink);
+
+	data->phylink = phylink;
+	return 0;
+}
+
+static int ax88179a_init_mdio(struct usbnet *dev)
+{
+	struct ax88179_data *data = dev->driver_priv;
+	int ret;
+
+	data->mdio = mdiobus_alloc();
+	if (!data->mdio)
+		return -ENOMEM;
+
+	data->mdio->priv = dev;
+	data->mdio->read = ax88179_mdiobus_read;
+	data->mdio->write = ax88179_mdiobus_write;
+	data->mdio->read_c45 = ax88179_mdiobus_read_c45;
+	data->mdio->write_c45 = ax88179_mdiobus_write_c45;
+	data->mdio->name = "AX88179A MDIO Bus";
+	data->mdio->phy_mask = ~(1 << AX88179_PHY_ID);
+	/* mii bus name is usb-<usb bus number>-<usb device number> */
+	snprintf(data->mdio->id, MII_BUS_ID_SIZE, "usb-%03d:%03d",
+		 dev->udev->bus->busnum, dev->udev->devnum);
+
+	ret = mdiobus_register(data->mdio);
+	if (ret) {
+		netdev_err(dev->net, "Could not register MDIO bus (err %d)\n", ret);
+		mdiobus_free(data->mdio);
+		data->mdio = NULL;
+	}
+
+	return ret;
+}
+
+static int ax88179a_mii_ioctl(struct net_device *net, struct ifreq *rq, int cmd)
+{
+	struct ax88179_data *data = netdev2data(net);
+
+	return phylink_mii_ioctl(data->phylink, rq, cmd);
+}
+
+static int ax88179a_set_features(struct net_device *net, netdev_features_t features)
+{
+	struct usbnet *dev = netdev_priv(net);
+	netdev_features_t changed;
+	int ret;
+	u8 tmp;
+
+	changed = net->features ^ features;
+
+	ret = ax88179_set_features(net, features);
+	if (ret)
+		return ret;
+
+	if (changed & NETIF_F_HW_VLAN_CTAG_FILTER) {
+		if (features & NETIF_F_HW_VLAN_CTAG_FILTER) {
+			for (int i = 0; i < 256; i++) {
+				/* Address */
+				tmp = i;
+				ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_ADDRESS,
+						  1, 1, &tmp);
+				/* Data */
+				ax88179_write_u16(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_DATA0,
+						  2, 0);
+				ax88179_write_u8(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_CONTROL,
+						 1, AX_VLAN_CONTROL_WE);
+			}
+			ret = ax88179_read_cmd(dev, AX_ACCESS_MAC,
+					       AX88179A_VLAN_ID_CONTROL, 1, 1, &tmp);
+			if (ret < 0)
+				return ret;
+			tmp ^= AX_VLAN_CONTROL_VFE;
+			ret = ax88179_write_cmd(dev, AX_ACCESS_MAC,
+						AX88179A_VLAN_ID_CONTROL, 1, 1, &tmp);
+			if (ret < 0)
+				return ret;
+		}
+	}
+
+	if (changed & NETIF_F_HW_VLAN_CTAG_RX) {
+		ret = ax88179_read_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_CONTROL,
+				       1, 1, &tmp);
+		if (ret < 0)
+			return ret;
+		tmp ^= AX_VLAN_CONTROL_VSO;
+		ret = ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_CONTROL,
+					1, 1, &tmp);
+		if (ret < 0)
+			return ret;
+	}
+	return 0;
+}
+
+static const struct net_device_ops ax88179a_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	= dev_get_tstats64,
+	.ndo_change_mtu		= ax88179_change_mtu,
+	.ndo_set_mac_address	= ax88179_set_mac_addr,
+	.ndo_validate_addr	= eth_validate_addr,
+	.ndo_eth_ioctl		= ax88179a_mii_ioctl,
+	.ndo_set_rx_mode	= ax88179_set_multicast,
+	.ndo_set_features	= ax88179a_set_features,
+	.ndo_vlan_rx_add_vid	= ax88179a_vlan_rx_add_vid,
+	.ndo_vlan_rx_kill_vid	= ax88179a_vlan_rx_kill_vid,
+};
+
+static int ax88179a_bind(struct usbnet *dev, struct usb_interface *intf)
+{
+	struct usb_device *udev = interface_to_usbdev(intf);
+	struct ax88179_data *ax179_data;
+	int ret;
+
+	/* Check if vendor configuration */
+	if (udev->actconfig->desc.bConfigurationValue != 1) {
+		netdev_info(dev->net, "Switching to vendor mode\n");
+		usb_driver_set_configuration(udev, 1);
+		return -ENODEV;
+	}
+
+	ret = usbnet_get_endpoints(dev, intf);
+	if (ret < 0)
+		return ret;
+
+	ax179_data = kzalloc_obj(*ax179_data);
+	if (!ax179_data)
+		return -ENOMEM;
+
+	dev->driver_priv = ax179_data;
+
+	ret = ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_CHIP_STATUS,
+			       1, 1, &ax179_data->chip_version);
+	if (ret < 0)
+		goto err_nodev;
+
+	ax179_data->chip_version = (ax179_data->chip_version & 0xf0) >> 4;
+	ax179_data->is_ax88772d = 0;
+	if (ax179_data->chip_version == AX_VERSION_AX88179A) {
+		if (le16_to_cpu(udev->descriptor.bcdDevice) == 0x300)
+			ax179_data->is_ax88772d = 1;
+	}
+
+	for (int i = 0; i < 3; i++) {
+		ret = ax88179_read_cmd(dev, AX88179A_ACCESS_BL, AX88179A_SW_VERSION + i,
+				       1, 1, &ax179_data->fw_version[i]);
+		if (ret < 0)
+			ax179_data->fw_version[i] = 0xff;
+	}
+	ret = ax88179_read_cmd(dev, AX88179A_ACCESS_BL, AX88179A_SW_REVISION,
+			       1, 1, &ax179_data->fw_version[3]);
+	if (ret < 0)
+		ax179_data->fw_version[3] = 0xff;
+
+	netdev_info(dev->net, "AX88179A/279/772D Chip Version: %x, FW: %d.%d.%d.%d\n",
+		    ax179_data->chip_version,
+		    ax179_data->fw_version[0], ax179_data->fw_version[1],
+		    ax179_data->fw_version[2], ax179_data->fw_version[3]);
+
+	/* The AX88279 requires both the AX_RX_CTL_IPE and AX_RX_CTL_DROPCRCERR
+	 * bits set in AX_RX_CTL for creating correct RX-URBs. AX_RX_CTL_DROPCRCERR
+	 * is anyway set for all chips, make sure AX_RX_CTL_IPE is set via ip_align.
+	 * Also configure eeprom access parameters.
+	 */
+	if (ax179_data->chip_version == AX_VERSION_AX88279) {
+		ax179_data->ip_align = 1;
+		ax179_data->eeprom_read_cmd = AX88179A_FLASH_READ;
+		ax179_data->eeprom_write_cmd = AX88179A_FLASH_WRITE;
+		ax179_data->eeprom_block = 256;
+		ax179_data->eeprom_wen = 1;
+	} else {
+		ax179_data->ip_align = 0;
+		ax179_data->eeprom_read_cmd = AX_ACCESS_EFUS;
+		ax179_data->eeprom_write_cmd = AX_ACCESS_EFUS;
+		ax179_data->eeprom_block = 20;
+		ax179_data->eeprom_wen = 0;
+	}
+
+	ax179_data->resume = ax88179a_resume;
+	ax179_data->suspend = ax88179a_suspend;
+
+	dev->net->netdev_ops = &ax88179a_netdev_ops;
+	dev->net->ethtool_ops = &ax88179a_ethtool_ops;
+	dev->net->needed_headroom = 8;
+	dev->net->needed_tailroom = 8;
+	dev->net->min_mtu = ETH_MIN_MTU;
+	dev->hard_mtu = 9 * 1024;
+	dev->net->max_mtu = dev->hard_mtu - dev->net->hard_header_len;
+
+	if (!ax179_data->is_ax88772d)
+		dev->mii.supports_gmii = 1;
+
+	dev->net->features |= NETIF_F_SG | NETIF_F_IP_CSUM |
+			      NETIF_F_IPV6_CSUM | NETIF_F_RXCSUM | NETIF_F_TSO |
+			      NETIF_F_HW_VLAN_CTAG_TX;
+
+	dev->net->hw_features |= dev->net->features;
+	dev->net->hw_features |= NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_HW_VLAN_CTAG_FILTER;
+
+	dev->net->vlan_features = NETIF_F_SG | NETIF_F_IP_CSUM |
+				  NETIF_F_IPV6_CSUM | NETIF_F_RXCSUM | NETIF_F_TSO;
+
+	netif_set_tso_max_size(dev->net, 16384);
+
+	/* Enable Transmission of Link Speed byte in interrupt URB */
+	ax88179_write_cmd(dev, AX_FW_MODE, AX_FW_MODE_179A, 0, 0, NULL);
+	ax88179_write_cmd(dev, AX_RELOAD_EEPROM_EFUSE, 0, 0, 0, NULL);
+
+	/* Read MAC address from DTB or ASIX chip */
+	ax88179_get_mac_addr(dev);
+	memcpy(dev->net->perm_addr, dev->net->dev_addr, ETH_ALEN);
+
+	/* Power PHY for probing */
+	ax88179_write_u8(dev, AX88179A_PHY_POWER, 0, 0, AX_PHY_POWER);
+	msleep(250);
+
+	ret = ax88179a_init_mdio(dev);
+	if (ret)
+		goto err_nodev;
+
+	ret = ax88179a_phylink_setup(dev);
+	if (ret)
+		goto phylink_err;
+
+	ret = ax88179a_init_phy(dev);
+	if (ret)
+		goto initphy_err;
+
+	/* Keep this interface runtime-PM active by taking a usage ref.
+	 * Prevents runtime suspend while bound and avoids resume paths
+	 * that could deadlock (autoresume under RTNL while USB PM lock
+	 * is held, phylink/MDIO wants RTNL).
+	 */
+	pm_runtime_get_noresume(&intf->dev);
+
+	return 0;
+
+initphy_err:
+	phylink_destroy(ax179_data->phylink);
+phylink_err:
+	ax88179a_mdio_unregister(ax179_data);
+err_nodev:
+	kfree(ax179_data);
+	ax179_data = NULL;
+
+	return ret;
+}
+
+static void ax88179a_unbind(struct usbnet *dev, struct usb_interface *intf)
+{
+	struct ax88179_data *ax179_data = dev->driver_priv;
+
+	/* Configure RX control register => stop operation */
+	ax88179_write_u16(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, AX_RX_CTL_STOP);
+
+	rtnl_lock();
+	phylink_disconnect_phy(ax179_data->phylink);
+	rtnl_unlock();
+	phylink_destroy(ax179_data->phylink);
+	ax88179a_mdio_unregister(ax179_data);
+
+	ax88179_write_u8(dev, AX88179A_PHY_POWER, 0, 0, 0);
+
+	kfree(ax179_data);
+}
+
+static void ax88179a_rx_checksum(struct sk_buff *skb, u64 pkt_desc)
+{
+	u32 pkt_type;
+
+	skb->ip_summed = CHECKSUM_NONE;
+	/* checksum error bit is set */
+	if (pkt_desc & AX179A_RX_PD_L4_ERR || pkt_desc & AX179A_RX_PD_L3_ERR)
+		return;
+
+	pkt_type = pkt_desc & AX179A_RX_PD_L4_TYPE_MASK;
+	/* It must be a TCP or UDP packet with a valid checksum */
+	if (pkt_type == AX179A_RX_PD_L4_TCP || pkt_type == AX179A_RX_PD_L4_UDP)
+		skb->ip_summed = CHECKSUM_UNNECESSARY;
+}
+
+static int ax88179a_rx_fixup(struct usbnet *dev, struct sk_buff *skb)
+{
+	struct ax88179_data *ax179_data = dev->driver_priv;
+	struct sk_buff *ax_skb;
+	u32 hdr_off, pkt_end;
+	u64 *pkt_desc_ptr;
+	u16 vlan_tag;
+	u16 pkt_cnt;
+	u64 rx_hdr;
+
+	/* SKB contents for AX179A-based chips:
+	 *   <packet 1>
+	 *   ...
+	 *   <packet N>
+	 *   <per-packet metadata entry 1>
+	 *   ...
+	 *   <per-packet metadata entry N>
+	 *   <rx_hdr>
+	 *
+	 * where:
+	 *   <packet N> contains pkt_len data bytes and padding:
+	 *		2 bytes of IP alignment (optional, depends on AX_RX_CTL_IPE flag)
+	 *		packet data received
+	 *		optional padding to 8-bytes boundary
+	 *   <per-packet metadata entry N> contains 8 bytes:
+	 *		pkt_len and fields AX_RXHDR_*
+	 *   <rx-hdr>	contains 8 bytes:
+	 *		pkt_cnt and hdr_off (offset of <per-packet metadata entry 1>)
+	 *
+	 * pkt_cnt is number of entries in the per-packet metadata array.
+	 */
+
+	if (!skb || skb->len < sizeof(rx_hdr))
+		goto err;
+
+	/* RX Descriptor Header */
+	skb_trim(skb, skb->len - sizeof(rx_hdr));
+	rx_hdr = *(u64 *)skb_tail_pointer(skb);
+	le64_to_cpus(rx_hdr);
+
+	/* Check these packets */
+	hdr_off = FIELD_GET(AX179A_RX_DH_DESC_OFFSET, rx_hdr);
+	pkt_cnt = rx_hdr & AX179A_RX_DH_PKT_CNT_MASK;
+
+	/* Consistency check header position */
+	if (hdr_off != skb->len - (pkt_cnt * sizeof(rx_hdr)))
+		goto err;
+
+	/* Make sure that the bounds of the metadata array are inside the SKB
+	 * (and in front of the counter at the end).
+	 */
+	if (pkt_cnt * 8 + hdr_off > skb->len)
+		goto err;
+
+	/* Packets must not overlap the metadata array */
+	skb_trim(skb, hdr_off);
+
+	if (!pkt_cnt)
+		goto err;
+
+	/* Get the first RX packet descriptor */
+	pkt_desc_ptr = (u64 *)(skb->data + hdr_off);
+
+	pkt_end = 0;
+	while (pkt_cnt--) {
+		u64 pkt_desc = le64_to_cpu(*pkt_desc_ptr);
+		u32 pkt_len_plus_padd;
+		u32 pkt_len;
+
+		pkt_len = FIELD_GET(AX179A_RX_PD_LEN, pkt_desc);
+		pkt_len -= (ax179_data->ip_align ? 2 : 0);
+		pkt_len_plus_padd = ((pkt_len + 7 + (ax179_data->ip_align ? 2 : 0)) & 0x7FFF8);
+
+		pkt_end += pkt_len_plus_padd;
+		if (pkt_end > hdr_off || (pkt_cnt == 0 && pkt_end != hdr_off))
+			goto err;
+
+		if (pkt_desc & AX179A_RX_PD_DROP || !(pkt_desc & AX179A_RX_PD_RX_OK) ||
+		    pkt_len > (dev->hard_mtu + AX179A_RX_HW_PAD) || pkt_len < 2 + ETH_HLEN) {
+			skb_pull(skb, pkt_len_plus_padd);
+
+			/* Next RX Packet Descriptor */
+			pkt_desc_ptr++;
+			continue;
+		}
+
+		ax_skb = netdev_alloc_skb_ip_align(dev->net, pkt_len);
+		if (!ax_skb)
+			goto err;
+
+		skb_put(ax_skb, pkt_len);
+		memcpy(ax_skb->data, skb->data + (ax179_data->ip_align ? AX179A_RX_HW_PAD : 0),
+		       pkt_len);
+
+		if (ax179_data->rx_checksum)
+			ax88179a_rx_checksum(ax_skb, pkt_desc);
+
+		if (pkt_desc & AX179A_RX_PD_VLAN) {
+			vlan_tag = pkt_desc >> AX179A_RX_PD_VLAN_SHIFT;
+			__vlan_hwaccel_put_tag(ax_skb, htons(ETH_P_8021Q), vlan_tag);
+		}
+
+		usbnet_skb_return(dev, ax_skb);
+		skb_pull(skb, pkt_len_plus_padd);
+
+		/* Next RX Packet Header */
+		pkt_desc_ptr++;
+	}
+
+	return 1;
+
+err:
+	return 0;
+}
+
+static struct sk_buff *ax88179a_tx_fixup(struct usbnet *dev, struct sk_buff *skb, gfp_t flags)
+{
+	u64 tx_desc = skb->len & AX179A_TX_DESC_LEN_MASK;
+	int frame_size = dev->maxpacket;
+	struct sk_buff *ax_skb;
+	u64 *tx_desc_ptr;
+	int padding_size;
+	int headroom;
+	int tailroom;
+	u16 tci = 0;
+
+	/* TSO MSS */
+	tx_desc |= FIELD_PREP(AX179A_TX_DESC_MSS, (u64)skb_shinfo(skb)->gso_size);
+
+	headroom = (skb->len + sizeof(tx_desc)) % 8;
+	padding_size = headroom ? 8 - headroom : 0;
+
+	if (((skb->len + sizeof(tx_desc) + padding_size) % frame_size) == 0) {
+		padding_size += 8;
+		tx_desc |= AX179A_TX_DESC_DROP_PADD;
+	}
+
+	if ((dev->net->features & NETIF_F_HW_VLAN_CTAG_TX) && (vlan_get_tag(skb, &tci) >= 0)) {
+		tx_desc |= AX179A_TX_DESC_VLAN;
+		tx_desc |= FIELD_PREP(AX179A_TX_DESC_VLAN_ID, (u64)tci);
+	}
+
+	if (!dev->can_dma_sg && (dev->net->features & NETIF_F_SG) && skb_linearize(skb)) {
+		dev_kfree_skb_any(skb);
+		return NULL;
+	}
+
+	headroom = skb_headroom(skb);
+	tailroom = skb_tailroom(skb);
+
+	if (!(headroom >= sizeof(tx_desc) && tailroom >= padding_size)) {
+		ax_skb = skb_copy_expand(skb, sizeof(tx_desc), padding_size, flags);
+		dev_kfree_skb_any(skb);
+		skb = ax_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));
+	put_unaligned_le64(tx_desc, tx_desc_ptr);
+
+	usbnet_set_skb_tx_stats(skb, 1, 0);
+
+	return skb;
+}
+
+static int ax88179a_reset(struct usbnet *dev)
+{
+	struct ax88179_data *ax179_data = dev->driver_priv;
+	u16 *tmp16;
+	u8 buf[5];
+	u16 rxctl;
+	u8 *tmp;
+
+	tmp16 = (u16 *)buf;
+	tmp = (u8 *)buf;
+
+	/* Power up ethernet PHY */
+	ax88179_write_u8(dev, AX88179A_PHY_POWER, 0, 0, AX_PHY_POWER);
+	msleep(250);
+
+	/* Ethernet PHY Auto Detach*/
+	ax88179a_auto_detach(dev);
+
+	ax88179_write_u8(dev, AX_ACCESS_MAC, AX88179A_MAC_BULK_OUT_CTRL, 1, AX_MAC_EFF_EN);
+
+	ax88179_write_u16(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 0);
+
+	ax88179_write_u8(dev, AX_ACCESS_MAC, AX_PAUSE_WATERLVL_LOW, 1, 0x04);
+	ax88179_write_u8(dev, AX_ACCESS_MAC, AX_PAUSE_WATERLVL_HIGH, 1, 0x10);
+
+	*tmp = 0;
+	if (dev->net->features & NETIF_F_HW_VLAN_CTAG_FILTER)
+		*tmp |= AX_VLAN_CONTROL_VFE;
+	if (dev->net->features & NETIF_F_HW_VLAN_CTAG_RX)
+		*tmp |= AX_VLAN_CONTROL_VSO;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_CONTROL, 1, 1, tmp);
+
+	ax88179_write_u8(dev, AX_ACCESS_MAC, AX88179A_MAC_BM_INT_MASK, 1, 0xff);
+
+	ax88179_write_u8(dev, AX_ACCESS_MAC, AX88179A_MAC_BM_RX_DMA_CTL, 1, 0);
+	ax88179_write_u8(dev, AX_ACCESS_MAC, AX88179A_MAC_BM_TX_DMA_CTL, 1, 0);
+	ax88179_write_u8(dev, AX_ACCESS_MAC, AX88179A_MAC_ARC_CTRL, 1, 0);
+	ax88179_write_u8(dev, AX_ACCESS_MAC, AX88179A_MAC_SWP_CTRL, 1, 0);
+	ax88179_write_u8(dev, AX_ACCESS_MAC, AX88179A_MAC_TX_HDR_CKSUM, 1, 0);
+
+	/* Read MAC address from DTB or asix chip */
+	ax88179_get_mac_addr(dev);
+	memcpy(dev->net->perm_addr, dev->net->dev_addr, ETH_ALEN);
+
+	/* The Bulk-Register configuration for the AX88179A is done in
+	 * ax88179a_mac_link_up(), once the link is up for a given link and USB-speed.
+	 */
+	if (ax179_data->is_ax88772d)
+		dev->rx_urb_size = 1024 * 24;
+	else
+		dev->rx_urb_size = 1024 * 48;
+
+	/* Enable checksum offload */
+	if (dev->net->features & NETIF_F_RXCSUM) {
+		ax88179_write_u8(dev, AX_ACCESS_MAC, AX_RXCOE_CTL, 1,
+				 AX_RXCOE_IP | AX_RXCOE_TCP | AX_RXCOE_UDP |
+				 AX_RXCOE_TCPV6 | AX_RXCOE_UDPV6);
+		ax179_data->rx_checksum = 1;
+	} else {
+		ax179_data->rx_checksum = 0;
+		ax88179_write_u8(dev, AX_ACCESS_MAC, AX_RXCOE_CTL, 1, 0);
+	}
+
+	ax88179_write_u8(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, 1,
+			 AX_TXCOE_IP | AX_TXCOE_TCP | AX_TXCOE_UDP |
+			 AX_TXCOE_TCPV6 | AX_TXCOE_UDPV6);
+
+	/* Configure RX control register => start operation */
+	rxctl = AX_RX_CTL_DROPCRCERR | AX_RX_CTL_START | AX_RX_CTL_AP |
+		AX_RX_CTL_AMALL | AX_RX_CTL_AB;
+	if (ax179_data->ip_align)
+		rxctl |= AX_RX_CTL_IPE;
+	if (dev->net->flags & IFF_PROMISC)
+		rxctl |= AX_RX_CTL_PRO;
+	WRITE_ONCE(ax179_data->rxctl, rxctl);
+	ax88179_write_u16(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, rxctl);
+
+	if (ax179_data->chip_version < AX_VERSION_AX88179A)
+		*tmp = AX_MONITOR_MODE_PMETYPE | AX_MONITOR_MODE_PMEPOL | AX_MONITOR_MODE_RWMP;
+	else
+		*tmp = 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_FULL_DUPLEX;
+	if (!ax179_data->is_ax88772d)
+		*tmp16 |= AX_MEDIUM_GIGAMODE;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE, 2, 2, tmp16);
+
+	/* Check if WoL is supported */
+	ax179_data->wol_supported = 0;
+	if (ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_MONITOR_MOD,
+			     1, 1, tmp) > 0)
+		ax179_data->wol_supported = WAKE_MAGIC | WAKE_PHY;
+
+	/* Ensure EEE state is off, consistent with phylink setup */
+	ax88179_write_cmd(dev, AX_GPHY_CTL, AX_GPHY_EEE_CTRL, false, 0, NULL);
+
+	/* ax88179a_reset() may also be called from resume context,
+	 * phylink_resume() will start phylink, then.
+	 */
+	if (!ax179_data->in_pm)
+		phylink_start(ax179_data->phylink);
+
+	return 0;
+}
+
+static int ax88179a_stop(struct usbnet *dev)
+{
+	struct ax88179_data *ax179_data = dev->driver_priv;
+	u16 reg16;
+
+	phylink_stop(ax179_data->phylink);
+
+	ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE, 2, 2, &reg16);
+	reg16 &= ~AX_MEDIUM_RECEIVE_EN;
+	ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE, 2, 2, &reg16);
+
+	ax88179_write_u16(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 0);
+
+	ax88179_write_u8(dev, AX88179A_PHY_POWER, 0, 0, 0);
+
+	return 0;
+}
+
+const struct driver_info ax88179a_info = {
+	.description = "ASIX AX88179A USB 3.2 Gigabit Ethernet",
+	.bind = ax88179a_bind,
+	.unbind = ax88179a_unbind,
+	.status = ax88179a_status,
+	.reset = ax88179a_reset,
+	.stop = ax88179a_stop,
+	.flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_MULTI_PACKET | FLAG_AVOID_UNLINK_URBS,
+	.rx_fixup = ax88179a_rx_fixup,
+	.tx_fixup = ax88179a_tx_fixup,
+};
+
+const struct driver_info ax88772d_info = {
+	.description = "ASIX AX88772D/E USB 2.0 Fast Ethernet",
+	.bind = ax88179a_bind,
+	.unbind = ax88179a_unbind,
+	.status = ax88179a_status,
+	.reset = ax88179a_reset,
+	.stop = ax88179a_stop,
+	.flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_MULTI_PACKET | FLAG_AVOID_UNLINK_URBS,
+	.rx_fixup = ax88179a_rx_fixup,
+	.tx_fixup = ax88179a_tx_fixup,
+};
+
+const struct driver_info ax88279_info = {
+	.description = "ASIX AX88279 USB 3.2 2.5Gigabit Ethernet",
+	.bind = ax88179a_bind,
+	.unbind = ax88179a_unbind,
+	.status = ax88179a_status,
+	.reset = ax88179a_reset,
+	.stop = ax88179a_stop,
+	.flags = FLAG_ETHER | FLAG_FRAMING_AX | FLAG_MULTI_PACKET | FLAG_AVOID_UNLINK_URBS,
+	.rx_fixup = ax88179a_rx_fixup,
+	.tx_fixup = ax88179a_tx_fixup,
+};
diff --git a/include/linux/phy.h b/include/linux/phy.h
index 7c5098a0dd6cd..4143bffdcdb9b 100644
--- a/include/linux/phy.h
+++ b/include/linux/phy.h
@@ -64,6 +64,7 @@ extern const int phy_basic_ports_array[3];
 #define PHY_RST_AFTER_CLK_EN	0x00000002
 #define PHY_POLL_CABLE_TEST	0x00000004
 #define PHY_ALWAYS_CALL_SUSPEND	0x00000008
+#define PHY_BROKEN_FORCED	0x00000010
 #define MDIO_DEVICE_IS_PHY	0x80000000
 
 /**
diff --git a/include/linux/phylink.h b/include/linux/phylink.h
index 3a88a69882a61..a7b6294cc4a07 100644
--- a/include/linux/phylink.h
+++ b/include/linux/phylink.h
@@ -768,6 +768,7 @@ int phylink_ethtool_set_eee(struct phylink *link, struct ethtool_keee *eee);
 int phylink_mii_ioctl(struct phylink *, struct ifreq *, int);
 int phylink_speed_down(struct phylink *pl, bool sync);
 int phylink_speed_up(struct phylink *pl);
+void phylink_mac_interrupt(struct phylink *pl);
 
 #define phylink_zero(bm) \
 	bitmap_zero(bm, __ETHTOOL_LINK_MODE_MASK_NBITS)

29/3 2026/10/04 05:45 llm kmsan-evaluator
1m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 49912
							output: 24
							thoughts: 17757
						

Thoughts:
**Analysis of the AX88179 USB Ethernet Driver Patch Series for KMSAN Needs**

Okay, let's break down this patch series and decide if we need to fire up KMSAN.  Remember, KMSAN is for catching *uninitialized memory* reads, stuff that KASAN, LOCKDEP, or the standard detectors would *not* find. We're looking for new or modified kernel structures that get sent to user space (think `copy_to_user`, netlink, ioctls), conditional logic based on potentially garbage data, allocations where fields are left uninitialized, or any bounds logic that lets us read uninitialized bytes.

So, here's what I've found, section by section, through the patch:

1.  **Main Changes/Driver Updates:** This looks like it's adding support for more ASIX AX88179 variants and new PHY drivers, updating configurations.

2.  **PHY Driver:** This adds and modifies PHY drivers in `ax88796b.c`. I checked the new functions and variables, specifically:
    *   `asix_ax88279_read_status`:  It initializes all of the variables before using them and returns a status, so that seems clean.
    *   `asix_ax88279_config_aneg`:  Again, all variables here are initialized before use, with a variable called `ret` being used in conditional logic.
    *   `asix_ax88279_get_features`: Nothing to see here.
    *   `asix_ax88772D_get_features`: Nothing special here either.

3.  **PHY Interface Driver Updates:** In `phy.c`, there's a check that prevents auto-negotiation if a specific flag is set. No new uninitialized variables introduced.

4.  **Phylink Updates:** A new function is added in `phylink.c` that calls other `phy_mac_interrupt` so this is also clean.

5.  **USB Driver Updates:** The changes in `Kconfig` and `Makefile` are just renames and modularization.

6.  **ax88179_178a.c Modifications:** Code and function definitions that were in `ax88179_178a.c` are moved to `ax88179_lib.c`. This includes several USB command reading and writing functions, plus some EEPROM and MAC address functions. Let’s break it down:

    *   `ax88179_read_cmd`: This looks okay. It uses `u16 buf = 0;` and `u32 buf = 0;` to initialize buffers when the size is 2 or 4 respectively, handling potential read errors by ensuring a 0 is always returned.
    *   `ax88179_mdio_read`: This calls `ax88179_read_cmd` with `size == 2`, which will be assigned, but always has the error.
    *   `ax88179_get_eeprom`:  This reads EEPROM data.  The critical point here is that it uses `kzalloc` to allocate the `eeprom_buff`, so the buffer is initialized to zero before any data is read into it.
    *   `ax88179_get_mac_addr`: uses `memset`.
    *   Now, I *do* see a potential issue with the `ax88179_mdio_read` where it initializes a `u16 res` but I don't see any code where that `res` is read, so this may not matter, this is only used in MDIO.

7.  **ax88179a\_devices.c Modifications:** Here are the updates to `ax88179a\_devices.c`:

    *   `ax88179_mdiobus_read` and `ax179a_read_mmd`: Both are similar to earlier reads. They call `ax88179_read_cmd`, but the variables are safe.
    *   `ax88179a_status`: This looks safe, checking the actual length of the `urb`.
    *   `ax88179a_suspend`: Makes sure that the variables are checked so it's safe.
    *   `ax88179a_resume`: Similarly, this reads and checks a register.
    *   `ax88179a_vlan_rx_kill_vid` and `ax8


30/3 2026/10/04 05:46 tool grepper
0m Args:
{
  "Expression": "#define le64_to_cpus"
}

Results:
{
  "Output": "include/linux/byteorder/generic.h-110-#define cpu_to_le64s __cpu_to_le64s\ninclude/linux/byteorder/generic.h:111:#define le64_to_cpus __le64_to_cpus\ninclude/linux/byteorder/generic.h-112-#define cpu_to_le32s __cpu_to_le32s\n"
}

31/3 2026/10/04 05:46 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 68832
							output: 24
							thoughts: 35
						

Thoughts:
Okay, here's my take on those thoughts, tailored for an expert audience:

**Analyzing the `__le64_to_cpus` Function Call**

Alright, so the first step in this process is to figure out the requirements of the `__le64_to_cpus` function. I need to understand its expected input and behavior. Typically, when encountering a function like this, the goal is to see what kind of data it handles. Since it seems to be related to endian conversion, I anticipate it deals with 64-bit little-endian values, translating them to the CPU's native endianness.

To get a better understanding of how this function operates in this context, I'll quickly use `grep` to search the codebase for the function name. This should give me examples of where and how it's being used. Specifically, I'll be looking for any clues about the arguments passed, the context in which the conversion is performed, and any related data structures involved. This `grep` search will be my primary method to immediately see some context. After that I may need to dive into the definition, but seeing usage patterns is usually a good starting point.



32/3 2026/10/04 05:46 tool grepper
0m Args:
{
  "Expression": "define __le64_to_cpus"
}

Results:
{
  "Output": "include/uapi/linux/byteorder/big_endian.h=89=static __always_inline __u16 __be16_to_cpup(const __be16 *p)\n--\ninclude/uapi/linux/byteorder/big_endian.h-93-#define __cpu_to_le64s(x) __swab64s((x))\ninclude/uapi/linux/byteorder/big_endian.h:94:#define __le64_to_cpus(x) __swab64s((x))\ninclude/uapi/linux/byteorder/big_endian.h-95-#define __cpu_to_le32s(x) __swab32s((x))\n--\ninclude/uapi/linux/byteorder/little_endian.h=89=static __always_inline __u16 __be16_to_cpup(const __be16 *p)\n--\ninclude/uapi/linux/byteorder/little_endian.h-93-#define __cpu_to_le64s(x) do { (void)(x); } while (0)\ninclude/uapi/linux/byteorder/little_endian.h:94:#define __le64_to_cpus(x) do { (void)(x); } while (0)\ninclude/uapi/linux/byteorder/little_endian.h-95-#define __cpu_to_le32s(x) do { (void)(x); } while (0)\n"
}

33/3 2026/10/04 05:46 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 69514
							output: 34
							thoughts: 1104
						

Thoughts:
**Analyzing Potential Uninitialized Memory Read**

Okay, let's break this down. First, I noticed the use of `__swab64s((x))`. My initial thought was that this function takes a POINTER and, on little-endian architectures, it's a no-op defined as a macro. On big-endian systems, it would be a swap. I then got hung up on how `rx_hdr` was passed directly. It *shouldn't* compile on big endian, but it does, so it must be a no-op on my amd64. But then I realized, this isn't the key to the uninitialized memory issue.

Now, let's look at `rx_fixup`. I see that a new `skb` (`ax_skb`) is allocated, data is copied into it, and then it's processed, including optional VLAN tagging and checksumming.

The critical part jumps out at me: `skb_pull(skb, pkt_len_plus_padd)`. Wait, what's *that* doing in the loop? I immediately see that there's a memcpy to the new `ax_skb` with the data from the source `skb`. Then, I spot `skb_pull(skb, pkt_len_plus_padd);`. The problem is that the source `skb->data` gets advanced by `pkt_len_plus_padd` in the original `skb`. So each subsequent packet data is from an advanced pointer.

Then, I try to understand where `pkt_desc_ptr` comes from. Before the loop, the code checks the bounds of the metadata array, trims the `skb` with `skb_trim(skb, hdr_off)`, and then *crucially* sets `pkt_desc_ptr` to a location *past* what is now `skb->len`. That's not right!

Now I am certain of the issue! The `skb_trim` call trims `skb->len` to `hdr_off`. But then, `pkt_desc_ptr` points *past* this trimmed length into the memory that was previously outside the `skb->len`. This is really bad.

Then, in the loop, `skb_pull(skb, ...)` advances `skb->data`. Does `pkt_desc_ptr` also move? No! It is incremented, `pkt_desc_ptr++`. In the next iteration, the `memcpy` uses `skb->data`, and because it's advanced, it's pointing to the next packet.

I need to understand if the program is reading uninitialized memory. `skb->data` moves forward by `pkt_len_plus_padd` and it never exceeds the original `skb->data + hdr_off`. So there isn't uninitialized data read in the memcpy.

So, where is this `skb` even coming from? I need to track back the allocation. Okay, it's allocated within the `usbnet` framework. The driver allocates an skb of size `dev->rx_urb_size` and submits a bulk IN URB. The host controller fills it with data, up to `urb->actual_length` bytes.

Let me search the code to see how `rx_fixup` is called within the `usbnet` framework. I'll search for `rx_fixup` in `drivers/net/usb/usbnet.c`.



34/3 2026/10/04 05:46 tool grepper
0m Args:
{
  "Expression": "rx_fixup",
  "PathPrefix": "drivers/net/usb/usbnet.c"
}

Results:
{
  "Output": "drivers/net/usb/usbnet.c=312=static void __usbnet_status_stop_force(struct usbnet *dev)\n--\ndrivers/net/usb/usbnet.c-322-/* Passes this packet up the stack, updating its accounting.\ndrivers/net/usb/usbnet.c:323: * Some link protocols batch packets, so their rx_fixup paths\ndrivers/net/usb/usbnet.c-324- * can return clones as well as just modify the original skb.\n--\ndrivers/net/usb/usbnet.c=326=void usbnet_skb_return(struct usbnet *dev, struct sk_buff *skb)\n--\ndrivers/net/usb/usbnet.c-336-\ndrivers/net/usb/usbnet.c:337:\t/* only update if unset to allow minidriver rx_fixup override */\ndrivers/net/usb/usbnet.c-338-\tif (skb-\u003eprotocol == 0)\n--\ndrivers/net/usb/usbnet.c=569=static inline int rx_process(struct usbnet *dev, struct sk_buff *skb)\ndrivers/net/usb/usbnet.c-570-{\ndrivers/net/usb/usbnet.c:571:\tif (dev-\u003edriver_info-\u003erx_fixup \u0026\u0026\ndrivers/net/usb/usbnet.c:572:\t    !dev-\u003edriver_info-\u003erx_fixup(dev, skb)) {\ndrivers/net/usb/usbnet.c:573:\t\t/* With RX_ASSEMBLE, rx_fixup() must update counters */\ndrivers/net/usb/usbnet.c-574-\t\tif (!(dev-\u003edriver_info-\u003eflags \u0026 FLAG_RX_ASSEMBLE))\n"
}

35/3 2026/10/04 05:46 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 71856
							output: 38
							thoughts: 28
						

Thoughts:
Okay, here's my interpretation of what I'm about to do, framed as though it's my own thought process:

**Diving into the USBnet Driver's Receive Path**

Alright, time to dig into the `usbnet.c` driver, specifically the receive path. I need to understand how USB network traffic is handled at the lowest level. My focus is going to be on the `rx_process` and `rx_complete` functions. These are likely the core functions for receiving data over the USB interface, processing it, and handling completion.

I'll be looking for how the data is transferred from the USB device, how the driver interacts with the USB stack (likely using URBs – USB Request Blocks), and how the received data is then passed up the networking stack for processing by the higher layers. I'm expecting to see things like buffer allocation, descriptor parsing (if there are any relevant descriptors being used), data copying, error handling, and perhaps some form of interrupt or completion callback mechanism. I'll need to pay close attention to the synchronization mechanisms involved, too. This is a critical point to understanding performance and potential bottlenecks.



36/3 2026/10/04 05:46 tool read-file
0m Args:
{
  "File": "drivers/net/usb/usbnet.c",
  "FirstLine": 540,
  "LineCount": 50
}

Results:
{
  "Contents": " 540:\t\t\t\tnetif_dbg(dev, ifdown, dev-\u003enet, \"device gone\\n\");\n 541:\t\t\t\tnetif_device_detach(dev-\u003enet);\n 542:\t\t\t\tbreak;\n 543:\t\t\tcase -EHOSTUNREACH:\n 544:\t\t\t\tretval = -ENOLINK;\n 545:\t\t\t\tbreak;\n 546:\t\t\tdefault:\n 547:\t\t\t\tnetif_dbg(dev, rx_err, dev-\u003enet,\n 548:\t\t\t\t\t  \"rx submit, %d\\n\", retval);\n 549:\t\t\t\tqueue_work(system_bh_wq, \u0026dev-\u003ebh_work);\n 550:\t\t\t\tbreak;\n 551:\t\t\tcase 0:\n 552:\t\t\t\t__usbnet_queue_skb(\u0026dev-\u003erxq, skb, rx_start);\n 553:\t\t\t}\n 554:\t\t} else {\n 555:\t\t\tnetif_dbg(dev, ifdown, dev-\u003enet, \"rx: stopped\\n\");\n 556:\t\t\tretval = -ENOLINK;\n 557:\t\t}\n 558:\t\tspin_unlock_irqrestore(\u0026dev-\u003erxq.lock, lockflags);\n 559:\t\tif (retval) {\n 560:\t\t\tdev_kfree_skb_any(skb);\n 561:\t\t\tusb_free_urb(urb);\n 562:\t\t}\n 563:\t\treturn retval;\n 564:\t}\n 565:\t\n 566:\t\n 567:\t/*-------------------------------------------------------------------------*/\n 568:\t\n 569:\tstatic inline int rx_process(struct usbnet *dev, struct sk_buff *skb)\n 570:\t{\n 571:\t\tif (dev-\u003edriver_info-\u003erx_fixup \u0026\u0026\n 572:\t\t    !dev-\u003edriver_info-\u003erx_fixup(dev, skb)) {\n 573:\t\t\t/* With RX_ASSEMBLE, rx_fixup() must update counters */\n 574:\t\t\tif (!(dev-\u003edriver_info-\u003eflags \u0026 FLAG_RX_ASSEMBLE))\n 575:\t\t\t\tdev-\u003enet-\u003estats.rx_errors++;\n 576:\t\t\treturn -EPROTO;\n 577:\t\t}\n 578:\t\t// else network stack removes extra byte if we forced a short packet\n 579:\t\n 580:\t\t/* all data was already cloned from skb inside the driver */\n 581:\t\tif (dev-\u003edriver_info-\u003eflags \u0026 FLAG_MULTI_PACKET)\n 582:\t\t\treturn -EALREADY;\n 583:\t\n 584:\t\tif (skb-\u003elen \u003c ETH_HLEN) {\n 585:\t\t\tdev-\u003enet-\u003estats.rx_errors++;\n 586:\t\t\tdev-\u003enet-\u003estats.rx_length_errors++;\n 587:\t\t\tnetif_dbg(dev, rx_err, dev-\u003enet, \"rx length %d\\n\", skb-\u003elen);\n 588:\t\t\treturn -EPROTO;\n 589:\t\t}\n"
}

37/3 2026/10/04 05:46 llm kmsan-evaluator
0m Model:
gemini-3.8-flash

Args:
null

Results:
null

Tokens:
							input: 72961
							output: 522
							thoughts: 4333
						

Thoughts:
**Analysis of the AX88179 Driver Patch Series**

My initial observation is that the `FLAG_MULTI_PACKET` is set for several devices (AX88179A, AX88772D, AX88279). This means the driver handles fragmented packets, which involves cloning and allocating new `skb`s within `rx_fixup` and submitting them to the network stack via `usbnet_skb_return`. I've scrutinized the memory management in `ax88179a_rx_fixup`, specifically how `pkt_len` is calculated and used with `memcpy` to populate the `ax_skb`. The key is that `pkt_len` is validated against the received buffer bounds. Packets smaller than 16 bytes are skipped completely. If the packet is valid the values will be in range of 16 to hard_mtu + 2 bytes which also means that `pkt_len_plus_padd` bytes are within `hdr_off`, so within the received USB packet buffer. The bounds checks seem robust: `hdr_off` is checked against `skb->len`, and there are checks for the total packet length and the integrity of the packet descriptors.

Now, I'm concerned about the `ax88179_set_mac_addr` function, which utilizes standard `ndo_set_mac_address` and writes to the hardware MAC address register. I can see no issues here.

Looking deeper, I've identified a critical potential issue in `ax88179_change_mtu`. The code calls `rtnl_lock()` *inside* `ndo_change_mtu`, which is called when the RTNL is already held by the networking core. This is a classic deadlock scenario that LOCKDEP should catch.

I've reviewed other areas touched by this patch series, including:

*   `ax88179a_mii_ioctl`: Simply delegates to `phylink`.
*   `ax88179_set_eeprom`: Writes user-supplied data to EEPROM via USB commands.
*   `ax88179_set_features`: Configures hardware offload features via USB commands.
*   `ax88179_set_multicast`: This function carefully constructs multicast filters based on the network device's multicast address list. Data is pre-initialized, and writes are performed asynchronously.
*   The `ax88179_lib.c` functions (`ax88179_write_u16`, `ax88179_write_u8`) are used to write 16 and 8 bit registers via USB command which are initialized before the function call.
*   Suspend/resume wrappers are present, but don't seem to introduce memory management risks.
*   `ax88179a_vlan_rx_add_vid`/ `ax88179a_vlan_rx_kill_vid` correctly check return codes and read/write VLAN registers.
*   `ax88179a_set_features` performs read/modify/write operations on VLAN control registers. Return values are checked, and variables are initialized.

My conclusion is that `NeedsKMSAN = false`. I see no risk of uninitialized memory reads or information leaks to userspace that would necessitate KMSAN. KMSAN is for catching *uninitialized* memory reads/writes. This series is primarily refactoring the existing driver, extending to new chipsets, and integrating with `phylink`. Any new allocations and data copies are carefully handled, using `kzalloc` or `skb_put` to ensure data is properly initialized. All critical functions and calls are checked and validated for possible errors. The potential deadlock caught by LOCKDEP and buffer overflows covered by KASAN.



38/3 2026/10/04 05:47 tool set-results
0m Args:
{
  "KMSANReasoning": "The patch series refactors the ASIX AX88179/178A USB Ethernet driver into shared library components (`ax88179_lib.c`, `ax88179_lib.h`) and adds support for newer ASIX chip variants (AX88179A, AX88772D, AX88279) with phylink integration in `ax88179a_devices.c` and `ax88796b.c`.\n\nKMSAN vs KASAN / LOCKDEP analysis:\n1. Memory allocations: The private driver context `struct ax88179_data` is allocated using `kzalloc_obj` in `ax88179a_bind()` and `kzalloc` in `ax88179_bind()`. The MDIO bus is allocated via `mdiobus_alloc()` (zero-initialized). No struct members are left uninitialized.\n2. RX / TX path: In `ax88179a_rx_fixup()`, newly allocated skbs are populated via `skb_put()` and `memcpy()` for the exact packet length, avoiding uninitialized payload bytes. In `ax88179a_tx_fixup()`, any padding bytes are zeroed with `skb_put_zero()` and the 64-bit descriptor is initialized via `put_unaligned_le64()`.\n3. User-space copies / info-leaks: Ethtool handlers like `ax88179_get_eeprom()` allocate buffers using `kzalloc()` and strictly check that the read length matches before copying to the caller buffer; `ax88179a_get_drvinfo()` formats strings using `snprintf()`. No kernel stack or uninitialized heap padding is exposed to user space.\n4. Other bug detectors: Potential concurrency or locking issues (e.g. `rtnl_lock()` in `ax88179_change_mtu()`) are covered by LOCKDEP. Buffer boundary issues, packet parsing overflows, and use-after-free risks are covered by standard KASAN.\n\nThere are no uninitialized memory reads or information leak risks that would uniquely warrant a KMSAN fuzzing session.",
  "NeedsKMSAN": false
}

Results:
{
  "KMSANReasoning": "The patch series refactors the ASIX AX88179/178A USB Ethernet driver into shared library components (`ax88179_lib.c`, `ax88179_lib.h`) and adds support for newer ASIX chip variants (AX88179A, AX88772D, AX88279) with phylink integration in `ax88179a_devices.c` and `ax88796b.c`.\n\nKMSAN vs KASAN / LOCKDEP analysis:\n1. Memory allocations: The private driver context `struct ax88179_data` is allocated using `kzalloc_obj` in `ax88179a_bind()` and `kzalloc` in `ax88179_bind()`. The MDIO bus is allocated via `mdiobus_alloc()` (zero-initialized). No struct members are left uninitialized.\n2. RX / TX path: In `ax88179a_rx_fixup()`, newly allocated skbs are populated via `skb_put()` and `memcpy()` for the exact packet length, avoiding uninitialized payload bytes. In `ax88179a_tx_fixup()`, any padding bytes are zeroed with `skb_put_zero()` and the 64-bit descriptor is initialized via `put_unaligned_le64()`.\n3. User-space copies / info-leaks: Ethtool handlers like `ax88179_get_eeprom()` allocate buffers using `kzalloc()` and strictly check that the read length matches before copying to the caller buffer; `ax88179a_get_drvinfo()` formats strings using `snprintf()`. No kernel stack or uninitialized heap padding is exposed to user space.\n4. Other bug detectors: Potential concurrency or locking issues (e.g. `rtnl_lock()` in `ax88179_change_mtu()`) are covered by LOCKDEP. Buffer boundary issues, packet parsing overflows, and use-after-free risks are covered by standard KASAN.\n\nThere are no uninitialized memory reads or information leak risks that would uniquely warrant a KMSAN fuzzing session.",
  "NeedsKMSAN": false
}

LLM Calls Summary:
Total Calls Total Tokens Avg Tokens Total Duration (Seconds) Avg Duration (Seconds)
Tool Calls Summary:
Total Calls Total Duration (Seconds) Avg Duration (Seconds)