diff options
Diffstat (limited to 'drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c')
| -rw-r--r-- | drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c | 251 | 
1 files changed, 185 insertions, 66 deletions
diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c index 7a6d5ebe4e0..f89cc7a3fe6 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_hw.c @@ -1,6 +1,6 @@  /*   * QLogic qlcnic NIC Driver - * Copyright (c)  2009-2010 QLogic Corporation + * Copyright (c) 2009-2013 QLogic Corporation   *   * See LICENSE.qlcnic for copyright and licensing details.   */ @@ -344,21 +344,26 @@ qlcnic_pcie_sem_unlock(struct qlcnic_adapter *adapter, int sem)  	QLCRD32(adapter, QLCNIC_PCIE_REG(PCIE_SEM_UNLOCK(sem)));  } -static int qlcnic_ind_rd(struct qlcnic_adapter *adapter, u32 addr) +int qlcnic_ind_rd(struct qlcnic_adapter *adapter, u32 addr)  {  	u32 data;  	if (qlcnic_82xx_check(adapter))  		qlcnic_read_window_reg(addr, adapter->ahw->pci_base0, &data); -	else -		return -EIO; +	else { +		data = qlcnic_83xx_rd_reg_indirect(adapter, addr); +		if (data == -EIO) +			return -EIO; +	}  	return data;  } -static void qlcnic_ind_wr(struct qlcnic_adapter *adapter, u32 addr, u32 data) +void qlcnic_ind_wr(struct qlcnic_adapter *adapter, u32 addr, u32 data)  {  	if (qlcnic_82xx_check(adapter))  		qlcnic_write_window_reg(addr, adapter->ahw->pci_base0, data); +	else +		qlcnic_83xx_wrt_reg_indirect(adapter, addr, data);  }  static int @@ -417,9 +422,8 @@ qlcnic_send_cmd_descs(struct qlcnic_adapter *adapter,  	return 0;  } -static int -qlcnic_sre_macaddr_change(struct qlcnic_adapter *adapter, u8 *addr, -				__le16 vlan_id, unsigned op) +int qlcnic_82xx_sre_macaddr_change(struct qlcnic_adapter *adapter, u8 *addr, +				   __le16 vlan_id, u8 op)  {  	struct qlcnic_nic_req req;  	struct qlcnic_mac_req *mac_req; @@ -442,7 +446,29 @@ qlcnic_sre_macaddr_change(struct qlcnic_adapter *adapter, u8 *addr,  	return qlcnic_send_cmd_descs(adapter, (struct cmd_desc_type0 *)&req, 1);  } -static int qlcnic_nic_add_mac(struct qlcnic_adapter *adapter, const u8 *addr) +int qlcnic_nic_del_mac(struct qlcnic_adapter *adapter, const u8 *addr) +{ +	struct list_head *head; +	struct qlcnic_mac_list_s *cur; +	int err = -EINVAL; + +	/* Delete MAC from the existing list */ +	list_for_each(head, &adapter->mac_list) { +		cur = list_entry(head, struct qlcnic_mac_list_s, list); +		if (memcmp(addr, cur->mac_addr, ETH_ALEN) == 0) { +			err = qlcnic_sre_macaddr_change(adapter, cur->mac_addr, +							0, QLCNIC_MAC_DEL); +			if (err) +				return err; +			list_del(&cur->list); +			kfree(cur); +			return err; +		} +	} +	return err; +} + +int qlcnic_nic_add_mac(struct qlcnic_adapter *adapter, const u8 *addr)  {  	struct list_head *head;  	struct qlcnic_mac_list_s *cur; @@ -455,11 +481,9 @@ static int qlcnic_nic_add_mac(struct qlcnic_adapter *adapter, const u8 *addr)  	}  	cur = kzalloc(sizeof(struct qlcnic_mac_list_s), GFP_ATOMIC); -	if (cur == NULL) { -		dev_err(&adapter->netdev->dev, -			"failed to add mac address filter\n"); +	if (cur == NULL)  		return -ENOMEM; -	} +  	memcpy(cur->mac_addr, addr, ETH_ALEN);  	if (qlcnic_sre_macaddr_change(adapter, @@ -506,17 +530,17 @@ void qlcnic_set_multi(struct net_device *netdev)  	}  send_fw_cmd: -	if (mode == VPORT_MISS_MODE_ACCEPT_ALL) { +	if (mode == VPORT_MISS_MODE_ACCEPT_ALL && !adapter->fdb_mac_learn) {  		qlcnic_alloc_lb_filters_mem(adapter); -		adapter->mac_learn = 1; +		adapter->drv_mac_learn = true;  	} else { -		adapter->mac_learn = 0; +		adapter->drv_mac_learn = false;  	}  	qlcnic_nic_set_promisc(adapter, mode);  } -int qlcnic_nic_set_promisc(struct qlcnic_adapter *adapter, u32 mode) +int qlcnic_82xx_nic_set_promisc(struct qlcnic_adapter *adapter, u32 mode)  {  	struct qlcnic_nic_req req;  	u64 word; @@ -552,21 +576,23 @@ void qlcnic_free_mac_list(struct qlcnic_adapter *adapter)  void qlcnic_prune_lb_filters(struct qlcnic_adapter *adapter)  {  	struct qlcnic_filter *tmp_fil; -	struct hlist_node *tmp_hnode, *n; +	struct hlist_node *n;  	struct hlist_head *head;  	int i; +	unsigned long time; +	u8 cmd; -	for (i = 0; i < adapter->fhash.fmax; i++) { +	for (i = 0; i < adapter->fhash.fbucket_size; i++) {  		head = &(adapter->fhash.fhead[i]); - -		hlist_for_each_entry_safe(tmp_fil, tmp_hnode, n, head, fnode) -		{ -			if (jiffies > -				(QLCNIC_FILTER_AGE * HZ + tmp_fil->ftime)) { +		hlist_for_each_entry_safe(tmp_fil, n, head, fnode) { +			cmd =  tmp_fil->vlan_id ? QLCNIC_MAC_VLAN_DEL : +						  QLCNIC_MAC_DEL; +			time = tmp_fil->ftime; +			if (jiffies > (QLCNIC_FILTER_AGE * HZ + time)) {  				qlcnic_sre_macaddr_change(adapter, -					tmp_fil->faddr, tmp_fil->vlan_id, -					tmp_fil->vlan_id ? QLCNIC_MAC_VLAN_DEL : -					QLCNIC_MAC_DEL); +							  tmp_fil->faddr, +							  tmp_fil->vlan_id, +							  cmd);  				spin_lock_bh(&adapter->mac_learn_lock);  				adapter->fhash.fnum--;  				hlist_del(&tmp_fil->fnode); @@ -575,22 +601,40 @@ void qlcnic_prune_lb_filters(struct qlcnic_adapter *adapter)  			}  		}  	} +	for (i = 0; i < adapter->rx_fhash.fbucket_size; i++) { +		head = &(adapter->rx_fhash.fhead[i]); + +		hlist_for_each_entry_safe(tmp_fil, n, head, fnode) +		{ +			time = tmp_fil->ftime; +			if (jiffies > (QLCNIC_FILTER_AGE * HZ + time)) { +				spin_lock_bh(&adapter->rx_mac_learn_lock); +				adapter->rx_fhash.fnum--; +				hlist_del(&tmp_fil->fnode); +				spin_unlock_bh(&adapter->rx_mac_learn_lock); +				kfree(tmp_fil); +			} +		} +	}  }  void qlcnic_delete_lb_filters(struct qlcnic_adapter *adapter)  {  	struct qlcnic_filter *tmp_fil; -	struct hlist_node *tmp_hnode, *n; +	struct hlist_node *n;  	struct hlist_head *head;  	int i; +	u8 cmd; -	for (i = 0; i < adapter->fhash.fmax; i++) { +	for (i = 0; i < adapter->fhash.fbucket_size; i++) {  		head = &(adapter->fhash.fhead[i]); - -		hlist_for_each_entry_safe(tmp_fil, tmp_hnode, n, head, fnode) { -			qlcnic_sre_macaddr_change(adapter, tmp_fil->faddr, -				tmp_fil->vlan_id, tmp_fil->vlan_id ? -				QLCNIC_MAC_VLAN_DEL :  QLCNIC_MAC_DEL); +		hlist_for_each_entry_safe(tmp_fil, n, head, fnode) { +			cmd =  tmp_fil->vlan_id ? QLCNIC_MAC_VLAN_DEL : +						  QLCNIC_MAC_DEL; +			qlcnic_sre_macaddr_change(adapter, +						  tmp_fil->faddr, +						  tmp_fil->vlan_id, +						  cmd);  			spin_lock_bh(&adapter->mac_learn_lock);  			adapter->fhash.fnum--;  			hlist_del(&tmp_fil->fnode); @@ -620,12 +664,13 @@ static int qlcnic_set_fw_loopback(struct qlcnic_adapter *adapter, u8 flag)  	return rv;  } -int qlcnic_set_lb_mode(struct qlcnic_adapter *adapter, u8 mode) +int qlcnic_82xx_set_lb_mode(struct qlcnic_adapter *adapter, u8 mode)  {  	if (qlcnic_set_fw_loopback(adapter, mode))  		return -EIO; -	if (qlcnic_nic_set_promisc(adapter, VPORT_MISS_MODE_ACCEPT_ALL)) { +	if (qlcnic_nic_set_promisc(adapter, +				   VPORT_MISS_MODE_ACCEPT_ALL)) {  		qlcnic_set_fw_loopback(adapter, 0);  		return -EIO;  	} @@ -634,11 +679,11 @@ int qlcnic_set_lb_mode(struct qlcnic_adapter *adapter, u8 mode)  	return 0;  } -void qlcnic_clear_lb_mode(struct qlcnic_adapter *adapter) +int qlcnic_82xx_clear_lb_mode(struct qlcnic_adapter *adapter, u8 mode)  { -	int mode = VPORT_MISS_MODE_DROP;  	struct net_device *netdev = adapter->netdev; +	mode = VPORT_MISS_MODE_DROP;  	qlcnic_set_fw_loopback(adapter, 0);  	if (netdev->flags & IFF_PROMISC) @@ -648,12 +693,13 @@ void qlcnic_clear_lb_mode(struct qlcnic_adapter *adapter)  	qlcnic_nic_set_promisc(adapter, mode);  	msleep(1000); +	return 0;  }  /*   * Send the interrupt coalescing parameter set by ethtool to the card.   */ -int qlcnic_config_intr_coalesce(struct qlcnic_adapter *adapter) +void qlcnic_82xx_config_intr_coalesce(struct qlcnic_adapter *adapter)  {  	struct qlcnic_nic_req req;  	int rv; @@ -675,10 +721,14 @@ int qlcnic_config_intr_coalesce(struct qlcnic_adapter *adapter)  	if (rv != 0)  		dev_err(&adapter->netdev->dev,  			"Could not send interrupt coalescing parameters\n"); -	return rv;  } -int qlcnic_config_hw_lro(struct qlcnic_adapter *adapter, int enable) +#define QLCNIC_ENABLE_IPV4_LRO		1 +#define QLCNIC_ENABLE_IPV6_LRO		2 +#define QLCNIC_NO_DEST_IPV4_CHECK	(1 << 8) +#define QLCNIC_NO_DEST_IPV6_CHECK	(2 << 8) + +int qlcnic_82xx_config_hw_lro(struct qlcnic_adapter *adapter, int enable)  {  	struct qlcnic_nic_req req;  	u64 word; @@ -694,7 +744,15 @@ int qlcnic_config_hw_lro(struct qlcnic_adapter *adapter, int enable)  	word = QLCNIC_H2C_OPCODE_CONFIG_HW_LRO | ((u64)adapter->portnum << 16);  	req.req_hdr = cpu_to_le64(word); -	req.words[0] = cpu_to_le64(enable); +	word = 0; +	if (enable) { +		word = QLCNIC_ENABLE_IPV4_LRO | QLCNIC_NO_DEST_IPV4_CHECK; +		if (adapter->ahw->capabilities2 & QLCNIC_FW_CAP2_HW_LRO_IPV6) +			word |= QLCNIC_ENABLE_IPV6_LRO | +				QLCNIC_NO_DEST_IPV6_CHECK; +	} + +	req.words[0] = cpu_to_le64(word);  	rv = qlcnic_send_cmd_descs(adapter, (struct cmd_desc_type0 *)&req, 1);  	if (rv != 0) @@ -734,9 +792,12 @@ int qlcnic_config_bridged_mode(struct qlcnic_adapter *adapter, u32 enable)  } -#define RSS_HASHTYPE_IP_TCP	0x3 +#define QLCNIC_RSS_HASHTYPE_IP_TCP	0x3 +#define QLCNIC_ENABLE_TYPE_C_RSS	BIT_10 +#define QLCNIC_RSS_FEATURE_FLAG	(1ULL << 63) +#define QLCNIC_RSS_IND_TABLE_MASK	0x7ULL -int qlcnic_config_rss(struct qlcnic_adapter *adapter, int enable) +int qlcnic_82xx_config_rss(struct qlcnic_adapter *adapter, int enable)  {  	struct qlcnic_nic_req req;  	u64 word; @@ -761,13 +822,19 @@ int qlcnic_config_rss(struct qlcnic_adapter *adapter, int enable)  	 *	7-6: hash_type_ipv6  	 *	  8: enable  	 *        9: use indirection table -	 *    47-10: reserved -	 *    63-48: indirection table mask +	 *       10: type-c rss +	 *	 11: udp rss +	 *    47-12: reserved +	 *    62-48: indirection table mask +	 *	 63: feature flag  	 */ -	word =  ((u64)(RSS_HASHTYPE_IP_TCP & 0x3) << 4) | -		((u64)(RSS_HASHTYPE_IP_TCP & 0x3) << 6) | +	word =  ((u64)(QLCNIC_RSS_HASHTYPE_IP_TCP & 0x3) << 4) | +		((u64)(QLCNIC_RSS_HASHTYPE_IP_TCP & 0x3) << 6) |  		((u64)(enable & 0x1) << 8) | -		((0x7ULL) << 48); +		((u64)QLCNIC_RSS_IND_TABLE_MASK << 48) | +		(u64)QLCNIC_ENABLE_TYPE_C_RSS | +		(u64)QLCNIC_RSS_FEATURE_FLAG; +  	req.words[0] = cpu_to_le64(word);  	for (i = 0; i < 5; i++)  		req.words[i+1] = cpu_to_le64(key[i]); @@ -779,7 +846,8 @@ int qlcnic_config_rss(struct qlcnic_adapter *adapter, int enable)  	return rv;  } -int qlcnic_config_ipaddr(struct qlcnic_adapter *adapter, __be32 ip, int cmd) +void qlcnic_82xx_config_ipaddr(struct qlcnic_adapter *adapter, +			       __be32 ip, int cmd)  {  	struct qlcnic_nic_req req;  	struct qlcnic_ipaddr *ipa; @@ -801,23 +869,19 @@ int qlcnic_config_ipaddr(struct qlcnic_adapter *adapter, __be32 ip, int cmd)  		dev_err(&adapter->netdev->dev,  				"could not notify %s IP 0x%x reuqest\n",  				(cmd == QLCNIC_IP_UP) ? "Add" : "Remove", ip); - -	return rv;  } -int qlcnic_linkevent_request(struct qlcnic_adapter *adapter, int enable) +int qlcnic_82xx_linkevent_request(struct qlcnic_adapter *adapter, int enable)  {  	struct qlcnic_nic_req req;  	u64 word;  	int rv; -  	memset(&req, 0, sizeof(struct qlcnic_nic_req));  	req.qhdr = cpu_to_le64(QLCNIC_HOST_REQUEST << 23);  	word = QLCNIC_H2C_OPCODE_GET_LINKEVENT | ((u64)adapter->portnum << 16);  	req.req_hdr = cpu_to_le64(word);  	req.words[0] = cpu_to_le64(enable | (enable << 8)); -  	rv = qlcnic_send_cmd_descs(adapter, (struct cmd_desc_type0 *)&req, 1);  	if (rv != 0)  		dev_err(&adapter->netdev->dev, @@ -882,7 +946,8 @@ netdev_features_t qlcnic_fix_features(struct net_device *netdev,  {  	struct qlcnic_adapter *adapter = netdev_priv(netdev); -	if ((adapter->flags & QLCNIC_ESWITCH_ENABLED)) { +	if ((adapter->flags & QLCNIC_ESWITCH_ENABLED) && +	    qlcnic_82xx_check(adapter)) {  		netdev_features_t changed = features ^ netdev->features;  		features ^= changed & (NETIF_F_ALL_CSUM | NETIF_F_RXCSUM);  	} @@ -903,13 +968,15 @@ int qlcnic_set_features(struct net_device *netdev, netdev_features_t features)  	if (!(changed & NETIF_F_LRO))  		return 0; -	netdev->features = features ^ NETIF_F_LRO; +	netdev->features ^= NETIF_F_LRO;  	if (qlcnic_config_hw_lro(adapter, hw_lro))  		return -EIO; -	if ((hw_lro == 0) && qlcnic_send_lro_cleanup(adapter)) -		return -EIO; +	if (!hw_lro && qlcnic_82xx_check(adapter)) { +		if (qlcnic_send_lro_cleanup(adapter)) +			return -EIO; +	}  	return 0;  } @@ -981,8 +1048,8 @@ qlcnic_pci_set_crbwindow_2M(struct qlcnic_adapter *adapter, ulong off)  	return 0;  } -int -qlcnic_hw_write_wx_2M(struct qlcnic_adapter *adapter, ulong off, u32 data) +int qlcnic_82xx_hw_write_wx_2M(struct qlcnic_adapter *adapter, ulong off, +			       u32 data)  {  	unsigned long flags;  	int rv; @@ -1013,7 +1080,7 @@ qlcnic_hw_write_wx_2M(struct qlcnic_adapter *adapter, ulong off, u32 data)  	return -EIO;  } -int qlcnic_hw_read_wx_2M(struct qlcnic_adapter *adapter, ulong off) +int qlcnic_82xx_hw_read_wx_2M(struct qlcnic_adapter *adapter, ulong off)  {  	unsigned long flags;  	int rv; @@ -1042,7 +1109,6 @@ int qlcnic_hw_read_wx_2M(struct qlcnic_adapter *adapter, ulong off)  	return -1;  } -  void __iomem *qlcnic_get_ioaddr(struct qlcnic_hardware_context *ahw,  				u32 offset)  { @@ -1268,7 +1334,7 @@ int qlcnic_pci_mem_read_2M(struct qlcnic_adapter *adapter, u64 off, u64 *data)  	return ret;  } -int qlcnic_get_board_info(struct qlcnic_adapter *adapter) +int qlcnic_82xx_get_board_info(struct qlcnic_adapter *adapter)  {  	int offset, board_type, magic;  	struct pci_dev *pdev = adapter->pdev; @@ -1341,7 +1407,7 @@ qlcnic_wol_supported(struct qlcnic_adapter *adapter)  	return 0;  } -int qlcnic_config_led(struct qlcnic_adapter *adapter, u32 state, u32 rate) +int qlcnic_82xx_config_led(struct qlcnic_adapter *adapter, u32 state, u32 rate)  {  	struct qlcnic_nic_req   req;  	int rv; @@ -1353,7 +1419,7 @@ int qlcnic_config_led(struct qlcnic_adapter *adapter, u32 state, u32 rate)  	word = QLCNIC_H2C_OPCODE_CONFIG_LED | ((u64)adapter->portnum << 16);  	req.req_hdr = cpu_to_le64(word); -	req.words[0] = cpu_to_le64((u64)rate << 32); +	req.words[0] = cpu_to_le64(((u64)rate << 32) | adapter->portnum);  	req.words[1] = cpu_to_le64(state);  	rv = qlcnic_send_cmd_descs(adapter, (struct cmd_desc_type0 *)&req, 1); @@ -1362,3 +1428,56 @@ int qlcnic_config_led(struct qlcnic_adapter *adapter, u32 state, u32 rate)  	return rv;  } + +void qlcnic_82xx_get_func_no(struct qlcnic_adapter *adapter) +{ +	void __iomem *msix_base_addr; +	u32 func; +	u32 msix_base; + +	pci_read_config_dword(adapter->pdev, QLCNIC_MSIX_TABLE_OFFSET, &func); +	msix_base_addr = adapter->ahw->pci_base0 + QLCNIC_MSIX_BASE; +	msix_base = readl(msix_base_addr); +	func = (func - msix_base) / QLCNIC_MSIX_TBL_PGSIZE; +	adapter->ahw->pci_func = func; +} + +void qlcnic_82xx_read_crb(struct qlcnic_adapter *adapter, char *buf, +			  loff_t offset, size_t size) +{ +	u32 data; +	u64 qmdata; + +	if (ADDR_IN_RANGE(offset, QLCNIC_PCI_CAMQM, QLCNIC_PCI_CAMQM_END)) { +		qlcnic_pci_camqm_read_2M(adapter, offset, &qmdata); +		memcpy(buf, &qmdata, size); +	} else { +		data = QLCRD32(adapter, offset); +		memcpy(buf, &data, size); +	} +} + +void qlcnic_82xx_write_crb(struct qlcnic_adapter *adapter, char *buf, +			   loff_t offset, size_t size) +{ +	u32 data; +	u64 qmdata; + +	if (ADDR_IN_RANGE(offset, QLCNIC_PCI_CAMQM, QLCNIC_PCI_CAMQM_END)) { +		memcpy(&qmdata, buf, size); +		qlcnic_pci_camqm_write_2M(adapter, offset, qmdata); +	} else { +		memcpy(&data, buf, size); +		QLCWR32(adapter, offset, data); +	} +} + +int qlcnic_82xx_api_lock(struct qlcnic_adapter *adapter) +{ +	return qlcnic_pcie_sem_lock(adapter, 5, 0); +} + +void qlcnic_82xx_api_unlock(struct qlcnic_adapter *adapter) +{ +	qlcnic_pcie_sem_unlock(adapter, 5); +}  |