[PATCH net-next v5 07/13] ax88179_178a: Add VLAN offload support for AX88179A
Birger Koblitz <[email protected]> Sun, 02 Aug 2026 18:32:05 +0200
| Newsgroups | org.kernel.vger.linux-usb,org.kernel.vger.linux-kernel,org.kernel.vger.netdev |
|---|---|
| Message-ID | <[email protected]> |
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 <[email protected]> --- 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