diff options
Diffstat (limited to 'drivers/net/ethernet/qlogic')
23 files changed, 5418 insertions, 484 deletions
diff --git a/drivers/net/ethernet/qlogic/Kconfig b/drivers/net/ethernet/qlogic/Kconfig index a8669adecc9..0e1797295a4 100644 --- a/drivers/net/ethernet/qlogic/Kconfig +++ b/drivers/net/ethernet/qlogic/Kconfig @@ -35,6 +35,16 @@ config QLCNIC  	  This driver supports QLogic QLE8240 and QLE8242 Converged Ethernet  	  devices. +config QLCNIC_SRIOV +	bool "QLOGIC QLCNIC 83XX family SR-IOV Support" +	depends on QLCNIC && PCI_IOV +	default y +	---help--- +	  This configuration parameter enables Single Root Input Output +	  Virtualization support for QLE83XX Converged Ethernet devices. +	  This allows for virtual function acceleration in virtualized +	  environments. +  config QLGE  	tristate "QLogic QLGE 10Gb Ethernet Driver Support"  	depends on PCI diff --git a/drivers/net/ethernet/qlogic/netxen/netxen_nic.h b/drivers/net/ethernet/qlogic/netxen/netxen_nic.h index eb3dfdbb642..322a36b7672 100644 --- a/drivers/net/ethernet/qlogic/netxen/netxen_nic.h +++ b/drivers/net/ethernet/qlogic/netxen/netxen_nic.h @@ -955,9 +955,10 @@ typedef struct nx_mac_list_s {  	uint8_t mac_addr[ETH_ALEN+2];  } nx_mac_list_t; -struct nx_vlan_ip_list { +struct nx_ip_list {  	struct list_head list;  	__be32 ip_addr; +	bool master;  };  /* @@ -1605,7 +1606,7 @@ struct netxen_adapter {  	struct net_device *netdev;  	struct pci_dev *pdev;  	struct list_head mac_list; -	struct list_head vlan_ip_list; +	struct list_head ip_list;  	spinlock_t tx_clean_lock; diff --git a/drivers/net/ethernet/qlogic/netxen/netxen_nic_init.c b/drivers/net/ethernet/qlogic/netxen/netxen_nic_init.c index 4782dcfde73..7692dfd4f26 100644 --- a/drivers/net/ethernet/qlogic/netxen/netxen_nic_init.c +++ b/drivers/net/ethernet/qlogic/netxen/netxen_nic_init.c @@ -27,6 +27,7 @@  #include <linux/delay.h>  #include <linux/slab.h>  #include <linux/if_vlan.h> +#include <net/checksum.h>  #include "netxen_nic.h"  #include "netxen_nic_hw.h" @@ -1641,9 +1642,8 @@ netxen_process_lro(struct netxen_adapter *adapter,  	th = (struct tcphdr *)((skb->data + vhdr_len) + (iph->ihl << 2));  	length = (iph->ihl << 2) + (th->doff << 2) + lro_length; +	csum_replace2(&iph->check, iph->tot_len, htons(length));  	iph->tot_len = htons(length); -	iph->check = 0; -	iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);  	th->psh = push;  	th->seq = htonl(seq_number); diff --git a/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c b/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c index 501f49207da..af951f343ff 100644 --- a/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c +++ b/drivers/net/ethernet/qlogic/netxen/netxen_nic_main.c @@ -90,7 +90,7 @@ static irqreturn_t netxen_intr(int irq, void *data);  static irqreturn_t netxen_msi_intr(int irq, void *data);  static irqreturn_t netxen_msix_intr(int irq, void *data); -static void netxen_free_vlan_ip_list(struct netxen_adapter *); +static void netxen_free_ip_list(struct netxen_adapter *, bool);  static void netxen_restore_indev_addr(struct net_device *dev, unsigned long);  static struct rtnl_link_stats64 *netxen_nic_get_stats(struct net_device *dev,  						      struct rtnl_link_stats64 *stats); @@ -1345,7 +1345,7 @@ netxen_setup_netdev(struct netxen_adapter *adapter,  	}  	if (adapter->capabilities & NX_FW_CAPABILITY_FVLANTX) -		netdev->hw_features |= NETIF_F_HW_VLAN_TX; +		netdev->hw_features |= NETIF_F_HW_VLAN_CTAG_TX;  	if (adapter->capabilities & NX_FW_CAPABILITY_HW_LRO)  		netdev->hw_features |= NETIF_F_LRO; @@ -1450,7 +1450,7 @@ netxen_nic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)  	spin_lock_init(&adapter->tx_clean_lock);  	INIT_LIST_HEAD(&adapter->mac_list); -	INIT_LIST_HEAD(&adapter->vlan_ip_list); +	INIT_LIST_HEAD(&adapter->ip_list);  	err = netxen_setup_pci_map(adapter);  	if (err) @@ -1585,7 +1585,7 @@ static void netxen_nic_remove(struct pci_dev *pdev)  	cancel_work_sync(&adapter->tx_timeout_task); -	netxen_free_vlan_ip_list(adapter); +	netxen_free_ip_list(adapter, false);  	netxen_nic_detach(adapter);  	nx_decr_dev_ref_cnt(adapter); @@ -3137,62 +3137,77 @@ netxen_destip_supported(struct netxen_adapter *adapter)  }  static void -netxen_free_vlan_ip_list(struct netxen_adapter *adapter) +netxen_free_ip_list(struct netxen_adapter *adapter, bool master)  { -	struct nx_vlan_ip_list  *cur; -	struct list_head *head = &adapter->vlan_ip_list; +	struct nx_ip_list  *cur, *tmp_cur; -	while (!list_empty(head)) { -		cur = list_entry(head->next, struct nx_vlan_ip_list, list); -		netxen_config_ipaddr(adapter, cur->ip_addr, NX_IP_DOWN); -		list_del(&cur->list); -		kfree(cur); +	list_for_each_entry_safe(cur, tmp_cur, &adapter->ip_list, list) { +		if (master) { +			if (cur->master) { +				netxen_config_ipaddr(adapter, cur->ip_addr, +						     NX_IP_DOWN); +				list_del(&cur->list); +				kfree(cur); +			} +		} else { +			netxen_config_ipaddr(adapter, cur->ip_addr, NX_IP_DOWN); +			list_del(&cur->list); +			kfree(cur); +		}  	} -  } -static void -netxen_list_config_vlan_ip(struct netxen_adapter *adapter, + +static bool +netxen_list_config_ip(struct netxen_adapter *adapter,  		struct in_ifaddr *ifa, unsigned long event)  {  	struct net_device *dev; -	struct nx_vlan_ip_list *cur, *tmp_cur; +	struct nx_ip_list *cur, *tmp_cur;  	struct list_head *head; +	bool ret = false;  	dev = ifa->ifa_dev ? ifa->ifa_dev->dev : NULL;  	if (dev == NULL) -		return; - -	if (!is_vlan_dev(dev)) -		return; +		goto out;  	switch (event) {  	case NX_IP_UP: -		list_for_each(head, &adapter->vlan_ip_list) { -			cur = list_entry(head, struct nx_vlan_ip_list, list); +		list_for_each(head, &adapter->ip_list) { +			cur = list_entry(head, struct nx_ip_list, list);  			if (cur->ip_addr == ifa->ifa_address) -				return; +				goto out;  		} -		cur = kzalloc(sizeof(struct nx_vlan_ip_list), GFP_ATOMIC); +		cur = kzalloc(sizeof(struct nx_ip_list), GFP_ATOMIC);  		if (cur == NULL) -			return; - +			goto out; +		if (dev->priv_flags & IFF_802_1Q_VLAN) +			dev = vlan_dev_real_dev(dev); +		cur->master = !!netif_is_bond_master(dev);  		cur->ip_addr = ifa->ifa_address; -		list_add_tail(&cur->list, &adapter->vlan_ip_list); +		list_add_tail(&cur->list, &adapter->ip_list); +		netxen_config_ipaddr(adapter, ifa->ifa_address, NX_IP_UP); +		ret = true;  		break;  	case NX_IP_DOWN:  		list_for_each_entry_safe(cur, tmp_cur, -					&adapter->vlan_ip_list, list) { +					&adapter->ip_list, list) {  			if (cur->ip_addr == ifa->ifa_address) {  				list_del(&cur->list);  				kfree(cur); +				netxen_config_ipaddr(adapter, ifa->ifa_address, +						     NX_IP_DOWN); +				ret = true;  				break;  			}  		}  	} +out: +	return ret;  } +  static void  netxen_config_indev_addr(struct netxen_adapter *adapter,  		struct net_device *dev, unsigned long event) @@ -3209,14 +3224,10 @@ netxen_config_indev_addr(struct netxen_adapter *adapter,  	for_ifa(indev) {  		switch (event) {  		case NETDEV_UP: -			netxen_config_ipaddr(adapter, -					ifa->ifa_address, NX_IP_UP); -			netxen_list_config_vlan_ip(adapter, ifa, NX_IP_UP); +			netxen_list_config_ip(adapter, ifa, NX_IP_UP);  			break;  		case NETDEV_DOWN: -			netxen_config_ipaddr(adapter, -					ifa->ifa_address, NX_IP_DOWN); -			netxen_list_config_vlan_ip(adapter, ifa, NX_IP_DOWN); +			netxen_list_config_ip(adapter, ifa, NX_IP_DOWN);  			break;  		default:  			break; @@ -3231,23 +3242,78 @@ netxen_restore_indev_addr(struct net_device *netdev, unsigned long event)  {  	struct netxen_adapter *adapter = netdev_priv(netdev); -	struct nx_vlan_ip_list *pos, *tmp_pos; +	struct nx_ip_list *pos, *tmp_pos;  	unsigned long ip_event;  	ip_event = (event == NETDEV_UP) ? NX_IP_UP : NX_IP_DOWN;  	netxen_config_indev_addr(adapter, netdev, event); -	list_for_each_entry_safe(pos, tmp_pos, &adapter->vlan_ip_list, list) { +	list_for_each_entry_safe(pos, tmp_pos, &adapter->ip_list, list) {  		netxen_config_ipaddr(adapter, pos->ip_addr, ip_event);  	}  } +static inline bool +netxen_config_checkdev(struct net_device *dev) +{ +	struct netxen_adapter *adapter; + +	if (!is_netxen_netdev(dev)) +		return false; +	adapter = netdev_priv(dev); +	if (!adapter) +		return false; +	if (!netxen_destip_supported(adapter)) +		return false; +	if (adapter->is_up != NETXEN_ADAPTER_UP_MAGIC) +		return false; + +	return true; +} + +/** + * netxen_config_master - configure addresses based on master + * @dev: netxen device + * @event: netdev event + */ +static void netxen_config_master(struct net_device *dev, unsigned long event) +{ +	struct net_device *master, *slave; +	struct netxen_adapter *adapter = netdev_priv(dev); + +	rcu_read_lock(); +	master = netdev_master_upper_dev_get_rcu(dev); +	/* +	 * This is the case where the netxen nic is being +	 * enslaved and is dev_open()ed in bond_enslave() +	 * Now we should program the bond's (and its vlans') +	 * addresses in the netxen NIC. +	 */ +	if (master && netif_is_bond_master(master) && +	    !netif_is_bond_slave(dev)) { +		netxen_config_indev_addr(adapter, master, event); +		for_each_netdev_rcu(&init_net, slave) +			if (slave->priv_flags & IFF_802_1Q_VLAN && +			    vlan_dev_real_dev(slave) == master) +				netxen_config_indev_addr(adapter, slave, event); +	} +	rcu_read_unlock(); +	/* +	 * This is the case where the netxen nic is being +	 * released and is dev_close()ed in bond_release() +	 * just before IFF_BONDING is stripped. +	 */ +	if (!master && dev->priv_flags & IFF_BONDING) +		netxen_free_ip_list(adapter, true); +} +  static int netxen_netdev_event(struct notifier_block *this,  				 unsigned long event, void *ptr)  {  	struct netxen_adapter *adapter;  	struct net_device *dev = (struct net_device *)ptr;  	struct net_device *orig_dev = dev; +	struct net_device *slave;  recheck:  	if (dev == NULL) @@ -3257,19 +3323,28 @@ recheck:  		dev = vlan_dev_real_dev(dev);  		goto recheck;  	} - -	if (!is_netxen_netdev(dev)) -		goto done; - -	adapter = netdev_priv(dev); - -	if (!adapter) -		goto done; - -	if (adapter->is_up != NETXEN_ADAPTER_UP_MAGIC) -		goto done; - -	netxen_config_indev_addr(adapter, orig_dev, event); +	if (event == NETDEV_UP || event == NETDEV_DOWN) { +		/* If this is a bonding device, look for netxen-based slaves*/ +		if (netif_is_bond_master(dev)) { +			rcu_read_lock(); +			for_each_netdev_in_bond_rcu(dev, slave) { +				if (!netxen_config_checkdev(slave)) +					continue; +				adapter = netdev_priv(slave); +				netxen_config_indev_addr(adapter, +							 orig_dev, event); +			} +			rcu_read_unlock(); +		} else { +			if (!netxen_config_checkdev(dev)) +				goto done; +			adapter = netdev_priv(dev); +			/* Act only if the actual netxen is the target */ +			if (orig_dev == dev) +				netxen_config_master(dev, event); +			netxen_config_indev_addr(adapter, orig_dev, event); +		} +	}  done:  	return NOTIFY_DONE;  } @@ -3279,12 +3354,12 @@ netxen_inetaddr_event(struct notifier_block *this,  		unsigned long event, void *ptr)  {  	struct netxen_adapter *adapter; -	struct net_device *dev; - +	struct net_device *dev, *slave;  	struct in_ifaddr *ifa = (struct in_ifaddr *)ptr; +	unsigned long ip_event;  	dev = ifa->ifa_dev ? ifa->ifa_dev->dev : NULL; - +	ip_event = (event == NETDEV_UP) ? NX_IP_UP : NX_IP_DOWN;  recheck:  	if (dev == NULL)  		goto done; @@ -3293,31 +3368,24 @@ recheck:  		dev = vlan_dev_real_dev(dev);  		goto recheck;  	} - -	if (!is_netxen_netdev(dev)) -		goto done; - -	adapter = netdev_priv(dev); - -	if (!adapter || !netxen_destip_supported(adapter)) -		goto done; - -	if (adapter->is_up != NETXEN_ADAPTER_UP_MAGIC) -		goto done; - -	switch (event) { -	case NETDEV_UP: -		netxen_config_ipaddr(adapter, ifa->ifa_address, NX_IP_UP); -		netxen_list_config_vlan_ip(adapter, ifa, NX_IP_UP); -		break; -	case NETDEV_DOWN: -		netxen_config_ipaddr(adapter, ifa->ifa_address, NX_IP_DOWN); -		netxen_list_config_vlan_ip(adapter, ifa, NX_IP_DOWN); -		break; -	default: -		break; +	if (event == NETDEV_UP || event == NETDEV_DOWN) { +		/* If this is a bonding device, look for netxen-based slaves*/ +		if (netif_is_bond_master(dev)) { +			rcu_read_lock(); +			for_each_netdev_in_bond_rcu(dev, slave) { +				if (!netxen_config_checkdev(slave)) +					continue; +				adapter = netdev_priv(slave); +				netxen_list_config_ip(adapter, ifa, ip_event); +			} +			rcu_read_unlock(); +		} else { +			if (!netxen_config_checkdev(dev)) +				goto done; +			adapter = netdev_priv(dev); +			netxen_list_config_ip(adapter, ifa, ip_event); +		}  	} -  done:  	return NOTIFY_DONE;  } @@ -3334,7 +3402,7 @@ static void  netxen_restore_indev_addr(struct net_device *dev, unsigned long event)  { }  static void -netxen_free_vlan_ip_list(struct netxen_adapter *adapter) +netxen_free_ip_list(struct netxen_adapter *adapter, bool master)  { }  #endif diff --git a/drivers/net/ethernet/qlogic/qla3xxx.c b/drivers/net/ethernet/qlogic/qla3xxx.c index 8fd38cb6d26..91a8fcd6c24 100644 --- a/drivers/net/ethernet/qlogic/qla3xxx.c +++ b/drivers/net/ethernet/qlogic/qla3xxx.c @@ -312,7 +312,6 @@ static void ql_release_to_lrg_buf_free_list(struct ql3_adapter *qdev,  		lrg_buf_cb->skb = netdev_alloc_skb(qdev->ndev,  						   qdev->lrg_buffer_len);  		if (unlikely(!lrg_buf_cb->skb)) { -			netdev_err(qdev->ndev, "failed netdev_alloc_skb()\n");  			qdev->lrg_buf_skb_check++;  		} else {  			/* diff --git a/drivers/net/ethernet/qlogic/qlcnic/Makefile b/drivers/net/ethernet/qlogic/qlcnic/Makefile index 7722a203e38..4b1fb3faa3b 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/Makefile +++ b/drivers/net/ethernet/qlogic/qlcnic/Makefile @@ -8,4 +8,6 @@ qlcnic-y := qlcnic_hw.o qlcnic_main.o qlcnic_init.o \  	qlcnic_ethtool.o qlcnic_ctx.o qlcnic_io.o \  	qlcnic_sysfs.o qlcnic_minidump.o qlcnic_83xx_hw.o \  	qlcnic_83xx_init.o qlcnic_83xx_vnic.o \ -	qlcnic_minidump.o +	qlcnic_minidump.o qlcnic_sriov_common.o + +qlcnic-$(CONFIG_QLCNIC_SRIOV) += qlcnic_sriov_pf.o diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h index ba3c72fce1f..8d02dd75c9a 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic.h @@ -37,9 +37,9 @@  #include "qlcnic_83xx_hw.h"  #define _QLCNIC_LINUX_MAJOR 5 -#define _QLCNIC_LINUX_MINOR 1 -#define _QLCNIC_LINUX_SUBVERSION 35 -#define QLCNIC_LINUX_VERSIONID  "5.1.35" +#define _QLCNIC_LINUX_MINOR 2 +#define _QLCNIC_LINUX_SUBVERSION 41 +#define QLCNIC_LINUX_VERSIONID  "5.2.41"  #define QLCNIC_DRV_IDC_VER  0x01  #define QLCNIC_DRIVER_VERSION  ((_QLCNIC_LINUX_MAJOR << 16) |\  		 (_QLCNIC_LINUX_MINOR << 8) | (_QLCNIC_LINUX_SUBVERSION)) @@ -449,6 +449,7 @@ struct qlcnic_hardware_context {  	struct qlc_83xx_idc idc;  	struct qlc_83xx_fw_info fw_info;  	struct qlcnic_intrpt_config *intr_tbl; +	struct qlcnic_sriov *sriov;  	u32 *reg_tbl;  	u32 *ext_reg_tbl;  	u32 mbox_aen[QLC_83XX_MBX_AEN_CNT]; @@ -896,6 +897,7 @@ struct qlcnic_ipaddr {  #define QLCNIC_FW_RESET_OWNER		0x2000  #define QLCNIC_FW_HANG			0x4000  #define QLCNIC_FW_LRO_MSS_CAP		0x8000 +#define QLCNIC_TX_INTR_SHARED		0x10000  #define QLCNIC_IS_MSI_FAMILY(adapter) \  	((adapter)->flags & (QLCNIC_MSI_ENABLED | QLCNIC_MSIX_ENABLED)) @@ -914,7 +916,10 @@ struct qlcnic_ipaddr {  #define __QLCNIC_AER			5  #define __QLCNIC_DIAG_RES_ALLOC		6  #define __QLCNIC_LED_ENABLE		7 -#define __QLCNIC_ELB_INPROGRESS	8 +#define __QLCNIC_ELB_INPROGRESS		8 +#define __QLCNIC_SRIOV_ENABLE		10 +#define __QLCNIC_SRIOV_CAPABLE		11 +#define __QLCNIC_MBX_POLL_ENABLE	12  #define QLCNIC_INTERRUPT_TEST		1  #define QLCNIC_LOOPBACK_TEST		2 @@ -935,7 +940,7 @@ struct qlcnic_ipaddr {  struct qlcnic_filter {  	struct hlist_node fnode;  	u8 faddr[ETH_ALEN]; -	__le16 vlan_id; +	u16 vlan_id;  	unsigned long ftime;  }; @@ -972,9 +977,11 @@ struct qlcnic_adapter {  	u8 fw_fail_cnt;  	u8 tx_timeo_cnt;  	u8 need_fw_reset; +	u8 reset_ctx_cnt;  	u16 is_up; -	u16 pvid; +	u16 rx_pvid; +	u16 tx_pvid;  	u32 irq;  	u32 heartbeat; @@ -1006,9 +1013,11 @@ struct qlcnic_adapter {  	struct workqueue_struct *qlcnic_wq;  	struct delayed_work fw_work;  	struct delayed_work idc_aen_work; +	struct delayed_work mbx_poll_work;  	struct qlcnic_filter_hash fhash;  	struct qlcnic_filter_hash rx_fhash; +	struct list_head vf_mc_list;  	spinlock_t tx_clean_lock;  	spinlock_t mac_learn_lock; @@ -1051,7 +1060,11 @@ struct qlcnic_info_le {  	u8      total_pf;  	u8      total_rss_engines;  	__le16  max_vports; -	u8      reserved2[64]; +	__le16	linkstate_reg_offset; +	__le16	bit_offsets; +	__le16  max_local_ipv6_addrs; +	__le16  max_remote_ipv6_addrs; +	u8	reserved2[56];  } __packed;  struct qlcnic_info { @@ -1083,6 +1096,10 @@ struct qlcnic_info {  	u8      total_pf;  	u8      total_rss_engines;  	u16	max_vports; +	u16	linkstate_reg_offset; +	u16	bit_offsets; +	u16	max_local_ipv6_addrs; +	u16	max_remote_ipv6_addrs;  };  struct qlcnic_pci_info_le { @@ -1348,6 +1365,7 @@ struct _cdrp_cmd {  struct qlcnic_cmd_args {  	struct _cdrp_cmd req;  	struct _cdrp_cmd rsp; +	int op_type;  };  int qlcnic_fw_cmd_get_minidump_temp(struct qlcnic_adapter *adapter); @@ -1430,9 +1448,10 @@ void qlcnic_post_rx_buffers(struct qlcnic_adapter *adapter,  		struct qlcnic_host_rds_ring *rds_ring, u8 ring_id);  int qlcnic_process_rcv_ring(struct qlcnic_host_sds_ring *sds_ring, int max);  void qlcnic_set_multi(struct net_device *netdev); -int qlcnic_nic_add_mac(struct qlcnic_adapter *, const u8 *); +void __qlcnic_set_multi(struct net_device *, u16); +int qlcnic_nic_add_mac(struct qlcnic_adapter *, const u8 *, u16);  int qlcnic_nic_del_mac(struct qlcnic_adapter *, const u8 *); -void qlcnic_free_mac_list(struct qlcnic_adapter *adapter); +void qlcnic_82xx_free_mac_list(struct qlcnic_adapter *adapter);  int qlcnic_fw_cmd_set_mtu(struct qlcnic_adapter *adapter, int mtu);  int qlcnic_fw_cmd_set_drv_version(struct qlcnic_adapter *); @@ -1509,8 +1528,13 @@ int qlcnic_init_pci_info(struct qlcnic_adapter *);  int qlcnic_set_default_offload_settings(struct qlcnic_adapter *);  int qlcnic_reset_npar_config(struct qlcnic_adapter *);  int qlcnic_set_eswitch_port_config(struct qlcnic_adapter *); -void qlcnic_add_lb_filter(struct qlcnic_adapter *, struct sk_buff *, int, -			  __le16); +void qlcnic_add_lb_filter(struct qlcnic_adapter *, struct sk_buff *, int, u16); +int qlcnic_83xx_configure_opmode(struct qlcnic_adapter *adapter); +int qlcnic_read_mac_addr(struct qlcnic_adapter *); +int qlcnic_setup_netdev(struct qlcnic_adapter *, struct net_device *, int); +void qlcnic_sriov_vf_schedule_multi(struct net_device *); +void qlcnic_vf_add_mc_list(struct net_device *, u16); +  /*   * QLOGIC Board information   */ @@ -1567,11 +1591,14 @@ struct qlcnic_hardware_ops {  	int (*create_rx_ctx) (struct qlcnic_adapter *);  	int (*create_tx_ctx) (struct qlcnic_adapter *,  	struct qlcnic_host_tx_ring *, int); +	void (*del_rx_ctx) (struct qlcnic_adapter *); +	void (*del_tx_ctx) (struct qlcnic_adapter *, +			    struct qlcnic_host_tx_ring *);  	int (*setup_link_event) (struct qlcnic_adapter *, int);  	int (*get_nic_info) (struct qlcnic_adapter *, struct qlcnic_info *, u8);  	int (*get_pci_info) (struct qlcnic_adapter *, struct qlcnic_pci_info *);  	int (*set_nic_info) (struct qlcnic_adapter *, struct qlcnic_info *); -	int (*change_macvlan) (struct qlcnic_adapter *, u8*, __le16, u8); +	int (*change_macvlan) (struct qlcnic_adapter *, u8*, u16, u8);  	void (*napi_enable) (struct qlcnic_adapter *);  	void (*napi_disable) (struct qlcnic_adapter *);  	void (*config_intr_coal) (struct qlcnic_adapter *); @@ -1580,8 +1607,9 @@ struct qlcnic_hardware_ops {  	int (*config_loopback) (struct qlcnic_adapter *, u8);  	int (*clear_loopback) (struct qlcnic_adapter *, u8);  	int (*config_promisc_mode) (struct qlcnic_adapter *, u32); -	void (*change_l2_filter) (struct qlcnic_adapter *, u64 *, __le16); +	void (*change_l2_filter) (struct qlcnic_adapter *, u64 *, u16);  	int (*get_board_info) (struct qlcnic_adapter *); +	void (*free_mac_list) (struct qlcnic_adapter *);  };  extern struct qlcnic_nic_template qlcnic_vf_ops; @@ -1635,7 +1663,10 @@ static inline int qlcnic_alloc_mbx_args(struct qlcnic_cmd_args *mbx,  static inline int qlcnic_issue_cmd(struct qlcnic_adapter *adapter,  				   struct qlcnic_cmd_args *cmd)  { -	return adapter->ahw->hw_ops->mbx_cmd(adapter, cmd); +	if (adapter->ahw->hw_ops->mbx_cmd) +		return adapter->ahw->hw_ops->mbx_cmd(adapter, cmd); + +	return -EIO;  }  static inline void qlcnic_get_func_no(struct qlcnic_adapter *adapter) @@ -1655,12 +1686,14 @@ static inline void qlcnic_api_unlock(struct qlcnic_adapter *adapter)  static inline void qlcnic_add_sysfs(struct qlcnic_adapter *adapter)  { -	adapter->ahw->hw_ops->add_sysfs(adapter); +	if (adapter->ahw->hw_ops->add_sysfs) +		adapter->ahw->hw_ops->add_sysfs(adapter);  }  static inline void qlcnic_remove_sysfs(struct qlcnic_adapter *adapter)  { -	adapter->ahw->hw_ops->remove_sysfs(adapter); +	if (adapter->ahw->hw_ops->remove_sysfs) +		adapter->ahw->hw_ops->remove_sysfs(adapter);  }  static inline void @@ -1681,6 +1714,17 @@ static inline int qlcnic_fw_cmd_create_tx_ctx(struct qlcnic_adapter *adapter,  	return adapter->ahw->hw_ops->create_tx_ctx(adapter, ptr, ring);  } +static inline void qlcnic_fw_cmd_del_rx_ctx(struct qlcnic_adapter *adapter) +{ +	return adapter->ahw->hw_ops->del_rx_ctx(adapter); +} + +static inline void qlcnic_fw_cmd_del_tx_ctx(struct qlcnic_adapter *adapter, +					    struct qlcnic_host_tx_ring *ptr) +{ +	return adapter->ahw->hw_ops->del_tx_ctx(adapter, ptr); +} +  static inline int qlcnic_linkevent_request(struct qlcnic_adapter *adapter,  					   int enable)  { @@ -1706,7 +1750,7 @@ static inline int qlcnic_set_nic_info(struct qlcnic_adapter *adapter,  }  static inline int qlcnic_sre_macaddr_change(struct qlcnic_adapter *adapter, -					    u8 *addr, __le16 id, u8 cmd) +					    u8 *addr, u16 id, u8 cmd)  {  	return adapter->ahw->hw_ops->change_macvlan(adapter, addr, id, cmd);  } @@ -1765,7 +1809,7 @@ static inline int qlcnic_nic_set_promisc(struct qlcnic_adapter *adapter,  }  static inline void qlcnic_change_filter(struct qlcnic_adapter *adapter, -					u64 *addr, __le16 id) +					u64 *addr, u16 id)  {  	adapter->ahw->hw_ops->change_l2_filter(adapter, addr, id);  } @@ -1775,15 +1819,22 @@ static inline int qlcnic_get_board_info(struct qlcnic_adapter *adapter)  	return adapter->ahw->hw_ops->get_board_info(adapter);  } +static inline void qlcnic_free_mac_list(struct qlcnic_adapter *adapter) +{ +	return adapter->ahw->hw_ops->free_mac_list(adapter); +} +  static inline void qlcnic_dev_request_reset(struct qlcnic_adapter *adapter,  					    u32 key)  { -	adapter->nic_ops->request_reset(adapter, key); +	if (adapter->nic_ops->request_reset) +		adapter->nic_ops->request_reset(adapter, key);  }  static inline void qlcnic_cancel_idc_work(struct qlcnic_adapter *adapter)  { -	adapter->nic_ops->cancel_idc_work(adapter); +	if (adapter->nic_ops->cancel_idc_work) +		adapter->nic_ops->cancel_idc_work(adapter);  }  static inline irqreturn_t @@ -1819,6 +1870,7 @@ static inline void qlcnic_enable_int(struct qlcnic_host_sds_ring *sds_ring)  		writel(0xfbff, adapter->tgt_mask_reg);  } +extern const struct ethtool_ops qlcnic_sriov_vf_ethtool_ops;  extern const struct ethtool_ops qlcnic_ethtool_ops;  extern const struct ethtool_ops qlcnic_ethtool_failed_ops; @@ -1830,7 +1882,9 @@ extern const struct ethtool_ops qlcnic_ethtool_failed_ops;  	} while (0)  #define PCI_DEVICE_ID_QLOGIC_QLE834X    0x8030 +#define PCI_DEVICE_ID_QLOGIC_VF_QLE834X	0x8430  #define PCI_DEVICE_ID_QLOGIC_QLE824X	0x8020 +  static inline bool qlcnic_82xx_check(struct qlcnic_adapter *adapter)  {  	unsigned short device = adapter->pdev->device; @@ -1840,8 +1894,23 @@ static inline bool qlcnic_82xx_check(struct qlcnic_adapter *adapter)  static inline bool qlcnic_83xx_check(struct qlcnic_adapter *adapter)  {  	unsigned short device = adapter->pdev->device; -	return (device == PCI_DEVICE_ID_QLOGIC_QLE834X) ? true : false; +	bool status; + +	status = ((device == PCI_DEVICE_ID_QLOGIC_QLE834X) || +		  (device == PCI_DEVICE_ID_QLOGIC_VF_QLE834X)) ? true : false; + +	return status; +} + +static inline bool qlcnic_sriov_pf_check(struct qlcnic_adapter *adapter) +{ +	return (adapter->ahw->op_mode == QLCNIC_SRIOV_PF_FUNC) ? true : false;  } +static inline bool qlcnic_sriov_vf_check(struct qlcnic_adapter *adapter) +{ +	unsigned short device = adapter->pdev->device; +	return (device == PCI_DEVICE_ID_QLOGIC_VF_QLE834X) ? true : false; +}  #endif				/* __QLCNIC_H_ */ diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c index edd63f1230f..fd0829c2839 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c @@ -6,6 +6,7 @@   */  #include "qlcnic.h" +#include "qlcnic_sriov.h"  #include <linux/if_vlan.h>  #include <linux/ipv6.h>  #include <linux/ethtool.h> @@ -13,100 +14,7 @@  #define QLCNIC_MAX_TX_QUEUES		1  #define RSS_HASHTYPE_IP_TCP		0x3 - -/* status descriptor mailbox data - * @phy_addr: physical address of buffer - * @sds_ring_size: buffer size - * @intrpt_id: interrupt id - * @intrpt_val: source of interrupt - */ -struct qlcnic_sds_mbx { -	u64	phy_addr; -	u8	rsvd1[16]; -	u16	sds_ring_size; -	u16	rsvd2[3]; -	u16	intrpt_id; -	u8	intrpt_val; -	u8	rsvd3[5]; -} __packed; - -/* receive descriptor buffer data - * phy_addr_reg: physical address of regular buffer - * phy_addr_jmb: physical address of jumbo buffer - * reg_ring_sz: size of regular buffer - * reg_ring_len: no. of entries in regular buffer - * jmb_ring_len: no. of entries in jumbo buffer - * jmb_ring_sz: size of jumbo buffer - */ -struct qlcnic_rds_mbx { -	u64	phy_addr_reg; -	u64	phy_addr_jmb; -	u16	reg_ring_sz; -	u16	reg_ring_len; -	u16	jmb_ring_sz; -	u16	jmb_ring_len; -} __packed; - -/* host producers for regular and jumbo rings */ -struct __host_producer_mbx { -	u32	reg_buf; -	u32	jmb_buf; -} __packed; - -/* Receive context mailbox data outbox registers - * @state: state of the context - * @vport_id: virtual port id - * @context_id: receive context id - * @num_pci_func: number of pci functions of the port - * @phy_port: physical port id - */ -struct qlcnic_rcv_mbx_out { -	u8	rcv_num; -	u8	sts_num; -	u16	ctx_id; -	u8	state; -	u8	num_pci_func; -	u8	phy_port; -	u8	vport_id; -	u32	host_csmr[QLCNIC_MAX_RING_SETS]; -	struct __host_producer_mbx host_prod[QLCNIC_MAX_RING_SETS]; -} __packed; - -struct qlcnic_add_rings_mbx_out { -	u8      rcv_num; -	u8      sts_num; -	u16  ctx_id; -	u32  host_csmr[QLCNIC_MAX_RING_SETS]; -	struct __host_producer_mbx host_prod[QLCNIC_MAX_RING_SETS]; -} __packed; - -/* Transmit context mailbox inbox registers - * @phys_addr: DMA address of the transmit buffer - * @cnsmr_index: host consumer index - * @size: legth of transmit buffer ring - * @intr_id: interrput id - * @src: src of interrupt - */ -struct qlcnic_tx_mbx { -	u64	phys_addr; -	u64	cnsmr_index; -	u16	size; -	u16	intr_id; -	u8	src; -	u8	rsvd[3]; -} __packed; - -/* Transmit context mailbox outbox registers - * @host_prod: host producer index - * @ctx_id: transmit context id - * @state: state of the transmit context - */ -struct qlcnic_tx_mbx_out { -	u32	host_prod; -	u16	ctx_id; -	u8	state; -	u8	rsvd; -} __packed; +#define QLC_83XX_FW_MBX_CMD		0  static const struct qlcnic_mailbox_metadata qlcnic_83xx_mbx_tbl[] = {  	{QLCNIC_CMD_CONFIGURE_IP_ADDR, 6, 1}, @@ -156,9 +64,11 @@ static const struct qlcnic_mailbox_metadata qlcnic_83xx_mbx_tbl[] = {  	{QLCNIC_CMD_SET_LED_CONFIG, 5, 1},  	{QLCNIC_CMD_GET_LED_CONFIG, 1, 5},  	{QLCNIC_CMD_ADD_RCV_RINGS, 130, 26}, +	{QLCNIC_CMD_CONFIG_VPORT, 4, 4}, +	{QLCNIC_CMD_BC_EVENT_SETUP, 2, 1},  }; -static const u32 qlcnic_83xx_ext_reg_tbl[] = { +const u32 qlcnic_83xx_ext_reg_tbl[] = {  	0x38CC,		/* Global Reset */  	0x38F0,		/* Wildcard */  	0x38FC,		/* Informant */ @@ -204,7 +114,7 @@ static const u32 qlcnic_83xx_ext_reg_tbl[] = {  	0x34A4,		/* QLC_83XX_ASIC_TEMP */  }; -static const u32 qlcnic_83xx_reg_tbl[] = { +const u32 qlcnic_83xx_reg_tbl[] = {  	0x34A8,		/* PEG_HALT_STAT1 */  	0x34AC,		/* PEG_HALT_STAT2 */  	0x34B0,		/* FW_HEARTBEAT */ @@ -247,6 +157,8 @@ static struct qlcnic_hardware_ops qlcnic_83xx_hw_ops = {  	.process_lb_rcv_ring_diag	= qlcnic_83xx_process_rcv_ring_diag,  	.create_rx_ctx			= qlcnic_83xx_create_rx_ctx,  	.create_tx_ctx			= qlcnic_83xx_create_tx_ctx, +	.del_rx_ctx			= qlcnic_83xx_del_rx_ctx, +	.del_tx_ctx			= qlcnic_83xx_del_tx_ctx,  	.setup_link_event		= qlcnic_83xx_setup_link_event,  	.get_nic_info			= qlcnic_83xx_get_nic_info,  	.get_pci_info			= qlcnic_83xx_get_pci_info, @@ -260,6 +172,7 @@ static struct qlcnic_hardware_ops qlcnic_83xx_hw_ops = {  	.config_promisc_mode		= qlcnic_83xx_nic_set_promisc,  	.change_l2_filter		= qlcnic_83xx_change_l2_filter,  	.get_board_info			= qlcnic_83xx_get_port_info, +	.free_mac_list			= qlcnic_82xx_free_mac_list,  };  static struct qlcnic_nic_template qlcnic_83xx_ops = { @@ -355,14 +268,20 @@ int qlcnic_83xx_setup_intr(struct qlcnic_adapter *adapter, u8 num_intr)  					      num_intr));  	/* account for AEN interrupt MSI-X based interrupts */  	num_msix += 1; -	num_msix += adapter->max_drv_tx_rings; + +	if (!(adapter->flags & QLCNIC_TX_INTR_SHARED)) +		num_msix += adapter->max_drv_tx_rings; +  	err = qlcnic_enable_msix(adapter, num_msix);  	if (err == -ENOMEM)  		return err;  	if (adapter->flags & QLCNIC_MSIX_ENABLED)  		num_msix = adapter->ahw->num_msix; -	else +	else { +		if (qlcnic_sriov_vf_check(adapter)) +			return -EINVAL;  		num_msix = 1; +	}  	/* setup interrupt mapping table for fw */  	ahw->intr_tbl = vzalloc(num_msix *  				sizeof(struct qlcnic_intrpt_config)); @@ -421,12 +340,13 @@ inline void qlcnic_83xx_enable_legacy_msix_mbx_intr(struct qlcnic_adapter  	writel(0, adapter->ahw->pci_base0 + mask);  } -inline void qlcnic_83xx_disable_mbx_intr(struct qlcnic_adapter *adapter) +void qlcnic_83xx_disable_mbx_intr(struct qlcnic_adapter *adapter)  {  	u32 mask;  	mask = QLCRDX(adapter->ahw, QLCNIC_DEF_INT_MASK);  	writel(1, adapter->ahw->pci_base0 + mask); +	QLCWRX(adapter->ahw, QLCNIC_MBX_INTR_ENBL, 0);  }  static inline void qlcnic_83xx_get_mbx_data(struct qlcnic_adapter *adapter, @@ -482,7 +402,8 @@ static void qlcnic_83xx_poll_process_aen(struct qlcnic_adapter *adapter)  	event = readl(QLCNIC_MBX_FW(adapter->ahw, 0));  	if (event &  QLCNIC_MBX_ASYNC_EVENT) -		qlcnic_83xx_process_aen(adapter); +		__qlcnic_83xx_process_aen(adapter); +  out:  	qlcnic_83xx_enable_legacy_msix_mbx_intr(adapter);  	spin_unlock_irqrestore(&adapter->ahw->mbx_lock, flags); @@ -535,17 +456,15 @@ done:  void qlcnic_83xx_free_mbx_intr(struct qlcnic_adapter *adapter)  { -	u32 val = 0, num_msix = adapter->ahw->num_msix - 1; +	u32 num_msix; + +	qlcnic_83xx_disable_mbx_intr(adapter);  	if (adapter->flags & QLCNIC_MSIX_ENABLED)  		num_msix = adapter->ahw->num_msix - 1;  	else  		num_msix = 0; -	QLCWRX(adapter->ahw, QLCNIC_MBX_INTR_ENBL, val); - -	qlcnic_83xx_disable_mbx_intr(adapter); -  	msleep(20);  	synchronize_irq(adapter->msix_entries[num_msix].vector);  	free_irq(adapter->msix_entries[num_msix].vector, adapter); @@ -595,7 +514,7 @@ int qlcnic_83xx_setup_mbx_intr(struct qlcnic_adapter *adapter)  void qlcnic_83xx_get_func_no(struct qlcnic_adapter *adapter)  {  	u32 val = QLCRDX(adapter->ahw, QLCNIC_INFORMANT); -	adapter->ahw->pci_func = val & 0xf; +	adapter->ahw->pci_func = (val >> 24) & 0xff;  }  int qlcnic_83xx_cam_lock(struct qlcnic_adapter *adapter) @@ -707,6 +626,11 @@ void qlcnic_83xx_check_vf(struct qlcnic_adapter *adapter,  	ahw->fw_hal_version = 2;  	qlcnic_get_func_no(adapter); +	if (qlcnic_sriov_vf_check(adapter)) { +		qlcnic_sriov_vf_set_ops(adapter); +		return; +	} +  	/* Determine function privilege level */  	op_mode = QLCRDX(adapter->ahw, QLC_83XX_DRV_OP_MODE);  	if (op_mode == QLC_83XX_DEFAULT_OPMODE) @@ -722,6 +646,9 @@ void qlcnic_83xx_check_vf(struct qlcnic_adapter *adapter,  			 ahw->fw_hal_version);  		adapter->nic_ops = &qlcnic_vf_ops;  	} else { +		if (pci_find_ext_capability(adapter->pdev, +					    PCI_EXT_CAP_ID_SRIOV)) +			set_bit(__QLCNIC_SRIOV_CAPABLE, &adapter->state);  		adapter->nic_ops = &qlcnic_83xx_ops;  	}  } @@ -755,7 +682,7 @@ static void qlcnic_dump_mbx(struct qlcnic_adapter *adapter,  }  /* Mailbox response for mac rcode */ -static u32 qlcnic_83xx_mac_rcode(struct qlcnic_adapter *adapter) +u32 qlcnic_83xx_mac_rcode(struct qlcnic_adapter *adapter)  {  	u32 fw_data;  	u8 mac_cmd_rcode; @@ -769,7 +696,7 @@ static u32 qlcnic_83xx_mac_rcode(struct qlcnic_adapter *adapter)  	return 1;  } -static u32 qlcnic_83xx_mbx_poll(struct qlcnic_adapter *adapter) +u32 qlcnic_83xx_mbx_poll(struct qlcnic_adapter *adapter)  {  	u32 data;  	unsigned long wait_time = 0; @@ -832,7 +759,7 @@ poll:  		/* Get the FW response data */  		fw_data = readl(QLCNIC_MBX_FW(ahw, 0));  		if (fw_data &  QLCNIC_MBX_ASYNC_EVENT) { -			qlcnic_83xx_process_aen(adapter); +			__qlcnic_83xx_process_aen(adapter);  			mbx_val = QLCRDX(ahw, QLCNIC_HOST_MBX_CTRL);  			if (mbx_val)  				goto poll; @@ -884,6 +811,7 @@ int qlcnic_83xx_alloc_mbx_args(struct qlcnic_cmd_args *mbx,  	size = ARRAY_SIZE(qlcnic_83xx_mbx_tbl);  	for (i = 0; i < size; i++) {  		if (type == mbx_tbl[i].cmd) { +			mbx->op_type = QLC_83XX_FW_MBX_CMD;  			mbx->req.num = mbx_tbl[i].in_args;  			mbx->rsp.num = mbx_tbl[i].out_args;  			mbx->req.arg = kcalloc(mbx->req.num, sizeof(u32), @@ -901,10 +829,10 @@ int qlcnic_83xx_alloc_mbx_args(struct qlcnic_cmd_args *mbx,  			memset(mbx->rsp.arg, 0, sizeof(u32) * mbx->rsp.num);  			temp = adapter->ahw->fw_hal_version << 29;  			mbx->req.arg[0] = (type | (mbx->req.num << 16) | temp); -			break; +			return 0;  		}  	} -	return 0; +	return -EINVAL;  }  void qlcnic_83xx_idc_aen_work(struct work_struct *work) @@ -935,7 +863,7 @@ static void qlcnic_83xx_handle_idc_comp_aen(struct qlcnic_adapter *adapter,  	return;  } -void qlcnic_83xx_process_aen(struct qlcnic_adapter *adapter) +void __qlcnic_83xx_process_aen(struct qlcnic_adapter *adapter)  {  	u32 event[QLC_83XX_MBX_AEN_CNT];  	int i; @@ -960,6 +888,9 @@ void qlcnic_83xx_process_aen(struct qlcnic_adapter *adapter)  		break;  	case QLCNIC_MBX_TIME_EXTEND_EVENT:  		break; +	case QLCNIC_MBX_BC_EVENT: +		qlcnic_sriov_handle_bc_event(adapter, event[1]); +		break;  	case QLCNIC_MBX_SFP_INSERT_EVENT:  		dev_info(&adapter->pdev->dev, "SFP+ Insert AEN:0x%x.\n",  			 QLCNIC_MBX_RSP(event[0])); @@ -977,6 +908,53 @@ void qlcnic_83xx_process_aen(struct qlcnic_adapter *adapter)  	QLCWRX(ahw, QLCNIC_FW_MBX_CTRL, QLCNIC_CLR_OWNER);  } +static void qlcnic_83xx_process_aen(struct qlcnic_adapter *adapter) +{ +	struct qlcnic_hardware_context *ahw = adapter->ahw; +	u32 resp, event; +	unsigned long flags; + +	spin_lock_irqsave(&ahw->mbx_lock, flags); + +	resp = QLCRDX(ahw, QLCNIC_FW_MBX_CTRL); +	if (resp & QLCNIC_SET_OWNER) { +		event = readl(QLCNIC_MBX_FW(ahw, 0)); +		if (event &  QLCNIC_MBX_ASYNC_EVENT) +			__qlcnic_83xx_process_aen(adapter); +	} + +	spin_unlock_irqrestore(&ahw->mbx_lock, flags); +} + +static void qlcnic_83xx_mbx_poll_work(struct work_struct *work) +{ +	struct qlcnic_adapter *adapter; + +	adapter = container_of(work, struct qlcnic_adapter, mbx_poll_work.work); + +	if (!test_bit(__QLCNIC_MBX_POLL_ENABLE, &adapter->state)) +		return; + +	qlcnic_83xx_process_aen(adapter); +	queue_delayed_work(adapter->qlcnic_wq, &adapter->mbx_poll_work, +			   (HZ / 10)); +} + +void qlcnic_83xx_enable_mbx_poll(struct qlcnic_adapter *adapter) +{ +	if (test_and_set_bit(__QLCNIC_MBX_POLL_ENABLE, &adapter->state)) +		return; + +	INIT_DELAYED_WORK(&adapter->mbx_poll_work, qlcnic_83xx_mbx_poll_work); +} + +void qlcnic_83xx_disable_mbx_poll(struct qlcnic_adapter *adapter) +{ +	if (!test_and_clear_bit(__QLCNIC_MBX_POLL_ENABLE, &adapter->state)) +		return; +	cancel_delayed_work_sync(&adapter->mbx_poll_work); +} +  static int qlcnic_83xx_add_rings(struct qlcnic_adapter *adapter)  {  	int index, i, err, sds_mbx_size; @@ -1004,7 +982,8 @@ static int qlcnic_83xx_add_rings(struct qlcnic_adapter *adapter)  		sds = &recv_ctx->sds_rings[i];  		sds->consumer = 0;  		memset(sds->desc_head, 0, STATUS_DESC_RINGSIZE(sds)); -		sds_mbx.phy_addr = sds->phys_addr; +		sds_mbx.phy_addr_low = LSD(sds->phys_addr); +		sds_mbx.phy_addr_high = MSD(sds->phys_addr);  		sds_mbx.sds_ring_size = sds->num_desc;  		if (adapter->flags & QLCNIC_MSIX_ENABLED) @@ -1050,6 +1029,32 @@ out:  	return err;  } +void qlcnic_83xx_del_rx_ctx(struct qlcnic_adapter *adapter) +{ +	int err; +	u32 temp = 0; +	struct qlcnic_cmd_args cmd; +	struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx; + +	if (qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_DESTROY_RX_CTX)) +		return; + +	if (qlcnic_sriov_pf_check(adapter) || qlcnic_sriov_vf_check(adapter)) +		cmd.req.arg[0] |= (0x3 << 29); + +	if (qlcnic_sriov_pf_check(adapter)) +		qlcnic_pf_set_interface_id_del_rx_ctx(adapter, &temp); + +	cmd.req.arg[1] = recv_ctx->context_id | temp; +	err = qlcnic_issue_cmd(adapter, &cmd); +	if (err) +		dev_err(&adapter->pdev->dev, +			"Failed to destroy rx ctx in firmware\n"); + +	recv_ctx->state = QLCNIC_HOST_CTX_STATE_FREED; +	qlcnic_free_mbx_args(&cmd); +} +  int qlcnic_83xx_create_rx_ctx(struct qlcnic_adapter *adapter)  {  	int i, err, index, sds_mbx_size, rds_mbx_size; @@ -1080,9 +1085,17 @@ int qlcnic_83xx_create_rx_ctx(struct qlcnic_adapter *adapter)  	/* set mailbox hdr and capabilities */  	qlcnic_alloc_mbx_args(&cmd, adapter,  			      QLCNIC_CMD_CREATE_RX_CTX); + +	if (qlcnic_sriov_pf_check(adapter) || qlcnic_sriov_vf_check(adapter)) +		cmd.req.arg[0] |= (0x3 << 29); +  	cmd.req.arg[1] = cap;  	cmd.req.arg[5] = 1 | (num_rds << 5) | (num_sds << 8) |  			 (QLC_83XX_HOST_RDS_MODE_UNIQUE << 16); + +	if (qlcnic_sriov_pf_check(adapter)) +		qlcnic_pf_set_interface_id_create_rx_ctx(adapter, +							 &cmd.req.arg[6]);  	/* set up status rings, mbx 8-57/87 */  	index = QLC_83XX_HOST_SDS_MBX_IDX;  	for (i = 0; i < num_sds; i++) { @@ -1090,7 +1103,8 @@ int qlcnic_83xx_create_rx_ctx(struct qlcnic_adapter *adapter)  		sds = &recv_ctx->sds_rings[i];  		sds->consumer = 0;  		memset(sds->desc_head, 0, STATUS_DESC_RINGSIZE(sds)); -		sds_mbx.phy_addr = sds->phys_addr; +		sds_mbx.phy_addr_low = LSD(sds->phys_addr); +		sds_mbx.phy_addr_high = MSD(sds->phys_addr);  		sds_mbx.sds_ring_size = sds->num_desc;  		if (adapter->flags & QLCNIC_MSIX_ENABLED)  			intrpt_id = ahw->intr_tbl[i].id; @@ -1110,13 +1124,15 @@ int qlcnic_83xx_create_rx_ctx(struct qlcnic_adapter *adapter)  	rds = &recv_ctx->rds_rings[0];  	rds->producer = 0;  	memset(&rds_mbx, 0, rds_mbx_size); -	rds_mbx.phy_addr_reg = rds->phys_addr; +	rds_mbx.phy_addr_reg_low = LSD(rds->phys_addr); +	rds_mbx.phy_addr_reg_high = MSD(rds->phys_addr);  	rds_mbx.reg_ring_sz = rds->dma_size;  	rds_mbx.reg_ring_len = rds->num_desc;  	/* Jumbo ring */  	rds = &recv_ctx->rds_rings[1];  	rds->producer = 0; -	rds_mbx.phy_addr_jmb = rds->phys_addr; +	rds_mbx.phy_addr_jmb_low = LSD(rds->phys_addr); +	rds_mbx.phy_addr_jmb_high = MSD(rds->phys_addr);  	rds_mbx.jmb_ring_sz = rds->dma_size;  	rds_mbx.jmb_ring_len = rds->num_desc;  	buf = &cmd.req.arg[index]; @@ -1163,16 +1179,39 @@ out:  	return err;  } +void qlcnic_83xx_del_tx_ctx(struct qlcnic_adapter *adapter, +			    struct qlcnic_host_tx_ring *tx_ring) +{ +	struct qlcnic_cmd_args cmd; +	u32 temp = 0; + +	if (qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_DESTROY_TX_CTX)) +		return; + +	if (qlcnic_sriov_pf_check(adapter) || qlcnic_sriov_vf_check(adapter)) +		cmd.req.arg[0] |= (0x3 << 29); + +	if (qlcnic_sriov_pf_check(adapter)) +		qlcnic_pf_set_interface_id_del_tx_ctx(adapter, &temp); + +	cmd.req.arg[1] = tx_ring->ctx_id | temp; +	if (qlcnic_issue_cmd(adapter, &cmd)) +		dev_err(&adapter->pdev->dev, +			"Failed to destroy tx ctx in firmware\n"); +	qlcnic_free_mbx_args(&cmd); +} +  int qlcnic_83xx_create_tx_ctx(struct qlcnic_adapter *adapter,  			      struct qlcnic_host_tx_ring *tx, int ring)  {  	int err;  	u16 msix_id; -	u32 *buf, intr_mask; +	u32 *buf, intr_mask, temp = 0;  	struct qlcnic_cmd_args cmd;  	struct qlcnic_tx_mbx mbx;  	struct qlcnic_tx_mbx_out *mbx_out;  	struct qlcnic_hardware_context *ahw = adapter->ahw; +	u32 msix_vector;  	/* Reset host resources */  	tx->producer = 0; @@ -1182,13 +1221,21 @@ int qlcnic_83xx_create_tx_ctx(struct qlcnic_adapter *adapter,  	memset(&mbx, 0, sizeof(struct qlcnic_tx_mbx));  	/* setup mailbox inbox registerss */ -	mbx.phys_addr = tx->phys_addr; -	mbx.cnsmr_index = tx->hw_cons_phys_addr; +	mbx.phys_addr_low = LSD(tx->phys_addr); +	mbx.phys_addr_high = MSD(tx->phys_addr); +	mbx.cnsmr_index_low = LSD(tx->hw_cons_phys_addr); +	mbx.cnsmr_index_high = MSD(tx->hw_cons_phys_addr);  	mbx.size = tx->num_desc; -	if (adapter->flags & QLCNIC_MSIX_ENABLED) -		msix_id = ahw->intr_tbl[adapter->max_sds_rings + ring].id; -	else +	if (adapter->flags & QLCNIC_MSIX_ENABLED) { +		if (!(adapter->flags & QLCNIC_TX_INTR_SHARED)) +			msix_vector = adapter->max_sds_rings + ring; +		else +			msix_vector = adapter->max_sds_rings - 1; +		msix_id = ahw->intr_tbl[msix_vector].id; +	} else {  		msix_id = QLCRDX(ahw, QLCNIC_DEF_INT_ID); +	} +  	if (adapter->ahw->diag_test != QLCNIC_LOOPBACK_TEST)  		mbx.intr_id = msix_id;  	else @@ -1196,8 +1243,15 @@ int qlcnic_83xx_create_tx_ctx(struct qlcnic_adapter *adapter,  	mbx.src = 0;  	qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CREATE_TX_CTX); + +	if (qlcnic_sriov_pf_check(adapter) || qlcnic_sriov_vf_check(adapter)) +		cmd.req.arg[0] |= (0x3 << 29); + +	if (qlcnic_sriov_pf_check(adapter)) +		qlcnic_pf_set_interface_id_create_tx_ctx(adapter, &temp); +  	cmd.req.arg[1] = QLCNIC_CAP0_LEGACY_CONTEXT; -	cmd.req.arg[5] = QLCNIC_MAX_TX_QUEUES; +	cmd.req.arg[5] = QLCNIC_MAX_TX_QUEUES | temp;  	buf = &cmd.req.arg[6];  	memcpy(buf, &mbx, sizeof(struct qlcnic_tx_mbx));  	/* send the mailbox command*/ @@ -1210,7 +1264,8 @@ int qlcnic_83xx_create_tx_ctx(struct qlcnic_adapter *adapter,  	mbx_out = (struct qlcnic_tx_mbx_out *)&cmd.rsp.arg[2];  	tx->crb_cmd_producer = ahw->pci_base0 + mbx_out->host_prod;  	tx->ctx_id = mbx_out->ctx_id; -	if (adapter->flags & QLCNIC_MSIX_ENABLED) { +	if ((adapter->flags & QLCNIC_MSIX_ENABLED) && +	    !(adapter->flags & QLCNIC_TX_INTR_SHARED)) {  		intr_mask = ahw->intr_tbl[adapter->max_sds_rings + ring].src;  		tx->crb_intr_mask = ahw->pci_base0 + intr_mask;  	} @@ -1267,7 +1322,8 @@ static int qlcnic_83xx_diag_alloc_res(struct net_device *netdev, int test)  	if (adapter->ahw->diag_test == QLCNIC_LOOPBACK_TEST) {  		/* disable and free mailbox interrupt */ -		qlcnic_83xx_free_mbx_intr(adapter); +		if (!(adapter->flags & QLCNIC_MSIX_ENABLED)) +			qlcnic_83xx_free_mbx_intr(adapter);  		adapter->ahw->loopback_state = 0;  		adapter->ahw->hw_ops->setup_link_event(adapter, 1);  	} @@ -1295,12 +1351,14 @@ static void qlcnic_83xx_diag_free_res(struct net_device *netdev,  	qlcnic_detach(adapter);  	if (adapter->ahw->diag_test == QLCNIC_LOOPBACK_TEST) { -		err = qlcnic_83xx_setup_mbx_intr(adapter); -		if (err) { -			dev_err(&adapter->pdev->dev, -				"%s: failed to setup mbx interrupt\n", -				__func__); -			goto out; +		if (!(adapter->flags & QLCNIC_MSIX_ENABLED)) { +			err = qlcnic_83xx_setup_mbx_intr(adapter); +			if (err) { +				dev_err(&adapter->pdev->dev, +					"%s: failed to setup mbx interrupt\n", +					__func__); +				goto out; +			}  		}  	}  	adapter->ahw->diag_test = 0; @@ -1373,12 +1431,60 @@ mbx_err:  	}  } +int  qlcnic_83xx_set_led(struct net_device *netdev, +			 enum ethtool_phys_id_state state) +{ +	struct qlcnic_adapter *adapter = netdev_priv(netdev); +	int err = -EIO, active = 1; + +	if (adapter->ahw->op_mode == QLCNIC_NON_PRIV_FUNC) { +		netdev_warn(netdev, +			    "LED test is not supported in non-privileged mode\n"); +		return -EOPNOTSUPP; +	} + +	switch (state) { +	case ETHTOOL_ID_ACTIVE: +		if (test_and_set_bit(__QLCNIC_LED_ENABLE, &adapter->state)) +			return -EBUSY; + +		if (test_bit(__QLCNIC_RESETTING, &adapter->state)) +			break; + +		err = qlcnic_83xx_config_led(adapter, active, 0); +		if (err) +			netdev_err(netdev, "Failed to set LED blink state\n"); +		break; +	case ETHTOOL_ID_INACTIVE: +		active = 0; + +		if (test_bit(__QLCNIC_RESETTING, &adapter->state)) +			break; + +		err = qlcnic_83xx_config_led(adapter, active, 0); +		if (err) +			netdev_err(netdev, "Failed to reset LED blink state\n"); +		break; + +	default: +		return -EINVAL; +	} + +	if (!active || err) +		clear_bit(__QLCNIC_LED_ENABLE, &adapter->state); + +	return err; +} +  void qlcnic_83xx_register_nic_idc_func(struct qlcnic_adapter *adapter,  				       int enable)  {  	struct qlcnic_cmd_args cmd;  	int status; +	if (qlcnic_sriov_vf_check(adapter)) +		return; +  	if (enable) {  		qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_INIT_NIC_FUNC);  		cmd.req.arg[1] = BIT_0 | BIT_31; @@ -1441,24 +1547,35 @@ int qlcnic_83xx_setup_link_event(struct qlcnic_adapter *adapter, int enable)  	return err;  } +static void qlcnic_83xx_set_interface_id_promisc(struct qlcnic_adapter *adapter, +						 u32 *interface_id) +{ +	if (qlcnic_sriov_pf_check(adapter)) { +		qlcnic_pf_set_interface_id_promisc(adapter, interface_id); +	} else { +		if (!qlcnic_sriov_vf_check(adapter)) +			*interface_id = adapter->recv_ctx->context_id << 16; +	} +} +  int qlcnic_83xx_nic_set_promisc(struct qlcnic_adapter *adapter, u32 mode)  {  	int err; -	u32 temp; +	u32 temp = 0;  	struct qlcnic_cmd_args cmd;  	if (adapter->recv_ctx->state == QLCNIC_HOST_CTX_STATE_FREED)  		return -EIO;  	qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CONFIGURE_MAC_RX_MODE); -	temp = adapter->recv_ctx->context_id << 16; +	qlcnic_83xx_set_interface_id_promisc(adapter, &temp);  	cmd.req.arg[1] = (mode ? 1 : 0) | temp;  	err = qlcnic_issue_cmd(adapter, &cmd);  	if (err)  		dev_info(&adapter->pdev->dev,  			 "Promiscous mode config failed\n"); -	qlcnic_free_mbx_args(&cmd); +	qlcnic_free_mbx_args(&cmd);  	return err;  } @@ -1490,7 +1607,9 @@ int qlcnic_83xx_loopback_test(struct net_device *netdev, u8 mode)  	/* Poll for link up event before running traffic */  	do {  		msleep(500); -		qlcnic_83xx_process_aen(adapter); +		if (!(adapter->flags & QLCNIC_MSIX_ENABLED)) +			qlcnic_83xx_process_aen(adapter); +  		if (loop++ > QLCNIC_ILB_MAX_RCV_LOOP) {  			dev_info(&adapter->pdev->dev,  				 "Firmware didn't sent link up event to loopback request\n"); @@ -1550,7 +1669,9 @@ int qlcnic_83xx_set_lb_mode(struct qlcnic_adapter *adapter, u8 mode)  	/* Wait for Link and IDC Completion AEN */  	do {  		msleep(300); -		qlcnic_83xx_process_aen(adapter); +		if (!(adapter->flags & QLCNIC_MSIX_ENABLED)) +			qlcnic_83xx_process_aen(adapter); +  		if (loop++ > QLCNIC_ILB_MAX_RCV_LOOP) {  			dev_err(&adapter->pdev->dev,  				"FW did not generate IDC completion AEN\n"); @@ -1590,7 +1711,9 @@ int qlcnic_83xx_clear_lb_mode(struct qlcnic_adapter *adapter, u8 mode)  	/* Wait for Link and IDC Completion AEN */  	do {  		msleep(300); -		qlcnic_83xx_process_aen(adapter); +		if (!(adapter->flags & QLCNIC_MSIX_ENABLED)) +			qlcnic_83xx_process_aen(adapter); +  		if (loop++ > QLCNIC_ILB_MAX_RCV_LOOP) {  			dev_err(&adapter->pdev->dev,  				"Firmware didn't sent IDC completion AEN\n"); @@ -1604,21 +1727,31 @@ int qlcnic_83xx_clear_lb_mode(struct qlcnic_adapter *adapter, u8 mode)  	return status;  } +static void qlcnic_83xx_set_interface_id_ipaddr(struct qlcnic_adapter *adapter, +						u32 *interface_id) +{ +	if (qlcnic_sriov_pf_check(adapter)) { +		qlcnic_pf_set_interface_id_ipaddr(adapter, interface_id); +	} else { +		if (!qlcnic_sriov_vf_check(adapter)) +			*interface_id = adapter->recv_ctx->context_id << 16; +	} +} +  void qlcnic_83xx_config_ipaddr(struct qlcnic_adapter *adapter, __be32 ip,  			       int mode)  {  	int err; -	u32 temp, temp_ip; +	u32 temp = 0, temp_ip;  	struct qlcnic_cmd_args cmd;  	qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CONFIGURE_IP_ADDR); -	if (mode == QLCNIC_IP_UP) { -		temp = adapter->recv_ctx->context_id << 16; +	qlcnic_83xx_set_interface_id_ipaddr(adapter, &temp); + +	if (mode == QLCNIC_IP_UP)  		cmd.req.arg[1] = 1 | temp; -	} else { -		temp = adapter->recv_ctx->context_id << 16; +	else  		cmd.req.arg[1] = 2 | temp; -	}  	/*  	 * Adapter needs IP address in network byte order. @@ -1635,6 +1768,7 @@ void qlcnic_83xx_config_ipaddr(struct qlcnic_adapter *adapter, __be32 ip,  		dev_err(&adapter->netdev->dev,  			"could not notify %s IP 0x%x request\n",  			(mode == QLCNIC_IP_UP) ? "Add" : "Remove", ip); +  	qlcnic_free_mbx_args(&cmd);  } @@ -1701,11 +1835,22 @@ int qlcnic_83xx_config_rss(struct qlcnic_adapter *adapter, int enable)  } +static void qlcnic_83xx_set_interface_id_macaddr(struct qlcnic_adapter *adapter, +						 u32 *interface_id) +{ +	if (qlcnic_sriov_pf_check(adapter)) { +		qlcnic_pf_set_interface_id_macaddr(adapter, interface_id); +	} else { +		if (!qlcnic_sriov_vf_check(adapter)) +			*interface_id = adapter->recv_ctx->context_id << 16; +	} +} +  int qlcnic_83xx_sre_macaddr_change(struct qlcnic_adapter *adapter, u8 *addr, -				   __le16 vlan_id, u8 op) +				   u16 vlan_id, u8 op)  {  	int err; -	u32 *buf; +	u32 *buf, temp = 0;  	struct qlcnic_cmd_args cmd;  	struct qlcnic_macvlan_mbx mv; @@ -1715,11 +1860,21 @@ int qlcnic_83xx_sre_macaddr_change(struct qlcnic_adapter *adapter, u8 *addr,  	err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CONFIG_MAC_VLAN);  	if (err)  		return err; -	cmd.req.arg[1] = op | (1 << 8) | -			(adapter->recv_ctx->context_id << 16); -	mv.vlan = le16_to_cpu(vlan_id); -	memcpy(&mv.mac, addr, ETH_ALEN); +	if (vlan_id) +		op = (op == QLCNIC_MAC_ADD || op == QLCNIC_MAC_VLAN_ADD) ? +		     QLCNIC_MAC_VLAN_ADD : QLCNIC_MAC_VLAN_DEL; + +	cmd.req.arg[1] = op | (1 << 8); +	qlcnic_83xx_set_interface_id_macaddr(adapter, &temp); +	cmd.req.arg[1] |= temp; +	mv.vlan = vlan_id; +	mv.mac_addr0 = addr[0]; +	mv.mac_addr1 = addr[1]; +	mv.mac_addr2 = addr[2]; +	mv.mac_addr3 = addr[3]; +	mv.mac_addr4 = addr[4]; +	mv.mac_addr5 = addr[5];  	buf = &cmd.req.arg[2];  	memcpy(buf, &mv, sizeof(struct qlcnic_macvlan_mbx));  	err = qlcnic_issue_cmd(adapter, &cmd); @@ -1732,7 +1887,7 @@ int qlcnic_83xx_sre_macaddr_change(struct qlcnic_adapter *adapter, u8 *addr,  }  void qlcnic_83xx_change_l2_filter(struct qlcnic_adapter *adapter, u64 *addr, -				  __le16 vlan_id) +				  u16 vlan_id)  {  	u8 mac[ETH_ALEN];  	memcpy(&mac, addr, ETH_ALEN); @@ -1832,7 +1987,7 @@ irqreturn_t qlcnic_83xx_handle_aen(int irq, void *data)  	event = readl(QLCNIC_MBX_FW(adapter->ahw, 0));  	if (event &  QLCNIC_MBX_ASYNC_EVENT) -		qlcnic_83xx_process_aen(adapter); +		__qlcnic_83xx_process_aen(adapter);  out:  	mask = QLCRDX(adapter->ahw, QLCNIC_DEF_INT_MASK);  	writel(0, adapter->ahw->pci_base0 + mask); @@ -2008,14 +2163,17 @@ int qlcnic_83xx_get_pci_info(struct qlcnic_adapter *adapter,  int qlcnic_83xx_config_intrpt(struct qlcnic_adapter *adapter, bool op_type)  {  	int i, index, err; -	bool type;  	u8 max_ints; -	u32 val, temp; +	u32 val, temp, type;  	struct qlcnic_cmd_args cmd;  	max_ints = adapter->ahw->num_msix - 1;  	qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CONFIG_INTRPT);  	cmd.req.arg[1] = max_ints; + +	if (qlcnic_sriov_vf_check(adapter)) +		cmd.req.arg[1] |= (adapter->ahw->pci_func << 8) | BIT_16; +  	for (i = 0, index = 2; i < max_ints; i++) {  		type = op_type ? QLCNIC_INTRPT_ADD : QLCNIC_INTRPT_DEL;  		val = type | (adapter->ahw->intr_tbl[i].type << 4); @@ -2169,7 +2327,7 @@ static int qlcnic_83xx_poll_flash_status_reg(struct qlcnic_adapter *adapter)  	return 0;  } -static int qlcnic_83xx_enable_flash_write_op(struct qlcnic_adapter *adapter) +int qlcnic_83xx_enable_flash_write(struct qlcnic_adapter *adapter)  {  	int ret;  	u32 cmd; @@ -2187,7 +2345,7 @@ static int qlcnic_83xx_enable_flash_write_op(struct qlcnic_adapter *adapter)  	return 0;  } -static int qlcnic_83xx_disable_flash_write_op(struct qlcnic_adapter *adapter) +int qlcnic_83xx_disable_flash_write(struct qlcnic_adapter *adapter)  {  	int ret; @@ -2261,7 +2419,7 @@ int qlcnic_83xx_erase_flash_sector(struct qlcnic_adapter *adapter,  		return -EIO;  	if (adapter->ahw->fdt.mfg_id == adapter->flash_mfg_id) { -		ret = qlcnic_83xx_enable_flash_write_op(adapter); +		ret = qlcnic_83xx_enable_flash_write(adapter);  		if (ret) {  			qlcnic_83xx_unlock_flash(adapter);  			dev_err(&adapter->pdev->dev, @@ -2303,7 +2461,7 @@ int qlcnic_83xx_erase_flash_sector(struct qlcnic_adapter *adapter,  	}  	if (adapter->ahw->fdt.mfg_id == adapter->flash_mfg_id) { -		ret = qlcnic_83xx_disable_flash_write_op(adapter); +		ret = qlcnic_83xx_disable_flash_write(adapter);  		if (ret) {  			qlcnic_83xx_unlock_flash(adapter);  			dev_err(&adapter->pdev->dev, @@ -2343,8 +2501,8 @@ int qlcnic_83xx_flash_bulk_write(struct qlcnic_adapter *adapter, u32 addr,  	u32 temp;  	int ret = -EIO; -	if ((count < QLC_83XX_FLASH_BULK_WRITE_MIN) || -	    (count > QLC_83XX_FLASH_BULK_WRITE_MAX)) { +	if ((count < QLC_83XX_FLASH_WRITE_MIN) || +	    (count > QLC_83XX_FLASH_WRITE_MAX)) {  		dev_err(&adapter->pdev->dev,  			"%s: Invalid word count\n", __func__);  		return -EIO; @@ -2622,13 +2780,19 @@ int qlcnic_83xx_flash_read32(struct qlcnic_adapter *adapter, u32 flash_addr,  int qlcnic_83xx_test_link(struct qlcnic_adapter *adapter)  { +	u8 pci_func;  	int err;  	u32 config = 0, state;  	struct qlcnic_cmd_args cmd;  	struct qlcnic_hardware_context *ahw = adapter->ahw; -	state = readl(ahw->pci_base0 + QLC_83XX_LINK_STATE(ahw->pci_func)); -	if (!QLC_83xx_FUNC_VAL(state, ahw->pci_func)) { +	if (qlcnic_sriov_vf_check(adapter)) +		pci_func = adapter->portnum; +	else +		pci_func = ahw->pci_func; + +	state = readl(ahw->pci_base0 + QLC_83XX_LINK_STATE(pci_func)); +	if (!QLC_83xx_FUNC_VAL(state, pci_func)) {  		dev_info(&adapter->pdev->dev, "link state down\n");  		return config;  	} diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.h index 61f81f6c84a..4be411c2628 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.h +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.h @@ -12,6 +12,8 @@  #include <linux/etherdevice.h>  #include "qlcnic_hw.h" +#define QLCNIC_83XX_BAR0_LENGTH 0x4000 +  /* Directly mapped registers */  #define QLC_83XX_CRB_WIN_BASE		0x3800  #define QLC_83XX_CRB_WIN_FUNC(f)	(QLC_83XX_CRB_WIN_BASE+((f)*4)) @@ -86,6 +88,153 @@  #define QLC_83XX_MAX_RESET_SEQ_ENTRIES	16 +/* status descriptor mailbox data + * @phy_addr_{low|high}: physical address of buffer + * @sds_ring_size: buffer size + * @intrpt_id: interrupt id + * @intrpt_val: source of interrupt + */ +struct qlcnic_sds_mbx { +	u32	phy_addr_low; +	u32	phy_addr_high; +	u32	rsvd1[4]; +#if defined(__LITTLE_ENDIAN) +	u16	sds_ring_size; +	u16	rsvd2; +	u16	rsvd3[2]; +	u16	intrpt_id; +	u8	intrpt_val; +	u8	rsvd4; +#elif defined(__BIG_ENDIAN) +	u16	rsvd2; +	u16	sds_ring_size; +	u16	rsvd3[2]; +	u8	rsvd4; +	u8	intrpt_val; +	u16	intrpt_id; +#endif +	u32	rsvd5; +} __packed; + +/* receive descriptor buffer data + * phy_addr_reg_{low|high}: physical address of regular buffer + * phy_addr_jmb_{low|high}: physical address of jumbo buffer + * reg_ring_sz: size of regular buffer + * reg_ring_len: no. of entries in regular buffer + * jmb_ring_len: no. of entries in jumbo buffer + * jmb_ring_sz: size of jumbo buffer + */ +struct qlcnic_rds_mbx { +	u32	phy_addr_reg_low; +	u32	phy_addr_reg_high; +	u32	phy_addr_jmb_low; +	u32	phy_addr_jmb_high; +#if defined(__LITTLE_ENDIAN) +	u16	reg_ring_sz; +	u16	reg_ring_len; +	u16	jmb_ring_sz; +	u16	jmb_ring_len; +#elif defined(__BIG_ENDIAN) +	u16	reg_ring_len; +	u16	reg_ring_sz; +	u16	jmb_ring_len; +	u16	jmb_ring_sz; +#endif +} __packed; + +/* host producers for regular and jumbo rings */ +struct __host_producer_mbx { +	u32	reg_buf; +	u32	jmb_buf; +} __packed; + +/* Receive context mailbox data outbox registers + * @state: state of the context + * @vport_id: virtual port id + * @context_id: receive context id + * @num_pci_func: number of pci functions of the port + * @phy_port: physical port id + */ +struct qlcnic_rcv_mbx_out { +#if defined(__LITTLE_ENDIAN) +	u8	rcv_num; +	u8	sts_num; +	u16	ctx_id; +	u8	state; +	u8	num_pci_func; +	u8	phy_port; +	u8	vport_id; +#elif defined(__BIG_ENDIAN) +	u16	ctx_id; +	u8	sts_num; +	u8	rcv_num; +	u8	vport_id; +	u8	phy_port; +	u8	num_pci_func; +	u8	state; +#endif +	u32	host_csmr[QLCNIC_MAX_RING_SETS]; +	struct __host_producer_mbx host_prod[QLCNIC_MAX_RING_SETS]; +} __packed; + +struct qlcnic_add_rings_mbx_out { +#if defined(__LITTLE_ENDIAN) +	u8      rcv_num; +	u8      sts_num; +	u16	ctx_id; +#elif defined(__BIG_ENDIAN) +	u16	ctx_id; +	u8	sts_num; +	u8	rcv_num; +#endif +	u32  host_csmr[QLCNIC_MAX_RING_SETS]; +	struct __host_producer_mbx host_prod[QLCNIC_MAX_RING_SETS]; +} __packed; + +/* Transmit context mailbox inbox registers + * @phys_addr_{low|high}: DMA address of the transmit buffer + * @cnsmr_index_{low|high}: host consumer index + * @size: legth of transmit buffer ring + * @intr_id: interrput id + * @src: src of interrupt + */ +struct qlcnic_tx_mbx { +	u32	phys_addr_low; +	u32	phys_addr_high; +	u32	cnsmr_index_low; +	u32	cnsmr_index_high; +#if defined(__LITTLE_ENDIAN) +	u16	size; +	u16	intr_id; +	u8	src; +	u8	rsvd[3]; +#elif defined(__BIG_ENDIAN) +	u16	intr_id; +	u16	size; +	u8	rsvd[3]; +	u8	src; +#endif +} __packed; + +/* Transmit context mailbox outbox registers + * @host_prod: host producer index + * @ctx_id: transmit context id + * @state: state of the transmit context + */ + +struct qlcnic_tx_mbx_out { +	u32	host_prod; +#if defined(__LITTLE_ENDIAN) +	u16	ctx_id; +	u8	state; +	u8	rsvd; +#elif defined(__BIG_ENDIAN) +	u8	rsvd; +	u8	state; +	u16	ctx_id; +#endif +} __packed; +  struct qlcnic_intrpt_config {  	u8	type;  	u8	enabled; @@ -94,8 +243,23 @@ struct qlcnic_intrpt_config {  };  struct qlcnic_macvlan_mbx { -	u8	mac[ETH_ALEN]; +#if defined(__LITTLE_ENDIAN) +	u8	mac_addr0; +	u8	mac_addr1; +	u8	mac_addr2; +	u8	mac_addr3; +	u8	mac_addr4; +	u8	mac_addr5; +	u16	vlan; +#elif defined(__BIG_ENDIAN) +	u8	mac_addr3; +	u8	mac_addr2; +	u8	mac_addr1; +	u8	mac_addr0;  	u16	vlan; +	u8	mac_addr5; +	u8	mac_addr4; +#endif  };  struct qlc_83xx_fw_info { @@ -153,6 +317,18 @@ struct qlc_83xx_idc {  	char		**name;  }; +/* Device States */ +enum qlcnic_83xx_states { +	QLC_83XX_IDC_DEV_UNKNOWN, +	QLC_83XX_IDC_DEV_COLD, +	QLC_83XX_IDC_DEV_INIT, +	QLC_83XX_IDC_DEV_READY, +	QLC_83XX_IDC_DEV_NEED_RESET, +	QLC_83XX_IDC_DEV_NEED_QUISCENT, +	QLC_83XX_IDC_DEV_FAILED, +	QLC_83XX_IDC_DEV_QUISCENT +}; +  #define QLCNIC_MBX_RSP(reg)		LSW(reg)  #define QLCNIC_MBX_NUM_REGS(reg)	(MSW(reg) & 0x1FF)  #define QLCNIC_MBX_STATUS(reg)		(((reg) >> 25) & 0x7F) @@ -226,6 +402,7 @@ struct qlc_83xx_idc {  #define QLC_83XX_GET_FW_LRO_MSS_CAPABILITY(val)	(val & 0x20000)  #define QLC_83XX_VIRTUAL_NIC_MODE			0xFF  #define QLC_83XX_DEFAULT_MODE				0x0 +#define QLC_83XX_SRIOV_MODE				0x1  #define QLCNIC_BRDTYPE_83XX_10G			0x0083  #define QLC_83XX_FLASH_SPI_STATUS		0x2808E010 @@ -242,8 +419,8 @@ struct qlc_83xx_idc {  #define QLC_83XX_FLASH_BULK_WRITE_CMD		0xcadcadca  #define QLC_83XX_FLASH_READ_RETRY_COUNT	5000  #define QLC_83XX_FLASH_STATUS_READY		0x6 -#define QLC_83XX_FLASH_BULK_WRITE_MIN		2 -#define QLC_83XX_FLASH_BULK_WRITE_MAX		64 +#define QLC_83XX_FLASH_WRITE_MIN		2 +#define QLC_83XX_FLASH_WRITE_MAX		64  #define QLC_83XX_FLASH_STATUS_REG_POLL_DELAY	1  #define QLC_83XX_ERASE_MODE			1  #define QLC_83XX_WRITE_MODE			2 @@ -336,7 +513,7 @@ int qlcnic_83xx_clear_lb_mode(struct qlcnic_adapter *, u8);  int qlcnic_83xx_config_hw_lro(struct qlcnic_adapter *, int);  int qlcnic_83xx_config_rss(struct qlcnic_adapter *, int);  int qlcnic_83xx_config_intr_coalesce(struct qlcnic_adapter *); -void qlcnic_83xx_change_l2_filter(struct qlcnic_adapter *, u64 *, __le16); +void qlcnic_83xx_change_l2_filter(struct qlcnic_adapter *, u64 *, u16);  int qlcnic_83xx_get_pci_info(struct qlcnic_adapter *, struct qlcnic_pci_info *);  int qlcnic_83xx_set_nic_info(struct qlcnic_adapter *, struct qlcnic_info *);  void qlcnic_83xx_register_nic_idc_func(struct qlcnic_adapter *, int); @@ -351,11 +528,14 @@ int qlcnic_ind_rd(struct qlcnic_adapter *, u32);  int qlcnic_83xx_create_rx_ctx(struct qlcnic_adapter *);  int qlcnic_83xx_create_tx_ctx(struct qlcnic_adapter *,  			      struct qlcnic_host_tx_ring *, int); +void qlcnic_83xx_del_rx_ctx(struct qlcnic_adapter *); +void qlcnic_83xx_del_tx_ctx(struct qlcnic_adapter *, +			    struct qlcnic_host_tx_ring *);  int qlcnic_83xx_get_nic_info(struct qlcnic_adapter *, struct qlcnic_info *, u8);  int qlcnic_83xx_setup_link_event(struct qlcnic_adapter *, int);  void qlcnic_83xx_process_rcv_ring_diag(struct qlcnic_host_sds_ring *);  int qlcnic_83xx_config_intrpt(struct qlcnic_adapter *, bool); -int qlcnic_83xx_sre_macaddr_change(struct qlcnic_adapter *, u8 *, __le16, u8); +int qlcnic_83xx_sre_macaddr_change(struct qlcnic_adapter *, u8 *, u16, u8);  int qlcnic_83xx_get_mac_address(struct qlcnic_adapter *, u8 *);  void qlcnic_83xx_configure_mac(struct qlcnic_adapter *, u8 *, u8,  			       struct qlcnic_cmd_args *); @@ -368,6 +548,7 @@ void qlcnic_83xx_config_intr_coal(struct qlcnic_adapter *);  irqreturn_t qlcnic_83xx_handle_aen(int, void *);  int qlcnic_83xx_get_port_info(struct qlcnic_adapter *);  void qlcnic_83xx_enable_mbx_intrpt(struct qlcnic_adapter *); +void qlcnic_83xx_disable_mbx_intr(struct qlcnic_adapter *);  irqreturn_t qlcnic_83xx_clear_legacy_intr(struct qlcnic_adapter *);  irqreturn_t qlcnic_83xx_intr(int, void *);  irqreturn_t qlcnic_83xx_tmp_intr(int, void *); @@ -377,7 +558,7 @@ void qlcnic_83xx_disable_intr(struct qlcnic_adapter *,  			     struct qlcnic_host_sds_ring *);  void qlcnic_83xx_check_vf(struct qlcnic_adapter *,  			  const struct pci_device_id *); -void qlcnic_83xx_process_aen(struct qlcnic_adapter *); +void __qlcnic_83xx_process_aen(struct qlcnic_adapter *);  int qlcnic_83xx_get_port_config(struct qlcnic_adapter *);  int qlcnic_83xx_set_port_config(struct qlcnic_adapter *);  int qlcnic_enable_eswitch(struct qlcnic_adapter *, u8, u8); @@ -401,7 +582,7 @@ int qlcnic_83xx_read_flash_descriptor_table(struct qlcnic_adapter *);  int qlcnic_83xx_flash_read32(struct qlcnic_adapter *, u32, u8 *, int);  int qlcnic_83xx_lockless_flash_read32(struct qlcnic_adapter *,  				      u32, u8 *, int); -int qlcnic_83xx_init(struct qlcnic_adapter *); +int qlcnic_83xx_init(struct qlcnic_adapter *, int);  int qlcnic_83xx_idc_ready_state_entry(struct qlcnic_adapter *);  int qlcnic_83xx_check_hw_status(struct qlcnic_adapter *p_dev);  void qlcnic_83xx_idc_poll_dev_state(struct work_struct *); @@ -434,5 +615,12 @@ int qlcnic_83xx_get_regs_len(struct qlcnic_adapter *);  int qlcnic_83xx_get_registers(struct qlcnic_adapter *, u32 *);  int qlcnic_83xx_loopback_test(struct net_device *, u8);  int qlcnic_83xx_interrupt_test(struct net_device *); +int qlcnic_83xx_set_led(struct net_device *, enum ethtool_phys_id_state);  int qlcnic_83xx_flash_test(struct qlcnic_adapter *); +int qlcnic_83xx_enable_flash_write(struct qlcnic_adapter *); +int qlcnic_83xx_disable_flash_write(struct qlcnic_adapter *); +u32 qlcnic_83xx_mac_rcode(struct qlcnic_adapter *); +u32 qlcnic_83xx_mbx_poll(struct qlcnic_adapter *); +void qlcnic_83xx_enable_mbx_poll(struct qlcnic_adapter *); +void qlcnic_83xx_disable_mbx_poll(struct qlcnic_adapter *);  #endif diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_init.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_init.c index 5c033f268ca..6ea3a096054 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_init.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_init.c @@ -5,6 +5,7 @@   * See LICENSE.qlcnic for copyright and licensing details.   */ +#include "qlcnic_sriov.h"  #include "qlcnic.h"  #include "qlcnic_hw.h" @@ -25,12 +26,12 @@  #define QLC_83XX_OPCODE_POLL_READ_LIST		0x0100  static int qlcnic_83xx_init_default_driver(struct qlcnic_adapter *adapter); -static int qlcnic_83xx_configure_opmode(struct qlcnic_adapter *adapter);  static int qlcnic_83xx_check_heartbeat(struct qlcnic_adapter *p_dev);  static int qlcnic_83xx_restart_hw(struct qlcnic_adapter *adapter);  /* Template header */  struct qlc_83xx_reset_hdr { +#if defined(__LITTLE_ENDIAN)  	u16	version;  	u16	signature;  	u16	size; @@ -39,14 +40,31 @@ struct qlc_83xx_reset_hdr {  	u16	checksum;  	u16	init_offset;  	u16	start_offset; +#elif defined(__BIG_ENDIAN) +	u16	signature; +	u16	version; +	u16	entries; +	u16	size; +	u16	checksum; +	u16	hdr_size; +	u16	start_offset; +	u16	init_offset; +#endif  } __packed;  /* Command entry header. */  struct qlc_83xx_entry_hdr { -	u16 cmd; -	u16 size; -	u16 count; -	u16 delay; +#if defined(__LITTLE_ENDIAN) +	u16	cmd; +	u16	size; +	u16	count; +	u16	delay; +#elif defined(__BIG_ENDIAN) +	u16	size; +	u16	cmd; +	u16	delay; +	u16	count; +#endif  } __packed;  /* Generic poll command */ @@ -60,10 +78,17 @@ struct qlc_83xx_rmw {  	u32	mask;  	u32	xor_value;  	u32	or_value; +#if defined(__LITTLE_ENDIAN)  	u8	shl;  	u8	shr;  	u8	index_a;  	u8	rsvd; +#elif defined(__BIG_ENDIAN) +	u8	rsvd; +	u8	index_a; +	u8	shr; +	u8	shl; +#endif  } __packed;  /* Generic command with 2 DWORD */ @@ -90,18 +115,6 @@ static const char *const qlc_83xx_idc_states[] = {  	"Quiesce"  }; -/* Device States */ -enum qlcnic_83xx_states { -	QLC_83XX_IDC_DEV_UNKNOWN, -	QLC_83XX_IDC_DEV_COLD, -	QLC_83XX_IDC_DEV_INIT, -	QLC_83XX_IDC_DEV_READY, -	QLC_83XX_IDC_DEV_NEED_RESET, -	QLC_83XX_IDC_DEV_NEED_QUISCENT, -	QLC_83XX_IDC_DEV_FAILED, -	QLC_83XX_IDC_DEV_QUISCENT -}; -  static int  qlcnic_83xx_idc_check_driver_presence_reg(struct qlcnic_adapter *adapter)  { @@ -137,7 +150,8 @@ static int qlcnic_83xx_idc_update_audit_reg(struct qlcnic_adapter *adapter,  			return -EBUSY;  	} -	val = adapter->portnum & 0xf; +	val = QLCRDX(adapter->ahw, QLC_83XX_IDC_DRV_AUDIT); +	val |= (adapter->portnum & 0xf);  	val |= mode << 7;  	if (mode)  		seconds = jiffies / HZ - adapter->ahw->idc.sec_counter; @@ -376,14 +390,18 @@ static void qlcnic_83xx_idc_detach_driver(struct qlcnic_adapter *adapter)  	struct net_device *netdev = adapter->netdev;  	netif_device_detach(netdev); +  	/* Disable mailbox interrupt */ -	QLCWRX(adapter->ahw, QLCNIC_MBX_INTR_ENBL, 0); +	qlcnic_83xx_disable_mbx_intr(adapter);  	qlcnic_down(adapter, netdev);  	for (i = 0; i < adapter->ahw->num_msix; i++) {  		adapter->ahw->intr_tbl[i].id = i;  		adapter->ahw->intr_tbl[i].enabled = 0;  		adapter->ahw->intr_tbl[i].src = 0;  	} + +	if (qlcnic_sriov_pf_check(adapter)) +		qlcnic_sriov_pf_reset(adapter);  }  /** @@ -585,9 +603,15 @@ static int qlcnic_83xx_idc_check_fan_failure(struct qlcnic_adapter *adapter)  static int qlcnic_83xx_idc_reattach_driver(struct qlcnic_adapter *adapter)  { +	int err; +  	/* register for NIC IDC AEN Events */  	qlcnic_83xx_register_nic_idc_func(adapter, 1); +	err = qlcnic_sriov_pf_reinit(adapter); +	if (err) +		return err; +  	qlcnic_83xx_enable_mbx_intrpt(adapter);  	if (qlcnic_83xx_configure_opmode(adapter)) { @@ -1893,6 +1917,9 @@ int qlcnic_83xx_config_default_opmode(struct qlcnic_adapter *adapter)  	qlcnic_get_func_no(adapter);  	op_mode = QLCRDX(ahw, QLC_83XX_DRV_OP_MODE); +	if (test_bit(__QLCNIC_SRIOV_CAPABLE, &adapter->state)) +		op_mode = QLC_83XX_DEFAULT_OPMODE; +  	if (op_mode == QLC_83XX_DEFAULT_OPMODE) {  		adapter->nic_ops->init_driver = qlcnic_83xx_init_default_driver;  		ahw->idc.state_entry = qlcnic_83xx_idc_ready_state_entry; @@ -1922,6 +1949,16 @@ int qlcnic_83xx_get_nic_configuration(struct qlcnic_adapter *adapter)  	ahw->max_mac_filters = nic_info.max_mac_filters;  	ahw->max_mtu = nic_info.max_mtu; +	/* VNIC mode is detected by BIT_23 in capabilities. This bit is also +	 * set in case device is SRIOV capable. VNIC and SRIOV are mutually +	 * exclusive. So in case of sriov capable device load driver in +	 * default mode +	 */ +	if (test_bit(__QLCNIC_SRIOV_CAPABLE, &adapter->state)) { +		ahw->nic_mode = QLC_83XX_DEFAULT_MODE; +		return ahw->nic_mode; +	} +  	if (ahw->capabilities & BIT_23)  		ahw->nic_mode = QLC_83XX_VIRTUAL_NIC_MODE;  	else @@ -1930,7 +1967,7 @@ int qlcnic_83xx_get_nic_configuration(struct qlcnic_adapter *adapter)  	return ahw->nic_mode;  } -static int qlcnic_83xx_configure_opmode(struct qlcnic_adapter *adapter) +int qlcnic_83xx_configure_opmode(struct qlcnic_adapter *adapter)  {  	int ret; @@ -2008,10 +2045,13 @@ static void qlcnic_83xx_clear_function_resources(struct qlcnic_adapter *adapter)  	}  } -int qlcnic_83xx_init(struct qlcnic_adapter *adapter) +int qlcnic_83xx_init(struct qlcnic_adapter *adapter, int pci_using_dac)  {  	struct qlcnic_hardware_context *ahw = adapter->ahw; +	if (qlcnic_sriov_vf_check(adapter)) +		return qlcnic_sriov_vf_init(adapter, pci_using_dac); +  	if (qlcnic_83xx_check_hw_status(adapter))  		return -EIO; diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c index a69097c6b84..43562c25637 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ctx.c @@ -382,8 +382,7 @@ out_free_rq:  	return err;  } -static void -qlcnic_fw_cmd_destroy_rx_ctx(struct qlcnic_adapter *adapter) +void qlcnic_82xx_fw_cmd_del_rx_ctx(struct qlcnic_adapter *adapter)  {  	int err;  	struct qlcnic_cmd_args cmd; @@ -422,22 +421,20 @@ int qlcnic_82xx_fw_cmd_create_tx_ctx(struct qlcnic_adapter *adapter,  	rq_size = SIZEOF_HOSTRQ_TX(struct qlcnic_hostrq_tx_ctx);  	rq_addr = dma_alloc_coherent(&adapter->pdev->dev, rq_size, -			&rq_phys_addr, GFP_KERNEL); +				     &rq_phys_addr, GFP_KERNEL | __GFP_ZERO);  	if (!rq_addr)  		return -ENOMEM;  	rsp_size = SIZEOF_CARDRSP_TX(struct qlcnic_cardrsp_tx_ctx);  	rsp_addr = dma_alloc_coherent(&adapter->pdev->dev, rsp_size, -			&rsp_phys_addr, GFP_KERNEL); +				      &rsp_phys_addr, GFP_KERNEL | __GFP_ZERO);  	if (!rsp_addr) {  		err = -ENOMEM;  		goto out_free_rq;  	} -	memset(rq_addr, 0, rq_size);  	prq = rq_addr; -	memset(rsp_addr, 0, rsp_size);  	prsp = rsp_addr;  	prq->host_rsp_dma_addr = cpu_to_le64(rsp_phys_addr); @@ -486,13 +483,13 @@ out_free_rq:  	return err;  } -static void -qlcnic_fw_cmd_destroy_tx_ctx(struct qlcnic_adapter *adapter, -			     struct qlcnic_host_tx_ring *tx_ring) +void qlcnic_82xx_fw_cmd_del_tx_ctx(struct qlcnic_adapter *adapter, +				   struct qlcnic_host_tx_ring *tx_ring)  {  	struct qlcnic_cmd_args cmd;  	qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_DESTROY_TX_CTX); +  	cmd.req.arg[1] = tx_ring->ctx_id;  	if (qlcnic_issue_cmd(adapter, &cmd))  		dev_err(&adapter->pdev->dev, @@ -532,20 +529,15 @@ int qlcnic_alloc_hw_resources(struct qlcnic_adapter *adapter)  		ptr = (__le32 *)dma_alloc_coherent(&pdev->dev, sizeof(u32),  						   &tx_ring->hw_cons_phys_addr,  						   GFP_KERNEL); - -		if (ptr == NULL) { -			dev_err(&pdev->dev, "failed to allocate tx consumer\n"); +		if (ptr == NULL)  			return -ENOMEM; -		} +  		tx_ring->hw_consumer = ptr;  		/* cmd desc ring */  		addr = dma_alloc_coherent(&pdev->dev, TX_DESC_RINGSIZE(tx_ring),  					  &tx_ring->phys_addr,  					  GFP_KERNEL); -  		if (addr == NULL) { -			dev_err(&pdev->dev, -				"failed to allocate tx desc ring\n");  			err = -ENOMEM;  			goto err_out_free;  		} @@ -556,11 +548,9 @@ int qlcnic_alloc_hw_resources(struct qlcnic_adapter *adapter)  	for (ring = 0; ring < adapter->max_rds_rings; ring++) {  		rds_ring = &recv_ctx->rds_rings[ring];  		addr = dma_alloc_coherent(&adapter->pdev->dev, -				RCV_DESC_RINGSIZE(rds_ring), -				&rds_ring->phys_addr, GFP_KERNEL); +					  RCV_DESC_RINGSIZE(rds_ring), +					  &rds_ring->phys_addr, GFP_KERNEL);  		if (addr == NULL) { -			dev_err(&pdev->dev, -				"failed to allocate rds ring [%d]\n", ring);  			err = -ENOMEM;  			goto err_out_free;  		} @@ -572,11 +562,9 @@ int qlcnic_alloc_hw_resources(struct qlcnic_adapter *adapter)  		sds_ring = &recv_ctx->sds_rings[ring];  		addr = dma_alloc_coherent(&adapter->pdev->dev, -				STATUS_DESC_RINGSIZE(sds_ring), -				&sds_ring->phys_addr, GFP_KERNEL); +					  STATUS_DESC_RINGSIZE(sds_ring), +					  &sds_ring->phys_addr, GFP_KERNEL);  		if (addr == NULL) { -			dev_err(&pdev->dev, -				"failed to allocate sds ring [%d]\n", ring);  			err = -ENOMEM;  			goto err_out_free;  		} @@ -616,13 +604,12 @@ int qlcnic_fw_create_ctx(struct qlcnic_adapter *dev)  						  &dev->tx_ring[ring],  						  ring);  		if (err) { -			qlcnic_fw_cmd_destroy_rx_ctx(dev); +			qlcnic_fw_cmd_del_rx_ctx(dev);  			if (ring == 0)  				goto err_out;  			for (i = 0; i < ring; i++) -				qlcnic_fw_cmd_destroy_tx_ctx(dev, -							     &dev->tx_ring[i]); +				qlcnic_fw_cmd_del_tx_ctx(dev, &dev->tx_ring[i]);  			goto err_out;  		} @@ -644,10 +631,10 @@ void qlcnic_fw_destroy_ctx(struct qlcnic_adapter *adapter)  	int ring;  	if (test_and_clear_bit(__QLCNIC_FW_ATTACHED, &adapter->state)) { -		qlcnic_fw_cmd_destroy_rx_ctx(adapter); +		qlcnic_fw_cmd_del_rx_ctx(adapter);  		for (ring = 0; ring < adapter->max_drv_tx_rings; ring++) -			qlcnic_fw_cmd_destroy_tx_ctx(adapter, -						     &adapter->tx_ring[ring]); +			qlcnic_fw_cmd_del_tx_ctx(adapter, +						 &adapter->tx_ring[ring]);  		if (qlcnic_83xx_check(adapter) &&  		    (adapter->flags & QLCNIC_MSIX_ENABLED)) { @@ -655,7 +642,7 @@ void qlcnic_fw_destroy_ctx(struct qlcnic_adapter *adapter)  				qlcnic_83xx_config_intrpt(adapter, 0);  		}  		/* Allow dma queues to drain after context reset */ -		mdelay(20); +		msleep(20);  	}  } @@ -753,10 +740,9 @@ int qlcnic_82xx_get_nic_info(struct qlcnic_adapter *adapter,  	size_t  nic_size = sizeof(struct qlcnic_info_le);  	nic_info_addr = dma_alloc_coherent(&adapter->pdev->dev, nic_size, -				&nic_dma_t, GFP_KERNEL); +					   &nic_dma_t, GFP_KERNEL | __GFP_ZERO);  	if (!nic_info_addr)  		return -ENOMEM; -	memset(nic_info_addr, 0, nic_size);  	nic_info = nic_info_addr; @@ -804,11 +790,10 @@ int qlcnic_82xx_set_nic_info(struct qlcnic_adapter *adapter,  		return err;  	nic_info_addr = dma_alloc_coherent(&adapter->pdev->dev, nic_size, -			&nic_dma_t, GFP_KERNEL); +					   &nic_dma_t, GFP_KERNEL | __GFP_ZERO);  	if (!nic_info_addr)  		return -ENOMEM; -	memset(nic_info_addr, 0, nic_size);  	nic_info = nic_info_addr;  	nic_info->pci_func = cpu_to_le16(nic->pci_func); @@ -854,10 +839,10 @@ int qlcnic_82xx_get_pci_info(struct qlcnic_adapter *adapter,  	size_t pci_size = npar_size * QLCNIC_MAX_PCI_FUNC;  	pci_info_addr = dma_alloc_coherent(&adapter->pdev->dev, pci_size, -			&pci_info_dma_t, GFP_KERNEL); +					   &pci_info_dma_t, +					   GFP_KERNEL | __GFP_ZERO);  	if (!pci_info_addr)  		return -ENOMEM; -	memset(pci_info_addr, 0, pci_size);  	npar = pci_info_addr;  	qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_GET_PCI_INFO); @@ -949,12 +934,9 @@ int qlcnic_get_port_stats(struct qlcnic_adapter *adapter, const u8 func,  	}  	stats_addr = dma_alloc_coherent(&adapter->pdev->dev, stats_size, -			&stats_dma_t, GFP_KERNEL); -	if (!stats_addr) { -		dev_err(&adapter->pdev->dev, "Unable to allocate memory\n"); +					&stats_dma_t, GFP_KERNEL | __GFP_ZERO); +	if (!stats_addr)  		return -ENOMEM; -	} -	memset(stats_addr, 0, stats_size);  	arg1 = func | QLCNIC_STATS_VERSION << 8 | QLCNIC_STATS_PORT << 12;  	arg1 |= rx_tx << 15 | stats_size << 16; @@ -1003,13 +985,10 @@ int qlcnic_get_mac_stats(struct qlcnic_adapter *adapter,  		return -ENOMEM;  	stats_addr = dma_alloc_coherent(&adapter->pdev->dev, stats_size, -			&stats_dma_t, GFP_KERNEL); -	if (!stats_addr) { -		dev_err(&adapter->pdev->dev, -			"%s: Unable to allocate memory.\n", __func__); +					&stats_dma_t, GFP_KERNEL | __GFP_ZERO); +	if (!stats_addr)  		return -ENOMEM; -	} -	memset(stats_addr, 0, stats_size); +  	qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_GET_MAC_STATS);  	cmd.req.arg[1] = stats_size << 16;  	cmd.req.arg[2] = MSD(stats_dma_t); diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c index 5641f8ec49a..9f7aade4667 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_ethtool.c @@ -149,7 +149,8 @@ static const char qlcnic_gstrings_test[][ETH_GSTRING_LEN] = {  static inline int qlcnic_82xx_statistics(void)  { -	return QLCNIC_STATS_LEN + ARRAY_SIZE(qlcnic_83xx_mac_stats_strings); +	return ARRAY_SIZE(qlcnic_device_gstrings_stats) + +	       ARRAY_SIZE(qlcnic_83xx_mac_stats_strings);  }  static inline int qlcnic_83xx_statistics(void) @@ -858,9 +859,11 @@ clear_diag_irq:  	return ret;  } -#define QLCNIC_ILB_PKT_SIZE 64 -#define QLCNIC_NUM_ILB_PKT	16 -#define QLCNIC_ILB_MAX_RCV_LOOP 10 +#define QLCNIC_ILB_PKT_SIZE		64 +#define QLCNIC_NUM_ILB_PKT		16 +#define QLCNIC_ILB_MAX_RCV_LOOP		10 +#define QLCNIC_LB_PKT_POLL_DELAY_MSEC	1 +#define QLCNIC_LB_PKT_POLL_COUNT	20  static void qlcnic_create_loopback_buff(unsigned char *data, u8 mac[])  { @@ -897,9 +900,9 @@ int qlcnic_do_lb_test(struct qlcnic_adapter *adapter, u8 mode)  		loop = 0;  		do { -			msleep(1); +			msleep(QLCNIC_LB_PKT_POLL_DELAY_MSEC);  			qlcnic_process_rcv_ring_diag(sds_ring); -			if (loop++ > QLCNIC_ILB_MAX_RCV_LOOP) +			if (loop++ > QLCNIC_LB_PKT_POLL_COUNT)  				break;  		} while (!adapter->ahw->diag_cnt); @@ -1070,8 +1073,7 @@ qlcnic_get_strings(struct net_device *dev, u32 stringset, u8 *data)  	}  } -static void -qlcnic_fill_stats(u64 *data, void *stats, int type) +static u64 *qlcnic_fill_stats(u64 *data, void *stats, int type)  {  	if (type == QLCNIC_MAC_STATS) {  		struct qlcnic_mac_statistics *mac_stats = @@ -1120,6 +1122,7 @@ qlcnic_fill_stats(u64 *data, void *stats, int type)  		*data++ = QLCNIC_FILL_STATS(esw_stats->local_frames);  		*data++ = QLCNIC_FILL_STATS(esw_stats->numbytes);  	} +	return data;  }  static void qlcnic_get_ethtool_stats(struct net_device *dev, @@ -1147,7 +1150,7 @@ static void qlcnic_get_ethtool_stats(struct net_device *dev,  		/* Retrieve MAC statistics from firmware */  		memset(&mac_stats, 0, sizeof(struct qlcnic_mac_statistics));  		qlcnic_get_mac_stats(adapter, &mac_stats); -		qlcnic_fill_stats(data, &mac_stats, QLCNIC_MAC_STATS); +		data = qlcnic_fill_stats(data, &mac_stats, QLCNIC_MAC_STATS);  	}  	if (!(adapter->flags & QLCNIC_ESWITCH_ENABLED)) @@ -1159,7 +1162,7 @@ static void qlcnic_get_ethtool_stats(struct net_device *dev,  	if (ret)  		return; -	qlcnic_fill_stats(data, &port_stats.rx, QLCNIC_ESW_STATS); +	data = qlcnic_fill_stats(data, &port_stats.rx, QLCNIC_ESW_STATS);  	ret = qlcnic_get_port_stats(adapter, adapter->ahw->pci_func,  			QLCNIC_QUERY_TX_COUNTER, &port_stats.tx);  	if (ret) @@ -1176,7 +1179,8 @@ static int qlcnic_set_led(struct net_device *dev,  	int err = -EIO, active = 1;  	if (qlcnic_83xx_check(adapter)) -		return -EOPNOTSUPP; +		return qlcnic_83xx_set_led(dev, state); +  	if (adapter->ahw->op_mode == QLCNIC_NON_PRIV_FUNC) {  		netdev_warn(dev, "LED test not supported for non "  				"privilege function\n"); @@ -1537,3 +1541,25 @@ const struct ethtool_ops qlcnic_ethtool_ops = {  	.get_dump_data = qlcnic_get_dump_data,  	.set_dump = qlcnic_set_dump,  }; + +const struct ethtool_ops qlcnic_sriov_vf_ethtool_ops = { +	.get_settings		= qlcnic_get_settings, +	.get_drvinfo		= qlcnic_get_drvinfo, +	.get_regs_len		= qlcnic_get_regs_len, +	.get_regs		= qlcnic_get_regs, +	.get_link		= ethtool_op_get_link, +	.get_eeprom_len		= qlcnic_get_eeprom_len, +	.get_eeprom		= qlcnic_get_eeprom, +	.get_ringparam		= qlcnic_get_ringparam, +	.set_ringparam		= qlcnic_set_ringparam, +	.get_channels		= qlcnic_get_channels, +	.get_pauseparam		= qlcnic_get_pauseparam, +	.get_wol		= qlcnic_get_wol, +	.get_strings		= qlcnic_get_strings, +	.get_ethtool_stats	= qlcnic_get_ethtool_stats, +	.get_sset_count		= qlcnic_get_sset_count, +	.get_coalesce		= qlcnic_get_intr_coalesce, +	.set_coalesce		= qlcnic_set_intr_coalesce, +	.set_msglevel		= qlcnic_set_msglevel, +	.get_msglevel		= qlcnic_get_msglevel, +}; diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hdr.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hdr.h index 44197ca1456..c0f0c0d0a79 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hdr.h +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hdr.h @@ -669,7 +669,7 @@ enum {  #define QLCNIC_CMDPEG_CHECK_RETRY_COUNT	60  #define QLCNIC_CMDPEG_CHECK_DELAY	500  #define QLCNIC_HEARTBEAT_PERIOD_MSECS	200 -#define QLCNIC_HEARTBEAT_CHECK_RETRY_COUNT	45 +#define QLCNIC_HEARTBEAT_CHECK_RETRY_COUNT	10  #define QLCNIC_MAX_MC_COUNT		38  #define QLCNIC_WATCHDOG_TIMEOUTVALUE	5 @@ -714,7 +714,9 @@ enum {  	QLCNIC_MGMT_FUNC	= 0,  	QLCNIC_PRIV_FUNC	= 1,  	QLCNIC_NON_PRIV_FUNC	= 2, -	QLCNIC_UNKNOWN_FUNC_MODE = 3 +	QLCNIC_SRIOV_PF_FUNC	= 3, +	QLCNIC_SRIOV_VF_FUNC	= 4, +	QLCNIC_UNKNOWN_FUNC_MODE = 5  };  enum { diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c index f89cc7a3fe6..6a6512ba9f3 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c @@ -423,7 +423,7 @@ qlcnic_send_cmd_descs(struct qlcnic_adapter *adapter,  }  int qlcnic_82xx_sre_macaddr_change(struct qlcnic_adapter *adapter, u8 *addr, -				   __le16 vlan_id, u8 op) +				   u16 vlan_id, u8 op)  {  	struct qlcnic_nic_req req;  	struct qlcnic_mac_req *mac_req; @@ -441,7 +441,7 @@ int qlcnic_82xx_sre_macaddr_change(struct qlcnic_adapter *adapter, u8 *addr,  	memcpy(mac_req->mac_addr, addr, 6);  	vlan_req = (struct qlcnic_vlan_req *)&req.words[1]; -	vlan_req->vlan_id = vlan_id; +	vlan_req->vlan_id = cpu_to_le16(vlan_id);  	return qlcnic_send_cmd_descs(adapter, (struct cmd_desc_type0 *)&req, 1);  } @@ -468,7 +468,7 @@ int qlcnic_nic_del_mac(struct qlcnic_adapter *adapter, const u8 *addr)  	return err;  } -int qlcnic_nic_add_mac(struct qlcnic_adapter *adapter, const u8 *addr) +int qlcnic_nic_add_mac(struct qlcnic_adapter *adapter, const u8 *addr, u16 vlan)  {  	struct list_head *head;  	struct qlcnic_mac_list_s *cur; @@ -487,7 +487,7 @@ int qlcnic_nic_add_mac(struct qlcnic_adapter *adapter, const u8 *addr)  	memcpy(cur->mac_addr, addr, ETH_ALEN);  	if (qlcnic_sre_macaddr_change(adapter, -				cur->mac_addr, 0, QLCNIC_MAC_ADD)) { +				cur->mac_addr, vlan, QLCNIC_MAC_ADD)) {  		kfree(cur);  		return -EIO;  	} @@ -496,7 +496,7 @@ int qlcnic_nic_add_mac(struct qlcnic_adapter *adapter, const u8 *addr)  	return 0;  } -void qlcnic_set_multi(struct net_device *netdev) +void __qlcnic_set_multi(struct net_device *netdev, u16 vlan)  {  	struct qlcnic_adapter *adapter = netdev_priv(netdev);  	struct netdev_hw_addr *ha; @@ -508,8 +508,9 @@ void qlcnic_set_multi(struct net_device *netdev)  	if (!test_bit(__QLCNIC_FW_ATTACHED, &adapter->state))  		return; -	qlcnic_nic_add_mac(adapter, adapter->mac_addr); -	qlcnic_nic_add_mac(adapter, bcast_addr); +	if (!qlcnic_sriov_vf_check(adapter)) +		qlcnic_nic_add_mac(adapter, adapter->mac_addr, vlan); +	qlcnic_nic_add_mac(adapter, bcast_addr, vlan);  	if (netdev->flags & IFF_PROMISC) {  		if (!(adapter->flags & QLCNIC_PROMISC_DISABLED)) @@ -523,23 +524,55 @@ void qlcnic_set_multi(struct net_device *netdev)  		goto send_fw_cmd;  	} -	if (!netdev_mc_empty(netdev)) { +	if (!netdev_mc_empty(netdev) && !qlcnic_sriov_vf_check(adapter)) {  		netdev_for_each_mc_addr(ha, netdev) { -			qlcnic_nic_add_mac(adapter, ha->addr); +			qlcnic_nic_add_mac(adapter, ha->addr, vlan);  		}  	} +	if (qlcnic_sriov_vf_check(adapter)) +		qlcnic_vf_add_mc_list(netdev, vlan); +  send_fw_cmd: -	if (mode == VPORT_MISS_MODE_ACCEPT_ALL && !adapter->fdb_mac_learn) { -		qlcnic_alloc_lb_filters_mem(adapter); -		adapter->drv_mac_learn = true; -	} else { -		adapter->drv_mac_learn = false; +	if (!qlcnic_sriov_vf_check(adapter)) { +		if (mode == VPORT_MISS_MODE_ACCEPT_ALL && +		    !adapter->fdb_mac_learn) { +			qlcnic_alloc_lb_filters_mem(adapter); +			adapter->drv_mac_learn = true; +		} else { +			adapter->drv_mac_learn = false; +		}  	}  	qlcnic_nic_set_promisc(adapter, mode);  } +void qlcnic_set_multi(struct net_device *netdev) +{ +	struct qlcnic_adapter *adapter = netdev_priv(netdev); +	struct netdev_hw_addr *ha; +	struct qlcnic_mac_list_s *cur; + +	if (!test_bit(__QLCNIC_FW_ATTACHED, &adapter->state)) +		return; +	if (qlcnic_sriov_vf_check(adapter)) { +		if (!netdev_mc_empty(netdev)) { +			netdev_for_each_mc_addr(ha, netdev) { +				cur = kzalloc(sizeof(struct qlcnic_mac_list_s), +					      GFP_ATOMIC); +				if (cur == NULL) +					break; +				memcpy(cur->mac_addr, +				       ha->addr, ETH_ALEN); +				list_add_tail(&cur->list, &adapter->vf_mc_list); +			} +		} +		qlcnic_sriov_vf_schedule_multi(adapter->netdev); +		return; +	} +	__qlcnic_set_multi(netdev, 0); +} +  int qlcnic_82xx_nic_set_promisc(struct qlcnic_adapter *adapter, u32 mode)  {  	struct qlcnic_nic_req req; @@ -559,7 +592,7 @@ int qlcnic_82xx_nic_set_promisc(struct qlcnic_adapter *adapter, u32 mode)  				(struct cmd_desc_type0 *)&req, 1);  } -void qlcnic_free_mac_list(struct qlcnic_adapter *adapter) +void qlcnic_82xx_free_mac_list(struct qlcnic_adapter *adapter)  {  	struct qlcnic_mac_list_s *cur;  	struct list_head *head = &adapter->mac_list; diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.h index 5b8749eda11..95b1b573283 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.h +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.h @@ -83,6 +83,8 @@ enum qlcnic_regs {  #define QLCNIC_CMD_CONFIG_PORT			0x2e  #define QLCNIC_CMD_TEMP_SIZE			0x2f  #define QLCNIC_CMD_GET_TEMP_HDR			0x30 +#define QLCNIC_CMD_BC_EVENT_SETUP		0x31 +#define	QLCNIC_CMD_CONFIG_VPORT			0x32  #define QLCNIC_CMD_GET_MAC_STATS		0x37  #define QLCNIC_CMD_SET_DRV_VER			0x38  #define QLCNIC_CMD_CONFIGURE_RSS		0x41 @@ -114,6 +116,7 @@ enum qlcnic_regs {  #define QLCNIC_SET_FAC_DEF_MAC			5  #define QLCNIC_MBX_LINK_EVENT		0x8001 +#define QLCNIC_MBX_BC_EVENT		0x8002  #define QLCNIC_MBX_COMP_EVENT		0x8100  #define QLCNIC_MBX_REQUEST_EVENT	0x8101  #define QLCNIC_MBX_TIME_EXTEND_EVENT	0x8102 @@ -156,7 +159,7 @@ int qlcnic_82xx_nic_set_promisc(struct qlcnic_adapter *adapter, u32);  int qlcnic_82xx_napi_add(struct qlcnic_adapter *adapter,  			 struct net_device *netdev);  void qlcnic_82xx_change_filter(struct qlcnic_adapter *adapter, -			       u64 *uaddr, __le16 vlan_id); +			       u64 *uaddr, u16 vlan_id);  void qlcnic_82xx_config_intr_coalesce(struct qlcnic_adapter *adapter);  int qlcnic_82xx_config_rss(struct qlcnic_adapter *adapter, int);  void qlcnic_82xx_config_ipaddr(struct qlcnic_adapter *adapter, @@ -175,7 +178,10 @@ int qlcnic_82xx_issue_cmd(struct qlcnic_adapter *adapter,  int qlcnic_82xx_fw_cmd_create_rx_ctx(struct qlcnic_adapter *);  int qlcnic_82xx_fw_cmd_create_tx_ctx(struct qlcnic_adapter *,  				     struct qlcnic_host_tx_ring *tx_ring, int); -int qlcnic_82xx_sre_macaddr_change(struct qlcnic_adapter *, u8 *, __le16, u8); +void qlcnic_82xx_fw_cmd_del_rx_ctx(struct qlcnic_adapter *); +void qlcnic_82xx_fw_cmd_del_tx_ctx(struct qlcnic_adapter *, +				   struct qlcnic_host_tx_ring *); +int qlcnic_82xx_sre_macaddr_change(struct qlcnic_adapter *, u8 *, u16, u8);  int qlcnic_82xx_get_mac_address(struct qlcnic_adapter *, u8*);  int qlcnic_82xx_get_nic_info(struct qlcnic_adapter *, struct qlcnic_info *, u8);  int qlcnic_82xx_set_nic_info(struct qlcnic_adapter *, struct qlcnic_info *); diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c index 5fa847fe388..d3f8797efcc 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_io.c @@ -9,6 +9,7 @@  #include <linux/if_vlan.h>  #include <net/ip.h>  #include <linux/ipv6.h> +#include <net/checksum.h>  #include "qlcnic.h" @@ -146,7 +147,10 @@ static inline u8 qlcnic_mac_hash(u64 mac)  static inline u32 qlcnic_get_ref_handle(struct qlcnic_adapter *adapter,  					u16 handle, u8 ring_id)  { -	if (adapter->pdev->device == PCI_DEVICE_ID_QLOGIC_QLE834X) +	unsigned short device = adapter->pdev->device; + +	if ((device == PCI_DEVICE_ID_QLOGIC_QLE834X) || +	    (device == PCI_DEVICE_ID_QLOGIC_VF_QLE834X))  		return handle | (ring_id << 15);  	else  		return handle; @@ -158,7 +162,7 @@ static inline int qlcnic_82xx_is_lb_pkt(u64 sts_data)  }  void qlcnic_add_lb_filter(struct qlcnic_adapter *adapter, struct sk_buff *skb, -			  int loopback_pkt, __le16 vlan_id) +			  int loopback_pkt, u16 vlan_id)  {  	struct ethhdr *phdr = (struct ethhdr *)(skb->data);  	struct qlcnic_filter *fil, *tmp_fil; @@ -236,7 +240,7 @@ void qlcnic_add_lb_filter(struct qlcnic_adapter *adapter, struct sk_buff *skb,  }  void qlcnic_82xx_change_filter(struct qlcnic_adapter *adapter, u64 *uaddr, -			       __le16 vlan_id) +			       u16 vlan_id)  {  	struct cmd_desc_type0 *hwdesc;  	struct qlcnic_nic_req *req; @@ -261,7 +265,7 @@ void qlcnic_82xx_change_filter(struct qlcnic_adapter *adapter, u64 *uaddr,  	memcpy(mac_req->mac_addr, &uaddr, ETH_ALEN);  	vlan_req = (struct qlcnic_vlan_req *)&req->words[1]; -	vlan_req->vlan_id = vlan_id; +	vlan_req->vlan_id = cpu_to_le16(vlan_id);  	tx_ring->producer = get_next_index(producer, tx_ring->num_desc);  	smp_mb(); @@ -277,7 +281,7 @@ static void qlcnic_send_filter(struct qlcnic_adapter *adapter,  	struct net_device *netdev = adapter->netdev;  	struct ethhdr *phdr = (struct ethhdr *)(skb->data);  	u64 src_addr = 0; -	__le16 vlan_id = 0; +	u16 vlan_id = 0;  	u8 hindex;  	if (ether_addr_equal(phdr->h_source, adapter->mac_addr)) @@ -340,14 +344,14 @@ static int qlcnic_tx_pkt(struct qlcnic_adapter *adapter,  		flags = FLAGS_VLAN_OOB;  		vlan_tci = vlan_tx_tag_get(skb);  	} -	if (unlikely(adapter->pvid)) { +	if (unlikely(adapter->tx_pvid)) {  		if (vlan_tci && !(adapter->flags & QLCNIC_TAGGING_ENABLED))  			return -EIO;  		if (vlan_tci && (adapter->flags & QLCNIC_TAGGING_ENABLED))  			goto set_flags;  		flags = FLAGS_VLAN_OOB; -		vlan_tci = adapter->pvid; +		vlan_tci = adapter->tx_pvid;  	}  set_flags:  	qlcnic_set_tx_vlan_tci(first_desc, vlan_tci); @@ -975,10 +979,10 @@ static inline int qlcnic_check_rx_tagging(struct qlcnic_adapter *adapter,  		memmove(skb->data + VLAN_HLEN, eth_hdr, ETH_ALEN * 2);  		skb_pull(skb, VLAN_HLEN);  	} -	if (!adapter->pvid) +	if (!adapter->rx_pvid)  		return 0; -	if (*vlan_tag == adapter->pvid) { +	if (*vlan_tag == adapter->rx_pvid) {  		/* Outer vlan tag. Packet should follow non-vlan path */  		*vlan_tag = 0xffff;  		return 0; @@ -1024,8 +1028,7 @@ qlcnic_process_rcv(struct qlcnic_adapter *adapter,  	    (adapter->flags & QLCNIC_ESWITCH_ENABLED)) {  		t_vid = 0;  		is_lb_pkt = qlcnic_82xx_is_lb_pkt(sts_data0); -		qlcnic_add_lb_filter(adapter, skb, is_lb_pkt, -				     cpu_to_le16(t_vid)); +		qlcnic_add_lb_filter(adapter, skb, is_lb_pkt, t_vid);  	}  	if (length > rds_ring->skb_size) @@ -1045,7 +1048,7 @@ qlcnic_process_rcv(struct qlcnic_adapter *adapter,  	skb->protocol = eth_type_trans(skb, netdev);  	if (vid != 0xffff) -		__vlan_hwaccel_put_tag(skb, vid); +		__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vid);  	napi_gro_receive(&sds_ring->napi, skb); @@ -1102,8 +1105,7 @@ qlcnic_process_lro(struct qlcnic_adapter *adapter,  	    (adapter->flags & QLCNIC_ESWITCH_ENABLED)) {  		t_vid = 0;  		is_lb_pkt = qlcnic_82xx_is_lb_pkt(sts_data0); -		qlcnic_add_lb_filter(adapter, skb, is_lb_pkt, -				     cpu_to_le16(t_vid)); +		qlcnic_add_lb_filter(adapter, skb, is_lb_pkt, t_vid);  	}  	if (timestamp) @@ -1131,9 +1133,8 @@ qlcnic_process_lro(struct qlcnic_adapter *adapter,  		iph = (struct iphdr *)skb->data;  		th = (struct tcphdr *)(skb->data + (iph->ihl << 2));  		length = (iph->ihl << 2) + (th->doff << 2) + lro_length; +		csum_replace2(&iph->check, iph->tot_len, htons(length));  		iph->tot_len = htons(length); -		iph->check = 0; -		iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);  	}  	th->psh = push; @@ -1149,7 +1150,7 @@ qlcnic_process_lro(struct qlcnic_adapter *adapter,  	}  	if (vid != 0xffff) -		__vlan_hwaccel_put_tag(skb, vid); +		__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vid);  	netif_receive_skb(skb);  	adapter->stats.lro_pkts++; @@ -1496,8 +1497,7 @@ qlcnic_83xx_process_rcv(struct qlcnic_adapter *adapter,  	    (adapter->flags & QLCNIC_ESWITCH_ENABLED)) {  		t_vid = 0;  		is_lb_pkt = qlcnic_83xx_is_lb_pkt(sts_data[1], 0); -		qlcnic_add_lb_filter(adapter, skb, is_lb_pkt, -				     cpu_to_le16(t_vid)); +		qlcnic_add_lb_filter(adapter, skb, is_lb_pkt, t_vid);  	}  	if (length > rds_ring->skb_size) @@ -1514,7 +1514,7 @@ qlcnic_83xx_process_rcv(struct qlcnic_adapter *adapter,  	skb->protocol = eth_type_trans(skb, netdev);  	if (vid != 0xffff) -		__vlan_hwaccel_put_tag(skb, vid); +		__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vid);  	napi_gro_receive(&sds_ring->napi, skb); @@ -1566,8 +1566,7 @@ qlcnic_83xx_process_lro(struct qlcnic_adapter *adapter,  	    (adapter->flags & QLCNIC_ESWITCH_ENABLED)) {  		t_vid = 0;  		is_lb_pkt = qlcnic_83xx_is_lb_pkt(sts_data[1], 1); -		qlcnic_add_lb_filter(adapter, skb, is_lb_pkt, -				     cpu_to_le16(t_vid)); +		qlcnic_add_lb_filter(adapter, skb, is_lb_pkt, t_vid);  	}  	if (qlcnic_83xx_is_tstamp(sts_data[1]))  		data_offset = l4_hdr_offset + QLCNIC_TCP_TS_HDR_SIZE; @@ -1594,9 +1593,8 @@ qlcnic_83xx_process_lro(struct qlcnic_adapter *adapter,  		iph = (struct iphdr *)skb->data;  		th = (struct tcphdr *)(skb->data + (iph->ihl << 2));  		length = (iph->ihl << 2) + (th->doff << 2) + lro_length; +		csum_replace2(&iph->check, iph->tot_len, htons(length));  		iph->tot_len = htons(length); -		iph->check = 0; -		iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);  	}  	th->psh = push; @@ -1612,7 +1610,7 @@ qlcnic_83xx_process_lro(struct qlcnic_adapter *adapter,  	}  	if (vid != 0xffff) -		__vlan_hwaccel_put_tag(skb, vid); +		__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vid);  	netif_receive_skb(skb); @@ -1691,6 +1689,29 @@ skip:  	return count;  } +static int qlcnic_83xx_msix_sriov_vf_poll(struct napi_struct *napi, int budget) +{ +	int tx_complete; +	int work_done; +	struct qlcnic_host_sds_ring *sds_ring; +	struct qlcnic_adapter *adapter; +	struct qlcnic_host_tx_ring *tx_ring; + +	sds_ring = container_of(napi, struct qlcnic_host_sds_ring, napi); +	adapter = sds_ring->adapter; +	/* tx ring count = 1 */ +	tx_ring = adapter->tx_ring; + +	tx_complete = qlcnic_process_cmd_ring(adapter, tx_ring, budget); +	work_done = qlcnic_83xx_process_rcv_ring(sds_ring, budget); +	if ((work_done < budget) && tx_complete) { +		napi_complete(&sds_ring->napi); +		qlcnic_83xx_enable_intr(adapter, sds_ring); +	} + +	return work_done; +} +  static int qlcnic_83xx_poll(struct napi_struct *napi, int budget)  {  	int tx_complete; @@ -1768,7 +1789,8 @@ void qlcnic_83xx_napi_enable(struct qlcnic_adapter *adapter)  			qlcnic_83xx_enable_intr(adapter, sds_ring);  	} -	if (adapter->flags & QLCNIC_MSIX_ENABLED) { +	if ((adapter->flags & QLCNIC_MSIX_ENABLED) && +	    !(adapter->flags & QLCNIC_TX_INTR_SHARED)) {  		for (ring = 0; ring < adapter->max_drv_tx_rings; ring++) {  			tx_ring = &adapter->tx_ring[ring];  			napi_enable(&tx_ring->napi); @@ -1795,7 +1817,8 @@ void qlcnic_83xx_napi_disable(struct qlcnic_adapter *adapter)  		napi_disable(&sds_ring->napi);  	} -	if (adapter->flags & QLCNIC_MSIX_ENABLED) { +	if ((adapter->flags & QLCNIC_MSIX_ENABLED) && +	    !(adapter->flags & QLCNIC_TX_INTR_SHARED)) {  		for (ring = 0; ring < adapter->max_drv_tx_rings; ring++) {  			tx_ring = &adapter->tx_ring[ring];  			qlcnic_83xx_disable_tx_intr(adapter, tx_ring); @@ -1808,7 +1831,7 @@ void qlcnic_83xx_napi_disable(struct qlcnic_adapter *adapter)  int qlcnic_83xx_napi_add(struct qlcnic_adapter *adapter,  			 struct net_device *netdev)  { -	int ring, max_sds_rings; +	int ring, max_sds_rings, temp;  	struct qlcnic_host_sds_ring *sds_ring;  	struct qlcnic_host_tx_ring *tx_ring;  	struct qlcnic_recv_context *recv_ctx = adapter->recv_ctx; @@ -1819,14 +1842,23 @@ int qlcnic_83xx_napi_add(struct qlcnic_adapter *adapter,  	max_sds_rings = adapter->max_sds_rings;  	for (ring = 0; ring < adapter->max_sds_rings; ring++) {  		sds_ring = &recv_ctx->sds_rings[ring]; -		if (adapter->flags & QLCNIC_MSIX_ENABLED) -			netif_napi_add(netdev, &sds_ring->napi, -				       qlcnic_83xx_rx_poll, -				       QLCNIC_NETDEV_WEIGHT * 2); -		else +		if (adapter->flags & QLCNIC_MSIX_ENABLED) { +			if (!(adapter->flags & QLCNIC_TX_INTR_SHARED)) { +				netif_napi_add(netdev, &sds_ring->napi, +					       qlcnic_83xx_rx_poll, +					       QLCNIC_NETDEV_WEIGHT * 2); +			} else { +				temp = QLCNIC_NETDEV_WEIGHT / max_sds_rings; +				netif_napi_add(netdev, &sds_ring->napi, +					       qlcnic_83xx_msix_sriov_vf_poll, +					       temp); +			} + +		} else {  			netif_napi_add(netdev, &sds_ring->napi,  				       qlcnic_83xx_poll,  				       QLCNIC_NETDEV_WEIGHT / max_sds_rings); +		}  	}  	if (qlcnic_alloc_tx_rings(adapter, netdev)) { @@ -1834,7 +1866,8 @@ int qlcnic_83xx_napi_add(struct qlcnic_adapter *adapter,  		return -ENOMEM;  	} -	if (adapter->flags & QLCNIC_MSIX_ENABLED) { +	if ((adapter->flags & QLCNIC_MSIX_ENABLED) && +	    !(adapter->flags & QLCNIC_TX_INTR_SHARED)) {  		for (ring = 0; ring < adapter->max_drv_tx_rings; ring++) {  			tx_ring = &adapter->tx_ring[ring];  			netif_napi_add(netdev, &tx_ring->napi, @@ -1860,7 +1893,8 @@ void qlcnic_83xx_napi_del(struct qlcnic_adapter *adapter)  	qlcnic_free_sds_rings(adapter->recv_ctx); -	if ((adapter->flags & QLCNIC_MSIX_ENABLED)) { +	if ((adapter->flags & QLCNIC_MSIX_ENABLED) && +	    !(adapter->flags & QLCNIC_TX_INTR_SHARED)) {  		for (ring = 0; ring < adapter->max_drv_tx_rings; ring++) {  			tx_ring = &adapter->tx_ring[ring];  			netif_napi_del(&tx_ring->napi); diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c index 28a6d483836..247a9f9b7bd 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c @@ -9,6 +9,7 @@  #include <linux/interrupt.h>  #include "qlcnic.h" +#include "qlcnic_sriov.h"  #include "qlcnic_hw.h"  #include <linux/swab.h> @@ -85,8 +86,8 @@ static void qlcnic_dev_set_npar_ready(struct qlcnic_adapter *);  static int qlcnicvf_start_firmware(struct qlcnic_adapter *);  static void qlcnic_set_netdev_features(struct qlcnic_adapter *,  				struct qlcnic_esw_func_cfg *); -static int qlcnic_vlan_rx_add(struct net_device *, u16); -static int qlcnic_vlan_rx_del(struct net_device *, u16); +static int qlcnic_vlan_rx_add(struct net_device *, __be16, u16); +static int qlcnic_vlan_rx_del(struct net_device *, __be16, u16);  #define QLCNIC_IS_TSO_CAPABLE(adapter)	\  	((adapter)->ahw->capabilities & QLCNIC_FW_CAPABILITY_TSO) @@ -109,6 +110,7 @@ static u32 qlcnic_vlan_tx_check(struct qlcnic_adapter *adapter)  static DEFINE_PCI_DEVICE_TABLE(qlcnic_pci_tbl) = {  	ENTRY(PCI_DEVICE_ID_QLOGIC_QLE824X),  	ENTRY(PCI_DEVICE_ID_QLOGIC_QLE834X), +	ENTRY(PCI_DEVICE_ID_QLOGIC_VF_QLE834X),  	{0,}  }; @@ -198,8 +200,7 @@ void qlcnic_free_sds_rings(struct qlcnic_recv_context *recv_ctx)  	recv_ctx->sds_rings = NULL;  } -static int -qlcnic_read_mac_addr(struct qlcnic_adapter *adapter) +int qlcnic_read_mac_addr(struct qlcnic_adapter *adapter)  {  	u8 mac_addr[ETH_ALEN];  	struct net_device *netdev = adapter->netdev; @@ -225,6 +226,9 @@ static int qlcnic_set_mac(struct net_device *netdev, void *p)  	struct qlcnic_adapter *adapter = netdev_priv(netdev);  	struct sockaddr *addr = p; +	if (qlcnic_sriov_vf_check(adapter)) +		return -EINVAL; +  	if ((adapter->flags & QLCNIC_MAC_OVERRIDE_DISABLED))  		return -EOPNOTSUPP; @@ -253,11 +257,8 @@ static int qlcnic_fdb_del(struct ndmsg *ndm, struct nlattr *tb[],  	struct qlcnic_adapter *adapter = netdev_priv(netdev);  	int err = -EOPNOTSUPP; -	if (!adapter->fdb_mac_learn) { -		pr_info("%s: Driver mac learn is enabled, FDB operation not allowed\n", -			__func__); -		return err; -	} +	if (!adapter->fdb_mac_learn) +		return ndo_dflt_fdb_del(ndm, tb, netdev, addr);  	if (adapter->flags & QLCNIC_ESWITCH_ENABLED) {  		if (is_unicast_ether_addr(addr)) @@ -277,11 +278,8 @@ static int qlcnic_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],  	struct qlcnic_adapter *adapter = netdev_priv(netdev);  	int err = 0; -	if (!adapter->fdb_mac_learn) { -		pr_info("%s: Driver mac learn is enabled, FDB operation not allowed\n", -			__func__); -		return -EOPNOTSUPP; -	} +	if (!adapter->fdb_mac_learn) +		return ndo_dflt_fdb_add(ndm, tb, netdev, addr, flags);  	if (!(adapter->flags & QLCNIC_ESWITCH_ENABLED)) {  		pr_info("%s: FDB e-switch is not enabled\n", __func__); @@ -292,7 +290,7 @@ static int qlcnic_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],  		return err;  	if (is_unicast_ether_addr(addr)) -		err = qlcnic_nic_add_mac(adapter, addr); +		err = qlcnic_nic_add_mac(adapter, addr, 0);  	else if (is_multicast_ether_addr(addr))  		err = dev_mc_add_excl(netdev, addr);  	else @@ -306,11 +304,8 @@ static int qlcnic_fdb_dump(struct sk_buff *skb, struct netlink_callback *ncb,  {  	struct qlcnic_adapter *adapter = netdev_priv(netdev); -	if (!adapter->fdb_mac_learn) { -		pr_info("%s: Driver mac learn is enabled, FDB operation not allowed\n", -			__func__); -		return -EOPNOTSUPP; -	} +	if (!adapter->fdb_mac_learn) +		return ndo_dflt_fdb_dump(skb, ncb, netdev, idx);  	if (adapter->flags & QLCNIC_ESWITCH_ENABLED)  		idx = ndo_dflt_fdb_dump(skb, ncb, netdev, idx); @@ -346,6 +341,12 @@ static const struct net_device_ops qlcnic_netdev_ops = {  #ifdef CONFIG_NET_POLL_CONTROLLER  	.ndo_poll_controller = qlcnic_poll_controller,  #endif +#ifdef CONFIG_QLCNIC_SRIOV +	.ndo_set_vf_mac		= qlcnic_sriov_set_vf_mac, +	.ndo_set_vf_tx_rate	= qlcnic_sriov_set_vf_tx_rate, +	.ndo_get_vf_config	= qlcnic_sriov_get_vf_config, +	.ndo_set_vf_vlan	= qlcnic_sriov_set_vf_vlan, +#endif  };  static const struct net_device_ops qlcnic_netdev_failed_ops = { @@ -387,6 +388,8 @@ static struct qlcnic_hardware_ops qlcnic_hw_ops = {  	.process_lb_rcv_ring_diag	= qlcnic_82xx_process_rcv_ring_diag,  	.create_rx_ctx			= qlcnic_82xx_fw_cmd_create_rx_ctx,  	.create_tx_ctx			= qlcnic_82xx_fw_cmd_create_tx_ctx, +	.del_rx_ctx			= qlcnic_82xx_fw_cmd_del_rx_ctx, +	.del_tx_ctx			= qlcnic_82xx_fw_cmd_del_tx_ctx,  	.setup_link_event		= qlcnic_82xx_linkevent_request,  	.get_nic_info			= qlcnic_82xx_get_nic_info,  	.get_pci_info			= qlcnic_82xx_get_pci_info, @@ -402,13 +405,22 @@ static struct qlcnic_hardware_ops qlcnic_hw_ops = {  	.config_promisc_mode		= qlcnic_82xx_nic_set_promisc,  	.change_l2_filter		= qlcnic_82xx_change_filter,  	.get_board_info			= qlcnic_82xx_get_board_info, +	.free_mac_list			= qlcnic_82xx_free_mac_list,  };  int qlcnic_enable_msix(struct qlcnic_adapter *adapter, u32 num_msix)  {  	struct pci_dev *pdev = adapter->pdev;  	int err = -1, i; -	int max_tx_rings; +	int max_tx_rings, tx_vector; + +	if (adapter->flags & QLCNIC_TX_INTR_SHARED) { +		max_tx_rings = 0; +		tx_vector = 0; +	} else { +		max_tx_rings = adapter->max_drv_tx_rings; +		tx_vector = 1; +	}  	if (!adapter->msix_entries) {  		adapter->msix_entries = kcalloc(num_msix, @@ -431,7 +443,6 @@ int qlcnic_enable_msix(struct qlcnic_adapter *adapter, u32 num_msix)  			if (qlcnic_83xx_check(adapter)) {  				adapter->ahw->num_msix = num_msix;  				/* subtract mail box and tx ring vectors */ -				max_tx_rings = adapter->max_drv_tx_rings;  				adapter->max_sds_rings = num_msix -  							 max_tx_rings - 1;  			} else { @@ -444,11 +455,11 @@ int qlcnic_enable_msix(struct qlcnic_adapter *adapter, u32 num_msix)  				 "Unable to allocate %d MSI-X interrupt vectors\n",  				 num_msix);  			if (qlcnic_83xx_check(adapter)) { -				if (err < QLC_83XX_MINIMUM_VECTOR) +				if (err < (QLC_83XX_MINIMUM_VECTOR - tx_vector))  					return err; -				err -= (adapter->max_drv_tx_rings + 1); +				err -= (max_tx_rings + 1);  				num_msix = rounddown_pow_of_two(err); -				num_msix += (adapter->max_drv_tx_rings + 1); +				num_msix += (max_tx_rings + 1);  			} else {  				num_msix = rounddown_pow_of_two(err);  			} @@ -542,11 +553,10 @@ void qlcnic_teardown_intr(struct qlcnic_adapter *adapter)  	}  } -static void -qlcnic_cleanup_pci_map(struct qlcnic_adapter *adapter) +static void qlcnic_cleanup_pci_map(struct qlcnic_hardware_context *ahw)  { -	if (adapter->ahw->pci_base0 != NULL) -		iounmap(adapter->ahw->pci_base0); +	if (ahw->pci_base0 != NULL) +		iounmap(ahw->pci_base0);  }  static int qlcnic_get_act_pci_func(struct qlcnic_adapter *adapter) @@ -721,6 +731,7 @@ static void qlcnic_get_bar_length(u32 dev_id, ulong *bar)  		*bar = QLCNIC_82XX_BAR0_LENGTH;  		break;  	case PCI_DEVICE_ID_QLOGIC_QLE834X: +	case PCI_DEVICE_ID_QLOGIC_VF_QLE834X:  		*bar = QLCNIC_83XX_BAR0_LENGTH;  		break;  	default: @@ -751,7 +762,7 @@ static int qlcnic_setup_pci_map(struct pci_dev *pdev,  		return -EIO;  	} -	dev_info(&pdev->dev, "%dMB memory map\n", (int)(mem_len>>20)); +	dev_info(&pdev->dev, "%dKB memory map\n", (int)(mem_len >> 10));  	ahw->pci_base0 = mem_ptr0;  	ahw->pci_len0 = pci_len0; @@ -891,24 +902,50 @@ void qlcnic_set_vlan_config(struct qlcnic_adapter *adapter,  	else  		adapter->flags |= QLCNIC_TAGGING_ENABLED; -	if (esw_cfg->vlan_id) -		adapter->pvid = esw_cfg->vlan_id; -	else -		adapter->pvid = 0; +	if (esw_cfg->vlan_id) { +		adapter->rx_pvid = esw_cfg->vlan_id; +		adapter->tx_pvid = esw_cfg->vlan_id; +	} else { +		adapter->rx_pvid = 0; +		adapter->tx_pvid = 0; +	}  }  static int -qlcnic_vlan_rx_add(struct net_device *netdev, u16 vid) +qlcnic_vlan_rx_add(struct net_device *netdev, __be16 proto, u16 vid)  {  	struct qlcnic_adapter *adapter = netdev_priv(netdev); +	int err; + +	if (qlcnic_sriov_vf_check(adapter)) { +		err = qlcnic_sriov_cfg_vf_guest_vlan(adapter, vid, 1); +		if (err) { +			netdev_err(netdev, +				   "Cannot add VLAN filter for VLAN id %d, err=%d", +				   vid, err); +			return err; +		} +	} +  	set_bit(vid, adapter->vlans);  	return 0;  }  static int -qlcnic_vlan_rx_del(struct net_device *netdev, u16 vid) +qlcnic_vlan_rx_del(struct net_device *netdev, __be16 proto, u16 vid)  {  	struct qlcnic_adapter *adapter = netdev_priv(netdev); +	int err; + +	if (qlcnic_sriov_vf_check(adapter)) { +		err = qlcnic_sriov_cfg_vf_guest_vlan(adapter, vid, 0); +		if (err) { +			netdev_err(netdev, +				   "Cannot delete VLAN filter for VLAN id %d, err=%d", +				   vid, err); +			return err; +		} +	}  	qlcnic_restore_indev_addr(netdev, NETDEV_DOWN);  	clear_bit(vid, adapter->vlans); @@ -1292,7 +1329,8 @@ qlcnic_request_irq(struct qlcnic_adapter *adapter)  			}  		}  		if (qlcnic_83xx_check(adapter) && -		    (adapter->flags & QLCNIC_MSIX_ENABLED)) { +		    (adapter->flags & QLCNIC_MSIX_ENABLED) && +		    !(adapter->flags & QLCNIC_TX_INTR_SHARED)) {  			handler = qlcnic_msix_tx_intr;  			for (ring = 0; ring < adapter->max_drv_tx_rings;  			     ring++) { @@ -1328,7 +1366,8 @@ qlcnic_free_irq(struct qlcnic_adapter *adapter)  				free_irq(sds_ring->irq, sds_ring);  			}  		} -		if (qlcnic_83xx_check(adapter)) { +		if (qlcnic_83xx_check(adapter) && +		    !(adapter->flags & QLCNIC_TX_INTR_SHARED)) {  			for (ring = 0; ring < adapter->max_drv_tx_rings;  			     ring++) {  				tx_ring = &adapter->tx_ring[ring]; @@ -1418,9 +1457,12 @@ void __qlcnic_down(struct qlcnic_adapter *adapter, struct net_device *netdev)  	if (!test_and_clear_bit(__QLCNIC_DEV_UP, &adapter->state))  		return; +	if (qlcnic_sriov_vf_check(adapter)) +		qlcnic_sriov_cleanup_async_list(&adapter->ahw->sriov->bc);  	smp_mb();  	spin_lock(&adapter->tx_clean_lock);  	netif_carrier_off(netdev); +	adapter->ahw->linkup = 0;  	netif_tx_disable(netdev);  	qlcnic_free_mac_list(adapter); @@ -1685,7 +1727,7 @@ qlcnic_reset_context(struct qlcnic_adapter *adapter)  	return err;  } -static int +int  qlcnic_setup_netdev(struct qlcnic_adapter *adapter, struct net_device *netdev,  		    int pci_using_dac)  { @@ -1701,11 +1743,14 @@ qlcnic_setup_netdev(struct qlcnic_adapter *adapter, struct net_device *netdev,  	qlcnic_change_mtu(netdev, netdev->mtu); -	SET_ETHTOOL_OPS(netdev, &qlcnic_ethtool_ops); +	if (qlcnic_sriov_vf_check(adapter)) +		SET_ETHTOOL_OPS(netdev, &qlcnic_sriov_vf_ethtool_ops); +	else +		SET_ETHTOOL_OPS(netdev, &qlcnic_ethtool_ops);  	netdev->features |= (NETIF_F_SG | NETIF_F_IP_CSUM | NETIF_F_RXCSUM |  			     NETIF_F_IPV6_CSUM | NETIF_F_GRO | -			     NETIF_F_HW_VLAN_RX); +			     NETIF_F_HW_VLAN_CTAG_RX);  	netdev->vlan_features |= (NETIF_F_SG | NETIF_F_IP_CSUM |  				  NETIF_F_IPV6_CSUM); @@ -1720,7 +1765,10 @@ qlcnic_setup_netdev(struct qlcnic_adapter *adapter, struct net_device *netdev,  	}  	if (qlcnic_vlan_tx_check(adapter)) -		netdev->features |= (NETIF_F_HW_VLAN_TX); +		netdev->features |= (NETIF_F_HW_VLAN_CTAG_TX); + +	if (qlcnic_sriov_vf_check(adapter)) +		netdev->features |= NETIF_F_HW_VLAN_CTAG_FILTER;  	if (adapter->ahw->capabilities & QLCNIC_FW_CAPABILITY_HW_LRO)  		netdev->features |= NETIF_F_LRO; @@ -1820,6 +1868,9 @@ qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)  	u32 capab2;  	char board_name[QLCNIC_MAX_BOARD_NAME_LEN + 19]; /* MAC + ": " + name */ +	if (pdev->is_virtfn) +		return -ENODEV; +  	err = pci_enable_device(pdev);  	if (err)  		return err; @@ -1844,12 +1895,18 @@ qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)  	if (!ahw)  		goto err_out_free_res; -	if (ent->device == PCI_DEVICE_ID_QLOGIC_QLE824X) { +	switch (ent->device) { +	case PCI_DEVICE_ID_QLOGIC_QLE824X:  		ahw->hw_ops = &qlcnic_hw_ops; -		ahw->reg_tbl = (u32 *)qlcnic_reg_tbl; -	} else if (ent->device == PCI_DEVICE_ID_QLOGIC_QLE834X) { +		ahw->reg_tbl = (u32 *) qlcnic_reg_tbl; +		break; +	case PCI_DEVICE_ID_QLOGIC_QLE834X:  		qlcnic_83xx_register_map(ahw); -	} else { +		break; +	case PCI_DEVICE_ID_QLOGIC_VF_QLE834X: +		qlcnic_sriov_vf_register_map(ahw); +		break; +	default:  		goto err_out_free_hw_res;  	} @@ -1911,11 +1968,13 @@ qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)  	} else if (qlcnic_83xx_check(adapter)) {  		qlcnic_83xx_check_vf(adapter, ent);  		adapter->portnum = adapter->ahw->pci_func; -		err = qlcnic_83xx_init(adapter); +		err = qlcnic_83xx_init(adapter, pci_using_dac);  		if (err) {  			dev_err(&pdev->dev, "%s: failed\n", __func__);  			goto err_out_free_hw;  		} +		if (qlcnic_sriov_vf_check(adapter)) +			return 0;  	} else {  		dev_err(&pdev->dev,  			"%s: failed. Please Reboot\n", __func__); @@ -1932,6 +1991,12 @@ qlcnic_probe(struct pci_dev *pdev, const struct pci_device_id *ent)  			module_name(THIS_MODULE),  			board_name, adapter->ahw->revision_id);  	} + +	if (qlcnic_83xx_check(adapter) && !qlcnic_use_msi_x && +	    !!qlcnic_use_msi) +		dev_warn(&pdev->dev, +			 "83xx adapter do not support MSI interrupts\n"); +  	err = qlcnic_setup_intr(adapter, 0);  	if (err) {  		dev_err(&pdev->dev, "Failed to setup interrupt\n"); @@ -1999,7 +2064,7 @@ err_out_free_netdev:  	free_netdev(netdev);  err_out_iounmap: -	qlcnic_cleanup_pci_map(adapter); +	qlcnic_cleanup_pci_map(ahw);  err_out_free_hw_res:  	kfree(ahw); @@ -2024,11 +2089,13 @@ static void qlcnic_remove(struct pci_dev *pdev)  		return;  	netdev = adapter->netdev; +	qlcnic_sriov_pf_disable(adapter);  	qlcnic_cancel_idc_work(adapter);  	ahw = adapter->ahw;  	unregister_netdev(netdev); +	qlcnic_sriov_cleanup(adapter);  	if (qlcnic_83xx_check(adapter)) {  		qlcnic_83xx_free_mbx_intr(adapter); @@ -2054,7 +2121,7 @@ static void qlcnic_remove(struct pci_dev *pdev)  	qlcnic_remove_sysfs(adapter); -	qlcnic_cleanup_pci_map(adapter); +	qlcnic_cleanup_pci_map(adapter->ahw);  	qlcnic_release_firmware(adapter); @@ -2084,6 +2151,7 @@ static int __qlcnic_shutdown(struct pci_dev *pdev)  	if (netif_running(netdev))  		qlcnic_down(adapter, netdev); +	qlcnic_sriov_cleanup(adapter);  	if (qlcnic_82xx_check(adapter))  		qlcnic_clr_all_drv_state(adapter, 0); @@ -3238,8 +3306,10 @@ int qlcnic_set_max_rss(struct qlcnic_adapter *adapter, u8 data, size_t len)  	qlcnic_detach(adapter); -	if (qlcnic_83xx_check(adapter)) +	if (qlcnic_83xx_check(adapter)) {  		qlcnic_83xx_free_mbx_intr(adapter); +		qlcnic_83xx_enable_mbx_poll(adapter); +	}  	qlcnic_teardown_intr(adapter);  	err = qlcnic_setup_intr(adapter, data); @@ -3253,6 +3323,7 @@ int qlcnic_set_max_rss(struct qlcnic_adapter *adapter, u8 data, size_t len)  		/* register for NIC IDC AEN Events */  		qlcnic_83xx_register_nic_idc_func(adapter, 1);  		err = qlcnic_83xx_setup_mbx_intr(adapter); +		qlcnic_83xx_disable_mbx_poll(adapter);  		if (err) {  			dev_err(&adapter->pdev->dev,  				"failed to setup mbx interrupt\n"); @@ -3318,7 +3389,7 @@ void qlcnic_restore_indev_addr(struct net_device *netdev, unsigned long event)  	rcu_read_lock();  	for_each_set_bit(vid, adapter->vlans, VLAN_N_VID) { -		dev = __vlan_find_dev_deep(netdev, vid); +		dev = __vlan_find_dev_deep(netdev, htons(ETH_P_8021Q), vid);  		if (!dev)  			continue;  		qlcnic_config_indev_addr(adapter, dev, event); @@ -3432,7 +3503,10 @@ static struct pci_driver qlcnic_driver = {  	.resume = qlcnic_resume,  #endif  	.shutdown = qlcnic_shutdown, -	.err_handler = &qlcnic_err_handler +	.err_handler = &qlcnic_err_handler, +#ifdef CONFIG_QLCNIC_SRIOV +	.sriov_configure = qlcnic_pci_sriov_configure, +#endif  }; diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_minidump.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_minidump.c index abbd22c814a..4b9bab18ebd 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_minidump.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_minidump.c @@ -810,11 +810,8 @@ static int __qlcnic_fw_cmd_get_minidump_temp(struct qlcnic_adapter *adapter,  	tmp_addr = dma_alloc_coherent(&adapter->pdev->dev, temp_size,  				      &tmp_addr_t, GFP_KERNEL); -	if (!tmp_addr) { -		dev_err(&adapter->pdev->dev, -			"Can't get memory for FW dump template\n"); +	if (!tmp_addr)  		return -ENOMEM; -	}  	if (qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_GET_TEMP_HDR)) {  		err = -ENOMEM; diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov.h b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov.h new file mode 100644 index 00000000000..d85fbb57c25 --- /dev/null +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov.h @@ -0,0 +1,263 @@ +/* + * QLogic qlcnic NIC Driver + * Copyright (c) 2009-2013 QLogic Corporation + * + * See LICENSE.qlcnic for copyright and licensing details. + */ + +#ifndef _QLCNIC_83XX_SRIOV_H_ +#define _QLCNIC_83XX_SRIOV_H_ + +#include "qlcnic.h" +#include <linux/types.h> +#include <linux/pci.h> + +extern const u32 qlcnic_83xx_reg_tbl[]; +extern const u32 qlcnic_83xx_ext_reg_tbl[]; + +struct qlcnic_bc_payload { +	u64 payload[126]; +}; + +struct qlcnic_bc_hdr { +#if defined(__LITTLE_ENDIAN) +	u8	version; +	u8	msg_type:4; +	u8	rsvd1:3; +	u8	op_type:1; +	u8	num_cmds; +	u8	num_frags; +	u8	frag_num; +	u8	cmd_op; +	u16	seq_id; +	u64	rsvd3; +#elif defined(__BIG_ENDIAN) +	u8	num_frags; +	u8	num_cmds; +	u8	op_type:1; +	u8	rsvd1:3; +	u8	msg_type:4; +	u8	version; +	u16	seq_id; +	u8	cmd_op; +	u8	frag_num; +	u64	rsvd3; +#endif +}; + +enum qlcnic_bc_commands { +	QLCNIC_BC_CMD_CHANNEL_INIT = 0x0, +	QLCNIC_BC_CMD_CHANNEL_TERM = 0x1, +	QLCNIC_BC_CMD_GET_ACL = 0x2, +	QLCNIC_BC_CMD_CFG_GUEST_VLAN = 0x3, +}; + +#define QLC_BC_CMD 1 + +struct qlcnic_trans_list { +	/* Lock for manipulating list */ +	spinlock_t		lock; +	struct list_head	wait_list; +	int			count; +}; + +enum qlcnic_trans_state { +	QLC_INIT = 0, +	QLC_WAIT_FOR_CHANNEL_FREE, +	QLC_WAIT_FOR_RESP, +	QLC_ABORT, +	QLC_END, +}; + +struct qlcnic_bc_trans { +	u8				func_id; +	u8				active; +	u8				curr_rsp_frag; +	u8				curr_req_frag; +	u16				cmd_id; +	u16				req_pay_size; +	u16				rsp_pay_size; +	u32				trans_id; +	enum qlcnic_trans_state		trans_state; +	struct list_head		list; +	struct qlcnic_bc_hdr		*req_hdr; +	struct qlcnic_bc_hdr		*rsp_hdr; +	struct qlcnic_bc_payload	*req_pay; +	struct qlcnic_bc_payload	*rsp_pay; +	struct completion		resp_cmpl; +	struct qlcnic_vf_info		*vf; +}; + +enum qlcnic_vf_state { +	QLC_BC_VF_SEND = 0, +	QLC_BC_VF_RECV, +	QLC_BC_VF_CHANNEL, +	QLC_BC_VF_STATE, +	QLC_BC_VF_FLR, +	QLC_BC_VF_SOFT_FLR, +}; + +enum qlcnic_vlan_mode { +	QLC_NO_VLAN_MODE = 0, +	QLC_PVID_MODE, +	QLC_GUEST_VLAN_MODE, +}; + +struct qlcnic_resources { +	u16 num_tx_mac_filters; +	u16 num_rx_ucast_mac_filters; +	u16 num_rx_mcast_mac_filters; + +	u16 num_txvlan_keys; + +	u16 num_rx_queues; +	u16 num_tx_queues; + +	u16 num_rx_buf_rings; +	u16 num_rx_status_rings; + +	u16 num_destip; +	u32 num_lro_flows_supported; +	u16 max_local_ipv6_addrs; +	u16 max_remote_ipv6_addrs; +}; + +struct qlcnic_vport { +	u16			handle; +	u16			max_tx_bw; +	u16			min_tx_bw; +	u8			vlan_mode; +	u16			vlan; +	u8			qos; +	u8			mac[6]; +}; + +struct qlcnic_vf_info { +	u8				pci_func; +	u16				rx_ctx_id; +	u16				tx_ctx_id; +	unsigned long			state; +	struct completion		ch_free_cmpl; +	struct work_struct		trans_work; +	struct work_struct		flr_work; +	/* It synchronizes commands sent from VF */ +	struct mutex			send_cmd_lock; +	struct qlcnic_bc_trans		*send_cmd; +	struct qlcnic_bc_trans		*flr_trans; +	struct qlcnic_trans_list	rcv_act; +	struct qlcnic_trans_list	rcv_pend; +	struct qlcnic_adapter		*adapter; +	struct qlcnic_vport		*vp; +}; + +struct qlcnic_async_work_list { +	struct list_head	list; +	struct work_struct	work; +	void			*ptr; +}; + +struct qlcnic_back_channel { +	u16			trans_counter; +	struct workqueue_struct *bc_trans_wq; +	struct workqueue_struct *bc_async_wq; +	struct workqueue_struct *bc_flr_wq; +	struct list_head	async_list; +}; + +struct qlcnic_sriov { +	u16				vp_handle; +	u8				num_vfs; +	u8				any_vlan; +	u8				vlan_mode; +	u16				num_allowed_vlans; +	u16				*allowed_vlans; +	u16				vlan; +	struct qlcnic_resources		ff_max; +	struct qlcnic_back_channel	bc; +	struct qlcnic_vf_info		*vf_info; +}; + +int qlcnic_sriov_init(struct qlcnic_adapter *, int); +void qlcnic_sriov_cleanup(struct qlcnic_adapter *); +void __qlcnic_sriov_cleanup(struct qlcnic_adapter *); +void qlcnic_sriov_vf_register_map(struct qlcnic_hardware_context *); +int qlcnic_sriov_vf_init(struct qlcnic_adapter *, int); +void qlcnic_sriov_vf_set_ops(struct qlcnic_adapter *); +int qlcnic_sriov_func_to_index(struct qlcnic_adapter *, u8); +int qlcnic_sriov_channel_cfg_cmd(struct qlcnic_adapter *, u8); +void qlcnic_sriov_handle_bc_event(struct qlcnic_adapter *, u32); +int qlcnic_sriov_cfg_bc_intr(struct qlcnic_adapter *, u8); +void qlcnic_sriov_cleanup_async_list(struct qlcnic_back_channel *); +void qlcnic_sriov_cleanup_list(struct qlcnic_trans_list *); +int __qlcnic_sriov_add_act_list(struct qlcnic_sriov *, struct qlcnic_vf_info *, +				struct qlcnic_bc_trans *); +int qlcnic_sriov_get_vf_vport_info(struct qlcnic_adapter *, +				   struct qlcnic_info *, u16); +int qlcnic_sriov_cfg_vf_guest_vlan(struct qlcnic_adapter *, u16, u8); + +static inline bool qlcnic_sriov_enable_check(struct qlcnic_adapter *adapter) +{ +	return test_bit(__QLCNIC_SRIOV_ENABLE, &adapter->state) ? true : false; +} + +#ifdef CONFIG_QLCNIC_SRIOV +void qlcnic_sriov_pf_process_bc_cmd(struct qlcnic_adapter *, +				    struct qlcnic_bc_trans *, +				    struct qlcnic_cmd_args *); +void qlcnic_sriov_pf_disable(struct qlcnic_adapter *); +void qlcnic_sriov_pf_cleanup(struct qlcnic_adapter *); +int qlcnic_pci_sriov_configure(struct pci_dev *, int); +void qlcnic_pf_set_interface_id_create_rx_ctx(struct qlcnic_adapter *, u32 *); +void qlcnic_pf_set_interface_id_create_tx_ctx(struct qlcnic_adapter *, u32 *); +void qlcnic_pf_set_interface_id_del_rx_ctx(struct qlcnic_adapter *, u32 *); +void qlcnic_pf_set_interface_id_del_tx_ctx(struct qlcnic_adapter *, u32 *); +void qlcnic_pf_set_interface_id_promisc(struct qlcnic_adapter *, u32 *); +void qlcnic_pf_set_interface_id_ipaddr(struct qlcnic_adapter *, u32 *); +void qlcnic_pf_set_interface_id_macaddr(struct qlcnic_adapter *, u32 *); +void qlcnic_sriov_pf_handle_flr(struct qlcnic_sriov *, struct qlcnic_vf_info *); +bool qlcnic_sriov_soft_flr_check(struct qlcnic_adapter *, +				 struct qlcnic_bc_trans *, +				 struct qlcnic_vf_info *); +void qlcnic_sriov_pf_reset(struct qlcnic_adapter *); +int qlcnic_sriov_pf_reinit(struct qlcnic_adapter *); +int qlcnic_sriov_set_vf_mac(struct net_device *, int, u8 *); +int qlcnic_sriov_set_vf_tx_rate(struct net_device *, int, int); +int qlcnic_sriov_get_vf_config(struct net_device *, int , +			       struct ifla_vf_info *); +int qlcnic_sriov_set_vf_vlan(struct net_device *, int, u16, u8); +#else +static inline void qlcnic_sriov_pf_disable(struct qlcnic_adapter *adapter) {} +static inline void qlcnic_sriov_pf_cleanup(struct qlcnic_adapter *adapter) {} +static inline void +qlcnic_pf_set_interface_id_create_rx_ctx(struct qlcnic_adapter *adapter, +					 u32 *int_id) {} +static inline void +qlcnic_pf_set_interface_id_create_tx_ctx(struct qlcnic_adapter *adapter, +					 u32 *int_id) {} +static inline void +qlcnic_pf_set_interface_id_del_rx_ctx(struct qlcnic_adapter *adapter, +				      u32 *int_id) {} +static inline void +qlcnic_pf_set_interface_id_del_tx_ctx(struct qlcnic_adapter *adapter, +				      u32 *int_id) {} +static inline void +qlcnic_pf_set_interface_id_ipaddr(struct qlcnic_adapter *adapter, u32 *int_id) +{} +static inline void +qlcnic_pf_set_interface_id_macaddr(struct qlcnic_adapter *adapter, u32 *int_id) +{} +static inline void +qlcnic_pf_set_interface_id_promisc(struct qlcnic_adapter *adapter, u32 *int_id) +{} +static inline void qlcnic_sriov_pf_handle_flr(struct qlcnic_sriov *sriov, +					      struct qlcnic_vf_info *vf) {} +static inline bool qlcnic_sriov_soft_flr_check(struct qlcnic_adapter *adapter, +					       struct qlcnic_bc_trans *trans, +					       struct qlcnic_vf_info *vf) +{ return false; } +static inline void qlcnic_sriov_pf_reset(struct qlcnic_adapter *adapter) {} +static inline int qlcnic_sriov_pf_reinit(struct qlcnic_adapter *adapter) +{ return 0; } +#endif + +#endif diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c new file mode 100644 index 00000000000..44d547d78b8 --- /dev/null +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_common.c @@ -0,0 +1,1954 @@ +/* + * QLogic qlcnic NIC Driver + * Copyright (c) 2009-2013 QLogic Corporation + * + * See LICENSE.qlcnic for copyright and licensing details. + */ + +#include "qlcnic_sriov.h" +#include "qlcnic.h" +#include "qlcnic_83xx_hw.h" +#include <linux/types.h> + +#define QLC_BC_COMMAND	0 +#define QLC_BC_RESPONSE	1 + +#define QLC_MBOX_RESP_TIMEOUT		(10 * HZ) +#define QLC_MBOX_CH_FREE_TIMEOUT	(10 * HZ) + +#define QLC_BC_MSG		0 +#define QLC_BC_CFREE		1 +#define QLC_BC_FLR		2 +#define QLC_BC_HDR_SZ		16 +#define QLC_BC_PAYLOAD_SZ	(1024 - QLC_BC_HDR_SZ) + +#define QLC_DEFAULT_RCV_DESCRIPTORS_SRIOV_VF		2048 +#define QLC_DEFAULT_JUMBO_RCV_DESCRIPTORS_SRIOV_VF	512 + +#define QLC_83XX_VF_RESET_FAIL_THRESH	8 +#define QLC_BC_CMD_MAX_RETRY_CNT	5 + +static void qlcnic_sriov_vf_free_mac_list(struct qlcnic_adapter *); +static int qlcnic_sriov_alloc_bc_mbx_args(struct qlcnic_cmd_args *, u32); +static void qlcnic_sriov_vf_poll_dev_state(struct work_struct *); +static void qlcnic_sriov_vf_cancel_fw_work(struct qlcnic_adapter *); +static void qlcnic_sriov_cleanup_transaction(struct qlcnic_bc_trans *); +static int qlcnic_sriov_vf_mbx_op(struct qlcnic_adapter *, +				  struct qlcnic_cmd_args *); + +static struct qlcnic_hardware_ops qlcnic_sriov_vf_hw_ops = { +	.read_crb			= qlcnic_83xx_read_crb, +	.write_crb			= qlcnic_83xx_write_crb, +	.read_reg			= qlcnic_83xx_rd_reg_indirect, +	.write_reg			= qlcnic_83xx_wrt_reg_indirect, +	.get_mac_address		= qlcnic_83xx_get_mac_address, +	.setup_intr			= qlcnic_83xx_setup_intr, +	.alloc_mbx_args			= qlcnic_83xx_alloc_mbx_args, +	.mbx_cmd			= qlcnic_sriov_vf_mbx_op, +	.get_func_no			= qlcnic_83xx_get_func_no, +	.api_lock			= qlcnic_83xx_cam_lock, +	.api_unlock			= qlcnic_83xx_cam_unlock, +	.process_lb_rcv_ring_diag	= qlcnic_83xx_process_rcv_ring_diag, +	.create_rx_ctx			= qlcnic_83xx_create_rx_ctx, +	.create_tx_ctx			= qlcnic_83xx_create_tx_ctx, +	.del_rx_ctx			= qlcnic_83xx_del_rx_ctx, +	.del_tx_ctx			= qlcnic_83xx_del_tx_ctx, +	.setup_link_event		= qlcnic_83xx_setup_link_event, +	.get_nic_info			= qlcnic_83xx_get_nic_info, +	.get_pci_info			= qlcnic_83xx_get_pci_info, +	.set_nic_info			= qlcnic_83xx_set_nic_info, +	.change_macvlan			= qlcnic_83xx_sre_macaddr_change, +	.napi_enable			= qlcnic_83xx_napi_enable, +	.napi_disable			= qlcnic_83xx_napi_disable, +	.config_intr_coal		= qlcnic_83xx_config_intr_coal, +	.config_rss			= qlcnic_83xx_config_rss, +	.config_hw_lro			= qlcnic_83xx_config_hw_lro, +	.config_promisc_mode		= qlcnic_83xx_nic_set_promisc, +	.change_l2_filter		= qlcnic_83xx_change_l2_filter, +	.get_board_info			= qlcnic_83xx_get_port_info, +	.free_mac_list			= qlcnic_sriov_vf_free_mac_list, +}; + +static struct qlcnic_nic_template qlcnic_sriov_vf_ops = { +	.config_bridged_mode	= qlcnic_config_bridged_mode, +	.config_led		= qlcnic_config_led, +	.cancel_idc_work        = qlcnic_sriov_vf_cancel_fw_work, +	.napi_add		= qlcnic_83xx_napi_add, +	.napi_del		= qlcnic_83xx_napi_del, +	.config_ipaddr		= qlcnic_83xx_config_ipaddr, +	.clear_legacy_intr	= qlcnic_83xx_clear_legacy_intr, +}; + +static const struct qlcnic_mailbox_metadata qlcnic_sriov_bc_mbx_tbl[] = { +	{QLCNIC_BC_CMD_CHANNEL_INIT, 2, 2}, +	{QLCNIC_BC_CMD_CHANNEL_TERM, 2, 2}, +	{QLCNIC_BC_CMD_GET_ACL, 3, 14}, +	{QLCNIC_BC_CMD_CFG_GUEST_VLAN, 2, 2}, +}; + +static inline bool qlcnic_sriov_bc_msg_check(u32 val) +{ +	return (val & (1 << QLC_BC_MSG)) ? true : false; +} + +static inline bool qlcnic_sriov_channel_free_check(u32 val) +{ +	return (val & (1 << QLC_BC_CFREE)) ? true : false; +} + +static inline bool qlcnic_sriov_flr_check(u32 val) +{ +	return (val & (1 << QLC_BC_FLR)) ? true : false; +} + +static inline u8 qlcnic_sriov_target_func_id(u32 val) +{ +	return (val >> 4) & 0xff; +} + +static int qlcnic_sriov_virtid_fn(struct qlcnic_adapter *adapter, int vf_id) +{ +	struct pci_dev *dev = adapter->pdev; +	int pos; +	u16 stride, offset; + +	if (qlcnic_sriov_vf_check(adapter)) +		return 0; + +	pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_SRIOV); +	pci_read_config_word(dev, pos + PCI_SRIOV_VF_OFFSET, &offset); +	pci_read_config_word(dev, pos + PCI_SRIOV_VF_STRIDE, &stride); + +	return (dev->devfn + offset + stride * vf_id) & 0xff; +} + +int qlcnic_sriov_init(struct qlcnic_adapter *adapter, int num_vfs) +{ +	struct qlcnic_sriov *sriov; +	struct qlcnic_back_channel *bc; +	struct workqueue_struct *wq; +	struct qlcnic_vport *vp; +	struct qlcnic_vf_info *vf; +	int err, i; + +	if (!qlcnic_sriov_enable_check(adapter)) +		return -EIO; + +	sriov  = kzalloc(sizeof(struct qlcnic_sriov), GFP_KERNEL); +	if (!sriov) +		return -ENOMEM; + +	adapter->ahw->sriov = sriov; +	sriov->num_vfs = num_vfs; +	bc = &sriov->bc; +	sriov->vf_info = kzalloc(sizeof(struct qlcnic_vf_info) * +				 num_vfs, GFP_KERNEL); +	if (!sriov->vf_info) { +		err = -ENOMEM; +		goto qlcnic_free_sriov; +	} + +	wq = create_singlethread_workqueue("bc-trans"); +	if (wq == NULL) { +		err = -ENOMEM; +		dev_err(&adapter->pdev->dev, +			"Cannot create bc-trans workqueue\n"); +		goto qlcnic_free_vf_info; +	} + +	bc->bc_trans_wq = wq; + +	wq = create_singlethread_workqueue("async"); +	if (wq == NULL) { +		err = -ENOMEM; +		dev_err(&adapter->pdev->dev, "Cannot create async workqueue\n"); +		goto qlcnic_destroy_trans_wq; +	} + +	bc->bc_async_wq =  wq; +	INIT_LIST_HEAD(&bc->async_list); + +	for (i = 0; i < num_vfs; i++) { +		vf = &sriov->vf_info[i]; +		vf->adapter = adapter; +		vf->pci_func = qlcnic_sriov_virtid_fn(adapter, i); +		mutex_init(&vf->send_cmd_lock); +		INIT_LIST_HEAD(&vf->rcv_act.wait_list); +		INIT_LIST_HEAD(&vf->rcv_pend.wait_list); +		spin_lock_init(&vf->rcv_act.lock); +		spin_lock_init(&vf->rcv_pend.lock); +		init_completion(&vf->ch_free_cmpl); + +		if (qlcnic_sriov_pf_check(adapter)) { +			vp = kzalloc(sizeof(struct qlcnic_vport), GFP_KERNEL); +			if (!vp) { +				err = -ENOMEM; +				goto qlcnic_destroy_async_wq; +			} +			sriov->vf_info[i].vp = vp; +			vp->max_tx_bw = MAX_BW; +			random_ether_addr(vp->mac); +			dev_info(&adapter->pdev->dev, +				 "MAC Address %pM is configured for VF %d\n", +				 vp->mac, i); +		} +	} + +	return 0; + +qlcnic_destroy_async_wq: +	destroy_workqueue(bc->bc_async_wq); + +qlcnic_destroy_trans_wq: +	destroy_workqueue(bc->bc_trans_wq); + +qlcnic_free_vf_info: +	kfree(sriov->vf_info); + +qlcnic_free_sriov: +	kfree(adapter->ahw->sriov); +	return err; +} + +void qlcnic_sriov_cleanup_list(struct qlcnic_trans_list *t_list) +{ +	struct qlcnic_bc_trans *trans; +	struct qlcnic_cmd_args cmd; +	unsigned long flags; + +	spin_lock_irqsave(&t_list->lock, flags); + +	while (!list_empty(&t_list->wait_list)) { +		trans = list_first_entry(&t_list->wait_list, +					 struct qlcnic_bc_trans, list); +		list_del(&trans->list); +		t_list->count--; +		cmd.req.arg = (u32 *)trans->req_pay; +		cmd.rsp.arg = (u32 *)trans->rsp_pay; +		qlcnic_free_mbx_args(&cmd); +		qlcnic_sriov_cleanup_transaction(trans); +	} + +	spin_unlock_irqrestore(&t_list->lock, flags); +} + +void __qlcnic_sriov_cleanup(struct qlcnic_adapter *adapter) +{ +	struct qlcnic_sriov *sriov = adapter->ahw->sriov; +	struct qlcnic_back_channel *bc = &sriov->bc; +	struct qlcnic_vf_info *vf; +	int i; + +	if (!qlcnic_sriov_enable_check(adapter)) +		return; + +	qlcnic_sriov_cleanup_async_list(bc); +	destroy_workqueue(bc->bc_async_wq); + +	for (i = 0; i < sriov->num_vfs; i++) { +		vf = &sriov->vf_info[i]; +		qlcnic_sriov_cleanup_list(&vf->rcv_pend); +		cancel_work_sync(&vf->trans_work); +		qlcnic_sriov_cleanup_list(&vf->rcv_act); +	} + +	destroy_workqueue(bc->bc_trans_wq); + +	for (i = 0; i < sriov->num_vfs; i++) +		kfree(sriov->vf_info[i].vp); + +	kfree(sriov->vf_info); +	kfree(adapter->ahw->sriov); +} + +static void qlcnic_sriov_vf_cleanup(struct qlcnic_adapter *adapter) +{ +	qlcnic_sriov_channel_cfg_cmd(adapter, QLCNIC_BC_CMD_CHANNEL_TERM); +	qlcnic_sriov_cfg_bc_intr(adapter, 0); +	__qlcnic_sriov_cleanup(adapter); +} + +void qlcnic_sriov_cleanup(struct qlcnic_adapter *adapter) +{ +	if (qlcnic_sriov_pf_check(adapter)) +		qlcnic_sriov_pf_cleanup(adapter); + +	if (qlcnic_sriov_vf_check(adapter)) +		qlcnic_sriov_vf_cleanup(adapter); +} + +static int qlcnic_sriov_post_bc_msg(struct qlcnic_adapter *adapter, u32 *hdr, +				    u32 *pay, u8 pci_func, u8 size) +{ +	struct qlcnic_hardware_context *ahw = adapter->ahw; +	unsigned long flags; +	u32 rsp, mbx_val, fw_data, rsp_num, mbx_cmd, val; +	u16 opcode; +	u8 mbx_err_code; +	int i, j; + +	opcode = ((struct qlcnic_bc_hdr *)hdr)->cmd_op; + +	if (!test_bit(QLC_83XX_MBX_READY, &adapter->ahw->idc.status)) { +		dev_info(&adapter->pdev->dev, +			 "Mailbox cmd attempted, 0x%x\n", opcode); +		dev_info(&adapter->pdev->dev, "Mailbox detached\n"); +		return 0; +	} + +	spin_lock_irqsave(&ahw->mbx_lock, flags); + +	mbx_val = QLCRDX(ahw, QLCNIC_HOST_MBX_CTRL); +	if (mbx_val) { +		QLCDB(adapter, DRV, "Mailbox cmd attempted, 0x%x\n", opcode); +		spin_unlock_irqrestore(&ahw->mbx_lock, flags); +		return QLCNIC_RCODE_TIMEOUT; +	} +	/* Fill in mailbox registers */ +	val = size + (sizeof(struct qlcnic_bc_hdr) / sizeof(u32)); +	mbx_cmd = 0x31 | (val << 16) | (adapter->ahw->fw_hal_version << 29); + +	writel(mbx_cmd, QLCNIC_MBX_HOST(ahw, 0)); +	mbx_cmd = 0x1 | (1 << 4); + +	if (qlcnic_sriov_pf_check(adapter)) +		mbx_cmd |= (pci_func << 5); + +	writel(mbx_cmd, QLCNIC_MBX_HOST(ahw, 1)); +	for (i = 2, j = 0; j < (sizeof(struct qlcnic_bc_hdr) / sizeof(u32)); +			i++, j++) { +		writel(*(hdr++), QLCNIC_MBX_HOST(ahw, i)); +	} +	for (j = 0; j < size; j++, i++) +		writel(*(pay++), QLCNIC_MBX_HOST(ahw, i)); + +	/* Signal FW about the impending command */ +	QLCWRX(ahw, QLCNIC_HOST_MBX_CTRL, QLCNIC_SET_OWNER); + +	/* Waiting for the mailbox cmd to complete and while waiting here +	 * some AEN might arrive. If more than 5 seconds expire we can +	 * assume something is wrong. +	 */ +poll: +	rsp = qlcnic_83xx_mbx_poll(adapter); +	if (rsp != QLCNIC_RCODE_TIMEOUT) { +		/* Get the FW response data */ +		fw_data = readl(QLCNIC_MBX_FW(ahw, 0)); +		if (fw_data &  QLCNIC_MBX_ASYNC_EVENT) { +			__qlcnic_83xx_process_aen(adapter); +			mbx_val = QLCRDX(ahw, QLCNIC_HOST_MBX_CTRL); +			if (mbx_val) +				goto poll; +		} +		mbx_err_code = QLCNIC_MBX_STATUS(fw_data); +		rsp_num = QLCNIC_MBX_NUM_REGS(fw_data); +		opcode = QLCNIC_MBX_RSP(fw_data); + +		switch (mbx_err_code) { +		case QLCNIC_MBX_RSP_OK: +		case QLCNIC_MBX_PORT_RSP_OK: +			rsp = QLCNIC_RCODE_SUCCESS; +			break; +		default: +			if (opcode == QLCNIC_CMD_CONFIG_MAC_VLAN) { +				rsp = qlcnic_83xx_mac_rcode(adapter); +				if (!rsp) +					goto out; +			} +			dev_err(&adapter->pdev->dev, +				"MBX command 0x%x failed with err:0x%x\n", +				opcode, mbx_err_code); +			rsp = mbx_err_code; +			break; +		} +		goto out; +	} + +	dev_err(&adapter->pdev->dev, "MBX command 0x%x timed out\n", +		QLCNIC_MBX_RSP(mbx_cmd)); +	rsp = QLCNIC_RCODE_TIMEOUT; +out: +	/* clear fw mbx control register */ +	QLCWRX(ahw, QLCNIC_FW_MBX_CTRL, QLCNIC_CLR_OWNER); +	spin_unlock_irqrestore(&adapter->ahw->mbx_lock, flags); +	return rsp; +} + +static void qlcnic_sriov_vf_cfg_buff_desc(struct qlcnic_adapter *adapter) +{ +	adapter->num_rxd = QLC_DEFAULT_RCV_DESCRIPTORS_SRIOV_VF; +	adapter->max_rxd = MAX_RCV_DESCRIPTORS_10G; +	adapter->num_jumbo_rxd = QLC_DEFAULT_JUMBO_RCV_DESCRIPTORS_SRIOV_VF; +	adapter->max_jumbo_rxd = MAX_JUMBO_RCV_DESCRIPTORS_10G; +	adapter->num_txd = MAX_CMD_DESCRIPTORS; +	adapter->max_rds_rings = MAX_RDS_RINGS; +} + +int qlcnic_sriov_get_vf_vport_info(struct qlcnic_adapter *adapter, +				   struct qlcnic_info *npar_info, u16 vport_id) +{ +	struct device *dev = &adapter->pdev->dev; +	struct qlcnic_cmd_args cmd; +	int err; +	u32 status; + +	err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_GET_NIC_INFO); +	if (err) +		return err; + +	cmd.req.arg[1] = vport_id << 16 | 0x1; +	err = qlcnic_issue_cmd(adapter, &cmd); +	if (err) { +		dev_err(&adapter->pdev->dev, +			"Failed to get vport info, err=%d\n", err); +		qlcnic_free_mbx_args(&cmd); +		return err; +	} + +	status = cmd.rsp.arg[2] & 0xffff; +	if (status & BIT_0) +		npar_info->min_tx_bw = MSW(cmd.rsp.arg[2]); +	if (status & BIT_1) +		npar_info->max_tx_bw = LSW(cmd.rsp.arg[3]); +	if (status & BIT_2) +		npar_info->max_tx_ques = MSW(cmd.rsp.arg[3]); +	if (status & BIT_3) +		npar_info->max_tx_mac_filters = LSW(cmd.rsp.arg[4]); +	if (status & BIT_4) +		npar_info->max_rx_mcast_mac_filters = MSW(cmd.rsp.arg[4]); +	if (status & BIT_5) +		npar_info->max_rx_ucast_mac_filters = LSW(cmd.rsp.arg[5]); +	if (status & BIT_6) +		npar_info->max_rx_ip_addr = MSW(cmd.rsp.arg[5]); +	if (status & BIT_7) +		npar_info->max_rx_lro_flow = LSW(cmd.rsp.arg[6]); +	if (status & BIT_8) +		npar_info->max_rx_status_rings = MSW(cmd.rsp.arg[6]); +	if (status & BIT_9) +		npar_info->max_rx_buf_rings = LSW(cmd.rsp.arg[7]); + +	npar_info->max_rx_ques = MSW(cmd.rsp.arg[7]); +	npar_info->max_tx_vlan_keys = LSW(cmd.rsp.arg[8]); +	npar_info->max_local_ipv6_addrs = MSW(cmd.rsp.arg[8]); +	npar_info->max_remote_ipv6_addrs = LSW(cmd.rsp.arg[9]); + +	dev_info(dev, "\n\tmin_tx_bw: %d, max_tx_bw: %d max_tx_ques: %d,\n" +		 "\tmax_tx_mac_filters: %d max_rx_mcast_mac_filters: %d,\n" +		 "\tmax_rx_ucast_mac_filters: 0x%x, max_rx_ip_addr: %d,\n" +		 "\tmax_rx_lro_flow: %d max_rx_status_rings: %d,\n" +		 "\tmax_rx_buf_rings: %d, max_rx_ques: %d, max_tx_vlan_keys %d\n" +		 "\tlocal_ipv6_addr: %d, remote_ipv6_addr: %d\n", +		 npar_info->min_tx_bw, npar_info->max_tx_bw, +		 npar_info->max_tx_ques, npar_info->max_tx_mac_filters, +		 npar_info->max_rx_mcast_mac_filters, +		 npar_info->max_rx_ucast_mac_filters, npar_info->max_rx_ip_addr, +		 npar_info->max_rx_lro_flow, npar_info->max_rx_status_rings, +		 npar_info->max_rx_buf_rings, npar_info->max_rx_ques, +		 npar_info->max_tx_vlan_keys, npar_info->max_local_ipv6_addrs, +		 npar_info->max_remote_ipv6_addrs); + +	qlcnic_free_mbx_args(&cmd); +	return err; +} + +static int qlcnic_sriov_set_pvid_mode(struct qlcnic_adapter *adapter, +				      struct qlcnic_cmd_args *cmd) +{ +	adapter->rx_pvid = (cmd->rsp.arg[1] >> 16) & 0xffff; +	adapter->flags &= ~QLCNIC_TAGGING_ENABLED; +	return 0; +} + +static int qlcnic_sriov_set_guest_vlan_mode(struct qlcnic_adapter *adapter, +					    struct qlcnic_cmd_args *cmd) +{ +	struct qlcnic_sriov *sriov = adapter->ahw->sriov; +	int i, num_vlans; +	u16 *vlans; + +	if (sriov->allowed_vlans) +		return 0; + +	sriov->any_vlan = cmd->rsp.arg[2] & 0xf; +	if (!sriov->any_vlan) +		return 0; + +	sriov->num_allowed_vlans = cmd->rsp.arg[2] >> 16; +	num_vlans = sriov->num_allowed_vlans; +	sriov->allowed_vlans = kzalloc(sizeof(u16) * num_vlans, GFP_KERNEL); +	if (!sriov->allowed_vlans) +		return -ENOMEM; + +	vlans = (u16 *)&cmd->rsp.arg[3]; +	for (i = 0; i < num_vlans; i++) +		sriov->allowed_vlans[i] = vlans[i]; + +	return 0; +} + +static int qlcnic_sriov_get_vf_acl(struct qlcnic_adapter *adapter) +{ +	struct qlcnic_sriov *sriov = adapter->ahw->sriov; +	struct qlcnic_cmd_args cmd; +	int ret; + +	ret = qlcnic_sriov_alloc_bc_mbx_args(&cmd, QLCNIC_BC_CMD_GET_ACL); +	if (ret) +		return ret; + +	ret = qlcnic_issue_cmd(adapter, &cmd); +	if (ret) { +		dev_err(&adapter->pdev->dev, "Failed to get ACL, err=%d\n", +			ret); +	} else { +		sriov->vlan_mode = cmd.rsp.arg[1] & 0x3; +		switch (sriov->vlan_mode) { +		case QLC_GUEST_VLAN_MODE: +			ret = qlcnic_sriov_set_guest_vlan_mode(adapter, &cmd); +			break; +		case QLC_PVID_MODE: +			ret = qlcnic_sriov_set_pvid_mode(adapter, &cmd); +			break; +		} +	} + +	qlcnic_free_mbx_args(&cmd); +	return ret; +} + +static int qlcnic_sriov_vf_init_driver(struct qlcnic_adapter *adapter) +{ +	struct qlcnic_info nic_info; +	struct qlcnic_hardware_context *ahw = adapter->ahw; +	int err; + +	err = qlcnic_sriov_get_vf_vport_info(adapter, &nic_info, 0); +	if (err) +		return err; + +	err = qlcnic_get_nic_info(adapter, &nic_info, ahw->pci_func); +	if (err) +		return -EIO; + +	err = qlcnic_sriov_get_vf_acl(adapter); +	if (err) +		return err; + +	if (qlcnic_83xx_get_port_info(adapter)) +		return -EIO; + +	qlcnic_sriov_vf_cfg_buff_desc(adapter); +	adapter->flags |= QLCNIC_ADAPTER_INITIALIZED; +	dev_info(&adapter->pdev->dev, "HAL Version: %d\n", +		 adapter->ahw->fw_hal_version); + +	ahw->physical_port = (u8) nic_info.phys_port; +	ahw->switch_mode = nic_info.switch_mode; +	ahw->max_mtu = nic_info.max_mtu; +	ahw->op_mode = nic_info.op_mode; +	ahw->capabilities = nic_info.capabilities; +	return 0; +} + +static int qlcnic_sriov_setup_vf(struct qlcnic_adapter *adapter, +				 int pci_using_dac) +{ +	int err; + +	INIT_LIST_HEAD(&adapter->vf_mc_list); +	if (!qlcnic_use_msi_x && !!qlcnic_use_msi) +		dev_warn(&adapter->pdev->dev, +			 "83xx adapter do not support MSI interrupts\n"); + +	err = qlcnic_setup_intr(adapter, 1); +	if (err) { +		dev_err(&adapter->pdev->dev, "Failed to setup interrupt\n"); +		goto err_out_disable_msi; +	} + +	err = qlcnic_83xx_setup_mbx_intr(adapter); +	if (err) +		goto err_out_disable_msi; + +	err = qlcnic_sriov_init(adapter, 1); +	if (err) +		goto err_out_disable_mbx_intr; + +	err = qlcnic_sriov_cfg_bc_intr(adapter, 1); +	if (err) +		goto err_out_cleanup_sriov; + +	err = qlcnic_sriov_channel_cfg_cmd(adapter, QLCNIC_BC_CMD_CHANNEL_INIT); +	if (err) +		goto err_out_disable_bc_intr; + +	err = qlcnic_sriov_vf_init_driver(adapter); +	if (err) +		goto err_out_send_channel_term; + +	err = qlcnic_setup_netdev(adapter, adapter->netdev, pci_using_dac); +	if (err) +		goto err_out_send_channel_term; + +	pci_set_drvdata(adapter->pdev, adapter); +	dev_info(&adapter->pdev->dev, "%s: XGbE port initialized\n", +		 adapter->netdev->name); +	qlcnic_schedule_work(adapter, qlcnic_sriov_vf_poll_dev_state, +			     adapter->ahw->idc.delay); +	return 0; + +err_out_send_channel_term: +	qlcnic_sriov_channel_cfg_cmd(adapter, QLCNIC_BC_CMD_CHANNEL_TERM); + +err_out_disable_bc_intr: +	qlcnic_sriov_cfg_bc_intr(adapter, 0); + +err_out_cleanup_sriov: +	__qlcnic_sriov_cleanup(adapter); + +err_out_disable_mbx_intr: +	qlcnic_83xx_free_mbx_intr(adapter); + +err_out_disable_msi: +	qlcnic_teardown_intr(adapter); +	return err; +} + +static int qlcnic_sriov_check_dev_ready(struct qlcnic_adapter *adapter) +{ +	u32 state; + +	do { +		msleep(20); +		if (++adapter->fw_fail_cnt > QLC_BC_CMD_MAX_RETRY_CNT) +			return -EIO; +		state = QLCRDX(adapter->ahw, QLC_83XX_IDC_DEV_STATE); +	} while (state != QLC_83XX_IDC_DEV_READY); + +	return 0; +} + +int qlcnic_sriov_vf_init(struct qlcnic_adapter *adapter, int pci_using_dac) +{ +	struct qlcnic_hardware_context *ahw = adapter->ahw; +	int err; + +	spin_lock_init(&ahw->mbx_lock); +	set_bit(QLC_83XX_MBX_READY, &ahw->idc.status); +	set_bit(QLC_83XX_MODULE_LOADED, &ahw->idc.status); +	ahw->idc.delay = QLC_83XX_IDC_FW_POLL_DELAY; +	ahw->reset_context = 0; +	adapter->fw_fail_cnt = 0; +	ahw->msix_supported = 1; +	adapter->need_fw_reset = 0; +	adapter->flags |= QLCNIC_TX_INTR_SHARED; + +	err = qlcnic_sriov_check_dev_ready(adapter); +	if (err) +		return err; + +	err = qlcnic_sriov_setup_vf(adapter, pci_using_dac); +	if (err) +		return err; + +	if (qlcnic_read_mac_addr(adapter)) +		dev_warn(&adapter->pdev->dev, "failed to read mac addr\n"); + +	clear_bit(__QLCNIC_RESETTING, &adapter->state); +	return 0; +} + +void qlcnic_sriov_vf_set_ops(struct qlcnic_adapter *adapter) +{ +	struct qlcnic_hardware_context *ahw = adapter->ahw; + +	ahw->op_mode = QLCNIC_SRIOV_VF_FUNC; +	dev_info(&adapter->pdev->dev, +		 "HAL Version: %d Non Privileged SRIOV function\n", +		 ahw->fw_hal_version); +	adapter->nic_ops = &qlcnic_sriov_vf_ops; +	set_bit(__QLCNIC_SRIOV_ENABLE, &adapter->state); +	return; +} + +void qlcnic_sriov_vf_register_map(struct qlcnic_hardware_context *ahw) +{ +	ahw->hw_ops		= &qlcnic_sriov_vf_hw_ops; +	ahw->reg_tbl		= (u32 *)qlcnic_83xx_reg_tbl; +	ahw->ext_reg_tbl	= (u32 *)qlcnic_83xx_ext_reg_tbl; +} + +static u32 qlcnic_sriov_get_bc_paysize(u32 real_pay_size, u8 curr_frag) +{ +	u32 pay_size; + +	pay_size = real_pay_size / ((curr_frag + 1) * QLC_BC_PAYLOAD_SZ); + +	if (pay_size) +		pay_size = QLC_BC_PAYLOAD_SZ; +	else +		pay_size = real_pay_size % QLC_BC_PAYLOAD_SZ; + +	return pay_size; +} + +int qlcnic_sriov_func_to_index(struct qlcnic_adapter *adapter, u8 pci_func) +{ +	struct qlcnic_vf_info *vf_info = adapter->ahw->sriov->vf_info; +	u8 i; + +	if (qlcnic_sriov_vf_check(adapter)) +		return 0; + +	for (i = 0; i < adapter->ahw->sriov->num_vfs; i++) { +		if (vf_info[i].pci_func == pci_func) +			return i; +	} + +	return -EINVAL; +} + +static inline int qlcnic_sriov_alloc_bc_trans(struct qlcnic_bc_trans **trans) +{ +	*trans = kzalloc(sizeof(struct qlcnic_bc_trans), GFP_ATOMIC); +	if (!*trans) +		return -ENOMEM; + +	init_completion(&(*trans)->resp_cmpl); +	return 0; +} + +static inline int qlcnic_sriov_alloc_bc_msg(struct qlcnic_bc_hdr **hdr, +					    u32 size) +{ +	*hdr = kzalloc(sizeof(struct qlcnic_bc_hdr) * size, GFP_ATOMIC); +	if (!*hdr) +		return -ENOMEM; + +	return 0; +} + +static int qlcnic_sriov_alloc_bc_mbx_args(struct qlcnic_cmd_args *mbx, u32 type) +{ +	const struct qlcnic_mailbox_metadata *mbx_tbl; +	int i, size; + +	mbx_tbl = qlcnic_sriov_bc_mbx_tbl; +	size = ARRAY_SIZE(qlcnic_sriov_bc_mbx_tbl); + +	for (i = 0; i < size; i++) { +		if (type == mbx_tbl[i].cmd) { +			mbx->op_type = QLC_BC_CMD; +			mbx->req.num = mbx_tbl[i].in_args; +			mbx->rsp.num = mbx_tbl[i].out_args; +			mbx->req.arg = kcalloc(mbx->req.num, sizeof(u32), +					       GFP_ATOMIC); +			if (!mbx->req.arg) +				return -ENOMEM; +			mbx->rsp.arg = kcalloc(mbx->rsp.num, sizeof(u32), +					       GFP_ATOMIC); +			if (!mbx->rsp.arg) { +				kfree(mbx->req.arg); +				mbx->req.arg = NULL; +				return -ENOMEM; +			} +			memset(mbx->req.arg, 0, sizeof(u32) * mbx->req.num); +			memset(mbx->rsp.arg, 0, sizeof(u32) * mbx->rsp.num); +			mbx->req.arg[0] = (type | (mbx->req.num << 16) | +					   (3 << 29)); +			return 0; +		} +	} +	return -EINVAL; +} + +static int qlcnic_sriov_prepare_bc_hdr(struct qlcnic_bc_trans *trans, +				       struct qlcnic_cmd_args *cmd, +				       u16 seq, u8 msg_type) +{ +	struct qlcnic_bc_hdr *hdr; +	int i; +	u32 num_regs, bc_pay_sz; +	u16 remainder; +	u8 cmd_op, num_frags, t_num_frags; + +	bc_pay_sz = QLC_BC_PAYLOAD_SZ; +	if (msg_type == QLC_BC_COMMAND) { +		trans->req_pay = (struct qlcnic_bc_payload *)cmd->req.arg; +		trans->rsp_pay = (struct qlcnic_bc_payload *)cmd->rsp.arg; +		num_regs = cmd->req.num; +		trans->req_pay_size = (num_regs * 4); +		num_regs = cmd->rsp.num; +		trans->rsp_pay_size = (num_regs * 4); +		cmd_op = cmd->req.arg[0] & 0xff; +		remainder = (trans->req_pay_size) % (bc_pay_sz); +		num_frags = (trans->req_pay_size) / (bc_pay_sz); +		if (remainder) +			num_frags++; +		t_num_frags = num_frags; +		if (qlcnic_sriov_alloc_bc_msg(&trans->req_hdr, num_frags)) +			return -ENOMEM; +		remainder = (trans->rsp_pay_size) % (bc_pay_sz); +		num_frags = (trans->rsp_pay_size) / (bc_pay_sz); +		if (remainder) +			num_frags++; +		if (qlcnic_sriov_alloc_bc_msg(&trans->rsp_hdr, num_frags)) +			return -ENOMEM; +		num_frags  = t_num_frags; +		hdr = trans->req_hdr; +	}  else { +		cmd->req.arg = (u32 *)trans->req_pay; +		cmd->rsp.arg = (u32 *)trans->rsp_pay; +		cmd_op = cmd->req.arg[0] & 0xff; +		remainder = (trans->rsp_pay_size) % (bc_pay_sz); +		num_frags = (trans->rsp_pay_size) / (bc_pay_sz); +		if (remainder) +			num_frags++; +		cmd->req.num = trans->req_pay_size / 4; +		cmd->rsp.num = trans->rsp_pay_size / 4; +		hdr = trans->rsp_hdr; +	} + +	trans->trans_id = seq; +	trans->cmd_id = cmd_op; +	for (i = 0; i < num_frags; i++) { +		hdr[i].version = 2; +		hdr[i].msg_type = msg_type; +		hdr[i].op_type = cmd->op_type; +		hdr[i].num_cmds = 1; +		hdr[i].num_frags = num_frags; +		hdr[i].frag_num = i + 1; +		hdr[i].cmd_op = cmd_op; +		hdr[i].seq_id = seq; +	} +	return 0; +} + +static void qlcnic_sriov_cleanup_transaction(struct qlcnic_bc_trans *trans) +{ +	if (!trans) +		return; +	kfree(trans->req_hdr); +	kfree(trans->rsp_hdr); +	kfree(trans); +} + +static int qlcnic_sriov_clear_trans(struct qlcnic_vf_info *vf, +				    struct qlcnic_bc_trans *trans, u8 type) +{ +	struct qlcnic_trans_list *t_list; +	unsigned long flags; +	int ret = 0; + +	if (type == QLC_BC_RESPONSE) { +		t_list = &vf->rcv_act; +		spin_lock_irqsave(&t_list->lock, flags); +		t_list->count--; +		list_del(&trans->list); +		if (t_list->count > 0) +			ret = 1; +		spin_unlock_irqrestore(&t_list->lock, flags); +	} +	if (type == QLC_BC_COMMAND) { +		while (test_and_set_bit(QLC_BC_VF_SEND, &vf->state)) +			msleep(100); +		vf->send_cmd = NULL; +		clear_bit(QLC_BC_VF_SEND, &vf->state); +	} +	return ret; +} + +static void qlcnic_sriov_schedule_bc_cmd(struct qlcnic_sriov *sriov, +					 struct qlcnic_vf_info *vf, +					 work_func_t func) +{ +	if (test_bit(QLC_BC_VF_FLR, &vf->state) || +	    vf->adapter->need_fw_reset) +		return; + +	INIT_WORK(&vf->trans_work, func); +	queue_work(sriov->bc.bc_trans_wq, &vf->trans_work); +} + +static inline void qlcnic_sriov_wait_for_resp(struct qlcnic_bc_trans *trans) +{ +	struct completion *cmpl = &trans->resp_cmpl; + +	if (wait_for_completion_timeout(cmpl, QLC_MBOX_RESP_TIMEOUT)) +		trans->trans_state = QLC_END; +	else +		trans->trans_state = QLC_ABORT; + +	return; +} + +static void qlcnic_sriov_handle_multi_frags(struct qlcnic_bc_trans *trans, +					    u8 type) +{ +	if (type == QLC_BC_RESPONSE) { +		trans->curr_rsp_frag++; +		if (trans->curr_rsp_frag < trans->rsp_hdr->num_frags) +			trans->trans_state = QLC_INIT; +		else +			trans->trans_state = QLC_END; +	} else { +		trans->curr_req_frag++; +		if (trans->curr_req_frag < trans->req_hdr->num_frags) +			trans->trans_state = QLC_INIT; +		else +			trans->trans_state = QLC_WAIT_FOR_RESP; +	} +} + +static void qlcnic_sriov_wait_for_channel_free(struct qlcnic_bc_trans *trans, +					       u8 type) +{ +	struct qlcnic_vf_info *vf = trans->vf; +	struct completion *cmpl = &vf->ch_free_cmpl; + +	if (!wait_for_completion_timeout(cmpl, QLC_MBOX_CH_FREE_TIMEOUT)) { +		trans->trans_state = QLC_ABORT; +		return; +	} + +	clear_bit(QLC_BC_VF_CHANNEL, &vf->state); +	qlcnic_sriov_handle_multi_frags(trans, type); +} + +static void qlcnic_sriov_pull_bc_msg(struct qlcnic_adapter *adapter, +				     u32 *hdr, u32 *pay, u32 size) +{ +	struct qlcnic_hardware_context *ahw = adapter->ahw; +	u32 fw_mbx; +	u8 i, max = 2, hdr_size, j; + +	hdr_size = (sizeof(struct qlcnic_bc_hdr) / sizeof(u32)); +	max = (size / sizeof(u32)) + hdr_size; + +	fw_mbx = readl(QLCNIC_MBX_FW(ahw, 0)); +	for (i = 2, j = 0; j < hdr_size; i++, j++) +		*(hdr++) = readl(QLCNIC_MBX_FW(ahw, i)); +	for (; j < max; i++, j++) +		*(pay++) = readl(QLCNIC_MBX_FW(ahw, i)); +} + +static int __qlcnic_sriov_issue_bc_post(struct qlcnic_vf_info *vf) +{ +	int ret = -EBUSY; +	u32 timeout = 10000; + +	do { +		if (!test_and_set_bit(QLC_BC_VF_CHANNEL, &vf->state)) { +			ret = 0; +			break; +		} +		mdelay(1); +	} while (--timeout); + +	return ret; +} + +static int qlcnic_sriov_issue_bc_post(struct qlcnic_bc_trans *trans, u8 type) +{ +	struct qlcnic_vf_info *vf = trans->vf; +	u32 pay_size, hdr_size; +	u32 *hdr, *pay; +	int ret; +	u8 pci_func = trans->func_id; + +	if (__qlcnic_sriov_issue_bc_post(vf)) +		return -EBUSY; + +	if (type == QLC_BC_COMMAND) { +		hdr = (u32 *)(trans->req_hdr + trans->curr_req_frag); +		pay = (u32 *)(trans->req_pay + trans->curr_req_frag); +		hdr_size = (sizeof(struct qlcnic_bc_hdr) / sizeof(u32)); +		pay_size = qlcnic_sriov_get_bc_paysize(trans->req_pay_size, +						       trans->curr_req_frag); +		pay_size = (pay_size / sizeof(u32)); +	} else { +		hdr = (u32 *)(trans->rsp_hdr + trans->curr_rsp_frag); +		pay = (u32 *)(trans->rsp_pay + trans->curr_rsp_frag); +		hdr_size = (sizeof(struct qlcnic_bc_hdr) / sizeof(u32)); +		pay_size = qlcnic_sriov_get_bc_paysize(trans->rsp_pay_size, +						       trans->curr_rsp_frag); +		pay_size = (pay_size / sizeof(u32)); +	} + +	ret = qlcnic_sriov_post_bc_msg(vf->adapter, hdr, pay, +				       pci_func, pay_size); +	return ret; +} + +static int __qlcnic_sriov_send_bc_msg(struct qlcnic_bc_trans *trans, +				      struct qlcnic_vf_info *vf, u8 type) +{ +	bool flag = true; +	int err = -EIO; + +	while (flag) { +		if (test_bit(QLC_BC_VF_FLR, &vf->state) || +		    vf->adapter->need_fw_reset) +			trans->trans_state = QLC_ABORT; + +		switch (trans->trans_state) { +		case QLC_INIT: +			trans->trans_state = QLC_WAIT_FOR_CHANNEL_FREE; +			if (qlcnic_sriov_issue_bc_post(trans, type)) +				trans->trans_state = QLC_ABORT; +			break; +		case QLC_WAIT_FOR_CHANNEL_FREE: +			qlcnic_sriov_wait_for_channel_free(trans, type); +			break; +		case QLC_WAIT_FOR_RESP: +			qlcnic_sriov_wait_for_resp(trans); +			break; +		case QLC_END: +			err = 0; +			flag = false; +			break; +		case QLC_ABORT: +			err = -EIO; +			flag = false; +			clear_bit(QLC_BC_VF_CHANNEL, &vf->state); +			break; +		default: +			err = -EIO; +			flag = false; +		} +	} +	return err; +} + +static int qlcnic_sriov_send_bc_cmd(struct qlcnic_adapter *adapter, +				    struct qlcnic_bc_trans *trans, int pci_func) +{ +	struct qlcnic_vf_info *vf; +	int err, index = qlcnic_sriov_func_to_index(adapter, pci_func); + +	if (index < 0) +		return -EIO; + +	vf = &adapter->ahw->sriov->vf_info[index]; +	trans->vf = vf; +	trans->func_id = pci_func; + +	if (!test_bit(QLC_BC_VF_STATE, &vf->state)) { +		if (qlcnic_sriov_pf_check(adapter)) +			return -EIO; +		if (qlcnic_sriov_vf_check(adapter) && +		    trans->cmd_id != QLCNIC_BC_CMD_CHANNEL_INIT) +			return -EIO; +	} + +	mutex_lock(&vf->send_cmd_lock); +	vf->send_cmd = trans; +	err = __qlcnic_sriov_send_bc_msg(trans, vf, QLC_BC_COMMAND); +	qlcnic_sriov_clear_trans(vf, trans, QLC_BC_COMMAND); +	mutex_unlock(&vf->send_cmd_lock); +	return err; +} + +static void __qlcnic_sriov_process_bc_cmd(struct qlcnic_adapter *adapter, +					  struct qlcnic_bc_trans *trans, +					  struct qlcnic_cmd_args *cmd) +{ +#ifdef CONFIG_QLCNIC_SRIOV +	if (qlcnic_sriov_pf_check(adapter)) { +		qlcnic_sriov_pf_process_bc_cmd(adapter, trans, cmd); +		return; +	} +#endif +	cmd->rsp.arg[0] |= (0x9 << 25); +	return; +} + +static void qlcnic_sriov_process_bc_cmd(struct work_struct *work) +{ +	struct qlcnic_vf_info *vf = container_of(work, struct qlcnic_vf_info, +						 trans_work); +	struct qlcnic_bc_trans *trans = NULL; +	struct qlcnic_adapter *adapter  = vf->adapter; +	struct qlcnic_cmd_args cmd; +	u8 req; + +	if (adapter->need_fw_reset) +		return; + +	if (test_bit(QLC_BC_VF_FLR, &vf->state)) +		return; + +	trans = list_first_entry(&vf->rcv_act.wait_list, +				 struct qlcnic_bc_trans, list); +	adapter = vf->adapter; + +	if (qlcnic_sriov_prepare_bc_hdr(trans, &cmd, trans->req_hdr->seq_id, +					QLC_BC_RESPONSE)) +		goto cleanup_trans; + +	__qlcnic_sriov_process_bc_cmd(adapter, trans, &cmd); +	trans->trans_state = QLC_INIT; +	__qlcnic_sriov_send_bc_msg(trans, vf, QLC_BC_RESPONSE); + +cleanup_trans: +	qlcnic_free_mbx_args(&cmd); +	req = qlcnic_sriov_clear_trans(vf, trans, QLC_BC_RESPONSE); +	qlcnic_sriov_cleanup_transaction(trans); +	if (req) +		qlcnic_sriov_schedule_bc_cmd(adapter->ahw->sriov, vf, +					     qlcnic_sriov_process_bc_cmd); +} + +static void qlcnic_sriov_handle_bc_resp(struct qlcnic_bc_hdr *hdr, +					struct qlcnic_vf_info *vf) +{ +	struct qlcnic_bc_trans *trans; +	u32 pay_size; + +	if (test_and_set_bit(QLC_BC_VF_SEND, &vf->state)) +		return; + +	trans = vf->send_cmd; + +	if (trans == NULL) +		goto clear_send; + +	if (trans->trans_id != hdr->seq_id) +		goto clear_send; + +	pay_size = qlcnic_sriov_get_bc_paysize(trans->rsp_pay_size, +					       trans->curr_rsp_frag); +	qlcnic_sriov_pull_bc_msg(vf->adapter, +				 (u32 *)(trans->rsp_hdr + trans->curr_rsp_frag), +				 (u32 *)(trans->rsp_pay + trans->curr_rsp_frag), +				 pay_size); +	if (++trans->curr_rsp_frag < trans->rsp_hdr->num_frags) +		goto clear_send; + +	complete(&trans->resp_cmpl); + +clear_send: +	clear_bit(QLC_BC_VF_SEND, &vf->state); +} + +int __qlcnic_sriov_add_act_list(struct qlcnic_sriov *sriov, +				struct qlcnic_vf_info *vf, +				struct qlcnic_bc_trans *trans) +{ +	struct qlcnic_trans_list *t_list = &vf->rcv_act; + +	t_list->count++; +	list_add_tail(&trans->list, &t_list->wait_list); +	if (t_list->count == 1) +		qlcnic_sriov_schedule_bc_cmd(sriov, vf, +					     qlcnic_sriov_process_bc_cmd); +	return 0; +} + +static int qlcnic_sriov_add_act_list(struct qlcnic_sriov *sriov, +				     struct qlcnic_vf_info *vf, +				     struct qlcnic_bc_trans *trans) +{ +	struct qlcnic_trans_list *t_list = &vf->rcv_act; + +	spin_lock(&t_list->lock); + +	__qlcnic_sriov_add_act_list(sriov, vf, trans); + +	spin_unlock(&t_list->lock); +	return 0; +} + +static void qlcnic_sriov_handle_pending_trans(struct qlcnic_sriov *sriov, +					      struct qlcnic_vf_info *vf, +					      struct qlcnic_bc_hdr *hdr) +{ +	struct qlcnic_bc_trans *trans = NULL; +	struct list_head *node; +	u32 pay_size, curr_frag; +	u8 found = 0, active = 0; + +	spin_lock(&vf->rcv_pend.lock); +	if (vf->rcv_pend.count > 0) { +		list_for_each(node, &vf->rcv_pend.wait_list) { +			trans = list_entry(node, struct qlcnic_bc_trans, list); +			if (trans->trans_id == hdr->seq_id) { +				found = 1; +				break; +			} +		} +	} + +	if (found) { +		curr_frag = trans->curr_req_frag; +		pay_size = qlcnic_sriov_get_bc_paysize(trans->req_pay_size, +						       curr_frag); +		qlcnic_sriov_pull_bc_msg(vf->adapter, +					 (u32 *)(trans->req_hdr + curr_frag), +					 (u32 *)(trans->req_pay + curr_frag), +					 pay_size); +		trans->curr_req_frag++; +		if (trans->curr_req_frag >= hdr->num_frags) { +			vf->rcv_pend.count--; +			list_del(&trans->list); +			active = 1; +		} +	} +	spin_unlock(&vf->rcv_pend.lock); + +	if (active) +		if (qlcnic_sriov_add_act_list(sriov, vf, trans)) +			qlcnic_sriov_cleanup_transaction(trans); + +	return; +} + +static void qlcnic_sriov_handle_bc_cmd(struct qlcnic_sriov *sriov, +				       struct qlcnic_bc_hdr *hdr, +				       struct qlcnic_vf_info *vf) +{ +	struct qlcnic_bc_trans *trans; +	struct qlcnic_adapter *adapter = vf->adapter; +	struct qlcnic_cmd_args cmd; +	u32 pay_size; +	int err; +	u8 cmd_op; + +	if (adapter->need_fw_reset) +		return; + +	if (!test_bit(QLC_BC_VF_STATE, &vf->state) && +	    hdr->op_type != QLC_BC_CMD && +	    hdr->cmd_op != QLCNIC_BC_CMD_CHANNEL_INIT) +		return; + +	if (hdr->frag_num > 1) { +		qlcnic_sriov_handle_pending_trans(sriov, vf, hdr); +		return; +	} + +	cmd_op = hdr->cmd_op; +	if (qlcnic_sriov_alloc_bc_trans(&trans)) +		return; + +	if (hdr->op_type == QLC_BC_CMD) +		err = qlcnic_sriov_alloc_bc_mbx_args(&cmd, cmd_op); +	else +		err = qlcnic_alloc_mbx_args(&cmd, adapter, cmd_op); + +	if (err) { +		qlcnic_sriov_cleanup_transaction(trans); +		return; +	} + +	cmd.op_type = hdr->op_type; +	if (qlcnic_sriov_prepare_bc_hdr(trans, &cmd, hdr->seq_id, +					QLC_BC_COMMAND)) { +		qlcnic_free_mbx_args(&cmd); +		qlcnic_sriov_cleanup_transaction(trans); +		return; +	} + +	pay_size = qlcnic_sriov_get_bc_paysize(trans->req_pay_size, +					 trans->curr_req_frag); +	qlcnic_sriov_pull_bc_msg(vf->adapter, +				 (u32 *)(trans->req_hdr + trans->curr_req_frag), +				 (u32 *)(trans->req_pay + trans->curr_req_frag), +				 pay_size); +	trans->func_id = vf->pci_func; +	trans->vf = vf; +	trans->trans_id = hdr->seq_id; +	trans->curr_req_frag++; + +	if (qlcnic_sriov_soft_flr_check(adapter, trans, vf)) +		return; + +	if (trans->curr_req_frag == trans->req_hdr->num_frags) { +		if (qlcnic_sriov_add_act_list(sriov, vf, trans)) { +			qlcnic_free_mbx_args(&cmd); +			qlcnic_sriov_cleanup_transaction(trans); +		} +	} else { +		spin_lock(&vf->rcv_pend.lock); +		list_add_tail(&trans->list, &vf->rcv_pend.wait_list); +		vf->rcv_pend.count++; +		spin_unlock(&vf->rcv_pend.lock); +	} +} + +static void qlcnic_sriov_handle_msg_event(struct qlcnic_sriov *sriov, +					  struct qlcnic_vf_info *vf) +{ +	struct qlcnic_bc_hdr hdr; +	u32 *ptr = (u32 *)&hdr; +	u8 msg_type, i; + +	for (i = 2; i < 6; i++) +		ptr[i - 2] = readl(QLCNIC_MBX_FW(vf->adapter->ahw, i)); +	msg_type = hdr.msg_type; + +	switch (msg_type) { +	case QLC_BC_COMMAND: +		qlcnic_sriov_handle_bc_cmd(sriov, &hdr, vf); +		break; +	case QLC_BC_RESPONSE: +		qlcnic_sriov_handle_bc_resp(&hdr, vf); +		break; +	} +} + +static void qlcnic_sriov_handle_flr_event(struct qlcnic_sriov *sriov, +					  struct qlcnic_vf_info *vf) +{ +	struct qlcnic_adapter *adapter = vf->adapter; + +	if (qlcnic_sriov_pf_check(adapter)) +		qlcnic_sriov_pf_handle_flr(sriov, vf); +	else +		dev_err(&adapter->pdev->dev, +			"Invalid event to VF. VF should not get FLR event\n"); +} + +void qlcnic_sriov_handle_bc_event(struct qlcnic_adapter *adapter, u32 event) +{ +	struct qlcnic_vf_info *vf; +	struct qlcnic_sriov *sriov; +	int index; +	u8 pci_func; + +	sriov = adapter->ahw->sriov; +	pci_func = qlcnic_sriov_target_func_id(event); +	index = qlcnic_sriov_func_to_index(adapter, pci_func); + +	if (index < 0) +		return; + +	vf = &sriov->vf_info[index]; +	vf->pci_func = pci_func; + +	if (qlcnic_sriov_channel_free_check(event)) +		complete(&vf->ch_free_cmpl); + +	if (qlcnic_sriov_flr_check(event)) { +		qlcnic_sriov_handle_flr_event(sriov, vf); +		return; +	} + +	if (qlcnic_sriov_bc_msg_check(event)) +		qlcnic_sriov_handle_msg_event(sriov, vf); +} + +int qlcnic_sriov_cfg_bc_intr(struct qlcnic_adapter *adapter, u8 enable) +{ +	struct qlcnic_cmd_args cmd; +	int err; + +	if (!test_bit(__QLCNIC_SRIOV_ENABLE, &adapter->state)) +		return 0; + +	if (qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_BC_EVENT_SETUP)) +		return -ENOMEM; + +	if (enable) +		cmd.req.arg[1] = (1 << 4) | (1 << 5) | (1 << 6) | (1 << 7); + +	err = qlcnic_83xx_mbx_op(adapter, &cmd); + +	if (err != QLCNIC_RCODE_SUCCESS) { +		dev_err(&adapter->pdev->dev, +			"Failed to %s bc events, err=%d\n", +			(enable ? "enable" : "disable"), err); +	} + +	qlcnic_free_mbx_args(&cmd); +	return err; +} + +static int qlcnic_sriov_retry_bc_cmd(struct qlcnic_adapter *adapter, +				     struct qlcnic_bc_trans *trans) +{ +	u8 max = QLC_BC_CMD_MAX_RETRY_CNT; +	u32 state; + +	state = QLCRDX(adapter->ahw, QLC_83XX_IDC_DEV_STATE); +	if (state == QLC_83XX_IDC_DEV_READY) { +		msleep(20); +		clear_bit(QLC_BC_VF_CHANNEL, &trans->vf->state); +		trans->trans_state = QLC_INIT; +		if (++adapter->fw_fail_cnt > max) +			return -EIO; +		else +			return 0; +	} + +	return -EIO; +} + +static int qlcnic_sriov_vf_mbx_op(struct qlcnic_adapter *adapter, +				  struct qlcnic_cmd_args *cmd) +{ +	struct qlcnic_hardware_context *ahw = adapter->ahw; +	struct device *dev = &adapter->pdev->dev; +	struct qlcnic_bc_trans *trans; +	int err; +	u32 rsp_data, opcode, mbx_err_code, rsp; +	u16 seq = ++adapter->ahw->sriov->bc.trans_counter; +	u8 func = ahw->pci_func; + +	rsp = qlcnic_sriov_alloc_bc_trans(&trans); +	if (rsp) +		return rsp; + +	rsp = qlcnic_sriov_prepare_bc_hdr(trans, cmd, seq, QLC_BC_COMMAND); +	if (rsp) +		goto cleanup_transaction; + +retry: +	if (!test_bit(QLC_83XX_MBX_READY, &adapter->ahw->idc.status)) { +		rsp = -EIO; +		QLCDB(adapter, DRV, "MBX not Ready!(cmd 0x%x) for VF 0x%x\n", +		      QLCNIC_MBX_RSP(cmd->req.arg[0]), func); +		goto err_out; +	} + +	err = qlcnic_sriov_send_bc_cmd(adapter, trans, func); +	if (err) { +		dev_err(dev, "MBX command 0x%x timed out for VF %d\n", +			(cmd->req.arg[0] & 0xffff), func); +		rsp = QLCNIC_RCODE_TIMEOUT; + +		/* After adapter reset PF driver may take some time to +		 * respond to VF's request. Retry request till maximum retries. +		 */ +		if ((trans->req_hdr->cmd_op == QLCNIC_BC_CMD_CHANNEL_INIT) && +		    !qlcnic_sriov_retry_bc_cmd(adapter, trans)) +			goto retry; + +		goto err_out; +	} + +	rsp_data = cmd->rsp.arg[0]; +	mbx_err_code = QLCNIC_MBX_STATUS(rsp_data); +	opcode = QLCNIC_MBX_RSP(cmd->req.arg[0]); + +	if ((mbx_err_code == QLCNIC_MBX_RSP_OK) || +	    (mbx_err_code == QLCNIC_MBX_PORT_RSP_OK)) { +		rsp = QLCNIC_RCODE_SUCCESS; +	} else { +		rsp = mbx_err_code; +		if (!rsp) +			rsp = 1; +		dev_err(dev, +			"MBX command 0x%x failed with err:0x%x for VF %d\n", +			opcode, mbx_err_code, func); +	} + +err_out: +	if (rsp == QLCNIC_RCODE_TIMEOUT) { +		ahw->reset_context = 1; +		adapter->need_fw_reset = 1; +		clear_bit(QLC_83XX_MBX_READY, &ahw->idc.status); +	} + +cleanup_transaction: +	qlcnic_sriov_cleanup_transaction(trans); +	return rsp; +} + +int qlcnic_sriov_channel_cfg_cmd(struct qlcnic_adapter *adapter, u8 cmd_op) +{ +	struct qlcnic_cmd_args cmd; +	struct qlcnic_vf_info *vf = &adapter->ahw->sriov->vf_info[0]; +	int ret; + +	if (qlcnic_sriov_alloc_bc_mbx_args(&cmd, cmd_op)) +		return -ENOMEM; + +	ret = qlcnic_issue_cmd(adapter, &cmd); +	if (ret) { +		dev_err(&adapter->pdev->dev, +			"Failed bc channel %s %d\n", cmd_op ? "term" : "init", +			ret); +		goto out; +	} + +	cmd_op = (cmd.rsp.arg[0] & 0xff); +	if (cmd.rsp.arg[0] >> 25 == 2) +		return 2; +	if (cmd_op == QLCNIC_BC_CMD_CHANNEL_INIT) +		set_bit(QLC_BC_VF_STATE, &vf->state); +	else +		clear_bit(QLC_BC_VF_STATE, &vf->state); + +out: +	qlcnic_free_mbx_args(&cmd); +	return ret; +} + +void qlcnic_vf_add_mc_list(struct net_device *netdev, u16 vlan) +{ +	struct qlcnic_adapter *adapter = netdev_priv(netdev); +	struct qlcnic_mac_list_s *cur; +	struct list_head *head, tmp_list; + +	INIT_LIST_HEAD(&tmp_list); +	head = &adapter->vf_mc_list; +	netif_addr_lock_bh(netdev); + +	while (!list_empty(head)) { +		cur = list_entry(head->next, struct qlcnic_mac_list_s, list); +		list_move(&cur->list, &tmp_list); +	} + +	netif_addr_unlock_bh(netdev); + +	while (!list_empty(&tmp_list)) { +		cur = list_entry((&tmp_list)->next, +				 struct qlcnic_mac_list_s, list); +		qlcnic_nic_add_mac(adapter, cur->mac_addr, vlan); +		list_del(&cur->list); +		kfree(cur); +	} +} + +void qlcnic_sriov_cleanup_async_list(struct qlcnic_back_channel *bc) +{ +	struct list_head *head = &bc->async_list; +	struct qlcnic_async_work_list *entry; + +	while (!list_empty(head)) { +		entry = list_entry(head->next, struct qlcnic_async_work_list, +				   list); +		cancel_work_sync(&entry->work); +		list_del(&entry->list); +		kfree(entry); +	} +} + +static void qlcnic_sriov_vf_set_multi(struct net_device *netdev) +{ +	struct qlcnic_adapter *adapter = netdev_priv(netdev); +	u16 vlan; + +	if (!test_bit(__QLCNIC_FW_ATTACHED, &adapter->state)) +		return; + +	vlan = adapter->ahw->sriov->vlan; +	__qlcnic_set_multi(netdev, vlan); +} + +static void qlcnic_sriov_handle_async_multi(struct work_struct *work) +{ +	struct qlcnic_async_work_list *entry; +	struct net_device *netdev; + +	entry = container_of(work, struct qlcnic_async_work_list, work); +	netdev = (struct net_device *)entry->ptr; + +	qlcnic_sriov_vf_set_multi(netdev); +	return; +} + +static struct qlcnic_async_work_list * +qlcnic_sriov_get_free_node_async_work(struct qlcnic_back_channel *bc) +{ +	struct list_head *node; +	struct qlcnic_async_work_list *entry = NULL; +	u8 empty = 0; + +	list_for_each(node, &bc->async_list) { +		entry = list_entry(node, struct qlcnic_async_work_list, list); +		if (!work_pending(&entry->work)) { +			empty = 1; +			break; +		} +	} + +	if (!empty) { +		entry = kzalloc(sizeof(struct qlcnic_async_work_list), +				GFP_ATOMIC); +		if (entry == NULL) +			return NULL; +		list_add_tail(&entry->list, &bc->async_list); +	} + +	return entry; +} + +static void qlcnic_sriov_schedule_bc_async_work(struct qlcnic_back_channel *bc, +						work_func_t func, void *data) +{ +	struct qlcnic_async_work_list *entry = NULL; + +	entry = qlcnic_sriov_get_free_node_async_work(bc); +	if (!entry) +		return; + +	entry->ptr = data; +	INIT_WORK(&entry->work, func); +	queue_work(bc->bc_async_wq, &entry->work); +} + +void qlcnic_sriov_vf_schedule_multi(struct net_device *netdev) +{ + +	struct qlcnic_adapter *adapter = netdev_priv(netdev); +	struct qlcnic_back_channel *bc = &adapter->ahw->sriov->bc; + +	if (adapter->need_fw_reset) +		return; + +	qlcnic_sriov_schedule_bc_async_work(bc, qlcnic_sriov_handle_async_multi, +					    netdev); +} + +static int qlcnic_sriov_vf_reinit_driver(struct qlcnic_adapter *adapter) +{ +	int err; + +	set_bit(QLC_83XX_MBX_READY, &adapter->ahw->idc.status); +	qlcnic_83xx_enable_mbx_intrpt(adapter); + +	err = qlcnic_sriov_cfg_bc_intr(adapter, 1); +	if (err) +		return err; + +	err = qlcnic_sriov_channel_cfg_cmd(adapter, QLCNIC_BC_CMD_CHANNEL_INIT); +	if (err) +		goto err_out_cleanup_bc_intr; + +	err = qlcnic_sriov_vf_init_driver(adapter); +	if (err) +		goto err_out_term_channel; + +	return 0; + +err_out_term_channel: +	qlcnic_sriov_channel_cfg_cmd(adapter, QLCNIC_BC_CMD_CHANNEL_TERM); + +err_out_cleanup_bc_intr: +	qlcnic_sriov_cfg_bc_intr(adapter, 0); +	return err; +} + +static void qlcnic_sriov_vf_attach(struct qlcnic_adapter *adapter) +{ +	struct net_device *netdev = adapter->netdev; + +	if (netif_running(netdev)) { +		if (!qlcnic_up(adapter, netdev)) +			qlcnic_restore_indev_addr(netdev, NETDEV_UP); +	} + +	netif_device_attach(netdev); +} + +static void qlcnic_sriov_vf_detach(struct qlcnic_adapter *adapter) +{ +	struct qlcnic_hardware_context *ahw = adapter->ahw; +	struct qlcnic_intrpt_config *intr_tbl = ahw->intr_tbl; +	struct net_device *netdev = adapter->netdev; +	u8 i, max_ints = ahw->num_msix - 1; + +	qlcnic_83xx_disable_mbx_intr(adapter); +	netif_device_detach(netdev); +	if (netif_running(netdev)) +		qlcnic_down(adapter, netdev); + +	for (i = 0; i < max_ints; i++) { +		intr_tbl[i].id = i; +		intr_tbl[i].enabled = 0; +		intr_tbl[i].src = 0; +	} +	ahw->reset_context = 0; +} + +static int qlcnic_sriov_vf_handle_dev_ready(struct qlcnic_adapter *adapter) +{ +	struct qlcnic_hardware_context *ahw = adapter->ahw; +	struct device *dev = &adapter->pdev->dev; +	struct qlc_83xx_idc *idc = &ahw->idc; +	u8 func = ahw->pci_func; +	u32 state; + +	if ((idc->prev_state == QLC_83XX_IDC_DEV_NEED_RESET) || +	    (idc->prev_state == QLC_83XX_IDC_DEV_INIT)) { +		if (!qlcnic_sriov_vf_reinit_driver(adapter)) { +			qlcnic_sriov_vf_attach(adapter); +			adapter->fw_fail_cnt = 0; +			dev_info(dev, +				 "%s: Reinitalization of VF 0x%x done after FW reset\n", +				 __func__, func); +		} else { +			dev_err(dev, +				"%s: Reinitialization of VF 0x%x failed after FW reset\n", +				__func__, func); +			state = QLCRDX(ahw, QLC_83XX_IDC_DEV_STATE); +			dev_info(dev, "Current state 0x%x after FW reset\n", +				 state); +		} +	} + +	return 0; +} + +static int qlcnic_sriov_vf_handle_context_reset(struct qlcnic_adapter *adapter) +{ +	struct qlcnic_hardware_context *ahw = adapter->ahw; +	struct device *dev = &adapter->pdev->dev; +	struct qlc_83xx_idc *idc = &ahw->idc; +	u8 func = ahw->pci_func; +	u32 state; + +	adapter->reset_ctx_cnt++; + +	/* Skip the context reset and check if FW is hung */ +	if (adapter->reset_ctx_cnt < 3) { +		adapter->need_fw_reset = 1; +		clear_bit(QLC_83XX_MBX_READY, &idc->status); +		dev_info(dev, +			 "Resetting context, wait here to check if FW is in failed state\n"); +		return 0; +	} + +	/* Check if number of resets exceed the threshold. +	 * If it exceeds the threshold just fail the VF. +	 */ +	if (adapter->reset_ctx_cnt > QLC_83XX_VF_RESET_FAIL_THRESH) { +		clear_bit(QLC_83XX_MODULE_LOADED, &idc->status); +		adapter->tx_timeo_cnt = 0; +		adapter->fw_fail_cnt = 0; +		adapter->reset_ctx_cnt = 0; +		qlcnic_sriov_vf_detach(adapter); +		dev_err(dev, +			"Device context resets have exceeded the threshold, device interface will be shutdown\n"); +		return -EIO; +	} + +	dev_info(dev, "Resetting context of VF 0x%x\n", func); +	dev_info(dev, "%s: Context reset count %d for VF 0x%x\n", +		 __func__, adapter->reset_ctx_cnt, func); +	set_bit(__QLCNIC_RESETTING, &adapter->state); +	adapter->need_fw_reset = 1; +	clear_bit(QLC_83XX_MBX_READY, &idc->status); +	qlcnic_sriov_vf_detach(adapter); +	adapter->need_fw_reset = 0; + +	if (!qlcnic_sriov_vf_reinit_driver(adapter)) { +		qlcnic_sriov_vf_attach(adapter); +		adapter->netdev->trans_start = jiffies; +		adapter->tx_timeo_cnt = 0; +		adapter->reset_ctx_cnt = 0; +		adapter->fw_fail_cnt = 0; +		dev_info(dev, "Done resetting context for VF 0x%x\n", func); +	} else { +		dev_err(dev, "%s: Reinitialization of VF 0x%x failed\n", +			__func__, func); +		state = QLCRDX(ahw, QLC_83XX_IDC_DEV_STATE); +		dev_info(dev, "%s: Current state 0x%x\n", __func__, state); +	} + +	return 0; +} + +static int qlcnic_sriov_vf_idc_ready_state(struct qlcnic_adapter *adapter) +{ +	struct qlcnic_hardware_context *ahw = adapter->ahw; +	int ret = 0; + +	if (ahw->idc.prev_state != QLC_83XX_IDC_DEV_READY) +		ret = qlcnic_sriov_vf_handle_dev_ready(adapter); +	else if (ahw->reset_context) +		ret = qlcnic_sriov_vf_handle_context_reset(adapter); + +	clear_bit(__QLCNIC_RESETTING, &adapter->state); +	return ret; +} + +static int qlcnic_sriov_vf_idc_failed_state(struct qlcnic_adapter *adapter) +{ +	struct qlc_83xx_idc *idc = &adapter->ahw->idc; + +	dev_err(&adapter->pdev->dev, "Device is in failed state\n"); +	if (idc->prev_state == QLC_83XX_IDC_DEV_READY) +		qlcnic_sriov_vf_detach(adapter); + +	clear_bit(QLC_83XX_MODULE_LOADED, &idc->status); +	clear_bit(__QLCNIC_RESETTING, &adapter->state); +	return -EIO; +} + +static int +qlcnic_sriov_vf_idc_need_quiescent_state(struct qlcnic_adapter *adapter) +{ +	struct qlc_83xx_idc *idc = &adapter->ahw->idc; + +	dev_info(&adapter->pdev->dev, "Device is in quiescent state\n"); +	if (idc->prev_state == QLC_83XX_IDC_DEV_READY) { +		set_bit(__QLCNIC_RESETTING, &adapter->state); +		adapter->tx_timeo_cnt = 0; +		adapter->reset_ctx_cnt = 0; +		clear_bit(QLC_83XX_MBX_READY, &idc->status); +		qlcnic_sriov_vf_detach(adapter); +	} + +	return 0; +} + +static int qlcnic_sriov_vf_idc_init_reset_state(struct qlcnic_adapter *adapter) +{ +	struct qlc_83xx_idc *idc = &adapter->ahw->idc; +	u8 func = adapter->ahw->pci_func; + +	if (idc->prev_state == QLC_83XX_IDC_DEV_READY) { +		dev_err(&adapter->pdev->dev, +			"Firmware hang detected by VF 0x%x\n", func); +		set_bit(__QLCNIC_RESETTING, &adapter->state); +		adapter->tx_timeo_cnt = 0; +		adapter->reset_ctx_cnt = 0; +		clear_bit(QLC_83XX_MBX_READY, &idc->status); +		qlcnic_sriov_vf_detach(adapter); +	} +	return 0; +} + +static int qlcnic_sriov_vf_idc_unknown_state(struct qlcnic_adapter *adapter) +{ +	dev_err(&adapter->pdev->dev, "%s: Device in unknown state\n", __func__); +	return 0; +} + +static void qlcnic_sriov_vf_poll_dev_state(struct work_struct *work) +{ +	struct qlcnic_adapter *adapter; +	struct qlc_83xx_idc *idc; +	int ret = 0; + +	adapter = container_of(work, struct qlcnic_adapter, fw_work.work); +	idc = &adapter->ahw->idc; +	idc->curr_state = QLCRDX(adapter->ahw, QLC_83XX_IDC_DEV_STATE); + +	switch (idc->curr_state) { +	case QLC_83XX_IDC_DEV_READY: +		ret = qlcnic_sriov_vf_idc_ready_state(adapter); +		break; +	case QLC_83XX_IDC_DEV_NEED_RESET: +	case QLC_83XX_IDC_DEV_INIT: +		ret = qlcnic_sriov_vf_idc_init_reset_state(adapter); +		break; +	case QLC_83XX_IDC_DEV_NEED_QUISCENT: +		ret = qlcnic_sriov_vf_idc_need_quiescent_state(adapter); +		break; +	case QLC_83XX_IDC_DEV_FAILED: +		ret = qlcnic_sriov_vf_idc_failed_state(adapter); +		break; +	case QLC_83XX_IDC_DEV_QUISCENT: +		break; +	default: +		ret = qlcnic_sriov_vf_idc_unknown_state(adapter); +	} + +	idc->prev_state = idc->curr_state; +	if (!ret && test_bit(QLC_83XX_MODULE_LOADED, &idc->status)) +		qlcnic_schedule_work(adapter, qlcnic_sriov_vf_poll_dev_state, +				     idc->delay); +} + +static void qlcnic_sriov_vf_cancel_fw_work(struct qlcnic_adapter *adapter) +{ +	while (test_and_set_bit(__QLCNIC_RESETTING, &adapter->state)) +		msleep(20); + +	clear_bit(QLC_83XX_MODULE_LOADED, &adapter->ahw->idc.status); +	clear_bit(__QLCNIC_RESETTING, &adapter->state); +	cancel_delayed_work_sync(&adapter->fw_work); +} + +static int qlcnic_sriov_validate_vlan_cfg(struct qlcnic_sriov *sriov, +					  u16 vid, u8 enable) +{ +	u16 vlan = sriov->vlan; +	u8 allowed = 0; +	int i; + +	if (sriov->vlan_mode != QLC_GUEST_VLAN_MODE) +		return -EINVAL; + +	if (enable) { +		if (vlan) +			return -EINVAL; + +		if (sriov->any_vlan) { +			for (i = 0; i < sriov->num_allowed_vlans; i++) { +				if (sriov->allowed_vlans[i] == vid) +					allowed = 1; +			} + +			if (!allowed) +				return -EINVAL; +		} +	} else { +		if (!vlan || vlan != vid) +			return -EINVAL; +	} + +	return 0; +} + +int qlcnic_sriov_cfg_vf_guest_vlan(struct qlcnic_adapter *adapter, +				   u16 vid, u8 enable) +{ +	struct qlcnic_sriov *sriov = adapter->ahw->sriov; +	struct qlcnic_cmd_args cmd; +	int ret; + +	if (vid == 0) +		return 0; + +	ret = qlcnic_sriov_validate_vlan_cfg(sriov, vid, enable); +	if (ret) +		return ret; + +	ret = qlcnic_sriov_alloc_bc_mbx_args(&cmd, +					     QLCNIC_BC_CMD_CFG_GUEST_VLAN); +	if (ret) +		return ret; + +	cmd.req.arg[1] = (enable & 1) | vid << 16; + +	qlcnic_sriov_cleanup_async_list(&sriov->bc); +	ret = qlcnic_issue_cmd(adapter, &cmd); +	if (ret) { +		dev_err(&adapter->pdev->dev, +			"Failed to configure guest VLAN, err=%d\n", ret); +	} else { +		qlcnic_free_mac_list(adapter); + +		if (enable) +			sriov->vlan = vid; +		else +			sriov->vlan = 0; + +		qlcnic_sriov_vf_set_multi(adapter->netdev); +	} + +	qlcnic_free_mbx_args(&cmd); +	return ret; +} + +static void qlcnic_sriov_vf_free_mac_list(struct qlcnic_adapter *adapter) +{ +	struct list_head *head = &adapter->mac_list; +	struct qlcnic_mac_list_s *cur; +	u16 vlan; + +	vlan = adapter->ahw->sriov->vlan; + +	while (!list_empty(head)) { +		cur = list_entry(head->next, struct qlcnic_mac_list_s, list); +		qlcnic_sre_macaddr_change(adapter, cur->mac_addr, +					  vlan, QLCNIC_MAC_DEL); +		list_del(&cur->list); +		kfree(cur); +	} +} diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_pf.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_pf.c new file mode 100644 index 00000000000..c81be2da119 --- /dev/null +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sriov_pf.c @@ -0,0 +1,1780 @@ +/* + * QLogic qlcnic NIC Driver + * Copyright (c) 2009-2013 QLogic Corporation + * + * See LICENSE.qlcnic for copyright and licensing details. + */ + +#include "qlcnic_sriov.h" +#include "qlcnic.h" +#include <linux/types.h> + +#define QLCNIC_SRIOV_VF_MAX_MAC 1 +#define QLC_VF_MIN_TX_RATE	100 +#define QLC_VF_MAX_TX_RATE	9999 + +static int qlcnic_sriov_pf_get_vport_handle(struct qlcnic_adapter *, u8); + +struct qlcnic_sriov_cmd_handler { +	int (*fn) (struct qlcnic_bc_trans *, struct qlcnic_cmd_args *); +}; + +struct qlcnic_sriov_fw_cmd_handler { +	u32 cmd; +	int (*fn) (struct qlcnic_bc_trans *, struct qlcnic_cmd_args *); +}; + +static int qlcnic_sriov_pf_set_vport_info(struct qlcnic_adapter *adapter, +					  struct qlcnic_info *npar_info, +					  u16 vport_id) +{ +	struct qlcnic_cmd_args cmd; +	int err; + +	if (qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_SET_NIC_INFO)) +		return -ENOMEM; + +	cmd.req.arg[1] = (vport_id << 16) | 0x1; +	cmd.req.arg[2] = npar_info->bit_offsets; +	cmd.req.arg[2] |= npar_info->min_tx_bw << 16; +	cmd.req.arg[3] = npar_info->max_tx_bw | (npar_info->max_tx_ques << 16); +	cmd.req.arg[4] = npar_info->max_tx_mac_filters; +	cmd.req.arg[4] |= npar_info->max_rx_mcast_mac_filters << 16; +	cmd.req.arg[5] = npar_info->max_rx_ucast_mac_filters | +			 (npar_info->max_rx_ip_addr << 16); +	cmd.req.arg[6] = npar_info->max_rx_lro_flow | +			 (npar_info->max_rx_status_rings << 16); +	cmd.req.arg[7] = npar_info->max_rx_buf_rings | +			 (npar_info->max_rx_ques << 16); +	cmd.req.arg[8] = npar_info->max_tx_vlan_keys; +	cmd.req.arg[8] |= npar_info->max_local_ipv6_addrs << 16; +	cmd.req.arg[9] = npar_info->max_remote_ipv6_addrs; + +	err = qlcnic_issue_cmd(adapter, &cmd); +	if (err) +		dev_err(&adapter->pdev->dev, +			"Failed to set vport info, err=%d\n", err); + +	qlcnic_free_mbx_args(&cmd); +	return err; +} + +static int qlcnic_sriov_pf_cal_res_limit(struct qlcnic_adapter *adapter, +					 struct qlcnic_info *info, u16 func) +{ +	struct qlcnic_sriov *sriov = adapter->ahw->sriov; +	struct qlcnic_resources *res = &sriov->ff_max; +	u32 temp, num_vf_macs, num_vfs, max; +	int ret = -EIO, vpid, id; +	struct qlcnic_vport *vp; + +	vpid = qlcnic_sriov_pf_get_vport_handle(adapter, func); +	if (vpid < 0) +		return -EINVAL; + +	num_vfs = sriov->num_vfs; +	max = num_vfs + 1; +	info->bit_offsets = 0xffff; +	info->max_tx_ques = res->num_tx_queues / max; +	info->max_rx_mcast_mac_filters = res->num_rx_mcast_mac_filters; +	num_vf_macs = QLCNIC_SRIOV_VF_MAX_MAC; + +	if (adapter->ahw->pci_func == func) { +		temp = res->num_rx_mcast_mac_filters - (num_vfs * num_vf_macs); +		info->max_rx_ucast_mac_filters = temp; +		temp = res->num_tx_mac_filters - (num_vfs * num_vf_macs); +		info->max_tx_mac_filters = temp; +		info->min_tx_bw = 0; +		info->max_tx_bw = MAX_BW; +	} else { +		id = qlcnic_sriov_func_to_index(adapter, func); +		if (id < 0) +			return id; +		vp = sriov->vf_info[id].vp; +		info->min_tx_bw = vp->min_tx_bw; +		info->max_tx_bw = vp->max_tx_bw; +		info->max_rx_ucast_mac_filters = num_vf_macs; +		info->max_tx_mac_filters = num_vf_macs; +	} + +	info->max_rx_ip_addr = res->num_destip / max; +	info->max_rx_status_rings = res->num_rx_status_rings / max; +	info->max_rx_buf_rings = res->num_rx_buf_rings / max; +	info->max_rx_ques = res->num_rx_queues / max; +	info->max_rx_lro_flow = res->num_lro_flows_supported / max; +	info->max_tx_vlan_keys = res->num_txvlan_keys; +	info->max_local_ipv6_addrs = res->max_local_ipv6_addrs; +	info->max_remote_ipv6_addrs = res->max_remote_ipv6_addrs; + +	ret = qlcnic_sriov_pf_set_vport_info(adapter, info, vpid); +	if (ret) +		return ret; + +	return 0; +} + +static void qlcnic_sriov_pf_set_ff_max_res(struct qlcnic_adapter *adapter, +					   struct qlcnic_info *info) +{ +	struct qlcnic_resources *ff_max = &adapter->ahw->sriov->ff_max; + +	ff_max->num_tx_mac_filters = info->max_tx_mac_filters; +	ff_max->num_rx_ucast_mac_filters = info->max_rx_ucast_mac_filters; +	ff_max->num_rx_mcast_mac_filters = info->max_rx_mcast_mac_filters; +	ff_max->num_txvlan_keys = info->max_tx_vlan_keys; +	ff_max->num_rx_queues = info->max_rx_ques; +	ff_max->num_tx_queues = info->max_tx_ques; +	ff_max->num_lro_flows_supported = info->max_rx_lro_flow; +	ff_max->num_destip = info->max_rx_ip_addr; +	ff_max->num_rx_buf_rings = info->max_rx_buf_rings; +	ff_max->num_rx_status_rings = info->max_rx_status_rings; +	ff_max->max_remote_ipv6_addrs = info->max_remote_ipv6_addrs; +	ff_max->max_local_ipv6_addrs = info->max_local_ipv6_addrs; +} + +static int qlcnic_sriov_get_pf_info(struct qlcnic_adapter *adapter, +				    struct qlcnic_info *npar_info) +{ +	int err; +	struct qlcnic_cmd_args cmd; + +	if (qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_GET_NIC_INFO)) +		return -ENOMEM; + +	cmd.req.arg[1] = 0x2; +	err = qlcnic_issue_cmd(adapter, &cmd); +	if (err) { +		dev_err(&adapter->pdev->dev, +			"Failed to get PF info, err=%d\n", err); +		goto out; +	} + +	npar_info->total_pf = cmd.rsp.arg[2] & 0xff; +	npar_info->total_rss_engines = (cmd.rsp.arg[2] >> 8) & 0xff; +	npar_info->max_vports = MSW(cmd.rsp.arg[2]); +	npar_info->max_tx_ques =  LSW(cmd.rsp.arg[3]); +	npar_info->max_tx_mac_filters = MSW(cmd.rsp.arg[3]); +	npar_info->max_rx_mcast_mac_filters = LSW(cmd.rsp.arg[4]); +	npar_info->max_rx_ucast_mac_filters = MSW(cmd.rsp.arg[4]); +	npar_info->max_rx_ip_addr = LSW(cmd.rsp.arg[5]); +	npar_info->max_rx_lro_flow = MSW(cmd.rsp.arg[5]); +	npar_info->max_rx_status_rings = LSW(cmd.rsp.arg[6]); +	npar_info->max_rx_buf_rings = MSW(cmd.rsp.arg[6]); +	npar_info->max_rx_ques = LSW(cmd.rsp.arg[7]); +	npar_info->max_tx_vlan_keys = MSW(cmd.rsp.arg[7]); +	npar_info->max_local_ipv6_addrs = LSW(cmd.rsp.arg[8]); +	npar_info->max_remote_ipv6_addrs = MSW(cmd.rsp.arg[8]); + +	qlcnic_sriov_pf_set_ff_max_res(adapter, npar_info); +	dev_info(&adapter->pdev->dev, +		 "\n\ttotal_pf: %d,\n" +		 "\n\ttotal_rss_engines: %d max_vports: %d max_tx_ques %d,\n" +		 "\tmax_tx_mac_filters: %d max_rx_mcast_mac_filters: %d,\n" +		 "\tmax_rx_ucast_mac_filters: 0x%x, max_rx_ip_addr: %d,\n" +		 "\tmax_rx_lro_flow: %d max_rx_status_rings: %d,\n" +		 "\tmax_rx_buf_rings: %d, max_rx_ques: %d, max_tx_vlan_keys %d\n" +		 "\tmax_local_ipv6_addrs: %d, max_remote_ipv6_addrs: %d\n", +		 npar_info->total_pf, npar_info->total_rss_engines, +		 npar_info->max_vports, npar_info->max_tx_ques, +		 npar_info->max_tx_mac_filters, +		 npar_info->max_rx_mcast_mac_filters, +		 npar_info->max_rx_ucast_mac_filters, npar_info->max_rx_ip_addr, +		 npar_info->max_rx_lro_flow, npar_info->max_rx_status_rings, +		 npar_info->max_rx_buf_rings, npar_info->max_rx_ques, +		 npar_info->max_tx_vlan_keys, npar_info->max_local_ipv6_addrs, +		 npar_info->max_remote_ipv6_addrs); + +out: +	qlcnic_free_mbx_args(&cmd); +	return err; +} + +static void qlcnic_sriov_pf_reset_vport_handle(struct qlcnic_adapter *adapter, +					       u8 func) +{ +	struct qlcnic_sriov  *sriov = adapter->ahw->sriov; +	struct qlcnic_vport *vp; +	int index; + +	if (adapter->ahw->pci_func == func) { +		sriov->vp_handle = 0; +	} else { +		index = qlcnic_sriov_func_to_index(adapter, func); +		if (index < 0) +			return; +		vp = sriov->vf_info[index].vp; +		vp->handle = 0; +	} +} + +static void qlcnic_sriov_pf_set_vport_handle(struct qlcnic_adapter *adapter, +					     u16 vport_handle, u8 func) +{ +	struct qlcnic_sriov  *sriov = adapter->ahw->sriov; +	struct qlcnic_vport *vp; +	int index; + +	if (adapter->ahw->pci_func == func) { +		sriov->vp_handle = vport_handle; +	} else { +		index = qlcnic_sriov_func_to_index(adapter, func); +		if (index < 0) +			return; +		vp = sriov->vf_info[index].vp; +		vp->handle = vport_handle; +	} +} + +static int qlcnic_sriov_pf_get_vport_handle(struct qlcnic_adapter *adapter, +					    u8 func) +{ +	struct qlcnic_sriov  *sriov = adapter->ahw->sriov; +	struct qlcnic_vf_info *vf_info; +	int index; + +	if (adapter->ahw->pci_func == func) { +		return sriov->vp_handle; +	} else { +		index = qlcnic_sriov_func_to_index(adapter, func); +		if (index >= 0) { +			vf_info = &sriov->vf_info[index]; +			return vf_info->vp->handle; +		} +	} + +	return -EINVAL; +} + +static int qlcnic_sriov_pf_config_vport(struct qlcnic_adapter *adapter, +					u8 flag, u16 func) +{ +	struct qlcnic_cmd_args cmd; +	int ret; +	int vpid; + +	if (qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CONFIG_VPORT)) +		return -ENOMEM; + +	if (flag) { +		cmd.req.arg[3] = func << 8; +	} else { +		vpid = qlcnic_sriov_pf_get_vport_handle(adapter, func); +		if (vpid < 0) { +			ret = -EINVAL; +			goto out; +		} +		cmd.req.arg[3] = ((vpid & 0xffff) << 8) | 1; +	} + +	ret = qlcnic_issue_cmd(adapter, &cmd); +	if (ret) { +		dev_err(&adapter->pdev->dev, +			"Failed %s vport, err %d for func 0x%x\n", +			(flag ? "enable" : "disable"), ret, func); +		goto out; +	} + +	if (flag) { +		vpid = cmd.rsp.arg[2] & 0xffff; +		qlcnic_sriov_pf_set_vport_handle(adapter, vpid, func); +	} else { +		qlcnic_sriov_pf_reset_vport_handle(adapter, func); +	} + +out: +	qlcnic_free_mbx_args(&cmd); +	return ret; +} + +static int qlcnic_sriov_pf_cfg_vlan_filtering(struct qlcnic_adapter *adapter, +					      u8 enable) +{ +	struct qlcnic_cmd_args cmd; +	int err; + +	err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_SET_NIC_INFO); +	if (err) +		return err; + +	cmd.req.arg[1] = 0x4; +	if (enable) +		cmd.req.arg[1] |= BIT_16; + +	err = qlcnic_issue_cmd(adapter, &cmd); +	if (err) +		dev_err(&adapter->pdev->dev, +			"Failed to configure VLAN filtering, err=%d\n", err); + +	qlcnic_free_mbx_args(&cmd); +	return err; +} + +static int qlcnic_sriov_pf_cfg_eswitch(struct qlcnic_adapter *adapter, +				       u8 func, u8 enable) +{ +	struct qlcnic_cmd_args cmd; +	int err = -EIO; + +	if (qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_TOGGLE_ESWITCH)) +		return -ENOMEM; + +	cmd.req.arg[0] |= (3 << 29); +	cmd.req.arg[1] = ((func & 0xf) << 2) | BIT_6 | BIT_1; +	if (enable) +		cmd.req.arg[1] |= BIT_0; + +	err = qlcnic_issue_cmd(adapter, &cmd); + +	if (err != QLCNIC_RCODE_SUCCESS) { +		dev_err(&adapter->pdev->dev, +			"Failed to enable sriov eswitch%d\n", err); +		err = -EIO; +	} + +	qlcnic_free_mbx_args(&cmd); +	return err; +} + +static void qlcnic_sriov_pf_del_flr_queue(struct qlcnic_adapter *adapter) +{ +	struct qlcnic_sriov *sriov = adapter->ahw->sriov; +	struct qlcnic_back_channel *bc = &sriov->bc; +	int i; + +	for (i = 0; i < sriov->num_vfs; i++) +		cancel_work_sync(&sriov->vf_info[i].flr_work); + +	destroy_workqueue(bc->bc_flr_wq); +} + +static int qlcnic_sriov_pf_create_flr_queue(struct qlcnic_adapter *adapter) +{ +	struct qlcnic_back_channel *bc = &adapter->ahw->sriov->bc; +	struct workqueue_struct *wq; + +	wq = create_singlethread_workqueue("qlcnic-flr"); +	if (wq == NULL) { +		dev_err(&adapter->pdev->dev, "Cannot create FLR workqueue\n"); +		return -ENOMEM; +	} + +	bc->bc_flr_wq =  wq; +	return 0; +} + +void qlcnic_sriov_pf_cleanup(struct qlcnic_adapter *adapter) +{ +	u8 func = adapter->ahw->pci_func; + +	if (!qlcnic_sriov_enable_check(adapter)) +		return; + +	qlcnic_sriov_pf_del_flr_queue(adapter); +	qlcnic_sriov_cfg_bc_intr(adapter, 0); +	qlcnic_sriov_pf_config_vport(adapter, 0, func); +	qlcnic_sriov_pf_cfg_eswitch(adapter, func, 0); +	qlcnic_sriov_pf_cfg_vlan_filtering(adapter, 0); +	__qlcnic_sriov_cleanup(adapter); +	adapter->ahw->op_mode = QLCNIC_MGMT_FUNC; +	clear_bit(__QLCNIC_SRIOV_ENABLE, &adapter->state); +} + +void qlcnic_sriov_pf_disable(struct qlcnic_adapter *adapter) +{ +	if (!qlcnic_sriov_pf_check(adapter)) +		return; + +	if (!qlcnic_sriov_enable_check(adapter)) +		return; + +	pci_disable_sriov(adapter->pdev); +	netdev_info(adapter->netdev, +		    "SR-IOV is disabled successfully on port %d\n", +		    adapter->portnum); +} + +static int qlcnic_pci_sriov_disable(struct qlcnic_adapter *adapter) +{ +	struct net_device *netdev = adapter->netdev; + +	if (netif_running(netdev)) +		__qlcnic_down(adapter, netdev); + +	qlcnic_sriov_pf_disable(adapter); + +	qlcnic_sriov_pf_cleanup(adapter); + +	/* After disabling SRIOV re-init the driver in default mode +	   configure opmode based on op_mode of function +	 */ +	if (qlcnic_83xx_configure_opmode(adapter)) +		return -EIO; + +	if (netif_running(netdev)) +		__qlcnic_up(adapter, netdev); + +	return 0; +} + +static int qlcnic_sriov_pf_init(struct qlcnic_adapter *adapter) +{ +	struct qlcnic_hardware_context *ahw = adapter->ahw; +	struct qlcnic_info nic_info, pf_info, vp_info; +	int err; +	u8 func = ahw->pci_func; + +	if (!qlcnic_sriov_enable_check(adapter)) +		return 0; + +	err = qlcnic_sriov_pf_cfg_vlan_filtering(adapter, 1); +	if (err) +		return err; + +	err = qlcnic_sriov_pf_cfg_eswitch(adapter, func, 1); +	if (err) +		goto disable_vlan_filtering; + +	err = qlcnic_sriov_pf_config_vport(adapter, 1, func); +	if (err) +		goto disable_eswitch; + +	err = qlcnic_sriov_get_pf_info(adapter, &pf_info); +	if (err) +		goto delete_vport; + +	err = qlcnic_get_nic_info(adapter, &nic_info, func); +	if (err) +		goto delete_vport; + +	err = qlcnic_sriov_pf_cal_res_limit(adapter, &vp_info, func); +	if (err) +		goto delete_vport; + +	err = qlcnic_sriov_cfg_bc_intr(adapter, 1); +	if (err) +		goto delete_vport; + +	ahw->physical_port = (u8) nic_info.phys_port; +	ahw->switch_mode = nic_info.switch_mode; +	ahw->max_mtu = nic_info.max_mtu; +	ahw->capabilities = nic_info.capabilities; +	ahw->nic_mode = QLC_83XX_SRIOV_MODE; +	return err; + +delete_vport: +	qlcnic_sriov_pf_config_vport(adapter, 0, func); + +disable_eswitch: +	qlcnic_sriov_pf_cfg_eswitch(adapter, func, 0); + +disable_vlan_filtering: +	qlcnic_sriov_pf_cfg_vlan_filtering(adapter, 0); + +	return err; +} + +static int qlcnic_sriov_pf_enable(struct qlcnic_adapter *adapter, int num_vfs) +{ +	int err; + +	if (!qlcnic_sriov_enable_check(adapter)) +		return 0; + +	err = pci_enable_sriov(adapter->pdev, num_vfs); +	if (err) +		qlcnic_sriov_pf_cleanup(adapter); + +	return err; +} + +static int __qlcnic_pci_sriov_enable(struct qlcnic_adapter *adapter, +				     int num_vfs) +{ +	int err = 0; + +	set_bit(__QLCNIC_SRIOV_ENABLE, &adapter->state); +	adapter->ahw->op_mode = QLCNIC_SRIOV_PF_FUNC; + +	err = qlcnic_sriov_init(adapter, num_vfs); +	if (err) +		goto clear_op_mode; + +	err = qlcnic_sriov_pf_create_flr_queue(adapter); +	if (err) +		goto sriov_cleanup; + +	err = qlcnic_sriov_pf_init(adapter); +	if (err) +		goto del_flr_queue; + +	err = qlcnic_sriov_pf_enable(adapter, num_vfs); +	return err; + +del_flr_queue: +	qlcnic_sriov_pf_del_flr_queue(adapter); + +sriov_cleanup: +	__qlcnic_sriov_cleanup(adapter); + +clear_op_mode: +	clear_bit(__QLCNIC_SRIOV_ENABLE, &adapter->state); +	adapter->ahw->op_mode = QLCNIC_MGMT_FUNC; +	return err; +} + +static int qlcnic_pci_sriov_enable(struct qlcnic_adapter *adapter, int num_vfs) +{ +	struct net_device *netdev = adapter->netdev; +	int err; + +	if (!(adapter->flags & QLCNIC_MSIX_ENABLED)) { +		netdev_err(netdev, +			   "SR-IOV cannot be enabled, when legacy interrupts are enabled\n"); +		return -EIO; +	} + +	if (netif_running(netdev)) +		__qlcnic_down(adapter, netdev); + +	err = __qlcnic_pci_sriov_enable(adapter, num_vfs); +	if (err) { +		netdev_info(netdev, "Failed to enable SR-IOV on port %d\n", +			    adapter->portnum); + +		err = -EIO; +		if (qlcnic_83xx_configure_opmode(adapter)) +			goto error; +	} else { +		netdev_info(netdev, +			    "SR-IOV is enabled successfully on port %d\n", +			    adapter->portnum); +		/* Return number of vfs enabled */ +		err = num_vfs; +	} +	if (netif_running(netdev)) +		__qlcnic_up(adapter, netdev); + +error: +	return err; +} + +int qlcnic_pci_sriov_configure(struct pci_dev *dev, int num_vfs) +{ +	struct qlcnic_adapter *adapter = pci_get_drvdata(dev); +	int err; + +	if (test_and_set_bit(__QLCNIC_RESETTING, &adapter->state)) +		return -EBUSY; + +	if (num_vfs == 0) +		err = qlcnic_pci_sriov_disable(adapter); +	else +		err = qlcnic_pci_sriov_enable(adapter, num_vfs); + +	clear_bit(__QLCNIC_RESETTING, &adapter->state); +	return err; +} + +static int qlcnic_sriov_set_vf_acl(struct qlcnic_adapter *adapter, u8 func) +{ +	struct qlcnic_cmd_args cmd; +	struct qlcnic_vport *vp; +	int err, id; + +	id = qlcnic_sriov_func_to_index(adapter, func); +	if (id < 0) +		return id; + +	vp = adapter->ahw->sriov->vf_info[id].vp; +	err = qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_SET_NIC_INFO); +	if (err) +		return err; + +	cmd.req.arg[1] = 0x3 | func << 16; +	if (vp->vlan_mode == QLC_PVID_MODE) { +		cmd.req.arg[2] |= BIT_6; +		cmd.req.arg[3] |= vp->vlan << 8; +	} + +	err = qlcnic_issue_cmd(adapter, &cmd); +	if (err) +		dev_err(&adapter->pdev->dev, "Failed to set ACL, err=%d\n", +			err); + +	qlcnic_free_mbx_args(&cmd); +	return err; +} + +static int qlcnic_sriov_set_vf_vport_info(struct qlcnic_adapter *adapter, +					  u16 func) +{ +	struct qlcnic_info defvp_info; +	int err; + +	err = qlcnic_sriov_pf_cal_res_limit(adapter, &defvp_info, func); +	if (err) +		return -EIO; + +	err = qlcnic_sriov_set_vf_acl(adapter, func); +	if (err) +		return err; + +	return 0; +} + +static int qlcnic_sriov_pf_channel_cfg_cmd(struct qlcnic_bc_trans *trans, +					   struct qlcnic_cmd_args *cmd) +{ +	struct qlcnic_vf_info *vf = trans->vf; +	struct qlcnic_adapter *adapter = vf->adapter; +	int err; +	u16 func = vf->pci_func; + +	cmd->rsp.arg[0] = trans->req_hdr->cmd_op; +	cmd->rsp.arg[0] |= (1 << 16); + +	if (trans->req_hdr->cmd_op == QLCNIC_BC_CMD_CHANNEL_INIT) { +		err = qlcnic_sriov_pf_config_vport(adapter, 1, func); +		if (!err) { +			err = qlcnic_sriov_set_vf_vport_info(adapter, func); +			if (err) +				qlcnic_sriov_pf_config_vport(adapter, 0, func); +		} +	} else { +		err = qlcnic_sriov_pf_config_vport(adapter, 0, func); +	} + +	if (err) +		goto err_out; + +	cmd->rsp.arg[0] |= (1 << 25); + +	if (trans->req_hdr->cmd_op == QLCNIC_BC_CMD_CHANNEL_INIT) +		set_bit(QLC_BC_VF_STATE, &vf->state); +	else +		clear_bit(QLC_BC_VF_STATE, &vf->state); + +	return err; + +err_out: +	cmd->rsp.arg[0] |= (2 << 25); +	return err; +} + +static int qlcnic_sriov_cfg_vf_def_mac(struct qlcnic_adapter *adapter, +				       struct qlcnic_vport *vp, +				       u16 func, u16 vlan, u8 op) +{ +	struct qlcnic_cmd_args cmd; +	struct qlcnic_macvlan_mbx mv; +	u8 *addr; +	int err; +	u32 *buf; +	int vpid; + +	if (qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_CONFIG_MAC_VLAN)) +		return -ENOMEM; + +	vpid = qlcnic_sriov_pf_get_vport_handle(adapter, func); +	if (vpid < 0) { +		err = -EINVAL; +		goto out; +	} + +	if (vlan) +		op = ((op == QLCNIC_MAC_ADD || op == QLCNIC_MAC_VLAN_ADD) ? +		      QLCNIC_MAC_VLAN_ADD : QLCNIC_MAC_VLAN_DEL); + +	cmd.req.arg[1] = op | (1 << 8) | (3 << 6); +	cmd.req.arg[1] |= ((vpid & 0xffff) << 16) | BIT_31; + +	addr = vp->mac; +	mv.vlan = vlan; +	mv.mac_addr0 = addr[0]; +	mv.mac_addr1 = addr[1]; +	mv.mac_addr2 = addr[2]; +	mv.mac_addr3 = addr[3]; +	mv.mac_addr4 = addr[4]; +	mv.mac_addr5 = addr[5]; +	buf = &cmd.req.arg[2]; +	memcpy(buf, &mv, sizeof(struct qlcnic_macvlan_mbx)); + +	err = qlcnic_issue_cmd(adapter, &cmd); + +	if (err) +		dev_err(&adapter->pdev->dev, +			"MAC-VLAN %s to CAM failed, err=%d.\n", +			((op == 1) ? "add " : "delete "), err); + +out: +	qlcnic_free_mbx_args(&cmd); +	return err; +} + +static int qlcnic_sriov_validate_create_rx_ctx(struct qlcnic_cmd_args *cmd) +{ +	if ((cmd->req.arg[0] >> 29) != 0x3) +		return -EINVAL; + +	return 0; +} + +static int qlcnic_sriov_pf_create_rx_ctx_cmd(struct qlcnic_bc_trans *tran, +					     struct qlcnic_cmd_args *cmd) +{ +	struct qlcnic_vf_info *vf = tran->vf; +	struct qlcnic_adapter *adapter = vf->adapter; +	struct qlcnic_rcv_mbx_out *mbx_out; +	int err; +	u16 vlan; + +	err = qlcnic_sriov_validate_create_rx_ctx(cmd); +	if (err) { +		cmd->rsp.arg[0] |= (0x6 << 25); +		return err; +	} + +	cmd->req.arg[6] = vf->vp->handle; +	err = qlcnic_issue_cmd(adapter, cmd); + +	vlan = vf->vp->vlan; +	if (!err) { +		mbx_out = (struct qlcnic_rcv_mbx_out *)&cmd->rsp.arg[1]; +		vf->rx_ctx_id = mbx_out->ctx_id; +		qlcnic_sriov_cfg_vf_def_mac(adapter, vf->vp, vf->pci_func, +					    vlan, QLCNIC_MAC_ADD); +	} else { +		vf->rx_ctx_id = 0; +	} + +	return err; +} + +static int qlcnic_sriov_pf_mac_address_cmd(struct qlcnic_bc_trans *trans, +					   struct qlcnic_cmd_args *cmd) +{ +	struct qlcnic_vf_info *vf = trans->vf; +	u8 type, *mac; + +	type = cmd->req.arg[1]; +	switch (type) { +	case QLCNIC_SET_STATION_MAC: +	case QLCNIC_SET_FAC_DEF_MAC: +		cmd->rsp.arg[0] = (2 << 25); +		break; +	case QLCNIC_GET_CURRENT_MAC: +		cmd->rsp.arg[0] = (1 << 25); +		mac = vf->vp->mac; +		cmd->rsp.arg[2] = mac[1] | ((mac[0] << 8) & 0xff00); +		cmd->rsp.arg[1] = mac[5] | ((mac[4] << 8) & 0xff00) | +				  ((mac[3]) << 16 & 0xff0000) | +				  ((mac[2]) << 24 & 0xff000000); +	} + +	return 0; +} + +static int qlcnic_sriov_validate_create_tx_ctx(struct qlcnic_cmd_args *cmd) +{ +	if ((cmd->req.arg[0] >> 29) != 0x3) +		return -EINVAL; + +	return 0; +} + +static int qlcnic_sriov_pf_create_tx_ctx_cmd(struct qlcnic_bc_trans *trans, +					     struct qlcnic_cmd_args *cmd) +{ +	struct qlcnic_vf_info *vf = trans->vf; +	struct qlcnic_adapter *adapter = vf->adapter; +	struct qlcnic_tx_mbx_out *mbx_out; +	int err; + +	err = qlcnic_sriov_validate_create_tx_ctx(cmd); +	if (err) { +		cmd->rsp.arg[0] |= (0x6 << 25); +		return err; +	} + +	cmd->req.arg[5] |= vf->vp->handle << 16; +	err = qlcnic_issue_cmd(adapter, cmd); +	if (!err) { +		mbx_out = (struct qlcnic_tx_mbx_out *)&cmd->rsp.arg[2]; +		vf->tx_ctx_id = mbx_out->ctx_id; +	} else { +		vf->tx_ctx_id = 0; +	} + +	return err; +} + +static int qlcnic_sriov_validate_del_rx_ctx(struct qlcnic_vf_info *vf, +					    struct qlcnic_cmd_args *cmd) +{ +	if ((cmd->req.arg[0] >> 29) != 0x3) +		return -EINVAL; + +	if ((cmd->req.arg[1] & 0xffff) != vf->rx_ctx_id) +		return -EINVAL; + +	return 0; +} + +static int qlcnic_sriov_pf_del_rx_ctx_cmd(struct qlcnic_bc_trans *trans, +					  struct qlcnic_cmd_args *cmd) +{ +	struct qlcnic_vf_info *vf = trans->vf; +	struct qlcnic_adapter *adapter = vf->adapter; +	int err; +	u16 vlan; + +	err = qlcnic_sriov_validate_del_rx_ctx(vf, cmd); +	if (err) { +		cmd->rsp.arg[0] |= (0x6 << 25); +		return err; +	} + +	vlan = vf->vp->vlan; +	qlcnic_sriov_cfg_vf_def_mac(adapter, vf->vp, vf->pci_func, +				    vlan, QLCNIC_MAC_DEL); +	cmd->req.arg[1] |= vf->vp->handle << 16; +	err = qlcnic_issue_cmd(adapter, cmd); + +	if (!err) +		vf->rx_ctx_id = 0; + +	return err; +} + +static int qlcnic_sriov_validate_del_tx_ctx(struct qlcnic_vf_info *vf, +					    struct qlcnic_cmd_args *cmd) +{ +	if ((cmd->req.arg[0] >> 29) != 0x3) +		return -EINVAL; + +	if ((cmd->req.arg[1] & 0xffff) != vf->tx_ctx_id) +		return -EINVAL; + +	return 0; +} + +static int qlcnic_sriov_pf_del_tx_ctx_cmd(struct qlcnic_bc_trans *trans, +					  struct qlcnic_cmd_args *cmd) +{ +	struct qlcnic_vf_info *vf = trans->vf; +	struct qlcnic_adapter *adapter = vf->adapter; +	int err; + +	err = qlcnic_sriov_validate_del_tx_ctx(vf, cmd); +	if (err) { +		cmd->rsp.arg[0] |= (0x6 << 25); +		return err; +	} + +	cmd->req.arg[1] |= vf->vp->handle << 16; +	err = qlcnic_issue_cmd(adapter, cmd); + +	if (!err) +		vf->tx_ctx_id = 0; + +	return err; +} + +static int qlcnic_sriov_validate_cfg_lro(struct qlcnic_vf_info *vf, +					 struct qlcnic_cmd_args *cmd) +{ +	if ((cmd->req.arg[1] >> 16) != vf->rx_ctx_id) +		return -EINVAL; + +	return 0; +} + +static int qlcnic_sriov_pf_cfg_lro_cmd(struct qlcnic_bc_trans *trans, +				       struct qlcnic_cmd_args *cmd) +{ +	struct qlcnic_vf_info *vf = trans->vf; +	struct qlcnic_adapter *adapter = vf->adapter; +	int err; + +	err = qlcnic_sriov_validate_cfg_lro(vf, cmd); +	if (err) { +		cmd->rsp.arg[0] |= (0x6 << 25); +		return err; +	} + +	err = qlcnic_issue_cmd(adapter, cmd); +	return err; +} + +static int qlcnic_sriov_pf_cfg_ip_cmd(struct qlcnic_bc_trans *trans, +				      struct qlcnic_cmd_args *cmd) +{ +	struct qlcnic_vf_info *vf = trans->vf; +	struct qlcnic_adapter *adapter = vf->adapter; +	int err = -EIO; +	u8 op; + +	op =  cmd->req.arg[1] & 0xff; + +	cmd->req.arg[1] |= vf->vp->handle << 16; +	cmd->req.arg[1] |= BIT_31; + +	err = qlcnic_issue_cmd(adapter, cmd); +	return err; +} + +static int qlcnic_sriov_validate_cfg_intrpt(struct qlcnic_vf_info *vf, +					    struct qlcnic_cmd_args *cmd) +{ +	if (((cmd->req.arg[1] >> 8) & 0xff) != vf->pci_func) +		return -EINVAL; + +	if (!(cmd->req.arg[1] & BIT_16)) +		return -EINVAL; + +	if ((cmd->req.arg[1] & 0xff) != 0x1) +		return -EINVAL; + +	return 0; +} + +static int qlcnic_sriov_pf_cfg_intrpt_cmd(struct qlcnic_bc_trans *trans, +					  struct qlcnic_cmd_args *cmd) +{ +	struct qlcnic_vf_info *vf = trans->vf; +	struct qlcnic_adapter *adapter = vf->adapter; +	int err; + +	err = qlcnic_sriov_validate_cfg_intrpt(vf, cmd); +	if (err) +		cmd->rsp.arg[0] |= (0x6 << 25); +	else +		err = qlcnic_issue_cmd(adapter, cmd); + +	return err; +} + +static int qlcnic_sriov_validate_mtu(struct qlcnic_adapter *adapter, +				     struct qlcnic_vf_info *vf, +				     struct qlcnic_cmd_args *cmd) +{ +	if (cmd->req.arg[1] != vf->rx_ctx_id) +		return -EINVAL; + +	if (cmd->req.arg[2] > adapter->ahw->max_mtu) +		return -EINVAL; + +	return 0; +} + +static int qlcnic_sriov_pf_set_mtu_cmd(struct qlcnic_bc_trans *trans, +				       struct qlcnic_cmd_args *cmd) +{ +	struct qlcnic_vf_info *vf = trans->vf; +	struct qlcnic_adapter *adapter = vf->adapter; +	int err; + +	err = qlcnic_sriov_validate_mtu(adapter, vf, cmd); +	if (err) +		cmd->rsp.arg[0] |= (0x6 << 25); +	else +		err = qlcnic_issue_cmd(adapter, cmd); + +	return err; +} + +static int qlcnic_sriov_validate_get_nic_info(struct qlcnic_vf_info *vf, +					      struct qlcnic_cmd_args *cmd) +{ +	if (cmd->req.arg[1] & BIT_31) { +		if (((cmd->req.arg[1] >> 16) & 0x7fff) != vf->pci_func) +			return -EINVAL; +	} else { +		cmd->req.arg[1] |= vf->vp->handle << 16; +	} + +	return 0; +} + +static int qlcnic_sriov_pf_get_nic_info_cmd(struct qlcnic_bc_trans *trans, +					    struct qlcnic_cmd_args *cmd) +{ +	struct qlcnic_vf_info *vf = trans->vf; +	struct qlcnic_adapter *adapter = vf->adapter; +	int err; + +	err = qlcnic_sriov_validate_get_nic_info(vf, cmd); +	if (err) { +		cmd->rsp.arg[0] |= (0x6 << 25); +		return err; +	} + +	err = qlcnic_issue_cmd(adapter, cmd); +	return err; +} + +static int qlcnic_sriov_validate_cfg_rss(struct qlcnic_vf_info *vf, +					 struct qlcnic_cmd_args *cmd) +{ +	if (cmd->req.arg[1] != vf->rx_ctx_id) +		return -EINVAL; + +	return 0; +} + +static int qlcnic_sriov_pf_cfg_rss_cmd(struct qlcnic_bc_trans *trans, +				       struct qlcnic_cmd_args *cmd) +{ +	struct qlcnic_vf_info *vf = trans->vf; +	struct qlcnic_adapter *adapter = vf->adapter; +	int err; + +	err = qlcnic_sriov_validate_cfg_rss(vf, cmd); +	if (err) +		cmd->rsp.arg[0] |= (0x6 << 25); +	else +		err = qlcnic_issue_cmd(adapter, cmd); + +	return err; +} + +static int qlcnic_sriov_validate_cfg_intrcoal(struct qlcnic_adapter *adapter, +					      struct qlcnic_vf_info *vf, +					      struct qlcnic_cmd_args *cmd) +{ +	struct qlcnic_nic_intr_coalesce *coal = &adapter->ahw->coal; +	u16 ctx_id, pkts, time; + +	ctx_id = cmd->req.arg[1] >> 16; +	pkts = cmd->req.arg[2] & 0xffff; +	time = cmd->req.arg[2] >> 16; + +	if (ctx_id != vf->rx_ctx_id) +		return -EINVAL; +	if (pkts > coal->rx_packets) +		return -EINVAL; +	if (time < coal->rx_time_us) +		return -EINVAL; + +	return 0; +} + +static int qlcnic_sriov_pf_cfg_intrcoal_cmd(struct qlcnic_bc_trans *tran, +					    struct qlcnic_cmd_args *cmd) +{ +	struct qlcnic_vf_info *vf = tran->vf; +	struct qlcnic_adapter *adapter = vf->adapter; +	int err; + +	err = qlcnic_sriov_validate_cfg_intrcoal(adapter, vf, cmd); +	if (err) { +		cmd->rsp.arg[0] |= (0x6 << 25); +		return err; +	} + +	err = qlcnic_issue_cmd(adapter, cmd); +	return err; +} + +static int qlcnic_sriov_validate_cfg_macvlan(struct qlcnic_adapter *adapter, +					     struct qlcnic_vf_info *vf, +					     struct qlcnic_cmd_args *cmd) +{ +	struct qlcnic_macvlan_mbx *macvlan; +	struct qlcnic_vport *vp = vf->vp; +	u8 op, new_op; + +	if (!(cmd->req.arg[1] & BIT_8)) +		return -EINVAL; + +	cmd->req.arg[1] |= (vf->vp->handle << 16); +	cmd->req.arg[1] |= BIT_31; + +	macvlan = (struct qlcnic_macvlan_mbx *)&cmd->req.arg[2]; +	if (!(macvlan->mac_addr0 & BIT_0)) { +		dev_err(&adapter->pdev->dev, +			"MAC address change is not allowed from VF %d", +			vf->pci_func); +		return -EINVAL; +	} + +	if (vp->vlan_mode == QLC_PVID_MODE) { +		op = cmd->req.arg[1] & 0x7; +		cmd->req.arg[1] &= ~0x7; +		new_op = (op == QLCNIC_MAC_ADD || op == QLCNIC_MAC_VLAN_ADD) ? +			 QLCNIC_MAC_VLAN_ADD : QLCNIC_MAC_VLAN_DEL; +		cmd->req.arg[3] |= vp->vlan << 16; +		cmd->req.arg[1] |= new_op; +	} + +	return 0; +} + +static int qlcnic_sriov_pf_cfg_macvlan_cmd(struct qlcnic_bc_trans *trans, +					   struct qlcnic_cmd_args *cmd) +{ +	struct qlcnic_vf_info *vf = trans->vf; +	struct qlcnic_adapter *adapter = vf->adapter; +	int err; + +	err = qlcnic_sriov_validate_cfg_macvlan(adapter, vf, cmd); +	if (err) { +		cmd->rsp.arg[0] |= (0x6 << 25); +		return err; +	} + +	err = qlcnic_issue_cmd(adapter, cmd); +	return err; +} + +static int qlcnic_sriov_validate_linkevent(struct qlcnic_vf_info *vf, +					   struct qlcnic_cmd_args *cmd) +{ +	if ((cmd->req.arg[1] >> 16) != vf->rx_ctx_id) +		return -EINVAL; + +	if (!(cmd->req.arg[1] & BIT_8)) +		return -EINVAL; + +	return 0; +} + +static int qlcnic_sriov_pf_linkevent_cmd(struct qlcnic_bc_trans *trans, +					 struct qlcnic_cmd_args *cmd) +{ +	struct qlcnic_vf_info *vf = trans->vf; +	struct qlcnic_adapter *adapter = vf->adapter; +	int err; + +	err = qlcnic_sriov_validate_linkevent(vf, cmd); +	if (err) { +		cmd->rsp.arg[0] |= (0x6 << 25); +		return err; +	} + +	err = qlcnic_issue_cmd(adapter, cmd); +	return err; +} + +static int qlcnic_sriov_pf_cfg_promisc_cmd(struct qlcnic_bc_trans *trans, +					   struct qlcnic_cmd_args *cmd) +{ +	struct qlcnic_vf_info *vf = trans->vf; +	struct qlcnic_adapter *adapter = vf->adapter; +	int err; + +	cmd->req.arg[1] |= vf->vp->handle << 16; +	cmd->req.arg[1] |= BIT_31; +	err = qlcnic_issue_cmd(adapter, cmd); +	return err; +} + +static int qlcnic_sriov_pf_get_acl_cmd(struct qlcnic_bc_trans *trans, +				       struct qlcnic_cmd_args *cmd) +{ +	struct qlcnic_vf_info *vf = trans->vf; +	struct qlcnic_vport *vp = vf->vp; +	u8 cmd_op, mode = vp->vlan_mode; + +	cmd_op = trans->req_hdr->cmd_op; +	cmd->rsp.arg[0] = (cmd_op & 0xffff) | 14 << 16 | 1 << 25; + +	switch (mode) { +	case QLC_GUEST_VLAN_MODE: +		cmd->rsp.arg[1] = mode | 1 << 8; +		cmd->rsp.arg[2] = 1 << 16; +		break; +	case QLC_PVID_MODE: +		cmd->rsp.arg[1] = mode | 1 << 8 | vp->vlan << 16; +		break; +	} + +	return 0; +} + +static int qlcnic_sriov_pf_del_guest_vlan(struct qlcnic_adapter *adapter, +					  struct qlcnic_vf_info *vf) + +{ +	struct qlcnic_vport *vp = vf->vp; + +	if (!vp->vlan) +		return -EINVAL; + +	if (!vf->rx_ctx_id) { +		vp->vlan = 0; +		return 0; +	} + +	qlcnic_sriov_cfg_vf_def_mac(adapter, vp, vf->pci_func, +				    vp->vlan, QLCNIC_MAC_DEL); +	vp->vlan = 0; +	qlcnic_sriov_cfg_vf_def_mac(adapter, vp, vf->pci_func, +				    0, QLCNIC_MAC_ADD); +	return 0; +} + +static int qlcnic_sriov_pf_add_guest_vlan(struct qlcnic_adapter *adapter, +					  struct qlcnic_vf_info *vf, +					  struct qlcnic_cmd_args *cmd) +{ +	struct qlcnic_vport *vp = vf->vp; +	int err = -EIO; + +	if (vp->vlan) +		return err; + +	if (!vf->rx_ctx_id) { +		vp->vlan = cmd->req.arg[1] >> 16; +		return 0; +	} + +	err = qlcnic_sriov_cfg_vf_def_mac(adapter, vp, vf->pci_func, +					  0, QLCNIC_MAC_DEL); +	if (err) +		return err; + +	vp->vlan = cmd->req.arg[1] >> 16; +	err = qlcnic_sriov_cfg_vf_def_mac(adapter, vp, vf->pci_func, +					  vp->vlan, QLCNIC_MAC_ADD); + +	if (err) { +		qlcnic_sriov_cfg_vf_def_mac(adapter, vp, vf->pci_func, +					    0, QLCNIC_MAC_ADD); +		vp->vlan = 0; +	} + +	return err; +} + +static int qlcnic_sriov_pf_cfg_guest_vlan_cmd(struct qlcnic_bc_trans *tran, +					      struct qlcnic_cmd_args *cmd) +{ +	struct qlcnic_vf_info  *vf = tran->vf; +	struct qlcnic_adapter *adapter =  vf->adapter; +	struct qlcnic_vport *vp = vf->vp; +	int err = -EIO; +	u8 op; + +	if (vp->vlan_mode != QLC_GUEST_VLAN_MODE) { +		cmd->rsp.arg[0] |= 2 << 25; +		return err; +	} + +	op = cmd->req.arg[1] & 0xf; + +	if (op) +		err = qlcnic_sriov_pf_add_guest_vlan(adapter, vf, cmd); +	else +		err = qlcnic_sriov_pf_del_guest_vlan(adapter, vf); + +	cmd->rsp.arg[0] |= err ? 2 << 25 : 1 << 25; +	return err; +} + +static const int qlcnic_pf_passthru_supp_cmds[] = { +	QLCNIC_CMD_GET_STATISTICS, +	QLCNIC_CMD_GET_PORT_CONFIG, +	QLCNIC_CMD_GET_LINK_STATUS, +}; + +static const struct qlcnic_sriov_cmd_handler qlcnic_pf_bc_cmd_hdlr[] = { +	[QLCNIC_BC_CMD_CHANNEL_INIT] = {&qlcnic_sriov_pf_channel_cfg_cmd}, +	[QLCNIC_BC_CMD_CHANNEL_TERM] = {&qlcnic_sriov_pf_channel_cfg_cmd}, +	[QLCNIC_BC_CMD_GET_ACL]	= {&qlcnic_sriov_pf_get_acl_cmd}, +	[QLCNIC_BC_CMD_CFG_GUEST_VLAN]	= {&qlcnic_sriov_pf_cfg_guest_vlan_cmd}, +}; + +static const struct qlcnic_sriov_fw_cmd_handler qlcnic_pf_fw_cmd_hdlr[] = { +	{QLCNIC_CMD_CREATE_RX_CTX, qlcnic_sriov_pf_create_rx_ctx_cmd}, +	{QLCNIC_CMD_CREATE_TX_CTX, qlcnic_sriov_pf_create_tx_ctx_cmd}, +	{QLCNIC_CMD_MAC_ADDRESS, qlcnic_sriov_pf_mac_address_cmd}, +	{QLCNIC_CMD_DESTROY_RX_CTX, qlcnic_sriov_pf_del_rx_ctx_cmd}, +	{QLCNIC_CMD_DESTROY_TX_CTX, qlcnic_sriov_pf_del_tx_ctx_cmd}, +	{QLCNIC_CMD_CONFIGURE_HW_LRO, qlcnic_sriov_pf_cfg_lro_cmd}, +	{QLCNIC_CMD_CONFIGURE_IP_ADDR, qlcnic_sriov_pf_cfg_ip_cmd}, +	{QLCNIC_CMD_CONFIG_INTRPT, qlcnic_sriov_pf_cfg_intrpt_cmd}, +	{QLCNIC_CMD_SET_MTU, qlcnic_sriov_pf_set_mtu_cmd}, +	{QLCNIC_CMD_GET_NIC_INFO, qlcnic_sriov_pf_get_nic_info_cmd}, +	{QLCNIC_CMD_CONFIGURE_RSS, qlcnic_sriov_pf_cfg_rss_cmd}, +	{QLCNIC_CMD_CONFIG_INTR_COAL, qlcnic_sriov_pf_cfg_intrcoal_cmd}, +	{QLCNIC_CMD_CONFIG_MAC_VLAN, qlcnic_sriov_pf_cfg_macvlan_cmd}, +	{QLCNIC_CMD_GET_LINK_EVENT, qlcnic_sriov_pf_linkevent_cmd}, +	{QLCNIC_CMD_CONFIGURE_MAC_RX_MODE, qlcnic_sriov_pf_cfg_promisc_cmd}, +}; + +void qlcnic_sriov_pf_process_bc_cmd(struct qlcnic_adapter *adapter, +				    struct qlcnic_bc_trans *trans, +				    struct qlcnic_cmd_args *cmd) +{ +	u8 size, cmd_op; + +	cmd_op = trans->req_hdr->cmd_op; + +	if (trans->req_hdr->op_type == QLC_BC_CMD) { +		size = ARRAY_SIZE(qlcnic_pf_bc_cmd_hdlr); +		if (cmd_op < size) { +			qlcnic_pf_bc_cmd_hdlr[cmd_op].fn(trans, cmd); +			return; +		} +	} else { +		int i; +		size = ARRAY_SIZE(qlcnic_pf_fw_cmd_hdlr); +		for (i = 0; i < size; i++) { +			if (cmd_op == qlcnic_pf_fw_cmd_hdlr[i].cmd) { +				qlcnic_pf_fw_cmd_hdlr[i].fn(trans, cmd); +				return; +			} +		} + +		size = ARRAY_SIZE(qlcnic_pf_passthru_supp_cmds); +		for (i = 0; i < size; i++) { +			if (cmd_op == qlcnic_pf_passthru_supp_cmds[i]) { +				qlcnic_issue_cmd(adapter, cmd); +				return; +			} +		} +	} + +	cmd->rsp.arg[0] |= (0x9 << 25); +} + +void qlcnic_pf_set_interface_id_create_rx_ctx(struct qlcnic_adapter *adapter, +					     u32 *int_id) +{ +	u16 vpid; + +	vpid = qlcnic_sriov_pf_get_vport_handle(adapter, +						adapter->ahw->pci_func); +	*int_id |= vpid; +} + +void qlcnic_pf_set_interface_id_del_rx_ctx(struct qlcnic_adapter *adapter, +					   u32 *int_id) +{ +	u16 vpid; + +	vpid = qlcnic_sriov_pf_get_vport_handle(adapter, +						adapter->ahw->pci_func); +	*int_id |= vpid << 16; +} + +void qlcnic_pf_set_interface_id_create_tx_ctx(struct qlcnic_adapter *adapter, +					      u32 *int_id) +{ +	int vpid; + +	vpid = qlcnic_sriov_pf_get_vport_handle(adapter, +						adapter->ahw->pci_func); +	*int_id |= vpid << 16; +} + +void qlcnic_pf_set_interface_id_del_tx_ctx(struct qlcnic_adapter *adapter, +					   u32 *int_id) +{ +	u16 vpid; + +	vpid = qlcnic_sriov_pf_get_vport_handle(adapter, +						adapter->ahw->pci_func); +	*int_id |= vpid << 16; +} + +void qlcnic_pf_set_interface_id_promisc(struct qlcnic_adapter *adapter, +					u32 *int_id) +{ +	u16 vpid; + +	vpid = qlcnic_sriov_pf_get_vport_handle(adapter, +						adapter->ahw->pci_func); +	*int_id |= (vpid << 16) | BIT_31; +} + +void qlcnic_pf_set_interface_id_ipaddr(struct qlcnic_adapter *adapter, +				       u32 *int_id) +{ +	u16 vpid; + +	vpid = qlcnic_sriov_pf_get_vport_handle(adapter, +						adapter->ahw->pci_func); +	*int_id |= (vpid << 16) | BIT_31; +} + +void qlcnic_pf_set_interface_id_macaddr(struct qlcnic_adapter *adapter, +					u32 *int_id) +{ +	u16 vpid; + +	vpid = qlcnic_sriov_pf_get_vport_handle(adapter, +						adapter->ahw->pci_func); +	*int_id |= (vpid << 16) | BIT_31; +} + +static void qlcnic_sriov_del_rx_ctx(struct qlcnic_adapter *adapter, +				    struct qlcnic_vf_info *vf) +{ +	struct qlcnic_cmd_args cmd; +	int vpid; + +	if (!vf->rx_ctx_id) +		return; + +	if (qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_DESTROY_RX_CTX)) +		return; + +	vpid = qlcnic_sriov_pf_get_vport_handle(adapter, vf->pci_func); +	if (vpid >= 0) { +		cmd.req.arg[1] = vf->rx_ctx_id | (vpid & 0xffff) << 16; +		if (qlcnic_issue_cmd(adapter, &cmd)) +			dev_err(&adapter->pdev->dev, +				"Failed to delete Tx ctx in firmware for func 0x%x\n", +				vf->pci_func); +		else +			vf->rx_ctx_id = 0; +	} + +	qlcnic_free_mbx_args(&cmd); +} + +static void qlcnic_sriov_del_tx_ctx(struct qlcnic_adapter *adapter, +				    struct qlcnic_vf_info *vf) +{ +	struct qlcnic_cmd_args cmd; +	int vpid; + +	if (!vf->tx_ctx_id) +		return; + +	if (qlcnic_alloc_mbx_args(&cmd, adapter, QLCNIC_CMD_DESTROY_TX_CTX)) +		return; + +	vpid = qlcnic_sriov_pf_get_vport_handle(adapter, vf->pci_func); +	if (vpid >= 0) { +		cmd.req.arg[1] |= vf->tx_ctx_id | (vpid & 0xffff) << 16; +		if (qlcnic_issue_cmd(adapter, &cmd)) +			dev_err(&adapter->pdev->dev, +				"Failed to delete Tx ctx in firmware for func 0x%x\n", +				vf->pci_func); +		else +			vf->tx_ctx_id = 0; +	} + +	qlcnic_free_mbx_args(&cmd); +} + +static int qlcnic_sriov_add_act_list_irqsave(struct qlcnic_sriov *sriov, +					     struct qlcnic_vf_info *vf, +					     struct qlcnic_bc_trans *trans) +{ +	struct qlcnic_trans_list *t_list = &vf->rcv_act; +	unsigned long flag; + +	spin_lock_irqsave(&t_list->lock, flag); + +	__qlcnic_sriov_add_act_list(sriov, vf, trans); + +	spin_unlock_irqrestore(&t_list->lock, flag); +	return 0; +} + +static void __qlcnic_sriov_process_flr(struct qlcnic_vf_info *vf) +{ +	struct qlcnic_adapter *adapter = vf->adapter; + +	qlcnic_sriov_cleanup_list(&vf->rcv_pend); +	cancel_work_sync(&vf->trans_work); +	qlcnic_sriov_cleanup_list(&vf->rcv_act); + +	if (test_bit(QLC_BC_VF_SOFT_FLR, &vf->state)) { +		qlcnic_sriov_del_tx_ctx(adapter, vf); +		qlcnic_sriov_del_rx_ctx(adapter, vf); +	} + +	qlcnic_sriov_pf_config_vport(adapter, 0, vf->pci_func); + +	clear_bit(QLC_BC_VF_FLR, &vf->state); +	if (test_bit(QLC_BC_VF_SOFT_FLR, &vf->state)) { +		qlcnic_sriov_add_act_list_irqsave(adapter->ahw->sriov, vf, +						  vf->flr_trans); +		clear_bit(QLC_BC_VF_SOFT_FLR, &vf->state); +		vf->flr_trans = NULL; +	} +} + +static void qlcnic_sriov_pf_process_flr(struct work_struct *work) +{ +	struct qlcnic_vf_info *vf; + +	vf = container_of(work, struct qlcnic_vf_info, flr_work); +	__qlcnic_sriov_process_flr(vf); +	return; +} + +static void qlcnic_sriov_schedule_flr(struct qlcnic_sriov *sriov, +				      struct qlcnic_vf_info *vf, +				      work_func_t func) +{ +	if (test_bit(__QLCNIC_RESETTING, &vf->adapter->state)) +		return; + +	INIT_WORK(&vf->flr_work, func); +	queue_work(sriov->bc.bc_flr_wq, &vf->flr_work); +} + +static void qlcnic_sriov_handle_soft_flr(struct qlcnic_adapter *adapter, +					 struct qlcnic_bc_trans *trans, +					 struct qlcnic_vf_info *vf) +{ +	struct qlcnic_sriov *sriov = adapter->ahw->sriov; + +	set_bit(QLC_BC_VF_FLR, &vf->state); +	clear_bit(QLC_BC_VF_STATE, &vf->state); +	set_bit(QLC_BC_VF_SOFT_FLR, &vf->state); +	vf->flr_trans = trans; +	qlcnic_sriov_schedule_flr(sriov, vf, qlcnic_sriov_pf_process_flr); +	netdev_info(adapter->netdev, "Software FLR for PCI func %d\n", +		    vf->pci_func); +} + +bool qlcnic_sriov_soft_flr_check(struct qlcnic_adapter *adapter, +				 struct qlcnic_bc_trans *trans, +				 struct qlcnic_vf_info *vf) +{ +	struct qlcnic_bc_hdr *hdr = trans->req_hdr; + +	if ((hdr->cmd_op == QLCNIC_BC_CMD_CHANNEL_INIT) && +	    (hdr->op_type == QLC_BC_CMD) && +	     test_bit(QLC_BC_VF_STATE, &vf->state)) { +		qlcnic_sriov_handle_soft_flr(adapter, trans, vf); +		return true; +	} + +	return false; +} + +void qlcnic_sriov_pf_handle_flr(struct qlcnic_sriov *sriov, +				struct qlcnic_vf_info *vf) +{ +	struct net_device *dev = vf->adapter->netdev; + +	if (!test_and_clear_bit(QLC_BC_VF_STATE, &vf->state)) { +		clear_bit(QLC_BC_VF_FLR, &vf->state); +		return; +	} + +	if (test_and_set_bit(QLC_BC_VF_FLR, &vf->state)) { +		netdev_info(dev, "FLR for PCI func %d in progress\n", +			    vf->pci_func); +		return; +	} + +	qlcnic_sriov_schedule_flr(sriov, vf, qlcnic_sriov_pf_process_flr); +	netdev_info(dev, "FLR received for PCI func %d\n", vf->pci_func); +} + +void qlcnic_sriov_pf_reset(struct qlcnic_adapter *adapter) +{ +	struct qlcnic_hardware_context *ahw = adapter->ahw; +	struct qlcnic_sriov *sriov = ahw->sriov; +	struct qlcnic_vf_info *vf; +	u16 num_vfs = sriov->num_vfs; +	int i; + +	for (i = 0; i < num_vfs; i++) { +		vf = &sriov->vf_info[i]; +		vf->rx_ctx_id = 0; +		vf->tx_ctx_id = 0; +		cancel_work_sync(&vf->flr_work); +		__qlcnic_sriov_process_flr(vf); +		clear_bit(QLC_BC_VF_STATE, &vf->state); +	} + +	qlcnic_sriov_pf_reset_vport_handle(adapter, ahw->pci_func); +	QLCWRX(ahw, QLCNIC_MBX_INTR_ENBL, (ahw->num_msix - 1) << 8); +} + +int qlcnic_sriov_pf_reinit(struct qlcnic_adapter *adapter) +{ +	struct qlcnic_hardware_context *ahw = adapter->ahw; +	int err; + +	if (!qlcnic_sriov_enable_check(adapter)) +		return 0; + +	ahw->op_mode = QLCNIC_SRIOV_PF_FUNC; + +	err = qlcnic_sriov_pf_init(adapter); +	if (err) +		return err; + +	dev_info(&adapter->pdev->dev, "%s: op_mode %d\n", +		 __func__, ahw->op_mode); +	return err; +} + +int qlcnic_sriov_set_vf_mac(struct net_device *netdev, int vf, u8 *mac) +{ +	struct qlcnic_adapter *adapter = netdev_priv(netdev); +	struct qlcnic_sriov *sriov = adapter->ahw->sriov; +	int i, num_vfs = sriov->num_vfs; +	struct qlcnic_vf_info *vf_info; +	u8 *curr_mac; + +	if (!qlcnic_sriov_pf_check(adapter)) +		return -EOPNOTSUPP; + +	if (!is_valid_ether_addr(mac) || vf >= num_vfs) +		return -EINVAL; + +	if (!compare_ether_addr(adapter->mac_addr, mac)) { +		netdev_err(netdev, "MAC address is already in use by the PF\n"); +		return -EINVAL; +	} + +	for (i = 0; i < num_vfs; i++) { +		vf_info = &sriov->vf_info[i]; +		if (!compare_ether_addr(vf_info->vp->mac, mac)) { +			netdev_err(netdev, +				   "MAC address is already in use by VF %d\n", +				   i); +			return -EINVAL; +		} +	} + +	vf_info = &sriov->vf_info[vf]; +	curr_mac = vf_info->vp->mac; + +	if (test_bit(QLC_BC_VF_STATE, &vf_info->state)) { +		netdev_err(netdev, +			   "MAC address change failed for VF %d, as VF driver is loaded. Please unload VF driver and retry the operation\n", +			   vf); +		return -EOPNOTSUPP; +	} + +	memcpy(curr_mac, mac, netdev->addr_len); +	netdev_info(netdev, "MAC Address %pM  is configured for VF %d\n", +		    mac, vf); +	return 0; +} + +int qlcnic_sriov_set_vf_tx_rate(struct net_device *netdev, int vf, int tx_rate) +{ +	struct qlcnic_adapter *adapter = netdev_priv(netdev); +	struct qlcnic_sriov *sriov = adapter->ahw->sriov; +	struct qlcnic_vf_info *vf_info; +	struct qlcnic_info nic_info; +	struct qlcnic_vport *vp; +	u16 vpid; + +	if (!qlcnic_sriov_pf_check(adapter)) +		return -EOPNOTSUPP; + +	if (vf >= sriov->num_vfs) +		return -EINVAL; + +	if (tx_rate >= 10000 || tx_rate < 100) { +		netdev_err(netdev, +			   "Invalid Tx rate, allowed range is [%d - %d]", +			   QLC_VF_MIN_TX_RATE, QLC_VF_MAX_TX_RATE); +		return -EINVAL; +	} + +	if (tx_rate == 0) +		tx_rate = 10000; + +	vf_info = &sriov->vf_info[vf]; +	vp = vf_info->vp; +	vpid = vp->handle; + +	if (test_bit(QLC_BC_VF_STATE, &vf_info->state)) { +		if (qlcnic_sriov_get_vf_vport_info(adapter, &nic_info, vpid)) +			return -EIO; + +		nic_info.max_tx_bw = tx_rate / 100; +		nic_info.bit_offsets = BIT_0; + +		if (qlcnic_sriov_pf_set_vport_info(adapter, &nic_info, vpid)) +			return -EIO; +	} + +	vp->max_tx_bw = tx_rate / 100; +	netdev_info(netdev, +		    "Setting Tx rate %d (Mbps), %d %% of PF bandwidth, for VF %d\n", +		    tx_rate, vp->max_tx_bw, vf); +	return 0; +} + +int qlcnic_sriov_set_vf_vlan(struct net_device *netdev, int vf, +			     u16 vlan, u8 qos) +{ +	struct qlcnic_adapter *adapter = netdev_priv(netdev); +	struct qlcnic_sriov *sriov = adapter->ahw->sriov; +	struct qlcnic_vf_info *vf_info; +	struct qlcnic_vport *vp; + +	if (!qlcnic_sriov_pf_check(adapter)) +		return -EOPNOTSUPP; + +	if (vf >= sriov->num_vfs || qos > 7) +		return -EINVAL; + +	if (vlan > MAX_VLAN_ID) { +		netdev_err(netdev, +			   "Invalid VLAN ID, allowed range is [0 - %d]\n", +			   MAX_VLAN_ID); +		return -EINVAL; +	} + +	vf_info = &sriov->vf_info[vf]; +	vp = vf_info->vp; +	if (test_bit(QLC_BC_VF_STATE, &vf_info->state)) { +		netdev_err(netdev, +			   "VLAN change failed for VF %d, as VF driver is loaded. Please unload VF driver and retry the operation\n", +			   vf); +		return -EOPNOTSUPP; +	} + +	switch (vlan) { +	case 4095: +		vp->vlan_mode = QLC_GUEST_VLAN_MODE; +		break; +	case 0: +		vp->vlan_mode = QLC_NO_VLAN_MODE; +		vp->vlan = 0; +		vp->qos = 0; +		break; +	default: +		vp->vlan_mode = QLC_PVID_MODE; +		vp->vlan = vlan; +		vp->qos = qos; +	} + +	netdev_info(netdev, "Setting VLAN %d, QoS %d, for VF %d\n", +		    vlan, qos, vf); +	return 0; +} + +int qlcnic_sriov_get_vf_config(struct net_device *netdev, +			       int vf, struct ifla_vf_info *ivi) +{ +	struct qlcnic_adapter *adapter = netdev_priv(netdev); +	struct qlcnic_sriov *sriov = adapter->ahw->sriov; +	struct qlcnic_vport *vp; + +	if (!qlcnic_sriov_pf_check(adapter)) +		return -EOPNOTSUPP; + +	if (vf >= sriov->num_vfs) +		return -EINVAL; + +	vp = sriov->vf_info[vf].vp; +	memcpy(&ivi->mac, vp->mac, ETH_ALEN); +	ivi->vlan = vp->vlan; +	ivi->qos = vp->qos; +	if (vp->max_tx_bw == MAX_BW) +		ivi->tx_rate = 0; +	else +		ivi->tx_rate = vp->max_tx_bw * 100; + +	ivi->vf = vf; +	return 0; +} diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c index 5ef328af61d..4e22e794a18 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_sysfs.c @@ -21,8 +21,6 @@  #include <linux/aer.h>  #include <linux/log2.h> -#include <linux/sysfs.h> -  #define QLC_STATUS_UNSUPPORTED_CMD	-2  int qlcnicvf_config_bridged_mode(struct qlcnic_adapter *adapter, u32 enable) @@ -886,6 +884,244 @@ static ssize_t qlcnic_sysfs_read_pci_config(struct file *file,  	return size;  } +static ssize_t qlcnic_83xx_sysfs_flash_read_handler(struct file *filp, +						    struct kobject *kobj, +						    struct bin_attribute *attr, +						    char *buf, loff_t offset, +						    size_t size) +{ +	unsigned char *p_read_buf; +	int  ret, count; +	struct device *dev = container_of(kobj, struct device, kobj); +	struct qlcnic_adapter *adapter = dev_get_drvdata(dev); + +	if (!size) +		return QL_STATUS_INVALID_PARAM; +	if (!buf) +		return QL_STATUS_INVALID_PARAM; + +	count = size / sizeof(u32); + +	if (size % sizeof(u32)) +		count++; + +	p_read_buf = kcalloc(size, sizeof(unsigned char), GFP_KERNEL); +	if (!p_read_buf) +		return -ENOMEM; +	if (qlcnic_83xx_lock_flash(adapter) != 0) { +		kfree(p_read_buf); +		return -EIO; +	} + +	ret = qlcnic_83xx_lockless_flash_read32(adapter, offset, p_read_buf, +						count); + +	if (ret) { +		qlcnic_83xx_unlock_flash(adapter); +		kfree(p_read_buf); +		return ret; +	} + +	qlcnic_83xx_unlock_flash(adapter); +	memcpy(buf, p_read_buf, size); +	kfree(p_read_buf); + +	return size; +} + +static int qlcnic_83xx_sysfs_flash_bulk_write(struct qlcnic_adapter *adapter, +					      char *buf, loff_t offset, +					      size_t size) +{ +	int  i, ret, count; +	unsigned char *p_cache, *p_src; + +	p_cache = kcalloc(size, sizeof(unsigned char), GFP_KERNEL); +	if (!p_cache) +		return -ENOMEM; + +	memcpy(p_cache, buf, size); +	p_src = p_cache; +	count = size / sizeof(u32); + +	if (qlcnic_83xx_lock_flash(adapter) != 0) { +		kfree(p_cache); +		return -EIO; +	} + +	if (adapter->ahw->fdt.mfg_id == adapter->flash_mfg_id) { +		ret = qlcnic_83xx_enable_flash_write(adapter); +		if (ret) { +			kfree(p_cache); +			qlcnic_83xx_unlock_flash(adapter); +			return -EIO; +		} +	} + +	for (i = 0; i < count / QLC_83XX_FLASH_WRITE_MAX; i++) { +		ret = qlcnic_83xx_flash_bulk_write(adapter, offset, +						   (u32 *)p_src, +						   QLC_83XX_FLASH_WRITE_MAX); + +		if (ret) { +			if (adapter->ahw->fdt.mfg_id == adapter->flash_mfg_id) { +				ret = qlcnic_83xx_disable_flash_write(adapter); +				if (ret) { +					kfree(p_cache); +					qlcnic_83xx_unlock_flash(adapter); +					return -EIO; +				} +			} + +			kfree(p_cache); +			qlcnic_83xx_unlock_flash(adapter); +			return -EIO; +		} + +		p_src = p_src + sizeof(u32)*QLC_83XX_FLASH_WRITE_MAX; +		offset = offset + sizeof(u32)*QLC_83XX_FLASH_WRITE_MAX; +	} + +	if (adapter->ahw->fdt.mfg_id == adapter->flash_mfg_id) { +		ret = qlcnic_83xx_disable_flash_write(adapter); +		if (ret) { +			kfree(p_cache); +			qlcnic_83xx_unlock_flash(adapter); +			return -EIO; +		} +	} + +	kfree(p_cache); +	qlcnic_83xx_unlock_flash(adapter); + +	return 0; +} + +static int qlcnic_83xx_sysfs_flash_write(struct qlcnic_adapter *adapter, +					 char *buf, loff_t offset, size_t size) +{ +	int  i, ret, count; +	unsigned char *p_cache, *p_src; + +	p_cache = kcalloc(size, sizeof(unsigned char), GFP_KERNEL); +	if (!p_cache) +		return -ENOMEM; + +	memcpy(p_cache, buf, size); +	p_src = p_cache; +	count = size / sizeof(u32); + +	if (qlcnic_83xx_lock_flash(adapter) != 0) { +		kfree(p_cache); +		return -EIO; +	} + +	if (adapter->ahw->fdt.mfg_id == adapter->flash_mfg_id) { +		ret = qlcnic_83xx_enable_flash_write(adapter); +		if (ret) { +			kfree(p_cache); +			qlcnic_83xx_unlock_flash(adapter); +			return -EIO; +		} +	} + +	for (i = 0; i < count; i++) { +		ret = qlcnic_83xx_flash_write32(adapter, offset, (u32 *)p_src); +		if (ret) { +			if (adapter->ahw->fdt.mfg_id == adapter->flash_mfg_id) { +				ret = qlcnic_83xx_disable_flash_write(adapter); +				if (ret) { +					kfree(p_cache); +					qlcnic_83xx_unlock_flash(adapter); +					return -EIO; +				} +			} +			kfree(p_cache); +			qlcnic_83xx_unlock_flash(adapter); +			return -EIO; +		} + +		p_src = p_src + sizeof(u32); +		offset = offset + sizeof(u32); +	} + +	if (adapter->ahw->fdt.mfg_id == adapter->flash_mfg_id) { +		ret = qlcnic_83xx_disable_flash_write(adapter); +		if (ret) { +			kfree(p_cache); +			qlcnic_83xx_unlock_flash(adapter); +			return -EIO; +		} +	} + +	kfree(p_cache); +	qlcnic_83xx_unlock_flash(adapter); + +	return 0; +} + +static ssize_t qlcnic_83xx_sysfs_flash_write_handler(struct file *filp, +						     struct kobject *kobj, +						     struct bin_attribute *attr, +						     char *buf, loff_t offset, +						     size_t size) +{ +	int  ret; +	static int flash_mode; +	unsigned long data; +	struct device *dev = container_of(kobj, struct device, kobj); +	struct qlcnic_adapter *adapter = dev_get_drvdata(dev); + +	if (!buf) +		return QL_STATUS_INVALID_PARAM; + +	ret = kstrtoul(buf, 16, &data); + +	switch (data) { +	case QLC_83XX_FLASH_SECTOR_ERASE_CMD: +		flash_mode = QLC_83XX_ERASE_MODE; +		ret = qlcnic_83xx_erase_flash_sector(adapter, offset); +		if (ret) { +			dev_err(&adapter->pdev->dev, +				"%s failed at %d\n", __func__, __LINE__); +			return -EIO; +		} +		break; + +	case QLC_83XX_FLASH_BULK_WRITE_CMD: +		flash_mode = QLC_83XX_BULK_WRITE_MODE; +		break; + +	case QLC_83XX_FLASH_WRITE_CMD: +		flash_mode = QLC_83XX_WRITE_MODE; +		break; +	default: +		if (flash_mode == QLC_83XX_BULK_WRITE_MODE) { +			ret = qlcnic_83xx_sysfs_flash_bulk_write(adapter, buf, +								 offset, size); +			if (ret) { +				dev_err(&adapter->pdev->dev, +					"%s failed at %d\n", +					__func__, __LINE__); +				return -EIO; +			} +		} + +		if (flash_mode == QLC_83XX_WRITE_MODE) { +			ret = qlcnic_83xx_sysfs_flash_write(adapter, buf, +							    offset, size); +			if (ret) { +				dev_err(&adapter->pdev->dev, +					"%s failed at %d\n", __func__, +					__LINE__); +				return -EIO; +			} +		} +	} + +	return size; +} +  static struct device_attribute dev_attr_bridged_mode = {         .attr = {.name = "bridged_mode", .mode = (S_IRUGO | S_IWUSR)},         .show = qlcnic_show_bridged_mode, @@ -960,6 +1196,13 @@ static struct bin_attribute bin_attr_pm_config = {  	.write = qlcnic_sysfs_write_pm_config,  }; +static struct bin_attribute bin_attr_flash = { +	.attr = {.name = "flash", .mode = (S_IRUGO | S_IWUSR)}, +	.size = 0, +	.read = qlcnic_83xx_sysfs_flash_read_handler, +	.write = qlcnic_83xx_sysfs_flash_write_handler, +}; +  void qlcnic_create_sysfs_entries(struct qlcnic_adapter *adapter)  {  	struct device *dev = &adapter->pdev->dev; @@ -1048,10 +1291,18 @@ void qlcnic_82xx_remove_sysfs(struct qlcnic_adapter *adapter)  void qlcnic_83xx_add_sysfs(struct qlcnic_adapter *adapter)  { +	struct device *dev = &adapter->pdev->dev; +  	qlcnic_create_diag_entries(adapter); + +	if (sysfs_create_bin_file(&dev->kobj, &bin_attr_flash)) +		dev_info(dev, "failed to create flash sysfs entry\n");  }  void qlcnic_83xx_remove_sysfs(struct qlcnic_adapter *adapter)  { +	struct device *dev = &adapter->pdev->dev; +  	qlcnic_remove_diag_entries(adapter); +	sysfs_remove_bin_file(&dev->kobj, &bin_attr_flash);  } diff --git a/drivers/net/ethernet/qlogic/qlge/qlge_main.c b/drivers/net/ethernet/qlogic/qlge/qlge_main.c index 8033555e53c..87463bc701a 100644 --- a/drivers/net/ethernet/qlogic/qlge/qlge_main.c +++ b/drivers/net/ethernet/qlogic/qlge/qlge_main.c @@ -409,7 +409,7 @@ static int ql_set_mac_addr_reg(struct ql_adapter *qdev, u8 *addr, u32 type,  				      (qdev->  				       func << CAM_OUT_FUNC_SHIFT) |  					(0 << CAM_OUT_CQ_ID_SHIFT)); -			if (qdev->ndev->features & NETIF_F_HW_VLAN_RX) +			if (qdev->ndev->features & NETIF_F_HW_VLAN_CTAG_RX)  				cam_output |= CAM_OUT_RV;  			/* route to NIC core */  			ql_write32(qdev, MAC_ADDR_DATA, cam_output); @@ -1211,8 +1211,6 @@ static void ql_update_sbq(struct ql_adapter *qdev, struct rx_ring *rx_ring)  				    netdev_alloc_skb(qdev->ndev,  						     SMALL_BUFFER_SIZE);  				if (sbq_desc->p.skb == NULL) { -					netif_err(qdev, probe, qdev->ndev, -						  "Couldn't get an skb.\n");  					rx_ring->sbq_clean_idx = clean_idx;  					return;  				} @@ -1508,7 +1506,7 @@ static void ql_process_mac_rx_gro_page(struct ql_adapter *qdev,  	skb->ip_summed = CHECKSUM_UNNECESSARY;  	skb_record_rx_queue(skb, rx_ring->cq_id);  	if (vlan_id != 0xffff) -		__vlan_hwaccel_put_tag(skb, vlan_id); +		__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vlan_id);  	napi_gro_frags(napi);  } @@ -1527,8 +1525,6 @@ static void ql_process_mac_rx_page(struct ql_adapter *qdev,  	skb = netdev_alloc_skb(ndev, length);  	if (!skb) { -		netif_err(qdev, drv, qdev->ndev, -			  "Couldn't get an skb, need to unwind!.\n");  		rx_ring->rx_dropped++;  		put_page(lbq_desc->p.pg_chunk.page);  		return; @@ -1592,7 +1588,7 @@ static void ql_process_mac_rx_page(struct ql_adapter *qdev,  	skb_record_rx_queue(skb, rx_ring->cq_id);  	if (vlan_id != 0xffff) -		__vlan_hwaccel_put_tag(skb, vlan_id); +		__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vlan_id);  	if (skb->ip_summed == CHECKSUM_UNNECESSARY)  		napi_gro_receive(napi, skb);  	else @@ -1619,8 +1615,6 @@ static void ql_process_mac_rx_skb(struct ql_adapter *qdev,  	/* Allocate new_skb and copy */  	new_skb = netdev_alloc_skb(qdev->ndev, length + NET_IP_ALIGN);  	if (new_skb == NULL) { -		netif_err(qdev, probe, qdev->ndev, -			  "No skb available, drop the packet.\n");  		rx_ring->rx_dropped++;  		return;  	} @@ -1697,7 +1691,7 @@ static void ql_process_mac_rx_skb(struct ql_adapter *qdev,  	skb_record_rx_queue(skb, rx_ring->cq_id);  	if (vlan_id != 0xffff) -		__vlan_hwaccel_put_tag(skb, vlan_id); +		__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vlan_id);  	if (skb->ip_summed == CHECKSUM_UNNECESSARY)  		napi_gro_receive(&rx_ring->napi, skb);  	else @@ -2009,7 +2003,7 @@ static void ql_process_mac_split_rx_intr(struct ql_adapter *qdev,  	rx_ring->rx_bytes += skb->len;  	skb_record_rx_queue(skb, rx_ring->cq_id);  	if ((ib_mac_rsp->flags2 & IB_MAC_IOCB_RSP_V) && (vlan_id != 0)) -		__vlan_hwaccel_put_tag(skb, vlan_id); +		__vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), vlan_id);  	if (skb->ip_summed == CHECKSUM_UNNECESSARY)  		napi_gro_receive(&rx_ring->napi, skb);  	else @@ -2307,7 +2301,7 @@ static void qlge_vlan_mode(struct net_device *ndev, netdev_features_t features)  {  	struct ql_adapter *qdev = netdev_priv(ndev); -	if (features & NETIF_F_HW_VLAN_RX) { +	if (features & NETIF_F_HW_VLAN_CTAG_RX) {  		ql_write32(qdev, NIC_RCV_CFG, NIC_RCV_CFG_VLAN_MASK |  				 NIC_RCV_CFG_VLAN_MATCH_AND_NON);  	} else { @@ -2322,10 +2316,10 @@ static netdev_features_t qlge_fix_features(struct net_device *ndev,  	 * Since there is no support for separate rx/tx vlan accel  	 * enable/disable make sure tx flag is always in same state as rx.  	 */ -	if (features & NETIF_F_HW_VLAN_RX) -		features |= NETIF_F_HW_VLAN_TX; +	if (features & NETIF_F_HW_VLAN_CTAG_RX) +		features |= NETIF_F_HW_VLAN_CTAG_TX;  	else -		features &= ~NETIF_F_HW_VLAN_TX; +		features &= ~NETIF_F_HW_VLAN_CTAG_TX;  	return features;  } @@ -2335,7 +2329,7 @@ static int qlge_set_features(struct net_device *ndev,  {  	netdev_features_t changed = ndev->features ^ features; -	if (changed & NETIF_F_HW_VLAN_RX) +	if (changed & NETIF_F_HW_VLAN_CTAG_RX)  		qlge_vlan_mode(ndev, features);  	return 0; @@ -2354,7 +2348,7 @@ static int __qlge_vlan_rx_add_vid(struct ql_adapter *qdev, u16 vid)  	return err;  } -static int qlge_vlan_rx_add_vid(struct net_device *ndev, u16 vid) +static int qlge_vlan_rx_add_vid(struct net_device *ndev, __be16 proto, u16 vid)  {  	struct ql_adapter *qdev = netdev_priv(ndev);  	int status; @@ -2385,7 +2379,7 @@ static int __qlge_vlan_rx_kill_vid(struct ql_adapter *qdev, u16 vid)  	return err;  } -static int qlge_vlan_rx_kill_vid(struct net_device *ndev, u16 vid) +static int qlge_vlan_rx_kill_vid(struct net_device *ndev, __be16 proto, u16 vid)  {  	struct ql_adapter *qdev = netdev_priv(ndev);  	int status; @@ -4693,9 +4687,9 @@ static int qlge_probe(struct pci_dev *pdev,  	SET_NETDEV_DEV(ndev, &pdev->dev);  	ndev->hw_features = NETIF_F_SG | NETIF_F_IP_CSUM |  		NETIF_F_TSO | NETIF_F_TSO_ECN | -		NETIF_F_HW_VLAN_TX | NETIF_F_RXCSUM; +		NETIF_F_HW_VLAN_CTAG_TX | NETIF_F_RXCSUM;  	ndev->features = ndev->hw_features | -		NETIF_F_HW_VLAN_RX | NETIF_F_HW_VLAN_FILTER; +		NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_HW_VLAN_CTAG_FILTER;  	ndev->vlan_features = ndev->hw_features;  	if (test_bit(QL_DMA64, &qdev->flags))  |