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 --- drivers/net/usb/ax88179_lib.c | 2 + drivers/net/usb/ax88179a_devices.c | 100 +++++++++++++++++++++++++++++++++++++ 2 files changed, 102 insertions(+) diff --git a/drivers/net/usb/ax88179_lib.c b/drivers/net/usb/ax88179_lib.c index 7b5c17a01df2deac2cc0210bc88193be1abfbe23..26c874b31beafac796264e46ecc942b4bd460ac0 100644 --- a/drivers/net/usb/ax88179_lib.c +++ b/drivers/net/usb/ax88179_lib.c @@ -348,6 +348,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) { @@ -367,6 +368,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 2bd6e181dfbe319e569fa3d6325fd45ea0b9c732..b6c24a11660fcc17957452a86e66dbda3b71f2ec 100644 --- a/drivers/net/usb/ax88179a_devices.c +++ b/drivers/net/usb/ax88179a_devices.c @@ -261,6 +261,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); @@ -516,6 +572,47 @@ static int ax88179a_init_mdio(struct usbnet *dev) return ret; } +static int ax88179a_set_features(struct net_device *net, netdev_features_t features) +{ + u8 tmp; + int ret; + struct usbnet *dev = netdev_priv(net); + netdev_features_t 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, @@ -526,6 +623,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