[PATCH 6/9] ax88179_178a: Add VLAN offload support for AX88179A
From: Birger Koblitz <hidden>
Date: 2026-07-01 05:43:00
Also in:
linux-usb, lkml
Subsystem:
networking drivers, the rest, usb networking drivers · Maintainers:
Andrew Lunn, "David S. Miller", Eric Dumazet, Jakub Kicinski, Paolo Abeni, Linus Torvalds
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 <redacted> --- drivers/net/usb/ax88179_178a.c | 98 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 98 insertions(+)
diff --git a/drivers/net/usb/ax88179_178a.c b/drivers/net/usb/ax88179_178a.c
index 586c049c6f7422a853aeae5e9372ead3a6d106c5..f70f79ebc7f7c4d2321e033f7b8d09e6291c842f 100644
--- a/drivers/net/usb/ax88179_178a.c
+++ b/drivers/net/usb/ax88179_178a.c@@ -114,6 +114,17 @@ #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
@@ -1200,6 +1211,62 @@ static const struct ethtool_ops ax88179_ethtool_ops = { .get_ts_info = ethtool_op_get_ts_info, }; +static int ax179a_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 ax179a_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 ax88179_set_multicast(struct net_device *net) { struct usbnet *dev = netdev_priv(net);
@@ -1245,6 +1312,7 @@ 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) {
@@ -1264,6 +1332,34 @@ 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); + } + + 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; + /* 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;
@@ -1331,6 +1427,8 @@ static const struct net_device_ops ax88179_netdev_ops = { .ndo_eth_ioctl = usbnet_mii_ioctl, .ndo_set_rx_mode = ax88179_set_multicast, .ndo_set_features = ax88179_set_features, + .ndo_vlan_rx_add_vid = ax179a_vlan_rx_add_vid, + .ndo_vlan_rx_kill_vid = ax179a_vlan_rx_kill_vid, }; static int ax88179_check_eeprom(struct usbnet *dev)
--
2.47.3