18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
28c2ecf20Sopenharmony_ci/* Atlantic Network Driver
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Copyright (C) 2014-2019 aQuantia Corporation
58c2ecf20Sopenharmony_ci * Copyright (C) 2019-2020 Marvell International Ltd.
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci/* File aq_main.c: Main file for aQuantia Linux driver. */
98c2ecf20Sopenharmony_ci
108c2ecf20Sopenharmony_ci#include "aq_main.h"
118c2ecf20Sopenharmony_ci#include "aq_nic.h"
128c2ecf20Sopenharmony_ci#include "aq_pci_func.h"
138c2ecf20Sopenharmony_ci#include "aq_ethtool.h"
148c2ecf20Sopenharmony_ci#include "aq_ptp.h"
158c2ecf20Sopenharmony_ci#include "aq_filters.h"
168c2ecf20Sopenharmony_ci#include "aq_hw_utils.h"
178c2ecf20Sopenharmony_ci
188c2ecf20Sopenharmony_ci#include <linux/netdevice.h>
198c2ecf20Sopenharmony_ci#include <linux/module.h>
208c2ecf20Sopenharmony_ci#include <linux/ip.h>
218c2ecf20Sopenharmony_ci#include <linux/udp.h>
228c2ecf20Sopenharmony_ci#include <net/pkt_cls.h>
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL v2");
258c2ecf20Sopenharmony_ciMODULE_AUTHOR(AQ_CFG_DRV_AUTHOR);
268c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(AQ_CFG_DRV_DESC);
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_cistatic const char aq_ndev_driver_name[] = AQ_CFG_DRV_NAME;
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_cistatic const struct net_device_ops aq_ndev_ops;
318c2ecf20Sopenharmony_ci
328c2ecf20Sopenharmony_cistatic struct workqueue_struct *aq_ndev_wq;
338c2ecf20Sopenharmony_ci
348c2ecf20Sopenharmony_civoid aq_ndev_schedule_work(struct work_struct *work)
358c2ecf20Sopenharmony_ci{
368c2ecf20Sopenharmony_ci	queue_work(aq_ndev_wq, work);
378c2ecf20Sopenharmony_ci}
388c2ecf20Sopenharmony_ci
398c2ecf20Sopenharmony_cistruct net_device *aq_ndev_alloc(void)
408c2ecf20Sopenharmony_ci{
418c2ecf20Sopenharmony_ci	struct net_device *ndev = NULL;
428c2ecf20Sopenharmony_ci	struct aq_nic_s *aq_nic = NULL;
438c2ecf20Sopenharmony_ci
448c2ecf20Sopenharmony_ci	ndev = alloc_etherdev_mq(sizeof(struct aq_nic_s), AQ_HW_QUEUES_MAX);
458c2ecf20Sopenharmony_ci	if (!ndev)
468c2ecf20Sopenharmony_ci		return NULL;
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	aq_nic = netdev_priv(ndev);
498c2ecf20Sopenharmony_ci	aq_nic->ndev = ndev;
508c2ecf20Sopenharmony_ci	ndev->netdev_ops = &aq_ndev_ops;
518c2ecf20Sopenharmony_ci	ndev->ethtool_ops = &aq_ethtool_ops;
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	return ndev;
548c2ecf20Sopenharmony_ci}
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ciint aq_ndev_open(struct net_device *ndev)
578c2ecf20Sopenharmony_ci{
588c2ecf20Sopenharmony_ci	struct aq_nic_s *aq_nic = netdev_priv(ndev);
598c2ecf20Sopenharmony_ci	int err = 0;
608c2ecf20Sopenharmony_ci
618c2ecf20Sopenharmony_ci	err = aq_nic_init(aq_nic);
628c2ecf20Sopenharmony_ci	if (err < 0)
638c2ecf20Sopenharmony_ci		goto err_exit;
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci	err = aq_reapply_rxnfc_all_rules(aq_nic);
668c2ecf20Sopenharmony_ci	if (err < 0)
678c2ecf20Sopenharmony_ci		goto err_exit;
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci	err = aq_filters_vlans_update(aq_nic);
708c2ecf20Sopenharmony_ci	if (err < 0)
718c2ecf20Sopenharmony_ci		goto err_exit;
728c2ecf20Sopenharmony_ci
738c2ecf20Sopenharmony_ci	err = aq_nic_start(aq_nic);
748c2ecf20Sopenharmony_ci	if (err < 0) {
758c2ecf20Sopenharmony_ci		aq_nic_stop(aq_nic);
768c2ecf20Sopenharmony_ci		goto err_exit;
778c2ecf20Sopenharmony_ci	}
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_cierr_exit:
808c2ecf20Sopenharmony_ci	if (err < 0)
818c2ecf20Sopenharmony_ci		aq_nic_deinit(aq_nic, true);
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	return err;
848c2ecf20Sopenharmony_ci}
858c2ecf20Sopenharmony_ci
868c2ecf20Sopenharmony_ciint aq_ndev_close(struct net_device *ndev)
878c2ecf20Sopenharmony_ci{
888c2ecf20Sopenharmony_ci	struct aq_nic_s *aq_nic = netdev_priv(ndev);
898c2ecf20Sopenharmony_ci	int err = 0;
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	err = aq_nic_stop(aq_nic);
928c2ecf20Sopenharmony_ci	aq_nic_deinit(aq_nic, true);
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	return err;
958c2ecf20Sopenharmony_ci}
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_cistatic netdev_tx_t aq_ndev_start_xmit(struct sk_buff *skb, struct net_device *ndev)
988c2ecf20Sopenharmony_ci{
998c2ecf20Sopenharmony_ci	struct aq_nic_s *aq_nic = netdev_priv(ndev);
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_ci#if IS_REACHABLE(CONFIG_PTP_1588_CLOCK)
1028c2ecf20Sopenharmony_ci	if (unlikely(aq_utils_obj_test(&aq_nic->flags, AQ_NIC_PTP_DPATH_UP))) {
1038c2ecf20Sopenharmony_ci		/* Hardware adds the Timestamp for PTPv2 802.AS1
1048c2ecf20Sopenharmony_ci		 * and PTPv2 IPv4 UDP.
1058c2ecf20Sopenharmony_ci		 * We have to push even general 320 port messages to the ptp
1068c2ecf20Sopenharmony_ci		 * queue explicitly. This is a limitation of current firmware
1078c2ecf20Sopenharmony_ci		 * and hardware PTP design of the chip. Otherwise ptp stream
1088c2ecf20Sopenharmony_ci		 * will fail to sync
1098c2ecf20Sopenharmony_ci		 */
1108c2ecf20Sopenharmony_ci		if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_HW_TSTAMP) ||
1118c2ecf20Sopenharmony_ci		    unlikely((ip_hdr(skb)->version == 4) &&
1128c2ecf20Sopenharmony_ci			     (ip_hdr(skb)->protocol == IPPROTO_UDP) &&
1138c2ecf20Sopenharmony_ci			     ((udp_hdr(skb)->dest == htons(319)) ||
1148c2ecf20Sopenharmony_ci			      (udp_hdr(skb)->dest == htons(320)))) ||
1158c2ecf20Sopenharmony_ci		    unlikely(eth_hdr(skb)->h_proto == htons(ETH_P_1588)))
1168c2ecf20Sopenharmony_ci			return aq_ptp_xmit(aq_nic, skb);
1178c2ecf20Sopenharmony_ci	}
1188c2ecf20Sopenharmony_ci#endif
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci	skb_tx_timestamp(skb);
1218c2ecf20Sopenharmony_ci	return aq_nic_xmit(aq_nic, skb);
1228c2ecf20Sopenharmony_ci}
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_cistatic int aq_ndev_change_mtu(struct net_device *ndev, int new_mtu)
1258c2ecf20Sopenharmony_ci{
1268c2ecf20Sopenharmony_ci	struct aq_nic_s *aq_nic = netdev_priv(ndev);
1278c2ecf20Sopenharmony_ci	int err;
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci	err = aq_nic_set_mtu(aq_nic, new_mtu + ETH_HLEN);
1308c2ecf20Sopenharmony_ci
1318c2ecf20Sopenharmony_ci	if (err < 0)
1328c2ecf20Sopenharmony_ci		goto err_exit;
1338c2ecf20Sopenharmony_ci	ndev->mtu = new_mtu;
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_cierr_exit:
1368c2ecf20Sopenharmony_ci	return err;
1378c2ecf20Sopenharmony_ci}
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_cistatic int aq_ndev_set_features(struct net_device *ndev,
1408c2ecf20Sopenharmony_ci				netdev_features_t features)
1418c2ecf20Sopenharmony_ci{
1428c2ecf20Sopenharmony_ci	bool is_vlan_tx_insert = !!(features & NETIF_F_HW_VLAN_CTAG_TX);
1438c2ecf20Sopenharmony_ci	bool is_vlan_rx_strip = !!(features & NETIF_F_HW_VLAN_CTAG_RX);
1448c2ecf20Sopenharmony_ci	struct aq_nic_s *aq_nic = netdev_priv(ndev);
1458c2ecf20Sopenharmony_ci	bool need_ndev_restart = false;
1468c2ecf20Sopenharmony_ci	struct aq_nic_cfg_s *aq_cfg;
1478c2ecf20Sopenharmony_ci	bool is_lro = false;
1488c2ecf20Sopenharmony_ci	int err = 0;
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	aq_cfg = aq_nic_get_cfg(aq_nic);
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	if (!(features & NETIF_F_NTUPLE)) {
1538c2ecf20Sopenharmony_ci		if (aq_nic->ndev->features & NETIF_F_NTUPLE) {
1548c2ecf20Sopenharmony_ci			err = aq_clear_rxnfc_all_rules(aq_nic);
1558c2ecf20Sopenharmony_ci			if (unlikely(err))
1568c2ecf20Sopenharmony_ci				goto err_exit;
1578c2ecf20Sopenharmony_ci		}
1588c2ecf20Sopenharmony_ci	}
1598c2ecf20Sopenharmony_ci	if (!(features & NETIF_F_HW_VLAN_CTAG_FILTER)) {
1608c2ecf20Sopenharmony_ci		if (aq_nic->ndev->features & NETIF_F_HW_VLAN_CTAG_FILTER) {
1618c2ecf20Sopenharmony_ci			err = aq_filters_vlan_offload_off(aq_nic);
1628c2ecf20Sopenharmony_ci			if (unlikely(err))
1638c2ecf20Sopenharmony_ci				goto err_exit;
1648c2ecf20Sopenharmony_ci		}
1658c2ecf20Sopenharmony_ci	}
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	aq_cfg->features = features;
1688c2ecf20Sopenharmony_ci
1698c2ecf20Sopenharmony_ci	if (aq_cfg->aq_hw_caps->hw_features & NETIF_F_LRO) {
1708c2ecf20Sopenharmony_ci		is_lro = features & NETIF_F_LRO;
1718c2ecf20Sopenharmony_ci
1728c2ecf20Sopenharmony_ci		if (aq_cfg->is_lro != is_lro) {
1738c2ecf20Sopenharmony_ci			aq_cfg->is_lro = is_lro;
1748c2ecf20Sopenharmony_ci			need_ndev_restart = true;
1758c2ecf20Sopenharmony_ci		}
1768c2ecf20Sopenharmony_ci	}
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci	if ((aq_nic->ndev->features ^ features) & NETIF_F_RXCSUM) {
1798c2ecf20Sopenharmony_ci		err = aq_nic->aq_hw_ops->hw_set_offload(aq_nic->aq_hw,
1808c2ecf20Sopenharmony_ci							aq_cfg);
1818c2ecf20Sopenharmony_ci
1828c2ecf20Sopenharmony_ci		if (unlikely(err))
1838c2ecf20Sopenharmony_ci			goto err_exit;
1848c2ecf20Sopenharmony_ci	}
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	if (aq_cfg->is_vlan_rx_strip != is_vlan_rx_strip) {
1878c2ecf20Sopenharmony_ci		aq_cfg->is_vlan_rx_strip = is_vlan_rx_strip;
1888c2ecf20Sopenharmony_ci		need_ndev_restart = true;
1898c2ecf20Sopenharmony_ci	}
1908c2ecf20Sopenharmony_ci	if (aq_cfg->is_vlan_tx_insert != is_vlan_tx_insert) {
1918c2ecf20Sopenharmony_ci		aq_cfg->is_vlan_tx_insert = is_vlan_tx_insert;
1928c2ecf20Sopenharmony_ci		need_ndev_restart = true;
1938c2ecf20Sopenharmony_ci	}
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	if (need_ndev_restart && netif_running(ndev)) {
1968c2ecf20Sopenharmony_ci		aq_ndev_close(ndev);
1978c2ecf20Sopenharmony_ci		aq_ndev_open(ndev);
1988c2ecf20Sopenharmony_ci	}
1998c2ecf20Sopenharmony_ci
2008c2ecf20Sopenharmony_cierr_exit:
2018c2ecf20Sopenharmony_ci	return err;
2028c2ecf20Sopenharmony_ci}
2038c2ecf20Sopenharmony_ci
2048c2ecf20Sopenharmony_cistatic int aq_ndev_set_mac_address(struct net_device *ndev, void *addr)
2058c2ecf20Sopenharmony_ci{
2068c2ecf20Sopenharmony_ci	struct aq_nic_s *aq_nic = netdev_priv(ndev);
2078c2ecf20Sopenharmony_ci	int err = 0;
2088c2ecf20Sopenharmony_ci
2098c2ecf20Sopenharmony_ci	err = eth_mac_addr(ndev, addr);
2108c2ecf20Sopenharmony_ci	if (err < 0)
2118c2ecf20Sopenharmony_ci		goto err_exit;
2128c2ecf20Sopenharmony_ci	err = aq_nic_set_mac(aq_nic, ndev);
2138c2ecf20Sopenharmony_ci	if (err < 0)
2148c2ecf20Sopenharmony_ci		goto err_exit;
2158c2ecf20Sopenharmony_ci
2168c2ecf20Sopenharmony_cierr_exit:
2178c2ecf20Sopenharmony_ci	return err;
2188c2ecf20Sopenharmony_ci}
2198c2ecf20Sopenharmony_ci
2208c2ecf20Sopenharmony_cistatic void aq_ndev_set_multicast_settings(struct net_device *ndev)
2218c2ecf20Sopenharmony_ci{
2228c2ecf20Sopenharmony_ci	struct aq_nic_s *aq_nic = netdev_priv(ndev);
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	(void)aq_nic_set_multicast_list(aq_nic, ndev);
2258c2ecf20Sopenharmony_ci}
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci#if IS_REACHABLE(CONFIG_PTP_1588_CLOCK)
2288c2ecf20Sopenharmony_cistatic int aq_ndev_config_hwtstamp(struct aq_nic_s *aq_nic,
2298c2ecf20Sopenharmony_ci				   struct hwtstamp_config *config)
2308c2ecf20Sopenharmony_ci{
2318c2ecf20Sopenharmony_ci	if (config->flags)
2328c2ecf20Sopenharmony_ci		return -EINVAL;
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	switch (config->tx_type) {
2358c2ecf20Sopenharmony_ci	case HWTSTAMP_TX_OFF:
2368c2ecf20Sopenharmony_ci	case HWTSTAMP_TX_ON:
2378c2ecf20Sopenharmony_ci		break;
2388c2ecf20Sopenharmony_ci	default:
2398c2ecf20Sopenharmony_ci		return -ERANGE;
2408c2ecf20Sopenharmony_ci	}
2418c2ecf20Sopenharmony_ci
2428c2ecf20Sopenharmony_ci	switch (config->rx_filter) {
2438c2ecf20Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_L4_EVENT:
2448c2ecf20Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_L4_SYNC:
2458c2ecf20Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_L4_DELAY_REQ:
2468c2ecf20Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_L2_EVENT:
2478c2ecf20Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_L2_SYNC:
2488c2ecf20Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_L2_DELAY_REQ:
2498c2ecf20Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_SYNC:
2508c2ecf20Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_DELAY_REQ:
2518c2ecf20Sopenharmony_ci		config->rx_filter = HWTSTAMP_FILTER_PTP_V2_EVENT;
2528c2ecf20Sopenharmony_ci		break;
2538c2ecf20Sopenharmony_ci	case HWTSTAMP_FILTER_PTP_V2_EVENT:
2548c2ecf20Sopenharmony_ci	case HWTSTAMP_FILTER_NONE:
2558c2ecf20Sopenharmony_ci		break;
2568c2ecf20Sopenharmony_ci	default:
2578c2ecf20Sopenharmony_ci		return -ERANGE;
2588c2ecf20Sopenharmony_ci	}
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci	return aq_ptp_hwtstamp_config_set(aq_nic->aq_ptp, config);
2618c2ecf20Sopenharmony_ci}
2628c2ecf20Sopenharmony_ci#endif
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_cistatic int aq_ndev_hwtstamp_set(struct aq_nic_s *aq_nic, struct ifreq *ifr)
2658c2ecf20Sopenharmony_ci{
2668c2ecf20Sopenharmony_ci	struct hwtstamp_config config;
2678c2ecf20Sopenharmony_ci#if IS_REACHABLE(CONFIG_PTP_1588_CLOCK)
2688c2ecf20Sopenharmony_ci	int ret_val;
2698c2ecf20Sopenharmony_ci#endif
2708c2ecf20Sopenharmony_ci
2718c2ecf20Sopenharmony_ci	if (!aq_nic->aq_ptp)
2728c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	if (copy_from_user(&config, ifr->ifr_data, sizeof(config)))
2758c2ecf20Sopenharmony_ci		return -EFAULT;
2768c2ecf20Sopenharmony_ci#if IS_REACHABLE(CONFIG_PTP_1588_CLOCK)
2778c2ecf20Sopenharmony_ci	ret_val = aq_ndev_config_hwtstamp(aq_nic, &config);
2788c2ecf20Sopenharmony_ci	if (ret_val)
2798c2ecf20Sopenharmony_ci		return ret_val;
2808c2ecf20Sopenharmony_ci#endif
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	return copy_to_user(ifr->ifr_data, &config, sizeof(config)) ?
2838c2ecf20Sopenharmony_ci	       -EFAULT : 0;
2848c2ecf20Sopenharmony_ci}
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci#if IS_REACHABLE(CONFIG_PTP_1588_CLOCK)
2878c2ecf20Sopenharmony_cistatic int aq_ndev_hwtstamp_get(struct aq_nic_s *aq_nic, struct ifreq *ifr)
2888c2ecf20Sopenharmony_ci{
2898c2ecf20Sopenharmony_ci	struct hwtstamp_config config;
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci	if (!aq_nic->aq_ptp)
2928c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
2938c2ecf20Sopenharmony_ci
2948c2ecf20Sopenharmony_ci	aq_ptp_hwtstamp_config_get(aq_nic->aq_ptp, &config);
2958c2ecf20Sopenharmony_ci	return copy_to_user(ifr->ifr_data, &config, sizeof(config)) ?
2968c2ecf20Sopenharmony_ci	       -EFAULT : 0;
2978c2ecf20Sopenharmony_ci}
2988c2ecf20Sopenharmony_ci#endif
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_cistatic int aq_ndev_ioctl(struct net_device *netdev, struct ifreq *ifr, int cmd)
3018c2ecf20Sopenharmony_ci{
3028c2ecf20Sopenharmony_ci	struct aq_nic_s *aq_nic = netdev_priv(netdev);
3038c2ecf20Sopenharmony_ci
3048c2ecf20Sopenharmony_ci	switch (cmd) {
3058c2ecf20Sopenharmony_ci	case SIOCSHWTSTAMP:
3068c2ecf20Sopenharmony_ci		return aq_ndev_hwtstamp_set(aq_nic, ifr);
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci#if IS_REACHABLE(CONFIG_PTP_1588_CLOCK)
3098c2ecf20Sopenharmony_ci	case SIOCGHWTSTAMP:
3108c2ecf20Sopenharmony_ci		return aq_ndev_hwtstamp_get(aq_nic, ifr);
3118c2ecf20Sopenharmony_ci#endif
3128c2ecf20Sopenharmony_ci	}
3138c2ecf20Sopenharmony_ci
3148c2ecf20Sopenharmony_ci	return -EOPNOTSUPP;
3158c2ecf20Sopenharmony_ci}
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_cistatic int aq_ndo_vlan_rx_add_vid(struct net_device *ndev, __be16 proto,
3188c2ecf20Sopenharmony_ci				  u16 vid)
3198c2ecf20Sopenharmony_ci{
3208c2ecf20Sopenharmony_ci	struct aq_nic_s *aq_nic = netdev_priv(ndev);
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	if (!aq_nic->aq_hw_ops->hw_filter_vlan_set)
3238c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
3248c2ecf20Sopenharmony_ci
3258c2ecf20Sopenharmony_ci	set_bit(vid, aq_nic->active_vlans);
3268c2ecf20Sopenharmony_ci
3278c2ecf20Sopenharmony_ci	return aq_filters_vlans_update(aq_nic);
3288c2ecf20Sopenharmony_ci}
3298c2ecf20Sopenharmony_ci
3308c2ecf20Sopenharmony_cistatic int aq_ndo_vlan_rx_kill_vid(struct net_device *ndev, __be16 proto,
3318c2ecf20Sopenharmony_ci				   u16 vid)
3328c2ecf20Sopenharmony_ci{
3338c2ecf20Sopenharmony_ci	struct aq_nic_s *aq_nic = netdev_priv(ndev);
3348c2ecf20Sopenharmony_ci
3358c2ecf20Sopenharmony_ci	if (!aq_nic->aq_hw_ops->hw_filter_vlan_set)
3368c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
3378c2ecf20Sopenharmony_ci
3388c2ecf20Sopenharmony_ci	clear_bit(vid, aq_nic->active_vlans);
3398c2ecf20Sopenharmony_ci
3408c2ecf20Sopenharmony_ci	if (-ENOENT == aq_del_fvlan_by_vlan(aq_nic, vid))
3418c2ecf20Sopenharmony_ci		return aq_filters_vlans_update(aq_nic);
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	return 0;
3448c2ecf20Sopenharmony_ci}
3458c2ecf20Sopenharmony_ci
3468c2ecf20Sopenharmony_cistatic int aq_validate_mqprio_opt(struct aq_nic_s *self,
3478c2ecf20Sopenharmony_ci				  struct tc_mqprio_qopt_offload *mqprio,
3488c2ecf20Sopenharmony_ci				  const unsigned int num_tc)
3498c2ecf20Sopenharmony_ci{
3508c2ecf20Sopenharmony_ci	const bool has_min_rate = !!(mqprio->flags & TC_MQPRIO_F_MIN_RATE);
3518c2ecf20Sopenharmony_ci	struct aq_nic_cfg_s *aq_nic_cfg = aq_nic_get_cfg(self);
3528c2ecf20Sopenharmony_ci	const unsigned int tcs_max = min_t(u8, aq_nic_cfg->aq_hw_caps->tcs_max,
3538c2ecf20Sopenharmony_ci					   AQ_CFG_TCS_MAX);
3548c2ecf20Sopenharmony_ci
3558c2ecf20Sopenharmony_ci	if (num_tc > tcs_max) {
3568c2ecf20Sopenharmony_ci		netdev_err(self->ndev, "Too many TCs requested\n");
3578c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
3588c2ecf20Sopenharmony_ci	}
3598c2ecf20Sopenharmony_ci
3608c2ecf20Sopenharmony_ci	if (num_tc != 0 && !is_power_of_2(num_tc)) {
3618c2ecf20Sopenharmony_ci		netdev_err(self->ndev, "TC count should be power of 2\n");
3628c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
3638c2ecf20Sopenharmony_ci	}
3648c2ecf20Sopenharmony_ci
3658c2ecf20Sopenharmony_ci	if (has_min_rate && !ATL_HW_IS_CHIP_FEATURE(self->aq_hw, ANTIGUA)) {
3668c2ecf20Sopenharmony_ci		netdev_err(self->ndev, "Min tx rate is not supported\n");
3678c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
3688c2ecf20Sopenharmony_ci	}
3698c2ecf20Sopenharmony_ci
3708c2ecf20Sopenharmony_ci	return 0;
3718c2ecf20Sopenharmony_ci}
3728c2ecf20Sopenharmony_ci
3738c2ecf20Sopenharmony_cistatic int aq_ndo_setup_tc(struct net_device *dev, enum tc_setup_type type,
3748c2ecf20Sopenharmony_ci			   void *type_data)
3758c2ecf20Sopenharmony_ci{
3768c2ecf20Sopenharmony_ci	struct tc_mqprio_qopt_offload *mqprio = type_data;
3778c2ecf20Sopenharmony_ci	struct aq_nic_s *aq_nic = netdev_priv(dev);
3788c2ecf20Sopenharmony_ci	bool has_min_rate;
3798c2ecf20Sopenharmony_ci	bool has_max_rate;
3808c2ecf20Sopenharmony_ci	int err;
3818c2ecf20Sopenharmony_ci	int i;
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_ci	if (type != TC_SETUP_QDISC_MQPRIO)
3848c2ecf20Sopenharmony_ci		return -EOPNOTSUPP;
3858c2ecf20Sopenharmony_ci
3868c2ecf20Sopenharmony_ci	has_min_rate = !!(mqprio->flags & TC_MQPRIO_F_MIN_RATE);
3878c2ecf20Sopenharmony_ci	has_max_rate = !!(mqprio->flags & TC_MQPRIO_F_MAX_RATE);
3888c2ecf20Sopenharmony_ci
3898c2ecf20Sopenharmony_ci	err = aq_validate_mqprio_opt(aq_nic, mqprio, mqprio->qopt.num_tc);
3908c2ecf20Sopenharmony_ci	if (err)
3918c2ecf20Sopenharmony_ci		return err;
3928c2ecf20Sopenharmony_ci
3938c2ecf20Sopenharmony_ci	for (i = 0; i < mqprio->qopt.num_tc; i++) {
3948c2ecf20Sopenharmony_ci		if (has_max_rate) {
3958c2ecf20Sopenharmony_ci			u64 max_rate = mqprio->max_rate[i];
3968c2ecf20Sopenharmony_ci
3978c2ecf20Sopenharmony_ci			do_div(max_rate, AQ_MBPS_DIVISOR);
3988c2ecf20Sopenharmony_ci			aq_nic_setup_tc_max_rate(aq_nic, i, (u32)max_rate);
3998c2ecf20Sopenharmony_ci		}
4008c2ecf20Sopenharmony_ci
4018c2ecf20Sopenharmony_ci		if (has_min_rate) {
4028c2ecf20Sopenharmony_ci			u64 min_rate = mqprio->min_rate[i];
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci			do_div(min_rate, AQ_MBPS_DIVISOR);
4058c2ecf20Sopenharmony_ci			aq_nic_setup_tc_min_rate(aq_nic, i, (u32)min_rate);
4068c2ecf20Sopenharmony_ci		}
4078c2ecf20Sopenharmony_ci	}
4088c2ecf20Sopenharmony_ci
4098c2ecf20Sopenharmony_ci	return aq_nic_setup_tc_mqprio(aq_nic, mqprio->qopt.num_tc,
4108c2ecf20Sopenharmony_ci				      mqprio->qopt.prio_tc_map);
4118c2ecf20Sopenharmony_ci}
4128c2ecf20Sopenharmony_ci
4138c2ecf20Sopenharmony_cistatic const struct net_device_ops aq_ndev_ops = {
4148c2ecf20Sopenharmony_ci	.ndo_open = aq_ndev_open,
4158c2ecf20Sopenharmony_ci	.ndo_stop = aq_ndev_close,
4168c2ecf20Sopenharmony_ci	.ndo_start_xmit = aq_ndev_start_xmit,
4178c2ecf20Sopenharmony_ci	.ndo_set_rx_mode = aq_ndev_set_multicast_settings,
4188c2ecf20Sopenharmony_ci	.ndo_change_mtu = aq_ndev_change_mtu,
4198c2ecf20Sopenharmony_ci	.ndo_set_mac_address = aq_ndev_set_mac_address,
4208c2ecf20Sopenharmony_ci	.ndo_set_features = aq_ndev_set_features,
4218c2ecf20Sopenharmony_ci	.ndo_do_ioctl = aq_ndev_ioctl,
4228c2ecf20Sopenharmony_ci	.ndo_vlan_rx_add_vid = aq_ndo_vlan_rx_add_vid,
4238c2ecf20Sopenharmony_ci	.ndo_vlan_rx_kill_vid = aq_ndo_vlan_rx_kill_vid,
4248c2ecf20Sopenharmony_ci	.ndo_setup_tc = aq_ndo_setup_tc,
4258c2ecf20Sopenharmony_ci};
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_cistatic int __init aq_ndev_init_module(void)
4288c2ecf20Sopenharmony_ci{
4298c2ecf20Sopenharmony_ci	int ret;
4308c2ecf20Sopenharmony_ci
4318c2ecf20Sopenharmony_ci	aq_ndev_wq = create_singlethread_workqueue(aq_ndev_driver_name);
4328c2ecf20Sopenharmony_ci	if (!aq_ndev_wq) {
4338c2ecf20Sopenharmony_ci		pr_err("Failed to create workqueue\n");
4348c2ecf20Sopenharmony_ci		return -ENOMEM;
4358c2ecf20Sopenharmony_ci	}
4368c2ecf20Sopenharmony_ci
4378c2ecf20Sopenharmony_ci	ret = aq_pci_func_register_driver();
4388c2ecf20Sopenharmony_ci	if (ret) {
4398c2ecf20Sopenharmony_ci		destroy_workqueue(aq_ndev_wq);
4408c2ecf20Sopenharmony_ci		return ret;
4418c2ecf20Sopenharmony_ci	}
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_ci	return 0;
4448c2ecf20Sopenharmony_ci}
4458c2ecf20Sopenharmony_ci
4468c2ecf20Sopenharmony_cistatic void __exit aq_ndev_exit_module(void)
4478c2ecf20Sopenharmony_ci{
4488c2ecf20Sopenharmony_ci	aq_pci_func_unregister_driver();
4498c2ecf20Sopenharmony_ci
4508c2ecf20Sopenharmony_ci	if (aq_ndev_wq) {
4518c2ecf20Sopenharmony_ci		destroy_workqueue(aq_ndev_wq);
4528c2ecf20Sopenharmony_ci		aq_ndev_wq = NULL;
4538c2ecf20Sopenharmony_ci	}
4548c2ecf20Sopenharmony_ci}
4558c2ecf20Sopenharmony_ci
4568c2ecf20Sopenharmony_cimodule_init(aq_ndev_init_module);
4578c2ecf20Sopenharmony_cimodule_exit(aq_ndev_exit_module);
458