* [PATCH net-next v5 00/13] ax88179_178a: Add support for AX88179A-based chips
@ 2026-08-02 16:31 Birger Koblitz
2026-08-02 16:31 ` [PATCH net-next v5 01/13] ax88179_178a: Fix endianness of pause watermark register Birger Koblitz
` (13 more replies)
0 siblings, 14 replies; 32+ messages in thread
From: Birger Koblitz @ 2026-08-02 16:31 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Russell King, Andrew Lunn, Heiner Kallweit
Cc: linux-usb, netdev, linux-kernel, Birger Koblitz, Jianhui Xu
This adds support for the current generation of ASIX network adapter chips,
which are based on the AX88179A. This includes the AX88179A/B (1GBit-PHY),
AX88772D/E (100MBit) and AX88279 (2.5GBit).
The AX179A-based chips all provide both a CDC-NCM compatible USB interface,
and a proprietary vendor interface with more features. By default, the
proprietary vendor interface is not active and Linux will load the CDC-NCM
driver to support the devices. If the ax88179_178a module is configured by
the OS to have precedence over CDC-NCM, then this driver will switch the
device to use the vendor interface, and the device will be controlled by
the ax88179_178a driver when the device is probed again after an automatic
reset of the device bringing up the vendor interface.
The following hardware was tested:
Delock 66046 2.5GBit adapter (AX88279, FW: 1.2.0.0)
TP-Link UE306 1GBit adapter (AX88179B, FW: 1.3.0.0)
Renkforce RF-4708614 1GBit adapter (AX88179A, FW: 1.0.4.0)
UGREEN CR110 100MBit adapter (AX88722E, FW: 1.3.0.0)
The driver supports the following features
- EEE
- TCP segmentation offload
- VLAN filtering/tagging offload
(NETIF_F_HW_VLAN_CTAG_FILTER, NETIF_F_HW_VLAN_CTAG_RX/TX)
- RX/TX checksum offload
- FC/Pause configuration
- EEPROM read access
The code is based on the ASIX 4.1.0 out-of-tree driver published under
the GPL,, the aqc111 driver which provides support for the AX88279A,
and some tracing of USB-transfers of the Windows-driver.
Signed-off-by: Birger Koblitz <mail@birger-koblitz.de>
Tested-by: Jianhui Xu <neuromoments@gmail.com>
---
Changes in v5:
- Fixed read_status() and config_aneg() in PHY driver
- Introduced netdev2data() as convenience function
- netdev_info instances and debugging relicts removed
- Several instances of badly written/formatted code that was copy and pasted from
the original driver corrected
- select PHYLINK added to Kconfig when phylib dependency added
- PHYLINK selects PHYLIB, so not needed to specify separately
- Link to v4: https://lore.kernel.org/r/20260731-ax88179a-v4-0-2cf1f71b1dd2@birger-koblitz.de
Changes in v4:
- Split driver into library part and part2 for AX88179 and AX88179A-based
controllers
- Driver renamed ax88179
- Improved phylink use: use phylink standard functions for speed and EEE-settings,
correct MAC capabilities, removed ax88179_status() irq-urb callback
- Fixes in PHY driver for AX88179A integrated PHYs
- Link to v3: https://lore.kernel.org/r/20260724-ax88179a-v3-0-bdde4f905883@birger-koblitz.de
Changes in v3:
- Add PHY drivers for the PHYs of the AX88179A-based controllers
- Use phylink for the AX88179A-based chips
- Link to v2: https://lore.kernel.org/r/20260708-ax88179a-v2-0-0800fedb2e16@birger-koblitz.de
Changes in v2:
- Correctly use net-next prefix
- Fix compilation issue in HW support patch
- Split MMD support patch into patches for EEE/new chip support
- Do not use ADVERTISE_RESV but private flag definition
- Fix pause configuration to keep track of settings when autoneg disabled
- Fix issue with unitialized variable reported by kernel test robot <lkp@intel.com>
- Avoid white-space changes
- Link to v1: https://lore.kernel.org/r/20260701-ax88179a-v1-0-13685df67515@birger-koblitz.de
---
Birger Koblitz (13):
ax88179_178a: Fix endianness of pause watermark register
ax88179_178a: Split driver into library and device specific code
ax88179_178a: Add netdev2data() convenience function
ax88179_178a: Add HW support for AX179A-based chips
ax88179_178a: Add EEE configuration support for AX88179A MACs
ax88179_178a: Add EEE configuration support for AX88179A PHYs
ax88179_178a: Add VLAN offload support for AX88179A
ax88179_178a: Add AX179A/AX279 multicast configuration
ax88179_178a: Add Suspend/resume support for AX88179A/772D/279
ax88179_178a: Add ethtool get_drvinfo
ax88179_178a: Update driver name and information
ax88179_178a: Add support for AX88179A/772D/279 EEPROM access
ax88796b: Add support for AX88772D, AX88179A and AX88279
drivers/net/phy/ax88796b.c | 188 ++++++
drivers/net/usb/Kconfig | 10 +-
drivers/net/usb/Makefile | 3 +-
drivers/net/usb/ax88179_178a.c | 664 ++------------------
drivers/net/usb/ax88179_lib.c | 515 ++++++++++++++++
drivers/net/usb/ax88179_lib.h | 358 +++++++++++
drivers/net/usb/ax88179a_devices.c | 1200 ++++++++++++++++++++++++++++++++++++
7 files changed, 2314 insertions(+), 624 deletions(-)
---
base-commit: 1a9edf8be190decb17227e3cba540513d93ebb85
change-id: 20260630-ax88179a-a1d89fe21730
Best regards,
--
Birger Koblitz <mail@birger-koblitz.de>
^ permalink raw reply [flat|nested] 32+ messages in thread
* [PATCH net-next v5 01/13] ax88179_178a: Fix endianness of pause watermark register
2026-08-02 16:31 [PATCH net-next v5 00/13] ax88179_178a: Add support for AX88179A-based chips Birger Koblitz
@ 2026-08-02 16:31 ` Birger Koblitz
2026-08-02 16:32 ` [PATCH net-next v5 02/13] ax88179_178a: Split driver into library and device specific code Birger Koblitz
` (12 subsequent siblings)
13 siblings, 0 replies; 32+ messages in thread
From: Birger Koblitz @ 2026-08-02 16:31 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Russell King, Andrew Lunn, Heiner Kallweit
Cc: linux-usb, netdev, linux-kernel, Birger Koblitz, Jianhui Xu
The 16-bit pause watermark register is little endian as
described in the ASIX 4.1.0 out-of-tree driver. Correct the
register byte sequence but also swap the configuration values
used in the code in order to keep the current behaviour.
The endianness is relevant for 16-bit writes to the register.
Signed-off-by: Birger Koblitz <mail@birger-koblitz.de>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
---
drivers/net/usb/ax88179_178a.c | 9 ++++-----
1 file changed, 4 insertions(+), 5 deletions(-)
diff --git a/drivers/net/usb/ax88179_178a.c b/drivers/net/usb/ax88179_178a.c
index 98f899ea2e9462f1ba99281a875385241745458b..945c071dfd1d2f0816c779e1a401ac158adc8d99 100644
--- a/drivers/net/usb/ax88179_178a.c
+++ b/drivers/net/usb/ax88179_178a.c
@@ -32,8 +32,8 @@
#define AX_ACCESS_EEPROM 0x04
#define AX_ACCESS_EFUS 0x05
#define AX_RELOAD_EEPROM_EFUSE 0x06
-#define AX_PAUSE_WATERLVL_HIGH 0x54
-#define AX_PAUSE_WATERLVL_LOW 0x55
+#define AX_PAUSE_WATERLVL_LOW 0x54
+#define AX_PAUSE_WATERLVL_HIGH 0x55
#define PHYSICAL_LINK_STATUS 0x02
#define AX_USB_SS 0x04
@@ -1617,11 +1617,10 @@ static int ax88179_reset(struct usbnet *dev)
dev->rx_urb_size = 1024 * 20;
*tmp = 0x34;
- ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PAUSE_WATERLVL_LOW, 1, 1, tmp);
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PAUSE_WATERLVL_HIGH, 1, 1, tmp);
*tmp = 0x52;
- ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PAUSE_WATERLVL_HIGH,
- 1, 1, tmp);
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PAUSE_WATERLVL_LOW, 1, 1, tmp);
/* Enable checksum offload */
*tmp = AX_RXCOE_IP | AX_RXCOE_TCP | AX_RXCOE_UDP |
--
2.47.3
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH net-next v5 02/13] ax88179_178a: Split driver into library and device specific code
2026-08-02 16:31 [PATCH net-next v5 00/13] ax88179_178a: Add support for AX88179A-based chips Birger Koblitz
2026-08-02 16:31 ` [PATCH net-next v5 01/13] ax88179_178a: Fix endianness of pause watermark register Birger Koblitz
@ 2026-08-02 16:32 ` Birger Koblitz
2026-08-02 16:32 ` [PATCH net-next v5 03/13] ax88179_178a: Add netdev2data() convenience function Birger Koblitz
` (11 subsequent siblings)
13 siblings, 0 replies; 32+ messages in thread
From: Birger Koblitz @ 2026-08-02 16:32 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Russell King, Andrew Lunn, Heiner Kallweit
Cc: linux-usb, netdev, linux-kernel, Birger Koblitz, Jianhui Xu
Split the ax88179_178a module code into code common to the
AX88179/178a/179a and 279 family of controllers and device
specific code for the currently supported devices based
on the AX88179 and AX88178a.
Rename the module to ax88179 to reflect the broader scope of
controllers supported by the module.
Signed-off-by: Birger Koblitz <mail@birger-koblitz.de>
---
drivers/net/usb/Makefile | 3 +-
drivers/net/usb/ax88179_178a.c | 611 +----------------------------------------
drivers/net/usb/ax88179_lib.c | 453 ++++++++++++++++++++++++++++++
drivers/net/usb/ax88179_lib.h | 194 +++++++++++++
4 files changed, 650 insertions(+), 611 deletions(-)
diff --git a/drivers/net/usb/Makefile b/drivers/net/usb/Makefile
index 4964f7b326fbcedffc1e5a1bf004aea95560db8c..ddd76fa71e2ee670888df1c9715632e5c04a8149 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_178A) += ax88179.o
+ax88179-y := ax88179_178a.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 945c071dfd1d2f0816c779e1a401ac158adc8d99..66464a321dbcfe4370dad97d7efd5165983de6e4 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;
diff --git a/drivers/net/usb/ax88179_lib.c b/drivers/net/usb/ax88179_lib.c
new file mode 100644
index 0000000000000000000000000000000000000000..7b5c17a01df2deac2cc0210bc88193be1abfbe23
--- /dev/null
+++ b/drivers/net/usb/ax88179_lib.c
@@ -0,0 +1,453 @@
+// 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 "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 ret;
+ 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;
+
+ if (!dev) {
+ netdev_err(dev->net, "No net device.\n");
+ return -ENODEV;
+ }
+
+ 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 *dev, u8 cmd, u8 rtype, u16 val, u16 i, const void *d, u16 size);
+ struct ax88179_data *ax179_data = dev->driver_priv;
+
+ if (!dev) {
+ netdev_err(dev->net, "No net device.\n");
+ return -ENODEV;
+ }
+
+ 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 == 2) {
+ 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;
+}
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
+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);
+}
+
+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;
+}
+
+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;
+
+ 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;
+}
+
+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;
+}
+
diff --git a/drivers/net/usb/ax88179_lib.h b/drivers/net/usb/ax88179_lib.h
new file mode 100644
index 0000000000000000000000000000000000000000..b2ee1356727cf4b3fa05f0b0d5fd863ac3f9a87d
--- /dev/null
+++ b/drivers/net/usb/ax88179_lib.h
@@ -0,0 +1,194 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+#include <linux/usb.h>
+#include <linux/crc32.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 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
+
+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;
+};
+
+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_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);
+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);
+#endif /*__LINUX_USBNET_AX88179_H */
--
2.47.3
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH net-next v5 03/13] ax88179_178a: Add netdev2data() convenience function
2026-08-02 16:31 [PATCH net-next v5 00/13] ax88179_178a: Add support for AX88179A-based chips Birger Koblitz
2026-08-02 16:31 ` [PATCH net-next v5 01/13] ax88179_178a: Fix endianness of pause watermark register Birger Koblitz
2026-08-02 16:32 ` [PATCH net-next v5 02/13] ax88179_178a: Split driver into library and device specific code Birger Koblitz
@ 2026-08-02 16:32 ` Birger Koblitz
2026-08-02 21:10 ` Andrew Lunn
2026-08-02 16:32 ` [PATCH net-next v5 04/13] ax88179_178a: Add HW support for AX179A-based chips Birger Koblitz
` (10 subsequent siblings)
13 siblings, 1 reply; 32+ messages in thread
From: Birger Koblitz @ 2026-08-02 16:32 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Russell King, Andrew Lunn, Heiner Kallweit
Cc: linux-usb, netdev, linux-kernel, Birger Koblitz, Jianhui Xu
Add a convenience function for the ethtool ops that use
phylink and need to retrieve the ax88179_data structure from
net_device.
Signed-off-by: Birger Koblitz <mail@birger-koblitz.de>
---
drivers/net/usb/ax88179_lib.c | 7 +++++++
drivers/net/usb/ax88179_lib.h | 1 +
2 files changed, 8 insertions(+)
diff --git a/drivers/net/usb/ax88179_lib.c b/drivers/net/usb/ax88179_lib.c
index 7b5c17a01df2deac2cc0210bc88193be1abfbe23..34350ba3b96b94bc82072a578802a91ebffd7a6a 100644
--- a/drivers/net/usb/ax88179_lib.c
+++ b/drivers/net/usb/ax88179_lib.c
@@ -137,6 +137,13 @@ int ax88179_write_cmd(struct usbnet *dev, u8 cmd, u16 value, u16 index, u16 size
return ret;
}
+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;
diff --git a/drivers/net/usb/ax88179_lib.h b/drivers/net/usb/ax88179_lib.h
index b2ee1356727cf4b3fa05f0b0d5fd863ac3f9a87d..9ff2a94a6fb2de4ee85f25e913ae2fe263bcea7c 100644
--- a/drivers/net/usb/ax88179_lib.h
+++ b/drivers/net/usb/ax88179_lib.h
@@ -181,6 +181,7 @@ void ax88179_write_cmd_async(struct usbnet *dev, u8 cmd, u16 value, u16 index,
u16 size, void *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);
--
2.47.3
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH net-next v5 04/13] ax88179_178a: Add HW support for AX179A-based chips
2026-08-02 16:31 [PATCH net-next v5 00/13] ax88179_178a: Add support for AX88179A-based chips Birger Koblitz
` (2 preceding siblings ...)
2026-08-02 16:32 ` [PATCH net-next v5 03/13] ax88179_178a: Add netdev2data() convenience function Birger Koblitz
@ 2026-08-02 16:32 ` Birger Koblitz
2026-08-02 21:28 ` Andrew Lunn
` (2 more replies)
2026-08-02 16:32 ` [PATCH net-next v5 05/13] ax88179_178a: Add EEE configuration support for AX88179A MACs Birger Koblitz
` (9 subsequent siblings)
13 siblings, 3 replies; 32+ messages in thread
From: Birger Koblitz @ 2026-08-02 16:32 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Russell King, Andrew Lunn, Heiner Kallweit
Cc: linux-usb, netdev, linux-kernel, Birger Koblitz, Jianhui Xu
This adds bindings and HW support for AX179A-based USB-Ethernet
controllers. The AX179A-family of chips consists of the
AX88279 (2.5GBit PHY)
AX88179A/B (1GBit PHY, B variant has wider temperature range)
AX772D/E (100Mbit PHY)
The controllers all have the same vendor and device ID
(0x0b95, 0x1790) and are distinguished by their BCD device versions,
which are
2.00 AX88179A/B
3.00 AX88772D/E
4.00 AX88279
For all chips, the driver calls the same ax88179a_bind() function
and the chips are then distinguished by the chip version and
BCD device ID. The AX179A-based chips all provide both a CDC-NCM
compatible USB interface, and a proprietary vendor interface. By default,
the proprietary vendor interface is not active and Linux will load the
CDC-NCM driver to support the devices. If the ax88179_178a module is
configured by the OS to have precedence over CDC-NCM, then this driver
will switch the device to use the vendor interface, and the device will
be controlled by the ax88179_178a driver when the device is probed again
after an automatic reset by the device.
Signed-off-by: Birger Koblitz <mail@birger-koblitz.de>
---
drivers/net/usb/Kconfig | 2 +-
drivers/net/usb/Makefile | 2 +-
drivers/net/usb/ax88179_178a.c | 12 +
drivers/net/usb/ax88179_lib.h | 159 ++++++
drivers/net/usb/ax88179a_devices.c | 963 +++++++++++++++++++++++++++++++++++++
5 files changed, 1136 insertions(+), 2 deletions(-)
diff --git a/drivers/net/usb/Kconfig b/drivers/net/usb/Kconfig
index 52a5c0922c79fc52906d4d4040ec2b3411ff0984..115990b0d9537aea2776544c9768b034f32aa7a4 100644
--- a/drivers/net/usb/Kconfig
+++ b/drivers/net/usb/Kconfig
@@ -199,7 +199,7 @@ config USB_NET_AX88179_178A
tristate "ASIX AX88179/178A USB 3.0/2.0 to Gigabit Ethernet"
depends on USB_USBNET
select CRC32
- select PHYLIB
+ select PHYLINK
default y
help
This option adds support for ASIX AX88179 based USB 3.0/2.0
diff --git a/drivers/net/usb/Makefile b/drivers/net/usb/Makefile
index ddd76fa71e2ee670888df1c9715632e5c04a8149..2ecead0181eaf8d66fb6f5dbd6f2905b22b6eb57 100644
--- a/drivers/net/usb/Makefile
+++ b/drivers/net/usb/Makefile
@@ -13,7 +13,7 @@ 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.o
-ax88179-y := ax88179_178a.o ax88179_lib.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 66464a321dbcfe4370dad97d7efd5165983de6e4..6bd9704046009d7aad5ee6ddddc18faab5b030dc 100644
--- a/drivers/net/usb/ax88179_178a.c
+++ b/drivers/net/usb/ax88179_178a.c
@@ -1268,6 +1268,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, 0, 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, 0, 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,
diff --git a/drivers/net/usb/ax88179_lib.h b/drivers/net/usb/ax88179_lib.h
index 9ff2a94a6fb2de4ee85f25e913ae2fe263bcea7c..2e4d35c74ee8b32cd2916924b85d5427860bb08c 100644
--- a/drivers/net/usb/ax88179_lib.h
+++ b/drivers/net/usb/ax88179_lib.h
@@ -2,6 +2,7 @@
#include <linux/usb.h>
#include <linux/crc32.h>
+#include <linux/phylink.h>
#include <linux/usb/usbnet.h>
#ifndef __LINUX_USBNET_AX88179_H
@@ -25,17 +26,34 @@
#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
@@ -55,6 +73,15 @@
#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
@@ -84,7 +111,20 @@
#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
@@ -104,7 +144,51 @@
#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
@@ -157,6 +241,56 @@
#define GMII_PHY_PGSEL_PAGE3 0x0003
#define GMII_PHY_PGSEL_PAGE5 0x0005
+/* 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_MASK 0x7FFF
+#define AX179A_TX_DESC_MSS_SHIFT 0x20
+#define AX179A_TX_DESC_VLAN_MASK 0xFFFF
+#define AX179A_TX_DESC_VLAN_SHIFT 0x30
+
+/* 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_MASK 0x7FFF0000
+#define AX179A_RX_PD_LEN_SHIFT 0x10
+#define AX179A_RX_PD_VLAN_SHIFT 0x20
+
+/* RX Descriptor header */
+#define AX179A_RX_DH_PKT_CNT_MASK 0x1FFF
+#define AX179A_RX_DH_DESC_OFFSET_MASK 0xFFFFE000
+#define AX179A_RX_DH_DESC_OFFSET_SHIFT 0x0D
+
+#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;
@@ -165,6 +299,22 @@ struct ax88179_data {
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;
};
struct ax88179_int_data {
@@ -172,6 +322,10 @@ struct ax88179_int_data {
__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);
@@ -192,4 +346,9 @@ 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);
+
+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 0000000000000000000000000000000000000000..cef2a0131c1e6d60b28c2aab1e020e6b7468bc3c
--- /dev/null
+++ b/drivers/net/usb/ax88179a_devices.c
@@ -0,0 +1,963 @@
+// 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"
+
+#define AX88279_EEPROM_LEN 0x4000
+#define AX88179A_EEPROM_LEN (32 * 20)
+
+static const struct ax_bulkin_settings AX88179A_BULKIN_SIZE[] = {
+ {5, 0x7B, 0x00, 0x17, 0x0F}, /* 1G, SS */
+ {5, 0xC0, 0x02, 0x06, 0x0F}, /* 1G, HS */
+ {7, 0xF0, 0x00, 0x0C, 0x0F}, /* 100M, Full, SS */
+ {6, 0x00, 0x00, 0x06, 0x0F}, /* 100M, Half, SS */
+ {5, 0xC0, 0x04, 0x06, 0x0F}, /* 100M, Full, HS */
+ {7, 0xC0, 0x04, 0x06, 0x0F}, /* 100M, Half, HS */
+ {7, 0x00, 0x00, 0x03, 0x3F}, /* FS */
+};
+
+static const struct ax_bulkin_settings AX88772D_BULKIN_SIZE[] = {
+ {0, 0x00, 0x00, 0x00, 0x00}, /* 1G, SS (unused) */
+ {0, 0x00, 0x00, 0x00, 0x00}, /* 1G, HS (unused) */
+ {0, 0x00, 0x00, 0x00, 0x00}, /* 100M, Full, SS (unused) */
+ {0, 0x00, 0x00, 0x00, 0x00}, /* 100M, Half, SS (unused) */
+ {5, 0xC0, 0x04, 0x06, 0x0F}, /* 100M, Full, HS */
+ {7, 0xC0, 0x04, 0x06, 0x0F}, /* 100M, Half, HS */
+ {7, 0x00, 0x00, 0x03, 0x3F}, /* FS */
+};
+
+static const struct ax_bulkin_settings AX88279_BULKIN_SIZE[] = {
+ {5, 0x10, 0x01, 0x11, 0x0F}, /* 2.5G */
+ {7, 0xB3, 0x01, 0x11, 0x0F}, /* 1G, SS */
+ {7, 0xC0, 0x02, 0x06, 0x0F}, /* 1G, HS */
+ {7, 0x80, 0x01, 0x03, 0x0F}, /* 100M, Full, SS */
+ {7, 0x80, 0x01, 0x03, 0x0F}, /* 100M, Half, SS */
+ {7, 0x80, 0x01, 0x03, 0x0F}, /* 100M, Full, HS */
+ {7, 0x80, 0x01, 0x03, 0x0F}, /* 100M, Half, HS */
+ {7, 0x00, 0x00, 0x03, 0x3F}, /* FS */
+};
+
+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;
+
+ 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;
+ }
+
+ ax88179_read_cmd(dev, AX_ACCESS_PHY, phy_id, (__u16)regnum, 2, &res);
+ 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_cmd(dev, AX_ACCESS_PHY, phy_id, (__u16)regnum, 2, &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 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 void ax88179a_bulkin_config(struct usbnet *dev, u8 link_sts)
+{
+ struct ax88179_data *ax179_data = dev->driver_priv;
+ const struct ax_bulkin_settings *bulkin_data;
+ int index = 0;
+
+ switch (ax179_data->speed) {
+ case ETHER_LINK_2500: /* AX88279 only */
+ index = 0;
+ break;
+
+ case ETHER_LINK_1000: /* AX88279 & AX88178A */
+ if (ax179_data->chip_version == AX_VERSION_AX88279) {
+ if (link_sts & AX_USB_SS)
+ index = 1;
+ else if (link_sts & AX_USB_HS)
+ index = 2;
+ } else {
+ if (link_sts & AX_USB_SS)
+ index = 0;
+ else if (link_sts & AX_USB_HS)
+ index = 1;
+ }
+ break;
+
+ case ETHER_LINK_100:
+ if (ax179_data->chip_version == AX_VERSION_AX88279) {
+ if (link_sts & AX_USB_SS)
+ index = 3;
+ else if (link_sts & AX_USB_HS)
+ index = 5;
+ if (!ax179_data->full_duplex)
+ index++;
+ } else {
+ /* AX88279A & AX88277D */
+ if (link_sts & AX_USB_SS)
+ index = 2;
+ else if (link_sts & AX_USB_HS)
+ index = 4;
+ if (!ax179_data->full_duplex)
+ index++;
+ }
+ break;
+
+ case ETHER_LINK_10:
+ if (ax179_data->chip_version == AX_VERSION_AX88279)
+ index = 7;
+ else
+ index = 6;
+ break;
+
+ default: /* No link */
+ index = 0;
+ }
+
+ if (ax179_data->chip_version == AX_VERSION_AX88279 && (link_sts & AX_USB_FS))
+ index = 7;
+
+ 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_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 const struct ethtool_ops ax88179a_ethtool_ops = {
+ .get_link = ethtool_op_get_link,
+ .get_msglevel = usbnet_get_msglevel,
+ .set_msglevel = usbnet_set_msglevel,
+ .get_wol = ax88179_get_wol,
+ .set_wol = ax88179_set_wol,
+ .get_eeprom_len = ax88179a_get_eeprom_len,
+ .get_eeprom = ax88179_get_eeprom,
+ .set_eeprom = ax88179_set_eeprom,
+ .nway_reset = usbnet_nway_reset,
+ .get_link_ksettings = phy_ethtool_get_link_ksettings,
+ .set_link_ksettings = phy_ethtool_set_link_ksettings,
+ .get_pauseparam = ax88179a_get_pauseparam,
+ .set_pauseparam = ax88179a_set_pauseparam,
+ .get_ts_info = ethtool_op_get_ts_info,
+};
+
+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;
+ }
+
+ 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 tmp8, link_sts, reg8[3];
+ u16 tmp16, mode;
+
+ /* Stop RX/TX for link configuration */
+ tmp16 = AX_RX_CTL_STOP;
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2, &tmp16);
+ tmp8 = 0;
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_MAC_PATH, 1, 1, &tmp8);
+
+ tmp8 = 0xa5;
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_MAC_CDC_DELAY_TX, 1, 1, &tmp8);
+
+ tmp16 = 0x0410;
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PAUSE_WATERLVL_LOW, 2, 2, &tmp16);
+
+ tmp8 = 0;
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_ETH_TX_GAP, 1, 1, &tmp8);
+
+ tmp8 = 0x07;
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_EP5_EHR, 1, 1, &tmp8);
+
+ tmp8 = 0x28 | AX_NEW_PAUSE_EN;
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_NEW_PAUSE_CTRL, 1, 1, &tmp8);
+
+ 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);
+
+ reg8[0] = 0x40;
+ reg8[1] = AX_MAC_MIQFFCTRL_FORMAT | AX_MAC_MIQFFCTRL_DROP_CRC | AX_MAC_LSO_ERR_EN;
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_MAC_RX_DATA_CDC_CNT, 2, 2, reg8);
+
+ tmp8 = AX_XGMII_EN;
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_BFM_DATA, 1, 1, &tmp8);
+
+ tmp8 = 0x1C | AX_LSO_ENHANCE_EN;
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_MAC_LSO_ENHANCE_CTRL, 1, 1, &tmp8);
+
+ mode |= AX_MEDIUM_GIGAMODE | AX_MEDIUM_FULL_DUPLEX;
+
+ break;
+
+ case SPEED_1000:
+ mode |= AX_MEDIUM_GIGAMODE;
+ 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);
+
+ tmp8 = 0x40;
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_MAC_RX_DATA_CDC_CNT, 1, 1, &tmp8);
+ 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);
+
+ tmp8 = 0xFA;
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_MAC_RX_DATA_CDC_CNT, 1, 1, &tmp8);
+
+ speed = 10;
+ break;
+ }
+
+ ax88179_read_cmd(dev, AX_ACCESS_MAC, PHYSICAL_LINK_STATUS, 1, 1, &link_sts);
+ ax88179a_bulkin_config(dev, link_sts);
+
+ if (ax179_data->chip_version < AX_VERSION_AX88279) {
+ tmp8 = 0;
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_BFM_DATA, 1, 1, &tmp8);
+ }
+
+ if (duplex)
+ mode |= AX_MEDIUM_FULL_DUPLEX;
+
+ if (dev->net->mtu > 1500)
+ mode |= AX_MEDIUM_JUMBO_EN;
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE, 2, 2, &mode);
+
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2, &ax179_data->rxctl);
+
+ tmp8 = AX_MAC_RX_PATH_READY | AX_MAC_TX_PATH_READY;
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_MAC_PATH, 1, 1, &tmp8);
+}
+
+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,
+};
+
+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;
+
+ __set_bit(PHY_INTERFACE_MODE_INTERNAL,
+ data->phylink_config.supported_interfaces);
+ phy_if_mode = PHY_INTERFACE_MODE_INTERNAL;
+
+ 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 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 = usbnet_mii_ioctl,
+};
+
+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;
+ u8 reg8;
+
+ /* 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 (udev->descriptor.bcdDevice == 0x300)
+ ax179_data->is_ax88772d = 1;
+ }
+
+ for (int i = 0; i < 3; i++) {
+ ret = ax88179_read_cmd(dev, AX88179A_ACCESS_BL, (0xFD + i),
+ 1, 1, &ax179_data->fw_version[i]);
+ if (ret < 0)
+ ax179_data->fw_version[i] = 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;
+ }
+
+ 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 | NETIF_F_HW_VLAN_CTAG_RX |
+ NETIF_F_HW_VLAN_CTAG_FILTER;
+
+ dev->net->hw_features |= dev->net->features;
+
+ 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 */
+ reg8 = AX_PHY_POWER;
+ ax88179_write_cmd(dev, AX88179A_PHY_POWER, 0, 0, 1, ®8);
+ 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;
+
+ 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;
+ u16 tmp16;
+ u8 tmp8;
+
+ /* Configure RX control register => stop operation */
+ tmp16 = AX_RX_CTL_STOP;
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2, &tmp16);
+
+ rtnl_lock();
+ phylink_disconnect_phy(ax179_data->phylink);
+ rtnl_unlock();
+ phylink_destroy(ax179_data->phylink);
+ ax88179a_mdio_unregister(ax179_data);
+
+ tmp8 = 0;
+ ax88179_write_cmd(dev, AX88179A_PHY_POWER, 0, 0, 1, &tmp8);
+
+ 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 = le64_to_cpup((u64 *)skb_tail_pointer(skb));
+
+ /* Check these packets */
+ hdr_off = (rx_hdr & AX179A_RX_DH_DESC_OFFSET_MASK) >> AX179A_RX_DH_DESC_OFFSET_SHIFT;
+ 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_cpup(pkt_desc_ptr);
+ u32 pkt_len_plus_padd;
+ u32 pkt_len;
+
+ pkt_len = (u32)((pkt_desc & AX179A_RX_PD_LEN_MASK) >> AX179A_RX_PD_LEN_SHIFT)
+ - (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)) {
+ 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 & VLAN_VID_MASK);
+ }
+
+ 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 |= ((u64)(skb_shinfo(skb)->gso_size & AX179A_TX_DESC_MSS_MASK)) <<
+ AX179A_TX_DESC_MSS_SHIFT;
+
+ 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 |= ((u64)tci & AX179A_TX_DESC_VLAN_MASK) << AX179A_TX_DESC_VLAN_SHIFT;
+ }
+
+ if (!dev->can_dma_sg && (dev->net->features & NETIF_F_SG) && skb_linearize(skb))
+ return NULL;
+
+ headroom = skb_headroom(skb);
+ tailroom = skb_tailroom(skb);
+
+ if (!(headroom >= sizeof(tx_desc) && tailroom >= padding_size)) {
+ 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));
+ *tx_desc_ptr = cpu_to_le64(tx_desc);
+
+ 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];
+ u8 *tmp;
+
+ tmp16 = (u16 *)buf;
+ tmp = (u8 *)buf;
+
+ /* Power up ethernet PHY */
+ *tmp = AX_PHY_POWER;
+ ax88179_write_cmd(dev, AX88179A_PHY_POWER, 0, 0, 1, tmp);
+ msleep(250);
+
+ /* Ethernet PHY Auto Detach*/
+ ax88179a_auto_detach(dev);
+
+ *tmp = AX_MAC_EFF_EN;
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_MAC_BULK_OUT_CTRL, 1, 1, tmp);
+
+ *tmp16 = 0;
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2, tmp16);
+
+ *tmp = 0x04;
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PAUSE_WATERLVL_LOW, 1, 1, tmp);
+ *tmp = 0x10;
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_PAUSE_WATERLVL_HIGH, 1, 1, tmp);
+
+ *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);
+
+ *tmp = 0xff;
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_MAC_BM_INT_MASK, 1, 1, tmp);
+
+ *tmp = 0;
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_MAC_BM_RX_DMA_CTL, 1, 1, tmp);
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_MAC_BM_TX_DMA_CTL, 1, 1, tmp);
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_MAC_ARC_CTRL, 1, 1, tmp);
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_MAC_SWP_CTRL, 1, 1, tmp);
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_MAC_TX_HDR_CKSUM, 1, 1, tmp);
+
+ /* 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 */
+ *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);
+ ax179_data->rx_checksum = 1;
+
+ *tmp = AX_TXCOE_IP | AX_TXCOE_TCP | AX_TXCOE_UDP |
+ AX_TXCOE_TCPV6 | AX_TXCOE_UDPV6;
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_TXCOE_CTL, 1, 1, tmp);
+
+ /* Configure RX control register => start operation */
+ ax179_data->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)
+ ax179_data->rxctl |= AX_RX_CTL_IPE;
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2, &ax179_data->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;
+
+ phylink_start(ax179_data->phylink);
+
+ usbnet_link_change(dev, 0, 0);
+
+ return 0;
+}
+
+static int ax88179a_stop(struct usbnet *dev)
+{
+ struct ax88179_data *ax179_data = dev->driver_priv;
+ u16 reg16;
+ u8 reg8;
+
+ ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE, 2, 2, ®16);
+ reg16 &= ~AX_MEDIUM_RECEIVE_EN;
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_MEDIUM_STATUS_MODE, 2, 2, ®16);
+
+ reg16 = 0;
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX_RX_CTL, 2, 2, ®16);
+
+ reg8 = 0;
+ ax88179_read_cmd(dev, AX88179A_PHY_POWER, 0, 0, 1, ®8);
+
+ phylink_stop(ax179_data->phylink);
+
+ return 0;
+}
+
+const struct driver_info ax88179a_info = {
+ .description = "ASIX AX88179A USB 3.2 Gigabit Ethernet",
+ .bind = ax88179a_bind,
+ .unbind = ax88179a_unbind,
+ .status = ax88179_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 = ax88179_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 = ax88179_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,
+};
--
2.47.3
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH net-next v5 05/13] ax88179_178a: Add EEE configuration support for AX88179A MACs
2026-08-02 16:31 [PATCH net-next v5 00/13] ax88179_178a: Add support for AX88179A-based chips Birger Koblitz
` (3 preceding siblings ...)
2026-08-02 16:32 ` [PATCH net-next v5 04/13] ax88179_178a: Add HW support for AX179A-based chips Birger Koblitz
@ 2026-08-02 16:32 ` Birger Koblitz
2026-08-02 16:32 ` [PATCH net-next v5 06/13] ax88179_178a: Add EEE configuration support for AX88179A PHYs Birger Koblitz
` (8 subsequent siblings)
13 siblings, 0 replies; 32+ messages in thread
From: Birger Koblitz @ 2026-08-02 16:32 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Russell King, Andrew Lunn, Heiner Kallweit
Cc: linux-usb, netdev, linux-kernel, Birger Koblitz, Jianhui Xu
The AX88179A uses a simple HW configuration for EEE
via a single EEE configuration register without LPI timer support
Add support for this EEE enable/disable register and replace the EEE.
Signed-off-by: Birger Koblitz <mail@birger-koblitz.de>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
---
drivers/net/usb/ax88179a_devices.c | 2 ++
1 file changed, 2 insertions(+)
diff --git a/drivers/net/usb/ax88179a_devices.c b/drivers/net/usb/ax88179a_devices.c
index cef2a0131c1e6d60b28c2aab1e020e6b7468bc3c..22c7d1a4501dc052ebb16e06dff86e70c00c8381 100644
--- a/drivers/net/usb/ax88179a_devices.c
+++ b/drivers/net/usb/ax88179a_devices.c
@@ -400,6 +400,8 @@ 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)
--
2.47.3
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH net-next v5 06/13] ax88179_178a: Add EEE configuration support for AX88179A PHYs
2026-08-02 16:31 [PATCH net-next v5 00/13] ax88179_178a: Add support for AX88179A-based chips Birger Koblitz
` (4 preceding siblings ...)
2026-08-02 16:32 ` [PATCH net-next v5 05/13] ax88179_178a: Add EEE configuration support for AX88179A MACs Birger Koblitz
@ 2026-08-02 16:32 ` Birger Koblitz
2026-08-02 16:32 ` [PATCH net-next v5 07/13] ax88179_178a: Add VLAN offload support for AX88179A Birger Koblitz
` (7 subsequent siblings)
13 siblings, 0 replies; 32+ messages in thread
From: Birger Koblitz @ 2026-08-02 16:32 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Russell King, Andrew Lunn, Heiner Kallweit
Cc: linux-usb, netdev, linux-kernel, Birger Koblitz, Jianhui Xu
Configure phylink to work with the PHYs in the AX88197A controllers
and provide ethtool get/set eee operations calling into phylink.
Signed-off-by: Birger Koblitz <mail@birger-koblitz.de>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
---
drivers/net/usb/ax88179a_devices.c | 24 ++++++++++++++++++++++++
1 file changed, 24 insertions(+)
diff --git a/drivers/net/usb/ax88179a_devices.c b/drivers/net/usb/ax88179a_devices.c
index 22c7d1a4501dc052ebb16e06dff86e70c00c8381..be07effc20c3df0bad294f591b7150b78b0b5020 100644
--- a/drivers/net/usb/ax88179a_devices.c
+++ b/drivers/net/usb/ax88179a_devices.c
@@ -209,6 +209,20 @@ static int ax88179a_get_eeprom_len(struct net_device *net)
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_link = ethtool_op_get_link,
.get_msglevel = usbnet_get_msglevel,
@@ -218,6 +232,8 @@ static const struct ethtool_ops ax88179a_ethtool_ops = {
.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 = usbnet_nway_reset,
.get_link_ksettings = phy_ethtool_get_link_ksettings,
.set_link_ksettings = phy_ethtool_set_link_ksettings,
@@ -420,10 +436,18 @@ static int ax88179a_phylink_setup(struct usbnet *dev)
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 = true;
+ }
__set_bit(PHY_INTERFACE_MODE_INTERNAL,
data->phylink_config.supported_interfaces);
phy_if_mode = PHY_INTERFACE_MODE_INTERNAL;
+ 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))
--
2.47.3
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH net-next v5 07/13] ax88179_178a: Add VLAN offload support for AX88179A
2026-08-02 16:31 [PATCH net-next v5 00/13] ax88179_178a: Add support for AX88179A-based chips Birger Koblitz
` (5 preceding siblings ...)
2026-08-02 16:32 ` [PATCH net-next v5 06/13] ax88179_178a: Add EEE configuration support for AX88179A PHYs Birger Koblitz
@ 2026-08-02 16:32 ` Birger Koblitz
2026-08-02 21:37 ` Andrew Lunn
2026-08-02 16:32 ` [PATCH net-next v5 08/13] ax88179_178a: Add AX179A/AX279 multicast configuration Birger Koblitz
` (6 subsequent siblings)
13 siblings, 1 reply; 32+ messages in thread
From: Birger Koblitz @ 2026-08-02 16:32 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Russell King, Andrew Lunn, Heiner Kallweit
Cc: linux-usb, netdev, linux-kernel, Birger Koblitz, Jianhui Xu
The AX88179A-based chips support VLAN offload. Add configuration
support in netdev_ops. Features supported are:
NETIF_F_HW_VLAN_CTAG_TX, NETIF_F_HW_VLAN_CTAG_RX
and NETIF_F_HW_VLAN_CTAG_FILTER.
Signed-off-by: Birger Koblitz <mail@birger-koblitz.de>
---
drivers/net/usb/ax88179_lib.c | 2 +
drivers/net/usb/ax88179a_devices.c | 102 +++++++++++++++++++++++++++++++++++++
2 files changed, 104 insertions(+)
diff --git a/drivers/net/usb/ax88179_lib.c b/drivers/net/usb/ax88179_lib.c
index 34350ba3b96b94bc82072a578802a91ebffd7a6a..9d6df9d5c61c841965e9f7fa4b1075561b7f9c50 100644
--- a/drivers/net/usb/ax88179_lib.c
+++ b/drivers/net/usb/ax88179_lib.c
@@ -355,6 +355,7 @@ 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) {
@@ -374,6 +375,7 @@ int ax88179_set_features(struct net_device *net, netdev_features_t features)
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;
diff --git a/drivers/net/usb/ax88179a_devices.c b/drivers/net/usb/ax88179a_devices.c
index be07effc20c3df0bad294f591b7150b78b0b5020..213eb974b7da576bb6546532ce45716055da1de0 100644
--- a/drivers/net/usb/ax88179a_devices.c
+++ b/drivers/net/usb/ax88179a_devices.c
@@ -242,6 +242,62 @@ static const struct ethtool_ops ax88179a_ethtool_ops = {
.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;
+ u16 reg16;
+ u8 reg8;
+
+ ax88179_read_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_CONTROL, 1, 1, ®8);
+ vlan_ctrl = reg8;
+
+ /* Address */
+ reg8 = (vid / 16);
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_ADDRESS, 1, 1, ®8);
+
+ /* Data */
+ reg8 = vlan_ctrl | AX_VLAN_CONTROL_RD;
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_CONTROL, 1, 1, ®8);
+
+ ax88179_read_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_DATA0, 2, 2, ®16);
+ reg16 &= ~(1 << (vid % 16));
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_DATA0, 2, 2, ®16);
+
+ reg8 = vlan_ctrl | AX_VLAN_CONTROL_WE;
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_CONTROL, 1, 1, ®8);
+
+ 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;
+ u16 reg16;
+ u8 reg8;
+
+ ax88179_read_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_CONTROL, 1, 1, ®8);
+ vlan_ctrl = reg8;
+
+ /* Address */
+ reg8 = (vid / 16);
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_ADDRESS, 1, 1, ®8);
+
+ /* Data */
+ reg8 = vlan_ctrl | AX_VLAN_CONTROL_RD;
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_CONTROL, 1, 1, ®8);
+
+ ax88179_read_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_DATA0, 2, 2, ®16);
+ reg16 |= (1 << (vid % 16));
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_DATA0, 2, 2, ®16);
+
+ reg8 = vlan_ctrl | AX_VLAN_CONTROL_WE;
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_CONTROL, 1, 1, ®8);
+
+ return 0;
+}
+
static void ax88179a_mdio_unregister(struct ax88179_data *data)
{
mdiobus_unregister(data->mdio);
@@ -487,6 +543,49 @@ static int ax88179a_init_mdio(struct usbnet *dev)
return ret;
}
+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) {
+ ax88179_read_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_CONTROL, 1, 1, &tmp);
+ tmp ^= AX_VLAN_CONTROL_VFE;
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_CONTROL, 1, 1, &tmp);
+ if (features & NETIF_F_HW_VLAN_CTAG_FILTER) {
+ for (int i = 0; i < 256; i++) {
+ u16 tmp16 = 0;
+ /* Address */
+ tmp = i;
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_ADDRESS,
+ 1, 1, &tmp);
+ /* Data */
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_DATA0,
+ 2, 2, &tmp16);
+ tmp = AX_VLAN_CONTROL_WE;
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_CONTROL,
+ 1, 1, &tmp);
+ }
+ }
+ }
+
+ if (changed & NETIF_F_HW_VLAN_CTAG_RX) {
+ ax88179_read_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_CONTROL, 1, 1, &tmp);
+ tmp ^= AX_VLAN_CONTROL_VSO;
+ ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_CONTROL, 1, 1, &tmp);
+ }
+
+ return 0;
+}
+
static const struct net_device_ops ax88179a_netdev_ops = {
.ndo_open = usbnet_open,
.ndo_stop = usbnet_stop,
@@ -497,6 +596,9 @@ static const struct net_device_ops ax88179a_netdev_ops = {
.ndo_set_mac_address = ax88179_set_mac_addr,
.ndo_validate_addr = eth_validate_addr,
.ndo_eth_ioctl = usbnet_mii_ioctl,
+ .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)
--
2.47.3
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH net-next v5 08/13] ax88179_178a: Add AX179A/AX279 multicast configuration
2026-08-02 16:31 [PATCH net-next v5 00/13] ax88179_178a: Add support for AX88179A-based chips Birger Koblitz
` (6 preceding siblings ...)
2026-08-02 16:32 ` [PATCH net-next v5 07/13] ax88179_178a: Add VLAN offload support for AX88179A Birger Koblitz
@ 2026-08-02 16:32 ` Birger Koblitz
2026-08-02 16:32 ` [PATCH net-next v5 09/13] ax88179_178a: Add Suspend/resume support for AX88179A/772D/279 Birger Koblitz
` (5 subsequent siblings)
13 siblings, 0 replies; 32+ messages in thread
From: Birger Koblitz @ 2026-08-02 16:32 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Russell King, Andrew Lunn, Heiner Kallweit
Cc: linux-usb, netdev, linux-kernel, Birger Koblitz, Jianhui Xu
Add support for conditionally setting the ip_alignement flag
AX_RX_CTL_IPE in AX_RX_CTL and make sure that AX_RX_CTL_DROPCRCERR
is also set to be consistent with the initial configuration in
ax88179_reset()
Signed-off-by: Birger Koblitz <mail@birger-koblitz.de>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
---
drivers/net/usb/ax88179_lib.c | 8 ++++++--
drivers/net/usb/ax88179a_devices.c | 1 +
2 files changed, 7 insertions(+), 2 deletions(-)
diff --git a/drivers/net/usb/ax88179_lib.c b/drivers/net/usb/ax88179_lib.c
index 9d6df9d5c61c841965e9f7fa4b1075561b7f9c50..7a8c12027b9fcf7141694952528861e986d065ad 100644
--- a/drivers/net/usb/ax88179_lib.c
+++ b/drivers/net/usb/ax88179_lib.c
@@ -314,10 +314,14 @@ int ax88179_set_eeprom(struct net_device *net, struct ethtool_eeprom *eeprom, u8
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);
+ struct ax88179_data *data;
+
+ data = dev->driver_priv;
- data->rxctl = (AX_RX_CTL_START | AX_RX_CTL_AB | AX_RX_CTL_IPE);
+ 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;
diff --git a/drivers/net/usb/ax88179a_devices.c b/drivers/net/usb/ax88179a_devices.c
index 213eb974b7da576bb6546532ce45716055da1de0..70d0780ae5bcd9339dc456fffacdea86a4bf39a9 100644
--- a/drivers/net/usb/ax88179a_devices.c
+++ b/drivers/net/usb/ax88179a_devices.c
@@ -596,6 +596,7 @@ static const struct net_device_ops ax88179a_netdev_ops = {
.ndo_set_mac_address = ax88179_set_mac_addr,
.ndo_validate_addr = eth_validate_addr,
.ndo_eth_ioctl = usbnet_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,
--
2.47.3
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH net-next v5 09/13] ax88179_178a: Add Suspend/resume support for AX88179A/772D/279
2026-08-02 16:31 [PATCH net-next v5 00/13] ax88179_178a: Add support for AX88179A-based chips Birger Koblitz
` (7 preceding siblings ...)
2026-08-02 16:32 ` [PATCH net-next v5 08/13] ax88179_178a: Add AX179A/AX279 multicast configuration Birger Koblitz
@ 2026-08-02 16:32 ` Birger Koblitz
2026-08-02 21:44 ` Andrew Lunn
2026-08-02 16:32 ` [PATCH net-next v5 10/13] ax88179_178a: Add ethtool get_drvinfo Birger Koblitz
` (4 subsequent siblings)
13 siblings, 1 reply; 32+ messages in thread
From: Birger Koblitz @ 2026-08-02 16:32 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Russell King, Andrew Lunn, Heiner Kallweit
Cc: linux-usb, netdev, linux-kernel, Birger Koblitz, Jianhui Xu
The suspend and resume functions are called via the usb_driver structure,
for which the driver has only a single instance. Add wrapper functions
for the different implementations for the AX88179 and AX179A architecutres
in ax88179_lib which calls the 2 implementations in ax88179_178a and
ax88179a_devices, respectively.
Signed-off-by: Birger Koblitz <mail@birger-koblitz.de>
---
drivers/net/usb/ax88179_178a.c | 9 ++--
drivers/net/usb/ax88179_lib.c | 19 ++++++++
drivers/net/usb/ax88179_lib.h | 4 ++
drivers/net/usb/ax88179a_devices.c | 95 +++++++++++++++++++++++++++++++++++++-
4 files changed, 123 insertions(+), 4 deletions(-)
diff --git a/drivers/net/usb/ax88179_178a.c b/drivers/net/usb/ax88179_178a.c
index 6bd9704046009d7aad5ee6ddddc18faab5b030dc..8c22c2517cc08249c4e89625f142a76d4edc7d6a 100644
--- a/drivers/net/usb/ax88179_178a.c
+++ b/drivers/net/usb/ax88179_178a.c
@@ -684,6 +684,9 @@ 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;
+
dev->net->netdev_ops = &ax88179_netdev_ops;
dev->net->ethtool_ops = &ax88179_ethtool_ops;
dev->net->needed_headroom = 8;
@@ -1340,9 +1343,9 @@ static struct usb_driver ax88179_178a_driver = {
.name = "ax88179_178a",
.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,
diff --git a/drivers/net/usb/ax88179_lib.c b/drivers/net/usb/ax88179_lib.c
index 7a8c12027b9fcf7141694952528861e986d065ad..8aa5489d4bbc46faa1545cdfb85f65d8035c0c3c 100644
--- a/drivers/net/usb/ax88179_lib.c
+++ b/drivers/net/usb/ax88179_lib.c
@@ -464,3 +464,22 @@ int ax88179_set_mac_addr(struct net_device *net, void *p)
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
index 2e4d35c74ee8b32cd2916924b85d5427860bb08c..16a6dcbd6e66e37d3da0e57d2800c29d8a95a34a 100644
--- a/drivers/net/usb/ax88179_lib.h
+++ b/drivers/net/usb/ax88179_lib.h
@@ -315,6 +315,8 @@ struct ax88179_data {
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 {
@@ -346,6 +348,8 @@ 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;
diff --git a/drivers/net/usb/ax88179a_devices.c b/drivers/net/usb/ax88179a_devices.c
index 70d0780ae5bcd9339dc456fffacdea86a4bf39a9..4a3c2e77b1baba7e0c5bd5425179dfd1180b9b09 100644
--- a/drivers/net/usb/ax88179a_devices.c
+++ b/drivers/net/usb/ax88179a_devices.c
@@ -5,6 +5,8 @@
#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)
@@ -106,6 +108,53 @@ static int ax88179_mdiobus_write_c45(struct mii_bus *bus, int addr, int devnum,
return ax179a_write_mmd(dev, devnum, regnum, val);
}
+static int ax88179a_suspend(struct usb_interface *intf, pm_message_t message)
+{
+ struct usbnet *dev = usb_get_intfdata(intf);
+ struct ax88179_data *priv;
+ u16 tmp16;
+ u8 tmp8;
+
+ 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();
+ }
+
+ /* Enable WoL */
+ if (priv->wolopts) {
+ ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_MONITOR_MOD, 1, 1, &tmp8);
+ 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);
+ }
+
+ usbnet_suspend(intf, message);
+ ax88179_set_pm_mode(dev, false);
+ return 0;
+}
+
static int ax88179a_auto_detach(struct usbnet *dev)
{
u16 tmp16;
@@ -115,6 +164,43 @@ static int ax88179a_auto_detach(struct usbnet *dev)
return 0;
}
+static int ax88179a_resume(struct usb_interface *intf)
+{
+ struct usbnet *dev = usb_get_intfdata(intf);
+ struct ax88179_data *ax179_data;
+ u8 reg8;
+
+ ax179_data = dev->driver_priv;
+ ax88179_set_pm_mode(dev, true);
+
+ ax88179_read_cmd(dev, AX88179A_PHY_POWER, 0, 0, 1, ®8);
+ if (!(reg8 & AX_PHY_POWER)) {
+ reg8 = AX_PHY_POWER;
+ ax88179_write_cmd(dev, AX88179A_PHY_POWER, 0, 0, 1, ®8);
+ 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 ax88179_reset()
+ */
+ reg8 = 0;
+ ax88179_write_cmd(dev, AX88179A_PHY_POWER, 0, 0, 1, ®8);
+ msleep(250);
+
+ if (netif_running(dev->net)) {
+ rtnl_lock();
+ phylink_resume(ax179_data->phylink);
+ rtnl_unlock();
+ }
+
+ ax88179a_reset(dev);
+
+ ax88179_set_pm_mode(dev, false);
+
+ return usbnet_resume(intf);
+}
+
static void ax88179a_bulkin_config(struct usbnet *dev, u8 link_sts)
{
struct ax88179_data *ax179_data = dev->driver_priv;
@@ -668,6 +754,9 @@ static int ax88179a_bind(struct usbnet *dev, struct usb_interface *intf)
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;
@@ -1027,7 +1116,11 @@ static int ax88179a_reset(struct usbnet *dev)
1, 1, &tmp) > 0)
ax179_data->wol_supported = WAKE_MAGIC | WAKE_PHY;
- phylink_start(ax179_data->phylink);
+ /* ax88179a_reset() may also be called from resume context, phylink
+ * is already started, then.
+ */
+ if (!ax179_data->in_pm)
+ phylink_start(ax179_data->phylink);
usbnet_link_change(dev, 0, 0);
--
2.47.3
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH net-next v5 10/13] ax88179_178a: Add ethtool get_drvinfo
2026-08-02 16:31 [PATCH net-next v5 00/13] ax88179_178a: Add support for AX88179A-based chips Birger Koblitz
` (8 preceding siblings ...)
2026-08-02 16:32 ` [PATCH net-next v5 09/13] ax88179_178a: Add Suspend/resume support for AX88179A/772D/279 Birger Koblitz
@ 2026-08-02 16:32 ` Birger Koblitz
2026-08-02 16:32 ` [PATCH net-next v5 11/13] ax88179_178a: Update driver name and information Birger Koblitz
` (3 subsequent siblings)
13 siblings, 0 replies; 32+ messages in thread
From: Birger Koblitz @ 2026-08-02 16:32 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Russell King, Andrew Lunn, Heiner Kallweit
Cc: linux-usb, netdev, linux-kernel, Birger Koblitz, Jianhui Xu
Add ax88179a_get_drvinfo() as implementation of get_drvinfo, in order
to provide information about the device firmware.
Signed-off-by: Birger Koblitz <mail@birger-koblitz.de>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
---
drivers/net/usb/ax88179a_devices.c | 15 +++++++++++++++
1 file changed, 15 insertions(+)
diff --git a/drivers/net/usb/ax88179a_devices.c b/drivers/net/usb/ax88179a_devices.c
index 4a3c2e77b1baba7e0c5bd5425179dfd1180b9b09..37a55ff5464c6569ac72901378763ca419dd306a 100644
--- a/drivers/net/usb/ax88179a_devices.c
+++ b/drivers/net/usb/ax88179a_devices.c
@@ -201,6 +201,20 @@ static int ax88179a_resume(struct usb_interface *intf)
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)
{
struct ax88179_data *ax179_data = dev->driver_priv;
@@ -310,6 +324,7 @@ static int ax88179a_set_eee(struct net_device *net, struct ethtool_keee *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,
--
2.47.3
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH net-next v5 11/13] ax88179_178a: Update driver name and information
2026-08-02 16:31 [PATCH net-next v5 00/13] ax88179_178a: Add support for AX88179A-based chips Birger Koblitz
` (9 preceding siblings ...)
2026-08-02 16:32 ` [PATCH net-next v5 10/13] ax88179_178a: Add ethtool get_drvinfo Birger Koblitz
@ 2026-08-02 16:32 ` Birger Koblitz
2026-08-02 21:45 ` Andrew Lunn
2026-08-02 16:32 ` [PATCH net-next v5 12/13] ax88179_178a: Add support for AX88179A/772D/279 EEPROM access Birger Koblitz
` (2 subsequent siblings)
13 siblings, 1 reply; 32+ messages in thread
From: Birger Koblitz @ 2026-08-02 16:32 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Russell King, Andrew Lunn, Heiner Kallweit
Cc: linux-usb, netdev, linux-kernel, Birger Koblitz, Jianhui Xu
Add additionally supported devices to Kconfig description, add
further requirements such as PHYLINK.
Update driver name in usb_driver and update MODULE_DESCRIPTION
to include the additionally supported chips.
Signed-off-by: Birger Koblitz <mail@birger-koblitz.de>
---
drivers/net/usb/Kconfig | 10 +++++++---
drivers/net/usb/Makefile | 2 +-
drivers/net/usb/ax88179_178a.c | 8 ++++----
3 files changed, 12 insertions(+), 8 deletions(-)
diff --git a/drivers/net/usb/Kconfig b/drivers/net/usb/Kconfig
index 115990b0d9537aea2776544c9768b034f32aa7a4..4158455a2da76cd43a9a9d9d0e8c66028d57b095 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 3.0/2.0 to Gigabit Ethernet"
depends on USB_USBNET
- select CRC32
select PHYLINK
+ select AX88796B_PHY
+ select CRC32
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 2ecead0181eaf8d66fb6f5dbd6f2905b22b6eb57..4026f19ecb826a6465de38f1d9bb0e4c0af80421 100644
--- a/drivers/net/usb/Makefile
+++ b/drivers/net/usb/Makefile
@@ -12,7 +12,7 @@ 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.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
diff --git a/drivers/net/usb/ax88179_178a.c b/drivers/net/usb/ax88179_178a.c
index 8c22c2517cc08249c4e89625f142a76d4edc7d6a..6d6ee39b342e9b4e008bb2c1ee276dc27279a2f7 100644
--- a/drivers/net/usb/ax88179_178a.c
+++ b/drivers/net/usb/ax88179_178a.c
@@ -1339,8 +1339,8 @@ 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_wrapper,
@@ -1351,7 +1351,7 @@ static struct usb_driver ax88179_178a_driver = {
.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 based USB 3.0/2.0 Gigabit Ethernet Devices");
MODULE_LICENSE("GPL");
--
2.47.3
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH net-next v5 12/13] ax88179_178a: Add support for AX88179A/772D/279 EEPROM access
2026-08-02 16:31 [PATCH net-next v5 00/13] ax88179_178a: Add support for AX88179A-based chips Birger Koblitz
` (10 preceding siblings ...)
2026-08-02 16:32 ` [PATCH net-next v5 11/13] ax88179_178a: Update driver name and information Birger Koblitz
@ 2026-08-02 16:32 ` Birger Koblitz
2026-08-02 16:32 ` [PATCH net-next v5 13/13] ax88796b: Add support for AX88772D, AX88179A and AX88279 Birger Koblitz
2026-08-03 7:33 ` [PATCH net-next v5 00/13] ax88179_178a: Add support for AX88179A-based chips Jianhui Xu
13 siblings, 0 replies; 32+ messages in thread
From: Birger Koblitz @ 2026-08-02 16:32 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Russell King, Andrew Lunn, Heiner Kallweit
Cc: linux-usb, netdev, linux-kernel, Birger Koblitz, Jianhui Xu
The AX88179A/772D devices have 32 efuses with 20 bytes each,
which can be randomly programmed. The AX88279 has 16K FLASH.
Provide ethtool read capability for these devices. However,
no write access is provided.
Signed-off-by: Birger Koblitz <mail@birger-koblitz.de>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
---
drivers/net/usb/ax88179_178a.c | 19 +++++++++++++++
drivers/net/usb/ax88179_lib.c | 54 ++++++++++++++++++++++++++++++++----------
2 files changed, 61 insertions(+), 12 deletions(-)
diff --git a/drivers/net/usb/ax88179_178a.c b/drivers/net/usb/ax88179_178a.c
index 6d6ee39b342e9b4e008bb2c1ee276dc27279a2f7..be72ed955b6b8b1700d5f3624c59d7dea1b3d1dd 100644
--- a/drivers/net/usb/ax88179_178a.c
+++ b/drivers/net/usb/ax88179_178a.c
@@ -687,6 +687,19 @@ static int ax88179_bind(struct usbnet *dev, struct usb_interface *intf)
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;
@@ -711,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)
diff --git a/drivers/net/usb/ax88179_lib.c b/drivers/net/usb/ax88179_lib.c
index 8aa5489d4bbc46faa1545cdfb85f65d8035c0c3c..22209640734c884a0c83d76ad72e4364dd29c653 100644
--- a/drivers/net/usb/ax88179_lib.c
+++ b/drivers/net/usb/ax88179_lib.c
@@ -203,36 +203,61 @@ int ax88179_set_wol(struct net_device *net, struct ethtool_wolinfo *wolinfo)
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);
- u16 *eeprom_buff;
- int first_word, last_word;
- int i, ret;
+ 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_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);
+ 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;
- /* 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]);
+ 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 < 0) {
kfree(eeprom_buff);
return -EIO;
}
}
- memcpy(data, (u8 *)eeprom_buff + (eeprom->offset & 1), eeprom->len);
+ memcpy(data, eeprom_buff + eeprom->offset % ax179_data->eeprom_block, eeprom->len);
kfree(eeprom_buff);
return 0;
}
@@ -240,12 +265,17 @@ int ax88179_get_eeprom(struct net_device *net, struct ethtool_eeprom *eeprom, u8
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);
--
2.47.3
^ permalink raw reply related [flat|nested] 32+ messages in thread
* [PATCH net-next v5 13/13] ax88796b: Add support for AX88772D, AX88179A and AX88279
2026-08-02 16:31 [PATCH net-next v5 00/13] ax88179_178a: Add support for AX88179A-based chips Birger Koblitz
` (11 preceding siblings ...)
2026-08-02 16:32 ` [PATCH net-next v5 12/13] ax88179_178a: Add support for AX88179A/772D/279 EEPROM access Birger Koblitz
@ 2026-08-02 16:32 ` Birger Koblitz
2026-08-02 21:55 ` Andrew Lunn
2026-08-03 7:33 ` [PATCH net-next v5 00/13] ax88179_178a: Add support for AX88179A-based chips Jianhui Xu
13 siblings, 1 reply; 32+ messages in thread
From: Birger Koblitz @ 2026-08-02 16:32 UTC (permalink / raw)
To: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Russell King, Andrew Lunn, Heiner Kallweit
Cc: linux-usb, netdev, linux-kernel, Birger Koblitz, Jianhui Xu
The AX88772D, AX88179A and AX88279 are 100MBit, 1GBit and respectively
2.5 GBit PHYs that are found in ASIX USB-Ethernet controllers of the
same name.
Signed-off-by: Birger Koblitz <mail@birger-koblitz.de>
---
drivers/net/phy/ax88796b.c | 188 +++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 188 insertions(+)
diff --git a/drivers/net/phy/ax88796b.c b/drivers/net/phy/ax88796b.c
index f20ddf64914993f7097070d1f56c1103ec1e40e8..04788f3e247cbf86a291f373f895beec496d85cf 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,131 @@ 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_update_link(phydev);
+ if (ret)
+ return ret;
+
+ phydev->master_slave_get = MASTER_SLAVE_CFG_UNSUPPORTED;
+ phydev->master_slave_state = MASTER_SLAVE_STATE_UNSUPPORTED;
+ phydev->speed = SPEED_UNKNOWN;
+ phydev->duplex = DUPLEX_UNKNOWN;
+ phydev->pause = 0;
+ phydev->asym_pause = 0;
+ phydev->rate_matching = RATE_MATCH_PAUSE;
+
+ if (!phydev->link)
+ return 0;
+
+ ret = genphy_read_master_slave(phydev);
+ if (ret < 0)
+ return ret;
+
+ ret = genphy_read_lpa(phydev);
+ if (ret < 0)
+ return ret;
+
+ val = phy_read(phydev, MII_ADVERTISE);
+ if (val < 0)
+ return val;
+
+ /* Read actual speed from vendor register */
+ val = phy_read(phydev, AX_CTRL_STATUS);
+ 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
+ */
+ 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;
+
+ 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 -EINVAL;
+ }
+
+ 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;
+}
+
+/* AX88179A-based PHYs cannot access MMD registers via MII_MMD_CTRL/MII_MMD_DATA
+ * Make sure to use c45 access instead.
+ */
+static int asix_read_mmd(struct phy_device *phydev, int devnum, u16 reg)
+{
+ struct mii_bus *bus = phydev->mdio.bus;
+ int addr = phydev->mdio.addr;
+
+ return __mdiobus_c45_read(bus, addr, devnum, reg);
+}
+
+static int asix_write_mmd(struct phy_device *phydev, int devnum, u16 reg, u16 val)
+{
+ struct mii_bus *bus = phydev->mdio.bus;
+ int addr = phydev->mdio.addr;
+
+ return __mdiobus_c45_write(bus, addr, devnum, reg, val);
+}
+
static void asix_ax88772a_link_change_notify(struct phy_device *phydev)
{
/* Reset PHY, otherwise MII_LPA will provide outdated information.
@@ -94,6 +232,25 @@ 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_set_bit(ETHTOOL_LINK_MODE_1000baseT_Full_BIT, mask);
+ linkmode_andnot(phydev->supported, phydev->supported, mask);
+
+ return 0;
+}
+
static struct phy_driver asix_driver[] = {
{
PHY_ID_MATCH_EXACT(PHY_ID_ASIX_AX88772A),
@@ -111,6 +268,34 @@ 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 = asix_read_mmd,
+ .write_mmd = asix_write_mmd,
+ .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 = asix_read_mmd,
+ .write_mmd = asix_write_mmd,
+}, {
+ PHY_ID_MATCH_EXACT(PHY_ID_ASIX_AX88279),
+ .name = "Asix Electronics AX88279",
+ .flags = PHY_IS_INTERNAL,
+ .get_features = asix_ax88279_get_features,
+ .read_status = asix_ax88279_read_status,
+ .config_aneg = asix_ax88279_config_aneg,
+ .read_mmd = asix_read_mmd,
+ .write_mmd = asix_write_mmd,
+ .suspend = genphy_suspend,
+ .resume = genphy_resume,
}, {
PHY_ID_MATCH_MODEL(PHY_ID_ASIX_AX88796B),
.name = "Asix Electronics AX88796B",
@@ -124,6 +309,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) },
{ }
};
--
2.47.3
^ permalink raw reply related [flat|nested] 32+ messages in thread
* Re: [PATCH net-next v5 03/13] ax88179_178a: Add netdev2data() convenience function
2026-08-02 16:32 ` [PATCH net-next v5 03/13] ax88179_178a: Add netdev2data() convenience function Birger Koblitz
@ 2026-08-02 21:10 ` Andrew Lunn
0 siblings, 0 replies; 32+ messages in thread
From: Andrew Lunn @ 2026-08-02 21:10 UTC (permalink / raw)
To: Birger Koblitz
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Russell King, Heiner Kallweit, linux-usb, netdev,
linux-kernel, Jianhui Xu
On Sun, Aug 02, 2026 at 06:32:01PM +0200, Birger Koblitz wrote:
> Add a convenience function for the ethtool ops that use
> phylink and need to retrieve the ax88179_data structure from
> net_device.
>
> Signed-off-by: Birger Koblitz <mail@birger-koblitz.de>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Andrew
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH net-next v5 04/13] ax88179_178a: Add HW support for AX179A-based chips
2026-08-02 16:32 ` [PATCH net-next v5 04/13] ax88179_178a: Add HW support for AX179A-based chips Birger Koblitz
@ 2026-08-02 21:28 ` Andrew Lunn
2026-08-06 19:32 ` Birger Koblitz
2026-08-02 21:36 ` Andrew Lunn
2026-08-02 21:47 ` Andrew Lunn
2 siblings, 1 reply; 32+ messages in thread
From: Andrew Lunn @ 2026-08-02 21:28 UTC (permalink / raw)
To: Birger Koblitz
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Russell King, Heiner Kallweit, linux-usb, netdev,
linux-kernel, Jianhui Xu
This is more of a nitpick, given how much work you have already done
on this driver. I think designated initializer syntax could make this
more readable:
num ax_bulk_in_speeds {
BULK_IN_SPEED_1G_SS = 0,
BULK_IN_SPEED_1G_HS = 1,
BULK_IN_SPEED_100_FULL_SS = 2,
BULK_IN_SPEED_100_HALF_SS = 3,
...
};
> +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},
> +static void ax88179a_bulkin_config(struct usbnet *dev, u8 link_sts)
> +{
> + struct ax88179_data *ax179_data = dev->driver_priv;
> + const struct ax_bulkin_settings *bulkin_data;
> + int index = 0;
> +
> + switch (ax179_data->speed) {
> + case ETHER_LINK_2500: /* AX88279 only */
> + index = BULK_IN_SPEED_2500;
> + break;
> +
> + case ETHER_LINK_1000: /* AX88279 & AX88178A */
> + if (ax179_data->chip_version == AX_VERSION_AX88279) {
> + if (link_sts & AX_USB_SS)
> + index = BULK_IN_SPEED_1G_SS;
> + else if (link_sts & AX_USB_HS)
> + index = BULK_IN_SPEED_HS;
This removes the 1, 2, 3, etc making the relationship more obvious.
Andrew
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH net-next v5 04/13] ax88179_178a: Add HW support for AX179A-based chips
2026-08-02 16:32 ` [PATCH net-next v5 04/13] ax88179_178a: Add HW support for AX179A-based chips Birger Koblitz
2026-08-02 21:28 ` Andrew Lunn
@ 2026-08-02 21:36 ` Andrew Lunn
2026-08-06 19:34 ` Birger Koblitz
2026-08-02 21:47 ` Andrew Lunn
2 siblings, 1 reply; 32+ messages in thread
From: Andrew Lunn @ 2026-08-02 21:36 UTC (permalink / raw)
To: Birger Koblitz
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Russell King, Heiner Kallweit, linux-usb, netdev,
linux-kernel, Jianhui Xu
> +static void ax88179a_bulkin_config(struct usbnet *dev, u8 link_sts)
> +{
> + struct ax88179_data *ax179_data = dev->driver_priv;
> + const struct ax_bulkin_settings *bulkin_data;
> + int index = 0;
> +
> + switch (ax179_data->speed) {
What is setting ->speed?
> +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)
> +{
It should be coming from there, but i don't see any code setting it.
> + ax88179_read_cmd(dev, AX_ACCESS_MAC, PHYSICAL_LINK_STATUS, 1, 1, &link_sts);
> + ax88179a_bulkin_config(dev, link_sts);
It might be better to pass speed as a parameter.
I would probably play with ethtool and set the advertised speeds to
only include slower speeds, like 10Half, and make sure the link works
correctly.
Andrew
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH net-next v5 07/13] ax88179_178a: Add VLAN offload support for AX88179A
2026-08-02 16:32 ` [PATCH net-next v5 07/13] ax88179_178a: Add VLAN offload support for AX88179A Birger Koblitz
@ 2026-08-02 21:37 ` Andrew Lunn
0 siblings, 0 replies; 32+ messages in thread
From: Andrew Lunn @ 2026-08-02 21:37 UTC (permalink / raw)
To: Birger Koblitz
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Russell King, Heiner Kallweit, linux-usb, netdev,
linux-kernel, Jianhui Xu
On Sun, Aug 02, 2026 at 06:32:05PM +0200, Birger Koblitz wrote:
> The AX88179A-based chips support VLAN offload. Add configuration
> support in netdev_ops. Features supported are:
> NETIF_F_HW_VLAN_CTAG_TX, NETIF_F_HW_VLAN_CTAG_RX
> and NETIF_F_HW_VLAN_CTAG_FILTER.
>
> Signed-off-by: Birger Koblitz <mail@birger-koblitz.de>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Andrew
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH net-next v5 09/13] ax88179_178a: Add Suspend/resume support for AX88179A/772D/279
2026-08-02 16:32 ` [PATCH net-next v5 09/13] ax88179_178a: Add Suspend/resume support for AX88179A/772D/279 Birger Koblitz
@ 2026-08-02 21:44 ` Andrew Lunn
2026-08-03 8:53 ` Birger Koblitz
0 siblings, 1 reply; 32+ messages in thread
From: Andrew Lunn @ 2026-08-02 21:44 UTC (permalink / raw)
To: Birger Koblitz
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Russell King, Heiner Kallweit, linux-usb, netdev,
linux-kernel, Jianhui Xu
> + /* Enable WoL */
> + if (priv->wolopts) {
> + ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_MONITOR_MOD, 1, 1, &tmp8);
> + if (priv->wolopts & WAKE_PHY)
> + tmp8 |= AX_MONITOR_MODE_RWLC;
Have you tested WoL?
WoL is somewhat complex because it can be done in the PHY or the
MAC. If it can be done in the PHY, you can power off the MAC. If the
MAC needs to do it, it needs to stay awake.
But what i don't get is how the MAC is implementing WAKE_PHY?
Andrew
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH net-next v5 11/13] ax88179_178a: Update driver name and information
2026-08-02 16:32 ` [PATCH net-next v5 11/13] ax88179_178a: Update driver name and information Birger Koblitz
@ 2026-08-02 21:45 ` Andrew Lunn
0 siblings, 0 replies; 32+ messages in thread
From: Andrew Lunn @ 2026-08-02 21:45 UTC (permalink / raw)
To: Birger Koblitz
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Russell King, Heiner Kallweit, linux-usb, netdev,
linux-kernel, Jianhui Xu
On Sun, Aug 02, 2026 at 06:32:09PM +0200, Birger Koblitz wrote:
> Add additionally supported devices to Kconfig description, add
> further requirements such as PHYLINK.
>
> Update driver name in usb_driver and update MODULE_DESCRIPTION
> to include the additionally supported chips.
>
> Signed-off-by: Birger Koblitz <mail@birger-koblitz.de>
Reviewed-by: Andrew Lunn <andrew@lunn.ch>
Andrew
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH net-next v5 04/13] ax88179_178a: Add HW support for AX179A-based chips
2026-08-02 16:32 ` [PATCH net-next v5 04/13] ax88179_178a: Add HW support for AX179A-based chips Birger Koblitz
2026-08-02 21:28 ` Andrew Lunn
2026-08-02 21:36 ` Andrew Lunn
@ 2026-08-02 21:47 ` Andrew Lunn
2 siblings, 0 replies; 32+ messages in thread
From: Andrew Lunn @ 2026-08-02 21:47 UTC (permalink / raw)
To: Birger Koblitz
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Russell King, Heiner Kallweit, linux-usb, netdev,
linux-kernel, Jianhui Xu
> diff --git a/drivers/net/usb/ax88179a_devices.c b/drivers/net/usb/ax88179a_devices.c
Please add an entry to MAINTAINERS for this file, and add yourself as
the Maintainer.
Andrew
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH net-next v5 13/13] ax88796b: Add support for AX88772D, AX88179A and AX88279
2026-08-02 16:32 ` [PATCH net-next v5 13/13] ax88796b: Add support for AX88772D, AX88179A and AX88279 Birger Koblitz
@ 2026-08-02 21:55 ` Andrew Lunn
2026-08-06 19:35 ` Birger Koblitz
0 siblings, 1 reply; 32+ messages in thread
From: Andrew Lunn @ 2026-08-02 21:55 UTC (permalink / raw)
To: Birger Koblitz
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Russell King, Heiner Kallweit, linux-usb, netdev,
linux-kernel, Jianhui Xu
> +static int asix_ax88279_read_status(struct phy_device *phydev)
> +{
> + int ret, val;
> +
> + ret = genphy_update_link(phydev);
> + if (ret)
> + return ret;
> +
> + phydev->master_slave_get = MASTER_SLAVE_CFG_UNSUPPORTED;
> + phydev->master_slave_state = MASTER_SLAVE_STATE_UNSUPPORTED;
> + phydev->speed = SPEED_UNKNOWN;
> + phydev->duplex = DUPLEX_UNKNOWN;
> + phydev->pause = 0;
> + phydev->asym_pause = 0;
> + phydev->rate_matching = RATE_MATCH_PAUSE;
> +
> + if (!phydev->link)
> + return 0;
> +
> + ret = genphy_read_master_slave(phydev);
> + if (ret < 0)
> + return ret;
> +
> + ret = genphy_read_lpa(phydev);
> + if (ret < 0)
> + return ret;
This looks pretty similar to genphy_read_status(). Can you call that
here? And then the rest of the code here can deal with higher speeds.
> + /* Read actual speed from vendor register */
> + val = phy_read(phydev, AX_CTRL_STATUS);
> + 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
> + */
> + 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;
> +
> + 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 -EINVAL;
> + }
Why is that? Now that phylink is driving the PHY, and reporting
speeds, duplex etc, you should have everything you need to program the
MAC when the link mode is forced.
Andrew
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH net-next v5 00/13] ax88179_178a: Add support for AX88179A-based chips
2026-08-02 16:31 [PATCH net-next v5 00/13] ax88179_178a: Add support for AX88179A-based chips Birger Koblitz
` (12 preceding siblings ...)
2026-08-02 16:32 ` [PATCH net-next v5 13/13] ax88796b: Add support for AX88772D, AX88179A and AX88279 Birger Koblitz
@ 2026-08-03 7:33 ` Jianhui Xu
2026-08-03 9:53 ` Birger Koblitz
13 siblings, 1 reply; 32+ messages in thread
From: Jianhui Xu @ 2026-08-03 7:33 UTC (permalink / raw)
To: mail
Cc: andrew+netdev, andrew, davem, edumazet, hkallweit1, kuba,
linux-kernel, linux-usb, linux, netdev, neuromoments, pabeni
Hi Birger,
I tested v5 on the same ASIX AX88179B adapter (USB 0b95:1790,
bcdDevice 0x0200, firmware 1.3.0.0).
The 13 patches applied to net-next commit
df13c1df8147675470213ffff29dd5762fa321f5 and built successfully as
7.2.0-rc3-ax88179b-v5. The focused W=1 builds for ax88179.o and
ax88796b.o were clean.
Unfortunately I reproduced an intermittent cold-activation failure, so I cannot
add a Tested-by for v5. In four fresh direct-kernel QEMU starts with complete
diagnostic capture, the old lease was released before starting DHCPDISCOVER. Two
reported 1000baseT/Full carrier but timed out DHCP, while two succeeded
normally. In both failed runs, RX remained at zero while TX increased. Reloading
ax88179 and ax88796b recovered DHCP, RX, and bound traffic in both cases.
Before this follow-up campaign, in the earlier v5 validation session, I observed
a separate 100baseT/Full failure: the adapter negotiated carrier after I changed
the advertisement to 100baseT/Full-only, but ARP and bound traffic failed.
I then tried to reproduce that result with three new advertise-0x008
transitions. All three negotiated 100baseT/Full and passed bound gateway
traffic; two also explicitly passed traffic to the test host. These repetitions
included tests both with and without a preceding module reload. I could not
reproduce the earlier 100-Mbit failure.
That earlier v5 validation session also produced a separate guest ACPI S3
failure: after resume, the adapter returned with 1000baseT/Full carrier, but RX
remained frozen and traffic was broken. I then performed three new S3 cycles.
All three logged QEMU's same emulated-XHCI resume reinitialization and USB
reset, but returned working traffic with increasing RX counters immediately. The
final repetition also recreated the earlier speed, EEE, and pause setting
sequence and verified working gateway traffic immediately before suspend.
I could not reproduce the earlier frozen-RX result. This remains a QEMU
emulated-XHCI test, not a physical-XHCI suspend test.
While reviewing the speed result, I noticed ax88179a_bulkin_config() selects its
table using ax179_data->speed, while ax88179a_mac_link_up() receives the speed
argument but I could not find an assignment to that private member. The new
passing 100-Mbit tests do not support linking that source observation to the
original one-off failure, and the same pattern appears in v4.
EEE disable/restore, pause enable/restore, EEPROM read, module reload, and USB
detach/reattach otherwise worked. I also tried Wake-on-LAN in QEMU. The driver
accepted magic-packet wake, ethtool read back Wake-on: g, and USB wakeup was
enabled before the guest entered S3. QEMU was configured with USB remote-wake
suppression disabled, but the guest did not resume after directed-broadcast,
limited-broadcast, and unicast magic packets. I consider that result
inconclusive because I did not independently verify the physical USB
remote-wakeup propagation through QEMU.
Please let me know if you would like me to test a fix or collect a specific
register trace.
Thanks,
Jianhui
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH net-next v5 09/13] ax88179_178a: Add Suspend/resume support for AX88179A/772D/279
2026-08-02 21:44 ` Andrew Lunn
@ 2026-08-03 8:53 ` Birger Koblitz
2026-08-03 14:58 ` Andrew Lunn
0 siblings, 1 reply; 32+ messages in thread
From: Birger Koblitz @ 2026-08-03 8:53 UTC (permalink / raw)
To: Andrew Lunn
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Russell King, Heiner Kallweit, linux-usb, netdev,
linux-kernel, Jianhui Xu
On 02/08/2026 23:44, Andrew Lunn wrote:
>> + /* Enable WoL */
>> + if (priv->wolopts) {
>> + ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_MONITOR_MOD, 1, 1, &tmp8);
>> + if (priv->wolopts & WAKE_PHY)
>> + tmp8 |= AX_MONITOR_MODE_RWLC;
>
> Have you tested WoL?
Yes, I tested WoL with the initially submitted driver, and I later checked that
the register writes around suspend/resume stayed the same. In fact, without
proper WoL settings, the AX279 will immediately wake up the host again after
suspend once USB wakeup is enabled for the respective USB device. I successfully
tested WoL for PHY changes (link state changes, any type of packet received) for
the 772D, 179A and 279. I did not manage to get WoL to work with magic packets,
but I could also not make this work with the ASIX Windows driver or ASIX's
out-of-tree Linux driver, so I assumed I was just not able to send the right type
of magic packets or my network setup was broken.
>
> WoL is somewhat complex because it can be done in the PHY or the
> MAC. If it can be done in the PHY, you can power off the MAC. If the
> MAC needs to do it, it needs to stay awake.
>
> But what i don't get is how the MAC is implementing WAKE_PHY?
>
I really look at the suspend/resume functionality and WoL as a command that is
sent to the MCU, which then configures MAC/PHY and the USB interface in one
go and then puts the controller to sleep. It works by configuring a monitor mode
in the MAC (cmd AX_ACCESS_MAC, register AX_MONITOR_MODe) and then "calling"
AX88179A_WAKEUP_SETTING, register 8, with bits specifying
EPHY_LOW_POWER_EN | S5_WOL_EN | S5_WOL_LOW_POWER | 0x8000 which is a mix of USB
interface options and PHY configuration options plus a the EPHY_LOW_POWER_EN
command.
It now looks like suspend/resume is broken in v5 according to Jianhui's tests. Hhowever,
it was probably already broken in v4. Since this is intermittent, I wonder whether
the polling of the phy by phylink is to blame, which sometimes may still come after
the AX88179A_WAKEUP_SETTING sleep command. Can the polling be disabled by the driver
in the suspend() function?
Birger
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH net-next v5 00/13] ax88179_178a: Add support for AX88179A-based chips
2026-08-03 7:33 ` [PATCH net-next v5 00/13] ax88179_178a: Add support for AX88179A-based chips Jianhui Xu
@ 2026-08-03 9:53 ` Birger Koblitz
2026-08-03 14:39 ` Yuan Xu
0 siblings, 1 reply; 32+ messages in thread
From: Birger Koblitz @ 2026-08-03 9:53 UTC (permalink / raw)
To: Jianhui Xu
Cc: andrew+netdev, andrew, davem, edumazet, hkallweit1, kuba,
linux-kernel, linux-usb, linux, netdev, pabeni
Thanks so much for testing, again, Jianhui!
On 03/08/2026 09:33, Jianhui Xu wrote:
> Hi Birger,
>
> I tested v5 on the same ASIX AX88179B adapter (USB 0b95:1790,
> bcdDevice 0x0200, firmware 1.3.0.0).
>
> The 13 patches applied to net-next commit
> df13c1df8147675470213ffff29dd5762fa321f5 and built successfully as
> 7.2.0-rc3-ax88179b-v5. The focused W=1 builds for ax88179.o and
> ax88796b.o were clean.
>
> Unfortunately I reproduced an intermittent cold-activation failure, so I cannot
> add a Tested-by for v5. In four fresh direct-kernel QEMU starts with complete
> diagnostic capture, the old lease was released before starting DHCPDISCOVER. Two
> reported 1000baseT/Full carrier but timed out DHCP, while two succeeded
> normally. In both failed runs, RX remained at zero while TX increased. Reloading
> ax88179 and ax88796b recovered DHCP, RX, and bound traffic in both cases.
>
> Before this follow-up campaign, in the earlier v5 validation session, I observed
> a separate 100baseT/Full failure: the adapter negotiated carrier after I changed
> the advertisement to 100baseT/Full-only, but ARP and bound traffic failed.
> I then tried to reproduce that result with three new advertise-0x008
> transitions. All three negotiated 100baseT/Full and passed bound gateway
> traffic; two also explicitly passed traffic to the test host. These repetitions
> included tests both with and without a preceding module reload. I could not
> reproduce the earlier 100-Mbit failure.
>
> That earlier v5 validation session also produced a separate guest ACPI S3
> failure: after resume, the adapter returned with 1000baseT/Full carrier, but RX
> remained frozen and traffic was broken. I then performed three new S3 cycles.
> All three logged QEMU's same emulated-XHCI resume reinitialization and USB
> reset, but returned working traffic with increasing RX counters immediately. The
> final repetition also recreated the earlier speed, EEE, and pause setting
> sequence and verified working gateway traffic immediately before suspend.
> I could not reproduce the earlier frozen-RX result. This remains a QEMU
> emulated-XHCI test, not a physical-XHCI suspend test.
>
> While reviewing the speed result, I noticed ax88179a_bulkin_config() selects its
> table using ax179_data->speed, while ax88179a_mac_link_up() receives the speed
> argument but I could not find an assignment to that private member. The new
> passing 100-Mbit tests do not support linking that source observation to the
> original one-off failure, and the same pattern appears in v4.
This issue is not critically problematic, the speed variable only affects the bulk
configuration settins that control how data is assembled in the controller and
assembled to larger USB transfers, including timing and so on. This should only harm
performance, but not the issues with the link after suspend/resume. It was introduced
in v3 or v4 when the speed info no longer came from the USB interrupt URB.
The following should fix this:
diff --git a/drivers/net/usb/ax88179a_devices.c b/drivers/net/usb/ax88179a_devices.c
index 37a55ff5464c..a4d782979c63 100644
--- a/drivers/net/usb/ax88179a_devices.c
+++ b/drivers/net/usb/ax88179a_devices.c
@@ -215,13 +215,13 @@ static void ax88179a_get_drvinfo(struct net_device *net, struct ethtool_drvinfo
priv->fw_version[2], priv->fw_version[3]);
}
-static void ax88179a_bulkin_config(struct usbnet *dev, u8 link_sts)
+static void ax88179a_bulkin_config(struct usbnet *dev, u8 link_sts, u8 speed)
{
struct ax88179_data *ax179_data = dev->driver_priv;
const struct ax_bulkin_settings *bulkin_data;
int index = 0;
- switch (ax179_data->speed) {
+ switch (speed) {
case ETHER_LINK_2500: /* AX88279 only */
index = 0;
break;
@@ -451,6 +451,7 @@ static void ax88179a_mac_link_up(struct phylink_config *config,
struct usbnet *dev = netdev_priv(to_net_dev(config->dev));
struct ax88179_data *ax179_data = dev->driver_priv;
u8 tmp8, link_sts, reg8[3];
+ u8 bulk_config_speed = 0;
u16 tmp16, mode;
/* Stop RX/TX for link configuration */
@@ -503,11 +504,13 @@ static void ax88179a_mac_link_up(struct phylink_config *config,
ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_MAC_LSO_ENHANCE_CTRL, 1, 1, &tmp8);
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:
@@ -518,6 +521,8 @@ static void ax88179a_mac_link_up(struct phylink_config *config,
tmp8 = 0x40;
ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_MAC_RX_DATA_CDC_CNT, 1, 1, &tmp8);
+ if (!bulk_config_speed)
+ bulk_config_speed = ETHER_LINK_100;
break;
case SPEED_10:
@@ -529,12 +534,12 @@ static void ax88179a_mac_link_up(struct phylink_config *config,
tmp8 = 0xFA;
ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_MAC_RX_DATA_CDC_CNT, 1, 1, &tmp8);
- speed = 10;
+ 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);
+ ax88179a_bulkin_config(dev, link_sts, bulk_config_speed);
if (ax179_data->chip_version < AX_VERSION_AX88279) {
tmp8 = 0;
>
> EEE disable/restore, pause enable/restore, EEPROM read, module reload, and USB
> detach/reattach otherwise worked. I also tried Wake-on-LAN in QEMU. The driver
> accepted magic-packet wake, ethtool read back Wake-on: g, and USB wakeup was
> enabled before the guest entered S3. QEMU was configured with USB remote-wake
> suppression disabled, but the guest did not resume after directed-broadcast,
> limited-broadcast, and unicast magic packets. I consider that result
> inconclusive because I did not independently verify the physical USB
> remote-wakeup propagation through QEMU.
>
> Please let me know if you would like me to test a fix or collect a specific
> register trace.
Could you trace the calls to __ax88179_write_cmd (registers, values) around the
suspend/resume events. Since the issue is not deterministic, it is probably sequence/timing-related.
My suspicion is that phylink sends PHY-polls after the controller got the sleep command.
Are there differences in the sequence of calls for the successfull suspend/resume
cycles and the unsuccessful ones?
Thanks!
Birger
^ permalink raw reply related [flat|nested] 32+ messages in thread
* Re: [PATCH net-next v5 00/13] ax88179_178a: Add support for AX88179A-based chips
2026-08-03 9:53 ` Birger Koblitz
@ 2026-08-03 14:39 ` Yuan Xu
2026-08-06 19:38 ` Birger Koblitz
0 siblings, 1 reply; 32+ messages in thread
From: Yuan Xu @ 2026-08-03 14:39 UTC (permalink / raw)
To: Birger Koblitz
Cc: andrew+netdev, andrew, davem, edumazet, hkallweit1, kuba,
linux-kernel, linux-usb, linux, netdev, pabeni
Hi Birger,
I really appreciate all the work you’ve put into this!
I’ll apply the fix and run additional tests this weekend. I’ll report back
with the traces and any reproducible differences if I find them.
Thanks,
Jianhui
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH net-next v5 09/13] ax88179_178a: Add Suspend/resume support for AX88179A/772D/279
2026-08-03 8:53 ` Birger Koblitz
@ 2026-08-03 14:58 ` Andrew Lunn
0 siblings, 0 replies; 32+ messages in thread
From: Andrew Lunn @ 2026-08-03 14:58 UTC (permalink / raw)
To: Birger Koblitz
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Russell King, Heiner Kallweit, linux-usb, netdev,
linux-kernel, Jianhui Xu
On Mon, Aug 03, 2026 at 10:53:18AM +0200, Birger Koblitz wrote:
> On 02/08/2026 23:44, Andrew Lunn wrote:
> > > + /* Enable WoL */
> > > + if (priv->wolopts) {
> > > + ax88179_read_cmd(dev, AX_ACCESS_MAC, AX_MONITOR_MOD, 1, 1, &tmp8);
> > > + if (priv->wolopts & WAKE_PHY)
> > > + tmp8 |= AX_MONITOR_MODE_RWLC;
> >
> > Have you tested WoL?
> Yes, I tested WoL with the initially submitted driver, and I later checked that
> the register writes around suspend/resume stayed the same. In fact, without
> proper WoL settings, the AX279 will immediately wake up the host again after
> suspend once USB wakeup is enabled for the respective USB device. I successfully
> tested WoL for PHY changes (link state changes, any type of packet received) for
> the 772D, 179A and 279. I did not manage to get WoL to work with magic packets,
> but I could also not make this work with the ASIX Windows driver or ASIX's
> out-of-tree Linux driver, so I assumed I was just not able to send the right type
> of magic packets or my network setup was broken.
sudo apt install wakeonlan
wakeonlan AA:BB:CC:DD:EE:FF
I've used this in the past.
> I really look at the suspend/resume functionality and WoL as a command that is
> sent to the MCU, which then configures MAC/PHY and the USB interface in one
> go and then puts the controller to sleep. It works by configuring a monitor mode
> in the MAC (cmd AX_ACCESS_MAC, register AX_MONITOR_MODe) and then "calling"
> AX88179A_WAKEUP_SETTING, register 8, with bits specifying
> EPHY_LOW_POWER_EN | S5_WOL_EN | S5_WOL_LOW_POWER | 0x8000 which is a mix of USB
> interface options and PHY configuration options plus a the EPHY_LOW_POWER_EN
> command.
>
> It now looks like suspend/resume is broken in v5 according to Jianhui's tests. Hhowever,
> it was probably already broken in v4. Since this is intermittent, I wonder whether
> the polling of the phy by phylink is to blame, which sometimes may still come after
> the AX88179A_WAKEUP_SETTING sleep command. Can the polling be disabled by the driver
> in the suspend() function?
Better still, the PHY driver should configure WoL in the PHY, and use
phylinks WoL support for everything PHY related. It does require you
know what registers to set in the PHY.
As for waking up too early, we need to understand how the PHY is
waking the MAC. Often the PHYs have an interrupt output pin, which is
tied to a GPIO pin of the SoC which is wake capable. With a USB device
it will be different, but probably still an output from the PHY
connected to a pin of the USB MAC, which wakes the USB device.
It could be that interrupt output is wrongly configured, so it is
reporting some other event? Or it has not been cleared after the last
link up interrupt etc.
Andrew
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH net-next v5 04/13] ax88179_178a: Add HW support for AX179A-based chips
2026-08-02 21:28 ` Andrew Lunn
@ 2026-08-06 19:32 ` Birger Koblitz
0 siblings, 0 replies; 32+ messages in thread
From: Birger Koblitz @ 2026-08-06 19:32 UTC (permalink / raw)
To: Andrew Lunn
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Russell King, Heiner Kallweit, linux-usb, netdev,
linux-kernel, Jianhui Xu
On 02/08/2026 23:28, Andrew Lunn wrote:
> This is more of a nitpick, given how much work you have already done
> on this driver. I think designated initializer syntax could make this
> more readable:
>
> num ax_bulk_in_speeds {
> BULK_IN_SPEED_1G_SS = 0,
> BULK_IN_SPEED_1G_HS = 1,
> BULK_IN_SPEED_100_FULL_SS = 2,
> BULK_IN_SPEED_100_HALF_SS = 3,
> ...
> };
>
>
>> +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},
>
>> +static void ax88179a_bulkin_config(struct usbnet *dev, u8 link_sts)
>> +{
>> + struct ax88179_data *ax179_data = dev->driver_priv;
>> + const struct ax_bulkin_settings *bulkin_data;
>> + int index = 0;
>> +
>> + switch (ax179_data->speed) {
>> + case ETHER_LINK_2500: /* AX88279 only */
>> + index = BULK_IN_SPEED_2500;
>> + break;
>> +
>> + case ETHER_LINK_1000: /* AX88279 & AX88178A */
>> + if (ax179_data->chip_version == AX_VERSION_AX88279) {
>> + if (link_sts & AX_USB_SS)
>> + index = BULK_IN_SPEED_1G_SS;
>> + else if (link_sts & AX_USB_HS)
>> + index = BULK_IN_SPEED_HS;
>
> This removes the 1, 2, 3, etc making the relationship more obvious.
>
Will be done in v6. And the change even allows to remove the above
dependencies on the chip versions in ax88179a_bulkin_config(), since
the different ax_bulkin_settings structures no longer need to start at 0
index. Much more than nitpick!
Birger
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH net-next v5 04/13] ax88179_178a: Add HW support for AX179A-based chips
2026-08-02 21:36 ` Andrew Lunn
@ 2026-08-06 19:34 ` Birger Koblitz
0 siblings, 0 replies; 32+ messages in thread
From: Birger Koblitz @ 2026-08-06 19:34 UTC (permalink / raw)
To: Andrew Lunn
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Russell King, Heiner Kallweit, linux-usb, netdev,
linux-kernel, Jianhui Xu
On 02/08/2026 23:36, Andrew Lunn wrote:
>> +static void ax88179a_bulkin_config(struct usbnet *dev, u8 link_sts)
>> +{
>> + struct ax88179_data *ax179_data = dev->driver_priv;
>> + const struct ax_bulkin_settings *bulkin_data;
>> + int index = 0;
>> +
>> + switch (ax179_data->speed) {
>
> What is setting ->speed?
A leftover from before refactoring, sorry!
Fixed in v6.
>
>> +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)
>> +{
>
> It should be coming from there, but i don't see any code setting it.
>
>> + ax88179_read_cmd(dev, AX_ACCESS_MAC, PHYSICAL_LINK_STATUS, 1, 1, &link_sts);
>> + ax88179a_bulkin_config(dev, link_sts);
>
> It might be better to pass speed as a parameter.
>
> I would probably play with ethtool and set the advertised speeds to
> only include slower speeds, like 10Half, and make sure the link works
> correctly.
Indeed. The speed now comes now from phylink. I tested all speeds (including
unsupported ones) and the mac_link_up() implementationn now works
as expected, including after suspend/resume.
Birger
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH net-next v5 13/13] ax88796b: Add support for AX88772D, AX88179A and AX88279
2026-08-02 21:55 ` Andrew Lunn
@ 2026-08-06 19:35 ` Birger Koblitz
2026-08-06 21:16 ` Andrew Lunn
0 siblings, 1 reply; 32+ messages in thread
From: Birger Koblitz @ 2026-08-06 19:35 UTC (permalink / raw)
To: Andrew Lunn
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Russell King, Heiner Kallweit, linux-usb, netdev,
linux-kernel, Jianhui Xu
On 02/08/2026 23:55, Andrew Lunn wrote:
>> +static int asix_ax88279_read_status(struct phy_device *phydev)
>> +{
>> + int ret, val;
>> +
>> + ret = genphy_update_link(phydev);
>> + if (ret)
>> + return ret;
>> +
>> + phydev->master_slave_get = MASTER_SLAVE_CFG_UNSUPPORTED;
>> + phydev->master_slave_state = MASTER_SLAVE_STATE_UNSUPPORTED;
>> + phydev->speed = SPEED_UNKNOWN;
>> + phydev->duplex = DUPLEX_UNKNOWN;
>> + phydev->pause = 0;
>> + phydev->asym_pause = 0;
>> + phydev->rate_matching = RATE_MATCH_PAUSE;
>> +
>> + if (!phydev->link)
>> + return 0;
>> +
>> + ret = genphy_read_master_slave(phydev);
>> + if (ret < 0)
>> + return ret;
>> +
>> + ret = genphy_read_lpa(phydev);
>> + if (ret < 0)
>> + return ret;
>
> This looks pretty similar to genphy_read_status(). Can you call that
> here? And then the rest of the code here can deal with higher speeds.
Changed in v5.
>
>> + /* Read actual speed from vendor register */
>> + val = phy_read(phydev, AX_CTRL_STATUS);
>> + 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
>> + */
>> + 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;
>> +
>> + 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 -EINVAL;
>> + }
>
> Why is that? Now that phylink is driving the PHY, and reporting
> speeds, duplex etc, you should have everything you need to program the
> MAC when the link mode is forced.
>
I tried forcing the PHY speed in the past, and it does not work.
This is a limitation which is also acknowledged for the suspected underlying PHY
hardware Airoha EN8811H, see the head of air_en8811h.c
Birger
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH net-next v5 00/13] ax88179_178a: Add support for AX88179A-based chips
2026-08-03 14:39 ` Yuan Xu
@ 2026-08-06 19:38 ` Birger Koblitz
0 siblings, 0 replies; 32+ messages in thread
From: Birger Koblitz @ 2026-08-06 19:38 UTC (permalink / raw)
To: Yuan Xu
Cc: andrew+netdev, andrew, davem, edumazet, hkallweit1, kuba,
linux-kernel, linux-usb, linux, netdev, pabeni
Hi Yuan,
On 03/08/2026 16:39, Yuan Xu wrote:
> Hi Birger,
>
> I really appreciate all the work you’ve put into this!
Thanks so much. And thanks for testing!
>
> I’ll apply the fix and run additional tests this weekend. I’ll report back
> with the traces and any reproducible differences if I find them.
I sent out a v5. This should fix the issues you reported. I tested
extensively link speeds and resume/suspend using a laptop and a loop-back
cable between and AX88279 and an AX88179B using netns. I tested around 20
suspend/resume cycles and found no issues even with different speeds with
the fixes in v5.
Cheers,
Birger
^ permalink raw reply [flat|nested] 32+ messages in thread
* Re: [PATCH net-next v5 13/13] ax88796b: Add support for AX88772D, AX88179A and AX88279
2026-08-06 19:35 ` Birger Koblitz
@ 2026-08-06 21:16 ` Andrew Lunn
0 siblings, 0 replies; 32+ messages in thread
From: Andrew Lunn @ 2026-08-06 21:16 UTC (permalink / raw)
To: Birger Koblitz
Cc: Andrew Lunn, David S. Miller, Eric Dumazet, Jakub Kicinski,
Paolo Abeni, Russell King, Heiner Kallweit, linux-usb, netdev,
linux-kernel, Jianhui Xu
> > > +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 -EINVAL;
> > > + }
> >
> > Why is that? Now that phylink is driving the PHY, and reporting
> > speeds, duplex etc, you should have everything you need to program the
> > MAC when the link mode is forced.
> >
> I tried forcing the PHY speed in the past, and it does not work.
> This is a limitation which is also acknowledged for the suspected underlying PHY
> hardware Airoha EN8811H, see the head of air_en8811h.c
How well does this unwind on error? Is phydev->autoneg put back to the
old state to indicate autoneg is used?
This is one of the areas where phylib/phylink is missing a bit of
support infrastructure. We have a bit indicating the PHY support
autoneg, ETHTOOL_LINK_MODE_Autoneg_BIT, but there is no bit
ETHTOOL_LINK_MODE_Force_BIT, indicating the PHY supports forced
mode. So the core cannot enforce this and reject it in
ksettings_set().
Andrew
^ permalink raw reply [flat|nested] 32+ messages in thread
end of thread, other threads:[~2026-08-06 21:16 UTC | newest]
Thread overview: 32+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-02 16:31 [PATCH net-next v5 00/13] ax88179_178a: Add support for AX88179A-based chips Birger Koblitz
2026-08-02 16:31 ` [PATCH net-next v5 01/13] ax88179_178a: Fix endianness of pause watermark register Birger Koblitz
2026-08-02 16:32 ` [PATCH net-next v5 02/13] ax88179_178a: Split driver into library and device specific code Birger Koblitz
2026-08-02 16:32 ` [PATCH net-next v5 03/13] ax88179_178a: Add netdev2data() convenience function Birger Koblitz
2026-08-02 21:10 ` Andrew Lunn
2026-08-02 16:32 ` [PATCH net-next v5 04/13] ax88179_178a: Add HW support for AX179A-based chips Birger Koblitz
2026-08-02 21:28 ` Andrew Lunn
2026-08-06 19:32 ` Birger Koblitz
2026-08-02 21:36 ` Andrew Lunn
2026-08-06 19:34 ` Birger Koblitz
2026-08-02 21:47 ` Andrew Lunn
2026-08-02 16:32 ` [PATCH net-next v5 05/13] ax88179_178a: Add EEE configuration support for AX88179A MACs Birger Koblitz
2026-08-02 16:32 ` [PATCH net-next v5 06/13] ax88179_178a: Add EEE configuration support for AX88179A PHYs Birger Koblitz
2026-08-02 16:32 ` [PATCH net-next v5 07/13] ax88179_178a: Add VLAN offload support for AX88179A Birger Koblitz
2026-08-02 21:37 ` Andrew Lunn
2026-08-02 16:32 ` [PATCH net-next v5 08/13] ax88179_178a: Add AX179A/AX279 multicast configuration Birger Koblitz
2026-08-02 16:32 ` [PATCH net-next v5 09/13] ax88179_178a: Add Suspend/resume support for AX88179A/772D/279 Birger Koblitz
2026-08-02 21:44 ` Andrew Lunn
2026-08-03 8:53 ` Birger Koblitz
2026-08-03 14:58 ` Andrew Lunn
2026-08-02 16:32 ` [PATCH net-next v5 10/13] ax88179_178a: Add ethtool get_drvinfo Birger Koblitz
2026-08-02 16:32 ` [PATCH net-next v5 11/13] ax88179_178a: Update driver name and information Birger Koblitz
2026-08-02 21:45 ` Andrew Lunn
2026-08-02 16:32 ` [PATCH net-next v5 12/13] ax88179_178a: Add support for AX88179A/772D/279 EEPROM access Birger Koblitz
2026-08-02 16:32 ` [PATCH net-next v5 13/13] ax88796b: Add support for AX88772D, AX88179A and AX88279 Birger Koblitz
2026-08-02 21:55 ` Andrew Lunn
2026-08-06 19:35 ` Birger Koblitz
2026-08-06 21:16 ` Andrew Lunn
2026-08-03 7:33 ` [PATCH net-next v5 00/13] ax88179_178a: Add support for AX88179A-based chips Jianhui Xu
2026-08-03 9:53 ` Birger Koblitz
2026-08-03 14:39 ` Yuan Xu
2026-08-06 19:38 ` Birger Koblitz
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox