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 Reviewed-by: Andrew Lunn --- drivers/net/usb/ax88179a_devices.c | 138 ++++++++++++++++++++++++++++++++++++- 1 file changed, 137 insertions(+), 1 deletion(-) diff --git a/drivers/net/usb/ax88179a_devices.c b/drivers/net/usb/ax88179a_devices.c index e2ea9724433929d4cbfba0597787ddcb84dacf98..12ef59ed16b3b081e2538731db3564718fbbf915 100644 --- a/drivers/net/usb/ax88179a_devices.c +++ b/drivers/net/usb/ax88179a_devices.c @@ -258,6 +258,86 @@ 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; + int ret = 0; + u16 reg16; + u8 reg8; + + ret = ax88179_read_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_CONTROL, 1, 1, ®8); + if (ret < 0) + goto out; + vlan_ctrl = reg8; + + /* Address */ + reg8 = (vid / 16); + ret = ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_ADDRESS, 1, 1, ®8); + if (ret < 0) + goto out; + + /* Data */ + reg8 = vlan_ctrl | AX_VLAN_CONTROL_RD; + ret = ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_CONTROL, 1, 1, ®8); + if (ret < 0) + goto out; + + ret = ax88179_read_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_DATA0, 2, 2, ®16); + if (ret < 0) + goto out; + reg16 &= ~(1 << (vid % 16)); + ret = ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_DATA0, 2, 2, ®16); + if (ret < 0) + goto out; + + reg8 = vlan_ctrl | AX_VLAN_CONTROL_WE; + ret = ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_CONTROL, 1, 1, ®8); + +out: + return ret; +} + +static int ax88179a_vlan_rx_add_vid(struct net_device *net, __be16 proto, u16 vid) +{ + struct usbnet *dev = netdev_priv(net); + u8 vlan_ctrl; + int ret = 0; + u16 reg16; + u8 reg8; + + ret = ax88179_read_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_CONTROL, 1, 1, ®8); + if (ret) + goto out; + vlan_ctrl = reg8; + + /* Address */ + reg8 = (vid / 16); + ret = ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_ADDRESS, 1, 1, ®8); + if (ret < 0) + goto out; + + /* Data */ + reg8 = vlan_ctrl | AX_VLAN_CONTROL_RD; + ret = ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_CONTROL, 1, 1, ®8); + if (ret < 0) + goto out; + + ax88179_read_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_DATA0, 2, 2, ®16); + if (ret < 0) + goto out; + reg16 |= (1 << (vid % 16)); + ret = ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_DATA0, 2, 2, ®16); + if (ret < 0) + goto out; + + reg8 = vlan_ctrl | AX_VLAN_CONTROL_WE; + ret = ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_CONTROL, 1, 1, ®8); + +out: + return ret; +} + static void ax88179a_mdio_unregister(struct ax88179_data *data) { mdiobus_unregister(data->mdio); @@ -517,6 +597,57 @@ static int ax88179a_mii_ioctl(struct net_device *net, struct ifreq *rq, int cmd) return phylink_mii_ioctl(data->phylink, rq, cmd); } +static int ax88179a_set_features(struct net_device *net, netdev_features_t features) +{ + struct usbnet *dev = netdev_priv(net); + netdev_features_t changed; + int ret = 0; + u8 tmp; + + changed = net->features ^ features; + + ret = ax88179_set_features(net, features); + if (ret) + return ret; + + if (changed & NETIF_F_HW_VLAN_CTAG_FILTER) { + if (features & NETIF_F_HW_VLAN_CTAG_FILTER) { + for (int i = 0; i < 256; i++) { + /* Address */ + tmp = i; + ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_ADDRESS, + 1, 1, &tmp); + /* Data */ + ax88179_write_u16(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_DATA0, + 2, 0); + ax88179_write_u8(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_CONTROL, + 1, AX_VLAN_CONTROL_WE); + } + ret = ax88179_read_cmd(dev, AX_ACCESS_MAC, + AX88179A_VLAN_ID_CONTROL, 1, 1, &tmp); + if (ret < 0) + return ret; + tmp ^= AX_VLAN_CONTROL_VFE; + ret = ax88179_write_cmd(dev, AX_ACCESS_MAC, + AX88179A_VLAN_ID_CONTROL, 1, 1, &tmp); + if (ret < 0) + return ret; + } + } + + if (changed & NETIF_F_HW_VLAN_CTAG_RX) { + ret = ax88179_read_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_CONTROL, + 1, 1, &tmp); + if (ret < 0) + return ret; + tmp ^= AX_VLAN_CONTROL_VSO; + ret = ax88179_write_cmd(dev, AX_ACCESS_MAC, AX88179A_VLAN_ID_CONTROL, + 1, 1, &tmp); + } + + return ret; +} + static const struct net_device_ops ax88179a_netdev_ops = { .ndo_open = usbnet_open, .ndo_stop = usbnet_stop, @@ -527,6 +658,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 = ax88179a_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) @@ -611,7 +745,9 @@ static int ax88179a_bind(struct usbnet *dev, struct usb_interface *intf) 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_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; -- 2.47.3