162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2005 - 2016 Broadcom 462306a36Sopenharmony_ci * All rights reserved. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * Contact Information: 762306a36Sopenharmony_ci * linux-drivers@emulex.com 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * Emulex 1062306a36Sopenharmony_ci * 3333 Susan Street 1162306a36Sopenharmony_ci * Costa Mesa, CA 92626 1262306a36Sopenharmony_ci */ 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include <linux/prefetch.h> 1562306a36Sopenharmony_ci#include <linux/module.h> 1662306a36Sopenharmony_ci#include "be.h" 1762306a36Sopenharmony_ci#include "be_cmds.h" 1862306a36Sopenharmony_ci#include <asm/div64.h> 1962306a36Sopenharmony_ci#include <linux/if_bridge.h> 2062306a36Sopenharmony_ci#include <net/busy_poll.h> 2162306a36Sopenharmony_ci#include <net/vxlan.h> 2262306a36Sopenharmony_ci 2362306a36Sopenharmony_ciMODULE_DESCRIPTION(DRV_DESC); 2462306a36Sopenharmony_ciMODULE_AUTHOR("Emulex Corporation"); 2562306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci/* num_vfs module param is obsolete. 2862306a36Sopenharmony_ci * Use sysfs method to enable/disable VFs. 2962306a36Sopenharmony_ci */ 3062306a36Sopenharmony_cistatic unsigned int num_vfs; 3162306a36Sopenharmony_cimodule_param(num_vfs, uint, 0444); 3262306a36Sopenharmony_ciMODULE_PARM_DESC(num_vfs, "Number of PCI VFs to initialize"); 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_cistatic ushort rx_frag_size = 2048; 3562306a36Sopenharmony_cimodule_param(rx_frag_size, ushort, 0444); 3662306a36Sopenharmony_ciMODULE_PARM_DESC(rx_frag_size, "Size of a fragment that holds rcvd data."); 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci/* Per-module error detection/recovery workq shared across all functions. 3962306a36Sopenharmony_ci * Each function schedules its own work request on this shared workq. 4062306a36Sopenharmony_ci */ 4162306a36Sopenharmony_cistatic struct workqueue_struct *be_err_recovery_workq; 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cistatic const struct pci_device_id be_dev_ids[] = { 4462306a36Sopenharmony_ci#ifdef CONFIG_BE2NET_BE2 4562306a36Sopenharmony_ci { PCI_DEVICE(BE_VENDOR_ID, BE_DEVICE_ID1) }, 4662306a36Sopenharmony_ci { PCI_DEVICE(BE_VENDOR_ID, OC_DEVICE_ID1) }, 4762306a36Sopenharmony_ci#endif /* CONFIG_BE2NET_BE2 */ 4862306a36Sopenharmony_ci#ifdef CONFIG_BE2NET_BE3 4962306a36Sopenharmony_ci { PCI_DEVICE(BE_VENDOR_ID, BE_DEVICE_ID2) }, 5062306a36Sopenharmony_ci { PCI_DEVICE(BE_VENDOR_ID, OC_DEVICE_ID2) }, 5162306a36Sopenharmony_ci#endif /* CONFIG_BE2NET_BE3 */ 5262306a36Sopenharmony_ci#ifdef CONFIG_BE2NET_LANCER 5362306a36Sopenharmony_ci { PCI_DEVICE(EMULEX_VENDOR_ID, OC_DEVICE_ID3)}, 5462306a36Sopenharmony_ci { PCI_DEVICE(EMULEX_VENDOR_ID, OC_DEVICE_ID4)}, 5562306a36Sopenharmony_ci#endif /* CONFIG_BE2NET_LANCER */ 5662306a36Sopenharmony_ci#ifdef CONFIG_BE2NET_SKYHAWK 5762306a36Sopenharmony_ci { PCI_DEVICE(EMULEX_VENDOR_ID, OC_DEVICE_ID5)}, 5862306a36Sopenharmony_ci { PCI_DEVICE(EMULEX_VENDOR_ID, OC_DEVICE_ID6)}, 5962306a36Sopenharmony_ci#endif /* CONFIG_BE2NET_SKYHAWK */ 6062306a36Sopenharmony_ci { 0 } 6162306a36Sopenharmony_ci}; 6262306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, be_dev_ids); 6362306a36Sopenharmony_ci 6462306a36Sopenharmony_ci/* Workqueue used by all functions for defering cmd calls to the adapter */ 6562306a36Sopenharmony_cistatic struct workqueue_struct *be_wq; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci/* UE Status Low CSR */ 6862306a36Sopenharmony_cistatic const char * const ue_status_low_desc[] = { 6962306a36Sopenharmony_ci "CEV", 7062306a36Sopenharmony_ci "CTX", 7162306a36Sopenharmony_ci "DBUF", 7262306a36Sopenharmony_ci "ERX", 7362306a36Sopenharmony_ci "Host", 7462306a36Sopenharmony_ci "MPU", 7562306a36Sopenharmony_ci "NDMA", 7662306a36Sopenharmony_ci "PTC ", 7762306a36Sopenharmony_ci "RDMA ", 7862306a36Sopenharmony_ci "RXF ", 7962306a36Sopenharmony_ci "RXIPS ", 8062306a36Sopenharmony_ci "RXULP0 ", 8162306a36Sopenharmony_ci "RXULP1 ", 8262306a36Sopenharmony_ci "RXULP2 ", 8362306a36Sopenharmony_ci "TIM ", 8462306a36Sopenharmony_ci "TPOST ", 8562306a36Sopenharmony_ci "TPRE ", 8662306a36Sopenharmony_ci "TXIPS ", 8762306a36Sopenharmony_ci "TXULP0 ", 8862306a36Sopenharmony_ci "TXULP1 ", 8962306a36Sopenharmony_ci "UC ", 9062306a36Sopenharmony_ci "WDMA ", 9162306a36Sopenharmony_ci "TXULP2 ", 9262306a36Sopenharmony_ci "HOST1 ", 9362306a36Sopenharmony_ci "P0_OB_LINK ", 9462306a36Sopenharmony_ci "P1_OB_LINK ", 9562306a36Sopenharmony_ci "HOST_GPIO ", 9662306a36Sopenharmony_ci "MBOX ", 9762306a36Sopenharmony_ci "ERX2 ", 9862306a36Sopenharmony_ci "SPARE ", 9962306a36Sopenharmony_ci "JTAG ", 10062306a36Sopenharmony_ci "MPU_INTPEND " 10162306a36Sopenharmony_ci}; 10262306a36Sopenharmony_ci 10362306a36Sopenharmony_ci/* UE Status High CSR */ 10462306a36Sopenharmony_cistatic const char * const ue_status_hi_desc[] = { 10562306a36Sopenharmony_ci "LPCMEMHOST", 10662306a36Sopenharmony_ci "MGMT_MAC", 10762306a36Sopenharmony_ci "PCS0ONLINE", 10862306a36Sopenharmony_ci "MPU_IRAM", 10962306a36Sopenharmony_ci "PCS1ONLINE", 11062306a36Sopenharmony_ci "PCTL0", 11162306a36Sopenharmony_ci "PCTL1", 11262306a36Sopenharmony_ci "PMEM", 11362306a36Sopenharmony_ci "RR", 11462306a36Sopenharmony_ci "TXPB", 11562306a36Sopenharmony_ci "RXPP", 11662306a36Sopenharmony_ci "XAUI", 11762306a36Sopenharmony_ci "TXP", 11862306a36Sopenharmony_ci "ARM", 11962306a36Sopenharmony_ci "IPC", 12062306a36Sopenharmony_ci "HOST2", 12162306a36Sopenharmony_ci "HOST3", 12262306a36Sopenharmony_ci "HOST4", 12362306a36Sopenharmony_ci "HOST5", 12462306a36Sopenharmony_ci "HOST6", 12562306a36Sopenharmony_ci "HOST7", 12662306a36Sopenharmony_ci "ECRC", 12762306a36Sopenharmony_ci "Poison TLP", 12862306a36Sopenharmony_ci "NETC", 12962306a36Sopenharmony_ci "PERIPH", 13062306a36Sopenharmony_ci "LLTXULP", 13162306a36Sopenharmony_ci "D2P", 13262306a36Sopenharmony_ci "RCON", 13362306a36Sopenharmony_ci "LDMA", 13462306a36Sopenharmony_ci "LLTXP", 13562306a36Sopenharmony_ci "LLTXPB", 13662306a36Sopenharmony_ci "Unknown" 13762306a36Sopenharmony_ci}; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci#define BE_VF_IF_EN_FLAGS (BE_IF_FLAGS_UNTAGGED | \ 14062306a36Sopenharmony_ci BE_IF_FLAGS_BROADCAST | \ 14162306a36Sopenharmony_ci BE_IF_FLAGS_MULTICAST | \ 14262306a36Sopenharmony_ci BE_IF_FLAGS_PASS_L3L4_ERRORS) 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_cistatic void be_queue_free(struct be_adapter *adapter, struct be_queue_info *q) 14562306a36Sopenharmony_ci{ 14662306a36Sopenharmony_ci struct be_dma_mem *mem = &q->dma_mem; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci if (mem->va) { 14962306a36Sopenharmony_ci dma_free_coherent(&adapter->pdev->dev, mem->size, mem->va, 15062306a36Sopenharmony_ci mem->dma); 15162306a36Sopenharmony_ci mem->va = NULL; 15262306a36Sopenharmony_ci } 15362306a36Sopenharmony_ci} 15462306a36Sopenharmony_ci 15562306a36Sopenharmony_cistatic int be_queue_alloc(struct be_adapter *adapter, struct be_queue_info *q, 15662306a36Sopenharmony_ci u16 len, u16 entry_size) 15762306a36Sopenharmony_ci{ 15862306a36Sopenharmony_ci struct be_dma_mem *mem = &q->dma_mem; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci memset(q, 0, sizeof(*q)); 16162306a36Sopenharmony_ci q->len = len; 16262306a36Sopenharmony_ci q->entry_size = entry_size; 16362306a36Sopenharmony_ci mem->size = len * entry_size; 16462306a36Sopenharmony_ci mem->va = dma_alloc_coherent(&adapter->pdev->dev, mem->size, 16562306a36Sopenharmony_ci &mem->dma, GFP_KERNEL); 16662306a36Sopenharmony_ci if (!mem->va) 16762306a36Sopenharmony_ci return -ENOMEM; 16862306a36Sopenharmony_ci return 0; 16962306a36Sopenharmony_ci} 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_cistatic void be_reg_intr_set(struct be_adapter *adapter, bool enable) 17262306a36Sopenharmony_ci{ 17362306a36Sopenharmony_ci u32 reg, enabled; 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci pci_read_config_dword(adapter->pdev, PCICFG_MEMBAR_CTRL_INT_CTRL_OFFSET, 17662306a36Sopenharmony_ci ®); 17762306a36Sopenharmony_ci enabled = reg & MEMBAR_CTRL_INT_CTRL_HOSTINTR_MASK; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci if (!enabled && enable) 18062306a36Sopenharmony_ci reg |= MEMBAR_CTRL_INT_CTRL_HOSTINTR_MASK; 18162306a36Sopenharmony_ci else if (enabled && !enable) 18262306a36Sopenharmony_ci reg &= ~MEMBAR_CTRL_INT_CTRL_HOSTINTR_MASK; 18362306a36Sopenharmony_ci else 18462306a36Sopenharmony_ci return; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci pci_write_config_dword(adapter->pdev, 18762306a36Sopenharmony_ci PCICFG_MEMBAR_CTRL_INT_CTRL_OFFSET, reg); 18862306a36Sopenharmony_ci} 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_cistatic void be_intr_set(struct be_adapter *adapter, bool enable) 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci int status = 0; 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci /* On lancer interrupts can't be controlled via this register */ 19562306a36Sopenharmony_ci if (lancer_chip(adapter)) 19662306a36Sopenharmony_ci return; 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci if (be_check_error(adapter, BE_ERROR_EEH)) 19962306a36Sopenharmony_ci return; 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci status = be_cmd_intr_set(adapter, enable); 20262306a36Sopenharmony_ci if (status) 20362306a36Sopenharmony_ci be_reg_intr_set(adapter, enable); 20462306a36Sopenharmony_ci} 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_cistatic void be_rxq_notify(struct be_adapter *adapter, u16 qid, u16 posted) 20762306a36Sopenharmony_ci{ 20862306a36Sopenharmony_ci u32 val = 0; 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ci if (be_check_error(adapter, BE_ERROR_HW)) 21162306a36Sopenharmony_ci return; 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci val |= qid & DB_RQ_RING_ID_MASK; 21462306a36Sopenharmony_ci val |= posted << DB_RQ_NUM_POSTED_SHIFT; 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci wmb(); 21762306a36Sopenharmony_ci iowrite32(val, adapter->db + DB_RQ_OFFSET); 21862306a36Sopenharmony_ci} 21962306a36Sopenharmony_ci 22062306a36Sopenharmony_cistatic void be_txq_notify(struct be_adapter *adapter, struct be_tx_obj *txo, 22162306a36Sopenharmony_ci u16 posted) 22262306a36Sopenharmony_ci{ 22362306a36Sopenharmony_ci u32 val = 0; 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci if (be_check_error(adapter, BE_ERROR_HW)) 22662306a36Sopenharmony_ci return; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci val |= txo->q.id & DB_TXULP_RING_ID_MASK; 22962306a36Sopenharmony_ci val |= (posted & DB_TXULP_NUM_POSTED_MASK) << DB_TXULP_NUM_POSTED_SHIFT; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci wmb(); 23262306a36Sopenharmony_ci iowrite32(val, adapter->db + txo->db_offset); 23362306a36Sopenharmony_ci} 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_cistatic void be_eq_notify(struct be_adapter *adapter, u16 qid, 23662306a36Sopenharmony_ci bool arm, bool clear_int, u16 num_popped, 23762306a36Sopenharmony_ci u32 eq_delay_mult_enc) 23862306a36Sopenharmony_ci{ 23962306a36Sopenharmony_ci u32 val = 0; 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci val |= qid & DB_EQ_RING_ID_MASK; 24262306a36Sopenharmony_ci val |= ((qid & DB_EQ_RING_ID_EXT_MASK) << DB_EQ_RING_ID_EXT_MASK_SHIFT); 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci if (be_check_error(adapter, BE_ERROR_HW)) 24562306a36Sopenharmony_ci return; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci if (arm) 24862306a36Sopenharmony_ci val |= 1 << DB_EQ_REARM_SHIFT; 24962306a36Sopenharmony_ci if (clear_int) 25062306a36Sopenharmony_ci val |= 1 << DB_EQ_CLR_SHIFT; 25162306a36Sopenharmony_ci val |= 1 << DB_EQ_EVNT_SHIFT; 25262306a36Sopenharmony_ci val |= num_popped << DB_EQ_NUM_POPPED_SHIFT; 25362306a36Sopenharmony_ci val |= eq_delay_mult_enc << DB_EQ_R2I_DLY_SHIFT; 25462306a36Sopenharmony_ci iowrite32(val, adapter->db + DB_EQ_OFFSET); 25562306a36Sopenharmony_ci} 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_civoid be_cq_notify(struct be_adapter *adapter, u16 qid, bool arm, u16 num_popped) 25862306a36Sopenharmony_ci{ 25962306a36Sopenharmony_ci u32 val = 0; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci val |= qid & DB_CQ_RING_ID_MASK; 26262306a36Sopenharmony_ci val |= ((qid & DB_CQ_RING_ID_EXT_MASK) << 26362306a36Sopenharmony_ci DB_CQ_RING_ID_EXT_MASK_SHIFT); 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci if (be_check_error(adapter, BE_ERROR_HW)) 26662306a36Sopenharmony_ci return; 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci if (arm) 26962306a36Sopenharmony_ci val |= 1 << DB_CQ_REARM_SHIFT; 27062306a36Sopenharmony_ci val |= num_popped << DB_CQ_NUM_POPPED_SHIFT; 27162306a36Sopenharmony_ci iowrite32(val, adapter->db + DB_CQ_OFFSET); 27262306a36Sopenharmony_ci} 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_cistatic int be_dev_mac_add(struct be_adapter *adapter, const u8 *mac) 27562306a36Sopenharmony_ci{ 27662306a36Sopenharmony_ci int i; 27762306a36Sopenharmony_ci 27862306a36Sopenharmony_ci /* Check if mac has already been added as part of uc-list */ 27962306a36Sopenharmony_ci for (i = 0; i < adapter->uc_macs; i++) { 28062306a36Sopenharmony_ci if (ether_addr_equal(adapter->uc_list[i].mac, mac)) { 28162306a36Sopenharmony_ci /* mac already added, skip addition */ 28262306a36Sopenharmony_ci adapter->pmac_id[0] = adapter->pmac_id[i + 1]; 28362306a36Sopenharmony_ci return 0; 28462306a36Sopenharmony_ci } 28562306a36Sopenharmony_ci } 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci return be_cmd_pmac_add(adapter, mac, adapter->if_handle, 28862306a36Sopenharmony_ci &adapter->pmac_id[0], 0); 28962306a36Sopenharmony_ci} 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_cistatic void be_dev_mac_del(struct be_adapter *adapter, int pmac_id) 29262306a36Sopenharmony_ci{ 29362306a36Sopenharmony_ci int i; 29462306a36Sopenharmony_ci 29562306a36Sopenharmony_ci /* Skip deletion if the programmed mac is 29662306a36Sopenharmony_ci * being used in uc-list 29762306a36Sopenharmony_ci */ 29862306a36Sopenharmony_ci for (i = 0; i < adapter->uc_macs; i++) { 29962306a36Sopenharmony_ci if (adapter->pmac_id[i + 1] == pmac_id) 30062306a36Sopenharmony_ci return; 30162306a36Sopenharmony_ci } 30262306a36Sopenharmony_ci be_cmd_pmac_del(adapter, adapter->if_handle, pmac_id, 0); 30362306a36Sopenharmony_ci} 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_cistatic int be_mac_addr_set(struct net_device *netdev, void *p) 30662306a36Sopenharmony_ci{ 30762306a36Sopenharmony_ci struct be_adapter *adapter = netdev_priv(netdev); 30862306a36Sopenharmony_ci struct device *dev = &adapter->pdev->dev; 30962306a36Sopenharmony_ci struct sockaddr *addr = p; 31062306a36Sopenharmony_ci int status; 31162306a36Sopenharmony_ci u8 mac[ETH_ALEN]; 31262306a36Sopenharmony_ci u32 old_pmac_id = adapter->pmac_id[0]; 31362306a36Sopenharmony_ci 31462306a36Sopenharmony_ci if (!is_valid_ether_addr(addr->sa_data)) 31562306a36Sopenharmony_ci return -EADDRNOTAVAIL; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci /* Proceed further only if, User provided MAC is different 31862306a36Sopenharmony_ci * from active MAC 31962306a36Sopenharmony_ci */ 32062306a36Sopenharmony_ci if (ether_addr_equal(addr->sa_data, adapter->dev_mac)) 32162306a36Sopenharmony_ci return 0; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci /* BE3 VFs without FILTMGMT privilege are not allowed to set its MAC 32462306a36Sopenharmony_ci * address 32562306a36Sopenharmony_ci */ 32662306a36Sopenharmony_ci if (BEx_chip(adapter) && be_virtfn(adapter) && 32762306a36Sopenharmony_ci !check_privilege(adapter, BE_PRIV_FILTMGMT)) 32862306a36Sopenharmony_ci return -EPERM; 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_ci /* if device is not running, copy MAC to netdev->dev_addr */ 33162306a36Sopenharmony_ci if (!netif_running(netdev)) 33262306a36Sopenharmony_ci goto done; 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci /* The PMAC_ADD cmd may fail if the VF doesn't have FILTMGMT 33562306a36Sopenharmony_ci * privilege or if PF did not provision the new MAC address. 33662306a36Sopenharmony_ci * On BE3, this cmd will always fail if the VF doesn't have the 33762306a36Sopenharmony_ci * FILTMGMT privilege. This failure is OK, only if the PF programmed 33862306a36Sopenharmony_ci * the MAC for the VF. 33962306a36Sopenharmony_ci */ 34062306a36Sopenharmony_ci mutex_lock(&adapter->rx_filter_lock); 34162306a36Sopenharmony_ci status = be_dev_mac_add(adapter, (u8 *)addr->sa_data); 34262306a36Sopenharmony_ci if (!status) { 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci /* Delete the old programmed MAC. This call may fail if the 34562306a36Sopenharmony_ci * old MAC was already deleted by the PF driver. 34662306a36Sopenharmony_ci */ 34762306a36Sopenharmony_ci if (adapter->pmac_id[0] != old_pmac_id) 34862306a36Sopenharmony_ci be_dev_mac_del(adapter, old_pmac_id); 34962306a36Sopenharmony_ci } 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci mutex_unlock(&adapter->rx_filter_lock); 35262306a36Sopenharmony_ci /* Decide if the new MAC is successfully activated only after 35362306a36Sopenharmony_ci * querying the FW 35462306a36Sopenharmony_ci */ 35562306a36Sopenharmony_ci status = be_cmd_get_active_mac(adapter, adapter->pmac_id[0], mac, 35662306a36Sopenharmony_ci adapter->if_handle, true, 0); 35762306a36Sopenharmony_ci if (status) 35862306a36Sopenharmony_ci goto err; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci /* The MAC change did not happen, either due to lack of privilege 36162306a36Sopenharmony_ci * or PF didn't pre-provision. 36262306a36Sopenharmony_ci */ 36362306a36Sopenharmony_ci if (!ether_addr_equal(addr->sa_data, mac)) { 36462306a36Sopenharmony_ci status = -EPERM; 36562306a36Sopenharmony_ci goto err; 36662306a36Sopenharmony_ci } 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci /* Remember currently programmed MAC */ 36962306a36Sopenharmony_ci ether_addr_copy(adapter->dev_mac, addr->sa_data); 37062306a36Sopenharmony_cidone: 37162306a36Sopenharmony_ci eth_hw_addr_set(netdev, addr->sa_data); 37262306a36Sopenharmony_ci dev_info(dev, "MAC address changed to %pM\n", addr->sa_data); 37362306a36Sopenharmony_ci return 0; 37462306a36Sopenharmony_cierr: 37562306a36Sopenharmony_ci dev_warn(dev, "MAC address change to %pM failed\n", addr->sa_data); 37662306a36Sopenharmony_ci return status; 37762306a36Sopenharmony_ci} 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_ci/* BE2 supports only v0 cmd */ 38062306a36Sopenharmony_cistatic void *hw_stats_from_cmd(struct be_adapter *adapter) 38162306a36Sopenharmony_ci{ 38262306a36Sopenharmony_ci if (BE2_chip(adapter)) { 38362306a36Sopenharmony_ci struct be_cmd_resp_get_stats_v0 *cmd = adapter->stats_cmd.va; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci return &cmd->hw_stats; 38662306a36Sopenharmony_ci } else if (BE3_chip(adapter)) { 38762306a36Sopenharmony_ci struct be_cmd_resp_get_stats_v1 *cmd = adapter->stats_cmd.va; 38862306a36Sopenharmony_ci 38962306a36Sopenharmony_ci return &cmd->hw_stats; 39062306a36Sopenharmony_ci } else { 39162306a36Sopenharmony_ci struct be_cmd_resp_get_stats_v2 *cmd = adapter->stats_cmd.va; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci return &cmd->hw_stats; 39462306a36Sopenharmony_ci } 39562306a36Sopenharmony_ci} 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci/* BE2 supports only v0 cmd */ 39862306a36Sopenharmony_cistatic void *be_erx_stats_from_cmd(struct be_adapter *adapter) 39962306a36Sopenharmony_ci{ 40062306a36Sopenharmony_ci if (BE2_chip(adapter)) { 40162306a36Sopenharmony_ci struct be_hw_stats_v0 *hw_stats = hw_stats_from_cmd(adapter); 40262306a36Sopenharmony_ci 40362306a36Sopenharmony_ci return &hw_stats->erx; 40462306a36Sopenharmony_ci } else if (BE3_chip(adapter)) { 40562306a36Sopenharmony_ci struct be_hw_stats_v1 *hw_stats = hw_stats_from_cmd(adapter); 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci return &hw_stats->erx; 40862306a36Sopenharmony_ci } else { 40962306a36Sopenharmony_ci struct be_hw_stats_v2 *hw_stats = hw_stats_from_cmd(adapter); 41062306a36Sopenharmony_ci 41162306a36Sopenharmony_ci return &hw_stats->erx; 41262306a36Sopenharmony_ci } 41362306a36Sopenharmony_ci} 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_cistatic void populate_be_v0_stats(struct be_adapter *adapter) 41662306a36Sopenharmony_ci{ 41762306a36Sopenharmony_ci struct be_hw_stats_v0 *hw_stats = hw_stats_from_cmd(adapter); 41862306a36Sopenharmony_ci struct be_pmem_stats *pmem_sts = &hw_stats->pmem; 41962306a36Sopenharmony_ci struct be_rxf_stats_v0 *rxf_stats = &hw_stats->rxf; 42062306a36Sopenharmony_ci struct be_port_rxf_stats_v0 *port_stats = 42162306a36Sopenharmony_ci &rxf_stats->port[adapter->port_num]; 42262306a36Sopenharmony_ci struct be_drv_stats *drvs = &adapter->drv_stats; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci be_dws_le_to_cpu(hw_stats, sizeof(*hw_stats)); 42562306a36Sopenharmony_ci drvs->rx_pause_frames = port_stats->rx_pause_frames; 42662306a36Sopenharmony_ci drvs->rx_crc_errors = port_stats->rx_crc_errors; 42762306a36Sopenharmony_ci drvs->rx_control_frames = port_stats->rx_control_frames; 42862306a36Sopenharmony_ci drvs->rx_in_range_errors = port_stats->rx_in_range_errors; 42962306a36Sopenharmony_ci drvs->rx_frame_too_long = port_stats->rx_frame_too_long; 43062306a36Sopenharmony_ci drvs->rx_dropped_runt = port_stats->rx_dropped_runt; 43162306a36Sopenharmony_ci drvs->rx_ip_checksum_errs = port_stats->rx_ip_checksum_errs; 43262306a36Sopenharmony_ci drvs->rx_tcp_checksum_errs = port_stats->rx_tcp_checksum_errs; 43362306a36Sopenharmony_ci drvs->rx_udp_checksum_errs = port_stats->rx_udp_checksum_errs; 43462306a36Sopenharmony_ci drvs->rxpp_fifo_overflow_drop = port_stats->rx_fifo_overflow; 43562306a36Sopenharmony_ci drvs->rx_dropped_tcp_length = port_stats->rx_dropped_tcp_length; 43662306a36Sopenharmony_ci drvs->rx_dropped_too_small = port_stats->rx_dropped_too_small; 43762306a36Sopenharmony_ci drvs->rx_dropped_too_short = port_stats->rx_dropped_too_short; 43862306a36Sopenharmony_ci drvs->rx_out_range_errors = port_stats->rx_out_range_errors; 43962306a36Sopenharmony_ci drvs->rx_input_fifo_overflow_drop = port_stats->rx_input_fifo_overflow; 44062306a36Sopenharmony_ci drvs->rx_dropped_header_too_small = 44162306a36Sopenharmony_ci port_stats->rx_dropped_header_too_small; 44262306a36Sopenharmony_ci drvs->rx_address_filtered = 44362306a36Sopenharmony_ci port_stats->rx_address_filtered + 44462306a36Sopenharmony_ci port_stats->rx_vlan_filtered; 44562306a36Sopenharmony_ci drvs->rx_alignment_symbol_errors = 44662306a36Sopenharmony_ci port_stats->rx_alignment_symbol_errors; 44762306a36Sopenharmony_ci 44862306a36Sopenharmony_ci drvs->tx_pauseframes = port_stats->tx_pauseframes; 44962306a36Sopenharmony_ci drvs->tx_controlframes = port_stats->tx_controlframes; 45062306a36Sopenharmony_ci 45162306a36Sopenharmony_ci if (adapter->port_num) 45262306a36Sopenharmony_ci drvs->jabber_events = rxf_stats->port1_jabber_events; 45362306a36Sopenharmony_ci else 45462306a36Sopenharmony_ci drvs->jabber_events = rxf_stats->port0_jabber_events; 45562306a36Sopenharmony_ci drvs->rx_drops_no_pbuf = rxf_stats->rx_drops_no_pbuf; 45662306a36Sopenharmony_ci drvs->rx_drops_no_erx_descr = rxf_stats->rx_drops_no_erx_descr; 45762306a36Sopenharmony_ci drvs->forwarded_packets = rxf_stats->forwarded_packets; 45862306a36Sopenharmony_ci drvs->rx_drops_mtu = rxf_stats->rx_drops_mtu; 45962306a36Sopenharmony_ci drvs->rx_drops_no_tpre_descr = rxf_stats->rx_drops_no_tpre_descr; 46062306a36Sopenharmony_ci drvs->rx_drops_too_many_frags = rxf_stats->rx_drops_too_many_frags; 46162306a36Sopenharmony_ci adapter->drv_stats.eth_red_drops = pmem_sts->eth_red_drops; 46262306a36Sopenharmony_ci} 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_cistatic void populate_be_v1_stats(struct be_adapter *adapter) 46562306a36Sopenharmony_ci{ 46662306a36Sopenharmony_ci struct be_hw_stats_v1 *hw_stats = hw_stats_from_cmd(adapter); 46762306a36Sopenharmony_ci struct be_pmem_stats *pmem_sts = &hw_stats->pmem; 46862306a36Sopenharmony_ci struct be_rxf_stats_v1 *rxf_stats = &hw_stats->rxf; 46962306a36Sopenharmony_ci struct be_port_rxf_stats_v1 *port_stats = 47062306a36Sopenharmony_ci &rxf_stats->port[adapter->port_num]; 47162306a36Sopenharmony_ci struct be_drv_stats *drvs = &adapter->drv_stats; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci be_dws_le_to_cpu(hw_stats, sizeof(*hw_stats)); 47462306a36Sopenharmony_ci drvs->pmem_fifo_overflow_drop = port_stats->pmem_fifo_overflow_drop; 47562306a36Sopenharmony_ci drvs->rx_priority_pause_frames = port_stats->rx_priority_pause_frames; 47662306a36Sopenharmony_ci drvs->rx_pause_frames = port_stats->rx_pause_frames; 47762306a36Sopenharmony_ci drvs->rx_crc_errors = port_stats->rx_crc_errors; 47862306a36Sopenharmony_ci drvs->rx_control_frames = port_stats->rx_control_frames; 47962306a36Sopenharmony_ci drvs->rx_in_range_errors = port_stats->rx_in_range_errors; 48062306a36Sopenharmony_ci drvs->rx_frame_too_long = port_stats->rx_frame_too_long; 48162306a36Sopenharmony_ci drvs->rx_dropped_runt = port_stats->rx_dropped_runt; 48262306a36Sopenharmony_ci drvs->rx_ip_checksum_errs = port_stats->rx_ip_checksum_errs; 48362306a36Sopenharmony_ci drvs->rx_tcp_checksum_errs = port_stats->rx_tcp_checksum_errs; 48462306a36Sopenharmony_ci drvs->rx_udp_checksum_errs = port_stats->rx_udp_checksum_errs; 48562306a36Sopenharmony_ci drvs->rx_dropped_tcp_length = port_stats->rx_dropped_tcp_length; 48662306a36Sopenharmony_ci drvs->rx_dropped_too_small = port_stats->rx_dropped_too_small; 48762306a36Sopenharmony_ci drvs->rx_dropped_too_short = port_stats->rx_dropped_too_short; 48862306a36Sopenharmony_ci drvs->rx_out_range_errors = port_stats->rx_out_range_errors; 48962306a36Sopenharmony_ci drvs->rx_dropped_header_too_small = 49062306a36Sopenharmony_ci port_stats->rx_dropped_header_too_small; 49162306a36Sopenharmony_ci drvs->rx_input_fifo_overflow_drop = 49262306a36Sopenharmony_ci port_stats->rx_input_fifo_overflow_drop; 49362306a36Sopenharmony_ci drvs->rx_address_filtered = port_stats->rx_address_filtered; 49462306a36Sopenharmony_ci drvs->rx_alignment_symbol_errors = 49562306a36Sopenharmony_ci port_stats->rx_alignment_symbol_errors; 49662306a36Sopenharmony_ci drvs->rxpp_fifo_overflow_drop = port_stats->rxpp_fifo_overflow_drop; 49762306a36Sopenharmony_ci drvs->tx_pauseframes = port_stats->tx_pauseframes; 49862306a36Sopenharmony_ci drvs->tx_controlframes = port_stats->tx_controlframes; 49962306a36Sopenharmony_ci drvs->tx_priority_pauseframes = port_stats->tx_priority_pauseframes; 50062306a36Sopenharmony_ci drvs->jabber_events = port_stats->jabber_events; 50162306a36Sopenharmony_ci drvs->rx_drops_no_pbuf = rxf_stats->rx_drops_no_pbuf; 50262306a36Sopenharmony_ci drvs->rx_drops_no_erx_descr = rxf_stats->rx_drops_no_erx_descr; 50362306a36Sopenharmony_ci drvs->forwarded_packets = rxf_stats->forwarded_packets; 50462306a36Sopenharmony_ci drvs->rx_drops_mtu = rxf_stats->rx_drops_mtu; 50562306a36Sopenharmony_ci drvs->rx_drops_no_tpre_descr = rxf_stats->rx_drops_no_tpre_descr; 50662306a36Sopenharmony_ci drvs->rx_drops_too_many_frags = rxf_stats->rx_drops_too_many_frags; 50762306a36Sopenharmony_ci adapter->drv_stats.eth_red_drops = pmem_sts->eth_red_drops; 50862306a36Sopenharmony_ci} 50962306a36Sopenharmony_ci 51062306a36Sopenharmony_cistatic void populate_be_v2_stats(struct be_adapter *adapter) 51162306a36Sopenharmony_ci{ 51262306a36Sopenharmony_ci struct be_hw_stats_v2 *hw_stats = hw_stats_from_cmd(adapter); 51362306a36Sopenharmony_ci struct be_pmem_stats *pmem_sts = &hw_stats->pmem; 51462306a36Sopenharmony_ci struct be_rxf_stats_v2 *rxf_stats = &hw_stats->rxf; 51562306a36Sopenharmony_ci struct be_port_rxf_stats_v2 *port_stats = 51662306a36Sopenharmony_ci &rxf_stats->port[adapter->port_num]; 51762306a36Sopenharmony_ci struct be_drv_stats *drvs = &adapter->drv_stats; 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci be_dws_le_to_cpu(hw_stats, sizeof(*hw_stats)); 52062306a36Sopenharmony_ci drvs->pmem_fifo_overflow_drop = port_stats->pmem_fifo_overflow_drop; 52162306a36Sopenharmony_ci drvs->rx_priority_pause_frames = port_stats->rx_priority_pause_frames; 52262306a36Sopenharmony_ci drvs->rx_pause_frames = port_stats->rx_pause_frames; 52362306a36Sopenharmony_ci drvs->rx_crc_errors = port_stats->rx_crc_errors; 52462306a36Sopenharmony_ci drvs->rx_control_frames = port_stats->rx_control_frames; 52562306a36Sopenharmony_ci drvs->rx_in_range_errors = port_stats->rx_in_range_errors; 52662306a36Sopenharmony_ci drvs->rx_frame_too_long = port_stats->rx_frame_too_long; 52762306a36Sopenharmony_ci drvs->rx_dropped_runt = port_stats->rx_dropped_runt; 52862306a36Sopenharmony_ci drvs->rx_ip_checksum_errs = port_stats->rx_ip_checksum_errs; 52962306a36Sopenharmony_ci drvs->rx_tcp_checksum_errs = port_stats->rx_tcp_checksum_errs; 53062306a36Sopenharmony_ci drvs->rx_udp_checksum_errs = port_stats->rx_udp_checksum_errs; 53162306a36Sopenharmony_ci drvs->rx_dropped_tcp_length = port_stats->rx_dropped_tcp_length; 53262306a36Sopenharmony_ci drvs->rx_dropped_too_small = port_stats->rx_dropped_too_small; 53362306a36Sopenharmony_ci drvs->rx_dropped_too_short = port_stats->rx_dropped_too_short; 53462306a36Sopenharmony_ci drvs->rx_out_range_errors = port_stats->rx_out_range_errors; 53562306a36Sopenharmony_ci drvs->rx_dropped_header_too_small = 53662306a36Sopenharmony_ci port_stats->rx_dropped_header_too_small; 53762306a36Sopenharmony_ci drvs->rx_input_fifo_overflow_drop = 53862306a36Sopenharmony_ci port_stats->rx_input_fifo_overflow_drop; 53962306a36Sopenharmony_ci drvs->rx_address_filtered = port_stats->rx_address_filtered; 54062306a36Sopenharmony_ci drvs->rx_alignment_symbol_errors = 54162306a36Sopenharmony_ci port_stats->rx_alignment_symbol_errors; 54262306a36Sopenharmony_ci drvs->rxpp_fifo_overflow_drop = port_stats->rxpp_fifo_overflow_drop; 54362306a36Sopenharmony_ci drvs->tx_pauseframes = port_stats->tx_pauseframes; 54462306a36Sopenharmony_ci drvs->tx_controlframes = port_stats->tx_controlframes; 54562306a36Sopenharmony_ci drvs->tx_priority_pauseframes = port_stats->tx_priority_pauseframes; 54662306a36Sopenharmony_ci drvs->jabber_events = port_stats->jabber_events; 54762306a36Sopenharmony_ci drvs->rx_drops_no_pbuf = rxf_stats->rx_drops_no_pbuf; 54862306a36Sopenharmony_ci drvs->rx_drops_no_erx_descr = rxf_stats->rx_drops_no_erx_descr; 54962306a36Sopenharmony_ci drvs->forwarded_packets = rxf_stats->forwarded_packets; 55062306a36Sopenharmony_ci drvs->rx_drops_mtu = rxf_stats->rx_drops_mtu; 55162306a36Sopenharmony_ci drvs->rx_drops_no_tpre_descr = rxf_stats->rx_drops_no_tpre_descr; 55262306a36Sopenharmony_ci drvs->rx_drops_too_many_frags = rxf_stats->rx_drops_too_many_frags; 55362306a36Sopenharmony_ci adapter->drv_stats.eth_red_drops = pmem_sts->eth_red_drops; 55462306a36Sopenharmony_ci if (be_roce_supported(adapter)) { 55562306a36Sopenharmony_ci drvs->rx_roce_bytes_lsd = port_stats->roce_bytes_received_lsd; 55662306a36Sopenharmony_ci drvs->rx_roce_bytes_msd = port_stats->roce_bytes_received_msd; 55762306a36Sopenharmony_ci drvs->rx_roce_frames = port_stats->roce_frames_received; 55862306a36Sopenharmony_ci drvs->roce_drops_crc = port_stats->roce_drops_crc; 55962306a36Sopenharmony_ci drvs->roce_drops_payload_len = 56062306a36Sopenharmony_ci port_stats->roce_drops_payload_len; 56162306a36Sopenharmony_ci } 56262306a36Sopenharmony_ci} 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_cistatic void populate_lancer_stats(struct be_adapter *adapter) 56562306a36Sopenharmony_ci{ 56662306a36Sopenharmony_ci struct be_drv_stats *drvs = &adapter->drv_stats; 56762306a36Sopenharmony_ci struct lancer_pport_stats *pport_stats = pport_stats_from_cmd(adapter); 56862306a36Sopenharmony_ci 56962306a36Sopenharmony_ci be_dws_le_to_cpu(pport_stats, sizeof(*pport_stats)); 57062306a36Sopenharmony_ci drvs->rx_pause_frames = pport_stats->rx_pause_frames_lo; 57162306a36Sopenharmony_ci drvs->rx_crc_errors = pport_stats->rx_crc_errors_lo; 57262306a36Sopenharmony_ci drvs->rx_control_frames = pport_stats->rx_control_frames_lo; 57362306a36Sopenharmony_ci drvs->rx_in_range_errors = pport_stats->rx_in_range_errors; 57462306a36Sopenharmony_ci drvs->rx_frame_too_long = pport_stats->rx_frames_too_long_lo; 57562306a36Sopenharmony_ci drvs->rx_dropped_runt = pport_stats->rx_dropped_runt; 57662306a36Sopenharmony_ci drvs->rx_ip_checksum_errs = pport_stats->rx_ip_checksum_errors; 57762306a36Sopenharmony_ci drvs->rx_tcp_checksum_errs = pport_stats->rx_tcp_checksum_errors; 57862306a36Sopenharmony_ci drvs->rx_udp_checksum_errs = pport_stats->rx_udp_checksum_errors; 57962306a36Sopenharmony_ci drvs->rx_dropped_tcp_length = 58062306a36Sopenharmony_ci pport_stats->rx_dropped_invalid_tcp_length; 58162306a36Sopenharmony_ci drvs->rx_dropped_too_small = pport_stats->rx_dropped_too_small; 58262306a36Sopenharmony_ci drvs->rx_dropped_too_short = pport_stats->rx_dropped_too_short; 58362306a36Sopenharmony_ci drvs->rx_out_range_errors = pport_stats->rx_out_of_range_errors; 58462306a36Sopenharmony_ci drvs->rx_dropped_header_too_small = 58562306a36Sopenharmony_ci pport_stats->rx_dropped_header_too_small; 58662306a36Sopenharmony_ci drvs->rx_input_fifo_overflow_drop = pport_stats->rx_fifo_overflow; 58762306a36Sopenharmony_ci drvs->rx_address_filtered = 58862306a36Sopenharmony_ci pport_stats->rx_address_filtered + 58962306a36Sopenharmony_ci pport_stats->rx_vlan_filtered; 59062306a36Sopenharmony_ci drvs->rx_alignment_symbol_errors = pport_stats->rx_symbol_errors_lo; 59162306a36Sopenharmony_ci drvs->rxpp_fifo_overflow_drop = pport_stats->rx_fifo_overflow; 59262306a36Sopenharmony_ci drvs->tx_pauseframes = pport_stats->tx_pause_frames_lo; 59362306a36Sopenharmony_ci drvs->tx_controlframes = pport_stats->tx_control_frames_lo; 59462306a36Sopenharmony_ci drvs->jabber_events = pport_stats->rx_jabbers; 59562306a36Sopenharmony_ci drvs->forwarded_packets = pport_stats->num_forwards_lo; 59662306a36Sopenharmony_ci drvs->rx_drops_mtu = pport_stats->rx_drops_mtu_lo; 59762306a36Sopenharmony_ci drvs->rx_drops_too_many_frags = 59862306a36Sopenharmony_ci pport_stats->rx_drops_too_many_frags_lo; 59962306a36Sopenharmony_ci} 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_cistatic void accumulate_16bit_val(u32 *acc, u16 val) 60262306a36Sopenharmony_ci{ 60362306a36Sopenharmony_ci#define lo(x) (x & 0xFFFF) 60462306a36Sopenharmony_ci#define hi(x) (x & 0xFFFF0000) 60562306a36Sopenharmony_ci bool wrapped = val < lo(*acc); 60662306a36Sopenharmony_ci u32 newacc = hi(*acc) + val; 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci if (wrapped) 60962306a36Sopenharmony_ci newacc += 65536; 61062306a36Sopenharmony_ci WRITE_ONCE(*acc, newacc); 61162306a36Sopenharmony_ci} 61262306a36Sopenharmony_ci 61362306a36Sopenharmony_cistatic void populate_erx_stats(struct be_adapter *adapter, 61462306a36Sopenharmony_ci struct be_rx_obj *rxo, u32 erx_stat) 61562306a36Sopenharmony_ci{ 61662306a36Sopenharmony_ci if (!BEx_chip(adapter)) 61762306a36Sopenharmony_ci rx_stats(rxo)->rx_drops_no_frags = erx_stat; 61862306a36Sopenharmony_ci else 61962306a36Sopenharmony_ci /* below erx HW counter can actually wrap around after 62062306a36Sopenharmony_ci * 65535. Driver accumulates a 32-bit value 62162306a36Sopenharmony_ci */ 62262306a36Sopenharmony_ci accumulate_16bit_val(&rx_stats(rxo)->rx_drops_no_frags, 62362306a36Sopenharmony_ci (u16)erx_stat); 62462306a36Sopenharmony_ci} 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_civoid be_parse_stats(struct be_adapter *adapter) 62762306a36Sopenharmony_ci{ 62862306a36Sopenharmony_ci struct be_erx_stats_v2 *erx = be_erx_stats_from_cmd(adapter); 62962306a36Sopenharmony_ci struct be_rx_obj *rxo; 63062306a36Sopenharmony_ci int i; 63162306a36Sopenharmony_ci u32 erx_stat; 63262306a36Sopenharmony_ci 63362306a36Sopenharmony_ci if (lancer_chip(adapter)) { 63462306a36Sopenharmony_ci populate_lancer_stats(adapter); 63562306a36Sopenharmony_ci } else { 63662306a36Sopenharmony_ci if (BE2_chip(adapter)) 63762306a36Sopenharmony_ci populate_be_v0_stats(adapter); 63862306a36Sopenharmony_ci else if (BE3_chip(adapter)) 63962306a36Sopenharmony_ci /* for BE3 */ 64062306a36Sopenharmony_ci populate_be_v1_stats(adapter); 64162306a36Sopenharmony_ci else 64262306a36Sopenharmony_ci populate_be_v2_stats(adapter); 64362306a36Sopenharmony_ci 64462306a36Sopenharmony_ci /* erx_v2 is longer than v0, v1. use v2 for v0, v1 access */ 64562306a36Sopenharmony_ci for_all_rx_queues(adapter, rxo, i) { 64662306a36Sopenharmony_ci erx_stat = erx->rx_drops_no_fragments[rxo->q.id]; 64762306a36Sopenharmony_ci populate_erx_stats(adapter, rxo, erx_stat); 64862306a36Sopenharmony_ci } 64962306a36Sopenharmony_ci } 65062306a36Sopenharmony_ci} 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_cistatic void be_get_stats64(struct net_device *netdev, 65362306a36Sopenharmony_ci struct rtnl_link_stats64 *stats) 65462306a36Sopenharmony_ci{ 65562306a36Sopenharmony_ci struct be_adapter *adapter = netdev_priv(netdev); 65662306a36Sopenharmony_ci struct be_drv_stats *drvs = &adapter->drv_stats; 65762306a36Sopenharmony_ci struct be_rx_obj *rxo; 65862306a36Sopenharmony_ci struct be_tx_obj *txo; 65962306a36Sopenharmony_ci u64 pkts, bytes; 66062306a36Sopenharmony_ci unsigned int start; 66162306a36Sopenharmony_ci int i; 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci for_all_rx_queues(adapter, rxo, i) { 66462306a36Sopenharmony_ci const struct be_rx_stats *rx_stats = rx_stats(rxo); 66562306a36Sopenharmony_ci 66662306a36Sopenharmony_ci do { 66762306a36Sopenharmony_ci start = u64_stats_fetch_begin(&rx_stats->sync); 66862306a36Sopenharmony_ci pkts = rx_stats(rxo)->rx_pkts; 66962306a36Sopenharmony_ci bytes = rx_stats(rxo)->rx_bytes; 67062306a36Sopenharmony_ci } while (u64_stats_fetch_retry(&rx_stats->sync, start)); 67162306a36Sopenharmony_ci stats->rx_packets += pkts; 67262306a36Sopenharmony_ci stats->rx_bytes += bytes; 67362306a36Sopenharmony_ci stats->multicast += rx_stats(rxo)->rx_mcast_pkts; 67462306a36Sopenharmony_ci stats->rx_dropped += rx_stats(rxo)->rx_drops_no_skbs + 67562306a36Sopenharmony_ci rx_stats(rxo)->rx_drops_no_frags; 67662306a36Sopenharmony_ci } 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci for_all_tx_queues(adapter, txo, i) { 67962306a36Sopenharmony_ci const struct be_tx_stats *tx_stats = tx_stats(txo); 68062306a36Sopenharmony_ci 68162306a36Sopenharmony_ci do { 68262306a36Sopenharmony_ci start = u64_stats_fetch_begin(&tx_stats->sync); 68362306a36Sopenharmony_ci pkts = tx_stats(txo)->tx_pkts; 68462306a36Sopenharmony_ci bytes = tx_stats(txo)->tx_bytes; 68562306a36Sopenharmony_ci } while (u64_stats_fetch_retry(&tx_stats->sync, start)); 68662306a36Sopenharmony_ci stats->tx_packets += pkts; 68762306a36Sopenharmony_ci stats->tx_bytes += bytes; 68862306a36Sopenharmony_ci } 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci /* bad pkts received */ 69162306a36Sopenharmony_ci stats->rx_errors = drvs->rx_crc_errors + 69262306a36Sopenharmony_ci drvs->rx_alignment_symbol_errors + 69362306a36Sopenharmony_ci drvs->rx_in_range_errors + 69462306a36Sopenharmony_ci drvs->rx_out_range_errors + 69562306a36Sopenharmony_ci drvs->rx_frame_too_long + 69662306a36Sopenharmony_ci drvs->rx_dropped_too_small + 69762306a36Sopenharmony_ci drvs->rx_dropped_too_short + 69862306a36Sopenharmony_ci drvs->rx_dropped_header_too_small + 69962306a36Sopenharmony_ci drvs->rx_dropped_tcp_length + 70062306a36Sopenharmony_ci drvs->rx_dropped_runt; 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci /* detailed rx errors */ 70362306a36Sopenharmony_ci stats->rx_length_errors = drvs->rx_in_range_errors + 70462306a36Sopenharmony_ci drvs->rx_out_range_errors + 70562306a36Sopenharmony_ci drvs->rx_frame_too_long; 70662306a36Sopenharmony_ci 70762306a36Sopenharmony_ci stats->rx_crc_errors = drvs->rx_crc_errors; 70862306a36Sopenharmony_ci 70962306a36Sopenharmony_ci /* frame alignment errors */ 71062306a36Sopenharmony_ci stats->rx_frame_errors = drvs->rx_alignment_symbol_errors; 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci /* receiver fifo overrun */ 71362306a36Sopenharmony_ci /* drops_no_pbuf is no per i/f, it's per BE card */ 71462306a36Sopenharmony_ci stats->rx_fifo_errors = drvs->rxpp_fifo_overflow_drop + 71562306a36Sopenharmony_ci drvs->rx_input_fifo_overflow_drop + 71662306a36Sopenharmony_ci drvs->rx_drops_no_pbuf; 71762306a36Sopenharmony_ci} 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_civoid be_link_status_update(struct be_adapter *adapter, u8 link_status) 72062306a36Sopenharmony_ci{ 72162306a36Sopenharmony_ci struct net_device *netdev = adapter->netdev; 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci if (!(adapter->flags & BE_FLAGS_LINK_STATUS_INIT)) { 72462306a36Sopenharmony_ci netif_carrier_off(netdev); 72562306a36Sopenharmony_ci adapter->flags |= BE_FLAGS_LINK_STATUS_INIT; 72662306a36Sopenharmony_ci } 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ci if (link_status) 72962306a36Sopenharmony_ci netif_carrier_on(netdev); 73062306a36Sopenharmony_ci else 73162306a36Sopenharmony_ci netif_carrier_off(netdev); 73262306a36Sopenharmony_ci 73362306a36Sopenharmony_ci netdev_info(netdev, "Link is %s\n", link_status ? "Up" : "Down"); 73462306a36Sopenharmony_ci} 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_cistatic int be_gso_hdr_len(struct sk_buff *skb) 73762306a36Sopenharmony_ci{ 73862306a36Sopenharmony_ci if (skb->encapsulation) 73962306a36Sopenharmony_ci return skb_inner_tcp_all_headers(skb); 74062306a36Sopenharmony_ci 74162306a36Sopenharmony_ci return skb_tcp_all_headers(skb); 74262306a36Sopenharmony_ci} 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_cistatic void be_tx_stats_update(struct be_tx_obj *txo, struct sk_buff *skb) 74562306a36Sopenharmony_ci{ 74662306a36Sopenharmony_ci struct be_tx_stats *stats = tx_stats(txo); 74762306a36Sopenharmony_ci u32 tx_pkts = skb_shinfo(skb)->gso_segs ? : 1; 74862306a36Sopenharmony_ci /* Account for headers which get duplicated in TSO pkt */ 74962306a36Sopenharmony_ci u32 dup_hdr_len = tx_pkts > 1 ? be_gso_hdr_len(skb) * (tx_pkts - 1) : 0; 75062306a36Sopenharmony_ci 75162306a36Sopenharmony_ci u64_stats_update_begin(&stats->sync); 75262306a36Sopenharmony_ci stats->tx_reqs++; 75362306a36Sopenharmony_ci stats->tx_bytes += skb->len + dup_hdr_len; 75462306a36Sopenharmony_ci stats->tx_pkts += tx_pkts; 75562306a36Sopenharmony_ci if (skb->encapsulation && skb->ip_summed == CHECKSUM_PARTIAL) 75662306a36Sopenharmony_ci stats->tx_vxlan_offload_pkts += tx_pkts; 75762306a36Sopenharmony_ci u64_stats_update_end(&stats->sync); 75862306a36Sopenharmony_ci} 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci/* Returns number of WRBs needed for the skb */ 76162306a36Sopenharmony_cistatic u32 skb_wrb_cnt(struct sk_buff *skb) 76262306a36Sopenharmony_ci{ 76362306a36Sopenharmony_ci /* +1 for the header wrb */ 76462306a36Sopenharmony_ci return 1 + (skb_headlen(skb) ? 1 : 0) + skb_shinfo(skb)->nr_frags; 76562306a36Sopenharmony_ci} 76662306a36Sopenharmony_ci 76762306a36Sopenharmony_cistatic inline void wrb_fill(struct be_eth_wrb *wrb, u64 addr, int len) 76862306a36Sopenharmony_ci{ 76962306a36Sopenharmony_ci wrb->frag_pa_hi = cpu_to_le32(upper_32_bits(addr)); 77062306a36Sopenharmony_ci wrb->frag_pa_lo = cpu_to_le32(lower_32_bits(addr)); 77162306a36Sopenharmony_ci wrb->frag_len = cpu_to_le32(len & ETH_WRB_FRAG_LEN_MASK); 77262306a36Sopenharmony_ci wrb->rsvd0 = 0; 77362306a36Sopenharmony_ci} 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci/* A dummy wrb is just all zeros. Using a separate routine for dummy-wrb 77662306a36Sopenharmony_ci * to avoid the swap and shift/mask operations in wrb_fill(). 77762306a36Sopenharmony_ci */ 77862306a36Sopenharmony_cistatic inline void wrb_fill_dummy(struct be_eth_wrb *wrb) 77962306a36Sopenharmony_ci{ 78062306a36Sopenharmony_ci wrb->frag_pa_hi = 0; 78162306a36Sopenharmony_ci wrb->frag_pa_lo = 0; 78262306a36Sopenharmony_ci wrb->frag_len = 0; 78362306a36Sopenharmony_ci wrb->rsvd0 = 0; 78462306a36Sopenharmony_ci} 78562306a36Sopenharmony_ci 78662306a36Sopenharmony_cistatic inline u16 be_get_tx_vlan_tag(struct be_adapter *adapter, 78762306a36Sopenharmony_ci struct sk_buff *skb) 78862306a36Sopenharmony_ci{ 78962306a36Sopenharmony_ci u8 vlan_prio; 79062306a36Sopenharmony_ci u16 vlan_tag; 79162306a36Sopenharmony_ci 79262306a36Sopenharmony_ci vlan_tag = skb_vlan_tag_get(skb); 79362306a36Sopenharmony_ci vlan_prio = skb_vlan_tag_get_prio(skb); 79462306a36Sopenharmony_ci /* If vlan priority provided by OS is NOT in available bmap */ 79562306a36Sopenharmony_ci if (!(adapter->vlan_prio_bmap & (1 << vlan_prio))) 79662306a36Sopenharmony_ci vlan_tag = (vlan_tag & ~VLAN_PRIO_MASK) | 79762306a36Sopenharmony_ci adapter->recommended_prio_bits; 79862306a36Sopenharmony_ci 79962306a36Sopenharmony_ci return vlan_tag; 80062306a36Sopenharmony_ci} 80162306a36Sopenharmony_ci 80262306a36Sopenharmony_ci/* Used only for IP tunnel packets */ 80362306a36Sopenharmony_cistatic u16 skb_inner_ip_proto(struct sk_buff *skb) 80462306a36Sopenharmony_ci{ 80562306a36Sopenharmony_ci return (inner_ip_hdr(skb)->version == 4) ? 80662306a36Sopenharmony_ci inner_ip_hdr(skb)->protocol : inner_ipv6_hdr(skb)->nexthdr; 80762306a36Sopenharmony_ci} 80862306a36Sopenharmony_ci 80962306a36Sopenharmony_cistatic u16 skb_ip_proto(struct sk_buff *skb) 81062306a36Sopenharmony_ci{ 81162306a36Sopenharmony_ci return (ip_hdr(skb)->version == 4) ? 81262306a36Sopenharmony_ci ip_hdr(skb)->protocol : ipv6_hdr(skb)->nexthdr; 81362306a36Sopenharmony_ci} 81462306a36Sopenharmony_ci 81562306a36Sopenharmony_cistatic inline bool be_is_txq_full(struct be_tx_obj *txo) 81662306a36Sopenharmony_ci{ 81762306a36Sopenharmony_ci return atomic_read(&txo->q.used) + BE_MAX_TX_FRAG_COUNT >= txo->q.len; 81862306a36Sopenharmony_ci} 81962306a36Sopenharmony_ci 82062306a36Sopenharmony_cistatic inline bool be_can_txq_wake(struct be_tx_obj *txo) 82162306a36Sopenharmony_ci{ 82262306a36Sopenharmony_ci return atomic_read(&txo->q.used) < txo->q.len / 2; 82362306a36Sopenharmony_ci} 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_cistatic inline bool be_is_tx_compl_pending(struct be_tx_obj *txo) 82662306a36Sopenharmony_ci{ 82762306a36Sopenharmony_ci return atomic_read(&txo->q.used) > txo->pend_wrb_cnt; 82862306a36Sopenharmony_ci} 82962306a36Sopenharmony_ci 83062306a36Sopenharmony_cistatic void be_get_wrb_params_from_skb(struct be_adapter *adapter, 83162306a36Sopenharmony_ci struct sk_buff *skb, 83262306a36Sopenharmony_ci struct be_wrb_params *wrb_params) 83362306a36Sopenharmony_ci{ 83462306a36Sopenharmony_ci u16 proto; 83562306a36Sopenharmony_ci 83662306a36Sopenharmony_ci if (skb_is_gso(skb)) { 83762306a36Sopenharmony_ci BE_WRB_F_SET(wrb_params->features, LSO, 1); 83862306a36Sopenharmony_ci wrb_params->lso_mss = skb_shinfo(skb)->gso_size; 83962306a36Sopenharmony_ci if (skb_is_gso_v6(skb) && !lancer_chip(adapter)) 84062306a36Sopenharmony_ci BE_WRB_F_SET(wrb_params->features, LSO6, 1); 84162306a36Sopenharmony_ci } else if (skb->ip_summed == CHECKSUM_PARTIAL) { 84262306a36Sopenharmony_ci if (skb->encapsulation) { 84362306a36Sopenharmony_ci BE_WRB_F_SET(wrb_params->features, IPCS, 1); 84462306a36Sopenharmony_ci proto = skb_inner_ip_proto(skb); 84562306a36Sopenharmony_ci } else { 84662306a36Sopenharmony_ci proto = skb_ip_proto(skb); 84762306a36Sopenharmony_ci } 84862306a36Sopenharmony_ci if (proto == IPPROTO_TCP) 84962306a36Sopenharmony_ci BE_WRB_F_SET(wrb_params->features, TCPCS, 1); 85062306a36Sopenharmony_ci else if (proto == IPPROTO_UDP) 85162306a36Sopenharmony_ci BE_WRB_F_SET(wrb_params->features, UDPCS, 1); 85262306a36Sopenharmony_ci } 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci if (skb_vlan_tag_present(skb)) { 85562306a36Sopenharmony_ci BE_WRB_F_SET(wrb_params->features, VLAN, 1); 85662306a36Sopenharmony_ci wrb_params->vlan_tag = be_get_tx_vlan_tag(adapter, skb); 85762306a36Sopenharmony_ci } 85862306a36Sopenharmony_ci 85962306a36Sopenharmony_ci BE_WRB_F_SET(wrb_params->features, CRC, 1); 86062306a36Sopenharmony_ci} 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_cistatic void wrb_fill_hdr(struct be_adapter *adapter, 86362306a36Sopenharmony_ci struct be_eth_hdr_wrb *hdr, 86462306a36Sopenharmony_ci struct be_wrb_params *wrb_params, 86562306a36Sopenharmony_ci struct sk_buff *skb) 86662306a36Sopenharmony_ci{ 86762306a36Sopenharmony_ci memset(hdr, 0, sizeof(*hdr)); 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci SET_TX_WRB_HDR_BITS(crc, hdr, 87062306a36Sopenharmony_ci BE_WRB_F_GET(wrb_params->features, CRC)); 87162306a36Sopenharmony_ci SET_TX_WRB_HDR_BITS(ipcs, hdr, 87262306a36Sopenharmony_ci BE_WRB_F_GET(wrb_params->features, IPCS)); 87362306a36Sopenharmony_ci SET_TX_WRB_HDR_BITS(tcpcs, hdr, 87462306a36Sopenharmony_ci BE_WRB_F_GET(wrb_params->features, TCPCS)); 87562306a36Sopenharmony_ci SET_TX_WRB_HDR_BITS(udpcs, hdr, 87662306a36Sopenharmony_ci BE_WRB_F_GET(wrb_params->features, UDPCS)); 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_ci SET_TX_WRB_HDR_BITS(lso, hdr, 87962306a36Sopenharmony_ci BE_WRB_F_GET(wrb_params->features, LSO)); 88062306a36Sopenharmony_ci SET_TX_WRB_HDR_BITS(lso6, hdr, 88162306a36Sopenharmony_ci BE_WRB_F_GET(wrb_params->features, LSO6)); 88262306a36Sopenharmony_ci SET_TX_WRB_HDR_BITS(lso_mss, hdr, wrb_params->lso_mss); 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_ci /* Hack to skip HW VLAN tagging needs evt = 1, compl = 0. When this 88562306a36Sopenharmony_ci * hack is not needed, the evt bit is set while ringing DB. 88662306a36Sopenharmony_ci */ 88762306a36Sopenharmony_ci SET_TX_WRB_HDR_BITS(event, hdr, 88862306a36Sopenharmony_ci BE_WRB_F_GET(wrb_params->features, VLAN_SKIP_HW)); 88962306a36Sopenharmony_ci SET_TX_WRB_HDR_BITS(vlan, hdr, 89062306a36Sopenharmony_ci BE_WRB_F_GET(wrb_params->features, VLAN)); 89162306a36Sopenharmony_ci SET_TX_WRB_HDR_BITS(vlan_tag, hdr, wrb_params->vlan_tag); 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci SET_TX_WRB_HDR_BITS(num_wrb, hdr, skb_wrb_cnt(skb)); 89462306a36Sopenharmony_ci SET_TX_WRB_HDR_BITS(len, hdr, skb->len); 89562306a36Sopenharmony_ci SET_TX_WRB_HDR_BITS(mgmt, hdr, 89662306a36Sopenharmony_ci BE_WRB_F_GET(wrb_params->features, OS2BMC)); 89762306a36Sopenharmony_ci} 89862306a36Sopenharmony_ci 89962306a36Sopenharmony_cistatic void unmap_tx_frag(struct device *dev, struct be_eth_wrb *wrb, 90062306a36Sopenharmony_ci bool unmap_single) 90162306a36Sopenharmony_ci{ 90262306a36Sopenharmony_ci dma_addr_t dma; 90362306a36Sopenharmony_ci u32 frag_len = le32_to_cpu(wrb->frag_len); 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_ci 90662306a36Sopenharmony_ci dma = (u64)le32_to_cpu(wrb->frag_pa_hi) << 32 | 90762306a36Sopenharmony_ci (u64)le32_to_cpu(wrb->frag_pa_lo); 90862306a36Sopenharmony_ci if (frag_len) { 90962306a36Sopenharmony_ci if (unmap_single) 91062306a36Sopenharmony_ci dma_unmap_single(dev, dma, frag_len, DMA_TO_DEVICE); 91162306a36Sopenharmony_ci else 91262306a36Sopenharmony_ci dma_unmap_page(dev, dma, frag_len, DMA_TO_DEVICE); 91362306a36Sopenharmony_ci } 91462306a36Sopenharmony_ci} 91562306a36Sopenharmony_ci 91662306a36Sopenharmony_ci/* Grab a WRB header for xmit */ 91762306a36Sopenharmony_cistatic u32 be_tx_get_wrb_hdr(struct be_tx_obj *txo) 91862306a36Sopenharmony_ci{ 91962306a36Sopenharmony_ci u32 head = txo->q.head; 92062306a36Sopenharmony_ci 92162306a36Sopenharmony_ci queue_head_inc(&txo->q); 92262306a36Sopenharmony_ci return head; 92362306a36Sopenharmony_ci} 92462306a36Sopenharmony_ci 92562306a36Sopenharmony_ci/* Set up the WRB header for xmit */ 92662306a36Sopenharmony_cistatic void be_tx_setup_wrb_hdr(struct be_adapter *adapter, 92762306a36Sopenharmony_ci struct be_tx_obj *txo, 92862306a36Sopenharmony_ci struct be_wrb_params *wrb_params, 92962306a36Sopenharmony_ci struct sk_buff *skb, u16 head) 93062306a36Sopenharmony_ci{ 93162306a36Sopenharmony_ci u32 num_frags = skb_wrb_cnt(skb); 93262306a36Sopenharmony_ci struct be_queue_info *txq = &txo->q; 93362306a36Sopenharmony_ci struct be_eth_hdr_wrb *hdr = queue_index_node(txq, head); 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_ci wrb_fill_hdr(adapter, hdr, wrb_params, skb); 93662306a36Sopenharmony_ci be_dws_cpu_to_le(hdr, sizeof(*hdr)); 93762306a36Sopenharmony_ci 93862306a36Sopenharmony_ci BUG_ON(txo->sent_skb_list[head]); 93962306a36Sopenharmony_ci txo->sent_skb_list[head] = skb; 94062306a36Sopenharmony_ci txo->last_req_hdr = head; 94162306a36Sopenharmony_ci atomic_add(num_frags, &txq->used); 94262306a36Sopenharmony_ci txo->last_req_wrb_cnt = num_frags; 94362306a36Sopenharmony_ci txo->pend_wrb_cnt += num_frags; 94462306a36Sopenharmony_ci} 94562306a36Sopenharmony_ci 94662306a36Sopenharmony_ci/* Setup a WRB fragment (buffer descriptor) for xmit */ 94762306a36Sopenharmony_cistatic void be_tx_setup_wrb_frag(struct be_tx_obj *txo, dma_addr_t busaddr, 94862306a36Sopenharmony_ci int len) 94962306a36Sopenharmony_ci{ 95062306a36Sopenharmony_ci struct be_eth_wrb *wrb; 95162306a36Sopenharmony_ci struct be_queue_info *txq = &txo->q; 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_ci wrb = queue_head_node(txq); 95462306a36Sopenharmony_ci wrb_fill(wrb, busaddr, len); 95562306a36Sopenharmony_ci queue_head_inc(txq); 95662306a36Sopenharmony_ci} 95762306a36Sopenharmony_ci 95862306a36Sopenharmony_ci/* Bring the queue back to the state it was in before be_xmit_enqueue() routine 95962306a36Sopenharmony_ci * was invoked. The producer index is restored to the previous packet and the 96062306a36Sopenharmony_ci * WRBs of the current packet are unmapped. Invoked to handle tx setup errors. 96162306a36Sopenharmony_ci */ 96262306a36Sopenharmony_cistatic void be_xmit_restore(struct be_adapter *adapter, 96362306a36Sopenharmony_ci struct be_tx_obj *txo, u32 head, bool map_single, 96462306a36Sopenharmony_ci u32 copied) 96562306a36Sopenharmony_ci{ 96662306a36Sopenharmony_ci struct device *dev; 96762306a36Sopenharmony_ci struct be_eth_wrb *wrb; 96862306a36Sopenharmony_ci struct be_queue_info *txq = &txo->q; 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ci dev = &adapter->pdev->dev; 97162306a36Sopenharmony_ci txq->head = head; 97262306a36Sopenharmony_ci 97362306a36Sopenharmony_ci /* skip the first wrb (hdr); it's not mapped */ 97462306a36Sopenharmony_ci queue_head_inc(txq); 97562306a36Sopenharmony_ci while (copied) { 97662306a36Sopenharmony_ci wrb = queue_head_node(txq); 97762306a36Sopenharmony_ci unmap_tx_frag(dev, wrb, map_single); 97862306a36Sopenharmony_ci map_single = false; 97962306a36Sopenharmony_ci copied -= le32_to_cpu(wrb->frag_len); 98062306a36Sopenharmony_ci queue_head_inc(txq); 98162306a36Sopenharmony_ci } 98262306a36Sopenharmony_ci 98362306a36Sopenharmony_ci txq->head = head; 98462306a36Sopenharmony_ci} 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ci/* Enqueue the given packet for transmit. This routine allocates WRBs for the 98762306a36Sopenharmony_ci * packet, dma maps the packet buffers and sets up the WRBs. Returns the number 98862306a36Sopenharmony_ci * of WRBs used up by the packet. 98962306a36Sopenharmony_ci */ 99062306a36Sopenharmony_cistatic u32 be_xmit_enqueue(struct be_adapter *adapter, struct be_tx_obj *txo, 99162306a36Sopenharmony_ci struct sk_buff *skb, 99262306a36Sopenharmony_ci struct be_wrb_params *wrb_params) 99362306a36Sopenharmony_ci{ 99462306a36Sopenharmony_ci u32 i, copied = 0, wrb_cnt = skb_wrb_cnt(skb); 99562306a36Sopenharmony_ci struct device *dev = &adapter->pdev->dev; 99662306a36Sopenharmony_ci bool map_single = false; 99762306a36Sopenharmony_ci u32 head; 99862306a36Sopenharmony_ci dma_addr_t busaddr; 99962306a36Sopenharmony_ci int len; 100062306a36Sopenharmony_ci 100162306a36Sopenharmony_ci head = be_tx_get_wrb_hdr(txo); 100262306a36Sopenharmony_ci 100362306a36Sopenharmony_ci if (skb->len > skb->data_len) { 100462306a36Sopenharmony_ci len = skb_headlen(skb); 100562306a36Sopenharmony_ci 100662306a36Sopenharmony_ci busaddr = dma_map_single(dev, skb->data, len, DMA_TO_DEVICE); 100762306a36Sopenharmony_ci if (dma_mapping_error(dev, busaddr)) 100862306a36Sopenharmony_ci goto dma_err; 100962306a36Sopenharmony_ci map_single = true; 101062306a36Sopenharmony_ci be_tx_setup_wrb_frag(txo, busaddr, len); 101162306a36Sopenharmony_ci copied += len; 101262306a36Sopenharmony_ci } 101362306a36Sopenharmony_ci 101462306a36Sopenharmony_ci for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { 101562306a36Sopenharmony_ci const skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; 101662306a36Sopenharmony_ci len = skb_frag_size(frag); 101762306a36Sopenharmony_ci 101862306a36Sopenharmony_ci busaddr = skb_frag_dma_map(dev, frag, 0, len, DMA_TO_DEVICE); 101962306a36Sopenharmony_ci if (dma_mapping_error(dev, busaddr)) 102062306a36Sopenharmony_ci goto dma_err; 102162306a36Sopenharmony_ci be_tx_setup_wrb_frag(txo, busaddr, len); 102262306a36Sopenharmony_ci copied += len; 102362306a36Sopenharmony_ci } 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_ci be_tx_setup_wrb_hdr(adapter, txo, wrb_params, skb, head); 102662306a36Sopenharmony_ci 102762306a36Sopenharmony_ci be_tx_stats_update(txo, skb); 102862306a36Sopenharmony_ci return wrb_cnt; 102962306a36Sopenharmony_ci 103062306a36Sopenharmony_cidma_err: 103162306a36Sopenharmony_ci adapter->drv_stats.dma_map_errors++; 103262306a36Sopenharmony_ci be_xmit_restore(adapter, txo, head, map_single, copied); 103362306a36Sopenharmony_ci return 0; 103462306a36Sopenharmony_ci} 103562306a36Sopenharmony_ci 103662306a36Sopenharmony_cistatic inline int qnq_async_evt_rcvd(struct be_adapter *adapter) 103762306a36Sopenharmony_ci{ 103862306a36Sopenharmony_ci return adapter->flags & BE_FLAGS_QNQ_ASYNC_EVT_RCVD; 103962306a36Sopenharmony_ci} 104062306a36Sopenharmony_ci 104162306a36Sopenharmony_cistatic struct sk_buff *be_insert_vlan_in_pkt(struct be_adapter *adapter, 104262306a36Sopenharmony_ci struct sk_buff *skb, 104362306a36Sopenharmony_ci struct be_wrb_params 104462306a36Sopenharmony_ci *wrb_params) 104562306a36Sopenharmony_ci{ 104662306a36Sopenharmony_ci bool insert_vlan = false; 104762306a36Sopenharmony_ci u16 vlan_tag = 0; 104862306a36Sopenharmony_ci 104962306a36Sopenharmony_ci skb = skb_share_check(skb, GFP_ATOMIC); 105062306a36Sopenharmony_ci if (unlikely(!skb)) 105162306a36Sopenharmony_ci return skb; 105262306a36Sopenharmony_ci 105362306a36Sopenharmony_ci if (skb_vlan_tag_present(skb)) { 105462306a36Sopenharmony_ci vlan_tag = be_get_tx_vlan_tag(adapter, skb); 105562306a36Sopenharmony_ci insert_vlan = true; 105662306a36Sopenharmony_ci } 105762306a36Sopenharmony_ci 105862306a36Sopenharmony_ci if (qnq_async_evt_rcvd(adapter) && adapter->pvid) { 105962306a36Sopenharmony_ci if (!insert_vlan) { 106062306a36Sopenharmony_ci vlan_tag = adapter->pvid; 106162306a36Sopenharmony_ci insert_vlan = true; 106262306a36Sopenharmony_ci } 106362306a36Sopenharmony_ci /* f/w workaround to set skip_hw_vlan = 1, informs the F/W to 106462306a36Sopenharmony_ci * skip VLAN insertion 106562306a36Sopenharmony_ci */ 106662306a36Sopenharmony_ci BE_WRB_F_SET(wrb_params->features, VLAN_SKIP_HW, 1); 106762306a36Sopenharmony_ci } 106862306a36Sopenharmony_ci 106962306a36Sopenharmony_ci if (insert_vlan) { 107062306a36Sopenharmony_ci skb = vlan_insert_tag_set_proto(skb, htons(ETH_P_8021Q), 107162306a36Sopenharmony_ci vlan_tag); 107262306a36Sopenharmony_ci if (unlikely(!skb)) 107362306a36Sopenharmony_ci return skb; 107462306a36Sopenharmony_ci __vlan_hwaccel_clear_tag(skb); 107562306a36Sopenharmony_ci } 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_ci /* Insert the outer VLAN, if any */ 107862306a36Sopenharmony_ci if (adapter->qnq_vid) { 107962306a36Sopenharmony_ci vlan_tag = adapter->qnq_vid; 108062306a36Sopenharmony_ci skb = vlan_insert_tag_set_proto(skb, htons(ETH_P_8021Q), 108162306a36Sopenharmony_ci vlan_tag); 108262306a36Sopenharmony_ci if (unlikely(!skb)) 108362306a36Sopenharmony_ci return skb; 108462306a36Sopenharmony_ci BE_WRB_F_SET(wrb_params->features, VLAN_SKIP_HW, 1); 108562306a36Sopenharmony_ci } 108662306a36Sopenharmony_ci 108762306a36Sopenharmony_ci return skb; 108862306a36Sopenharmony_ci} 108962306a36Sopenharmony_ci 109062306a36Sopenharmony_cistatic bool be_ipv6_exthdr_check(struct sk_buff *skb) 109162306a36Sopenharmony_ci{ 109262306a36Sopenharmony_ci struct ethhdr *eh = (struct ethhdr *)skb->data; 109362306a36Sopenharmony_ci u16 offset = ETH_HLEN; 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ci if (eh->h_proto == htons(ETH_P_IPV6)) { 109662306a36Sopenharmony_ci struct ipv6hdr *ip6h = (struct ipv6hdr *)(skb->data + offset); 109762306a36Sopenharmony_ci 109862306a36Sopenharmony_ci offset += sizeof(struct ipv6hdr); 109962306a36Sopenharmony_ci if (ip6h->nexthdr != NEXTHDR_TCP && 110062306a36Sopenharmony_ci ip6h->nexthdr != NEXTHDR_UDP) { 110162306a36Sopenharmony_ci struct ipv6_opt_hdr *ehdr = 110262306a36Sopenharmony_ci (struct ipv6_opt_hdr *)(skb->data + offset); 110362306a36Sopenharmony_ci 110462306a36Sopenharmony_ci /* offending pkt: 2nd byte following IPv6 hdr is 0xff */ 110562306a36Sopenharmony_ci if (ehdr->hdrlen == 0xff) 110662306a36Sopenharmony_ci return true; 110762306a36Sopenharmony_ci } 110862306a36Sopenharmony_ci } 110962306a36Sopenharmony_ci return false; 111062306a36Sopenharmony_ci} 111162306a36Sopenharmony_ci 111262306a36Sopenharmony_cistatic int be_vlan_tag_tx_chk(struct be_adapter *adapter, struct sk_buff *skb) 111362306a36Sopenharmony_ci{ 111462306a36Sopenharmony_ci return skb_vlan_tag_present(skb) || adapter->pvid || adapter->qnq_vid; 111562306a36Sopenharmony_ci} 111662306a36Sopenharmony_ci 111762306a36Sopenharmony_cistatic int be_ipv6_tx_stall_chk(struct be_adapter *adapter, struct sk_buff *skb) 111862306a36Sopenharmony_ci{ 111962306a36Sopenharmony_ci return BE3_chip(adapter) && be_ipv6_exthdr_check(skb); 112062306a36Sopenharmony_ci} 112162306a36Sopenharmony_ci 112262306a36Sopenharmony_cistatic struct sk_buff *be_lancer_xmit_workarounds(struct be_adapter *adapter, 112362306a36Sopenharmony_ci struct sk_buff *skb, 112462306a36Sopenharmony_ci struct be_wrb_params 112562306a36Sopenharmony_ci *wrb_params) 112662306a36Sopenharmony_ci{ 112762306a36Sopenharmony_ci struct vlan_ethhdr *veh = skb_vlan_eth_hdr(skb); 112862306a36Sopenharmony_ci unsigned int eth_hdr_len; 112962306a36Sopenharmony_ci struct iphdr *ip; 113062306a36Sopenharmony_ci 113162306a36Sopenharmony_ci /* For padded packets, BE HW modifies tot_len field in IP header 113262306a36Sopenharmony_ci * incorrecly when VLAN tag is inserted by HW. 113362306a36Sopenharmony_ci * For padded packets, Lancer computes incorrect checksum. 113462306a36Sopenharmony_ci */ 113562306a36Sopenharmony_ci eth_hdr_len = ntohs(skb->protocol) == ETH_P_8021Q ? 113662306a36Sopenharmony_ci VLAN_ETH_HLEN : ETH_HLEN; 113762306a36Sopenharmony_ci if (skb->len <= 60 && 113862306a36Sopenharmony_ci (lancer_chip(adapter) || BE3_chip(adapter) || 113962306a36Sopenharmony_ci skb_vlan_tag_present(skb)) && is_ipv4_pkt(skb)) { 114062306a36Sopenharmony_ci ip = (struct iphdr *)ip_hdr(skb); 114162306a36Sopenharmony_ci if (unlikely(pskb_trim(skb, eth_hdr_len + ntohs(ip->tot_len)))) 114262306a36Sopenharmony_ci goto tx_drop; 114362306a36Sopenharmony_ci } 114462306a36Sopenharmony_ci 114562306a36Sopenharmony_ci /* If vlan tag is already inlined in the packet, skip HW VLAN 114662306a36Sopenharmony_ci * tagging in pvid-tagging mode 114762306a36Sopenharmony_ci */ 114862306a36Sopenharmony_ci if (be_pvid_tagging_enabled(adapter) && 114962306a36Sopenharmony_ci veh->h_vlan_proto == htons(ETH_P_8021Q)) 115062306a36Sopenharmony_ci BE_WRB_F_SET(wrb_params->features, VLAN_SKIP_HW, 1); 115162306a36Sopenharmony_ci 115262306a36Sopenharmony_ci /* HW has a bug wherein it will calculate CSUM for VLAN 115362306a36Sopenharmony_ci * pkts even though it is disabled. 115462306a36Sopenharmony_ci * Manually insert VLAN in pkt. 115562306a36Sopenharmony_ci */ 115662306a36Sopenharmony_ci if (skb->ip_summed != CHECKSUM_PARTIAL && 115762306a36Sopenharmony_ci skb_vlan_tag_present(skb)) { 115862306a36Sopenharmony_ci skb = be_insert_vlan_in_pkt(adapter, skb, wrb_params); 115962306a36Sopenharmony_ci if (unlikely(!skb)) 116062306a36Sopenharmony_ci goto err; 116162306a36Sopenharmony_ci } 116262306a36Sopenharmony_ci 116362306a36Sopenharmony_ci /* HW may lockup when VLAN HW tagging is requested on 116462306a36Sopenharmony_ci * certain ipv6 packets. Drop such pkts if the HW workaround to 116562306a36Sopenharmony_ci * skip HW tagging is not enabled by FW. 116662306a36Sopenharmony_ci */ 116762306a36Sopenharmony_ci if (unlikely(be_ipv6_tx_stall_chk(adapter, skb) && 116862306a36Sopenharmony_ci (adapter->pvid || adapter->qnq_vid) && 116962306a36Sopenharmony_ci !qnq_async_evt_rcvd(adapter))) 117062306a36Sopenharmony_ci goto tx_drop; 117162306a36Sopenharmony_ci 117262306a36Sopenharmony_ci /* Manual VLAN tag insertion to prevent: 117362306a36Sopenharmony_ci * ASIC lockup when the ASIC inserts VLAN tag into 117462306a36Sopenharmony_ci * certain ipv6 packets. Insert VLAN tags in driver, 117562306a36Sopenharmony_ci * and set event, completion, vlan bits accordingly 117662306a36Sopenharmony_ci * in the Tx WRB. 117762306a36Sopenharmony_ci */ 117862306a36Sopenharmony_ci if (be_ipv6_tx_stall_chk(adapter, skb) && 117962306a36Sopenharmony_ci be_vlan_tag_tx_chk(adapter, skb)) { 118062306a36Sopenharmony_ci skb = be_insert_vlan_in_pkt(adapter, skb, wrb_params); 118162306a36Sopenharmony_ci if (unlikely(!skb)) 118262306a36Sopenharmony_ci goto err; 118362306a36Sopenharmony_ci } 118462306a36Sopenharmony_ci 118562306a36Sopenharmony_ci return skb; 118662306a36Sopenharmony_citx_drop: 118762306a36Sopenharmony_ci dev_kfree_skb_any(skb); 118862306a36Sopenharmony_cierr: 118962306a36Sopenharmony_ci return NULL; 119062306a36Sopenharmony_ci} 119162306a36Sopenharmony_ci 119262306a36Sopenharmony_cistatic struct sk_buff *be_xmit_workarounds(struct be_adapter *adapter, 119362306a36Sopenharmony_ci struct sk_buff *skb, 119462306a36Sopenharmony_ci struct be_wrb_params *wrb_params) 119562306a36Sopenharmony_ci{ 119662306a36Sopenharmony_ci int err; 119762306a36Sopenharmony_ci 119862306a36Sopenharmony_ci /* Lancer, SH and BE3 in SRIOV mode have a bug wherein 119962306a36Sopenharmony_ci * packets that are 32b or less may cause a transmit stall 120062306a36Sopenharmony_ci * on that port. The workaround is to pad such packets 120162306a36Sopenharmony_ci * (len <= 32 bytes) to a minimum length of 36b. 120262306a36Sopenharmony_ci */ 120362306a36Sopenharmony_ci if (skb->len <= 32) { 120462306a36Sopenharmony_ci if (skb_put_padto(skb, 36)) 120562306a36Sopenharmony_ci return NULL; 120662306a36Sopenharmony_ci } 120762306a36Sopenharmony_ci 120862306a36Sopenharmony_ci if (BEx_chip(adapter) || lancer_chip(adapter)) { 120962306a36Sopenharmony_ci skb = be_lancer_xmit_workarounds(adapter, skb, wrb_params); 121062306a36Sopenharmony_ci if (!skb) 121162306a36Sopenharmony_ci return NULL; 121262306a36Sopenharmony_ci } 121362306a36Sopenharmony_ci 121462306a36Sopenharmony_ci /* The stack can send us skbs with length greater than 121562306a36Sopenharmony_ci * what the HW can handle. Trim the extra bytes. 121662306a36Sopenharmony_ci */ 121762306a36Sopenharmony_ci WARN_ON_ONCE(skb->len > BE_MAX_GSO_SIZE); 121862306a36Sopenharmony_ci err = pskb_trim(skb, BE_MAX_GSO_SIZE); 121962306a36Sopenharmony_ci WARN_ON(err); 122062306a36Sopenharmony_ci 122162306a36Sopenharmony_ci return skb; 122262306a36Sopenharmony_ci} 122362306a36Sopenharmony_ci 122462306a36Sopenharmony_cistatic void be_xmit_flush(struct be_adapter *adapter, struct be_tx_obj *txo) 122562306a36Sopenharmony_ci{ 122662306a36Sopenharmony_ci struct be_queue_info *txq = &txo->q; 122762306a36Sopenharmony_ci struct be_eth_hdr_wrb *hdr = queue_index_node(txq, txo->last_req_hdr); 122862306a36Sopenharmony_ci 122962306a36Sopenharmony_ci /* Mark the last request eventable if it hasn't been marked already */ 123062306a36Sopenharmony_ci if (!(hdr->dw[2] & cpu_to_le32(TX_HDR_WRB_EVT))) 123162306a36Sopenharmony_ci hdr->dw[2] |= cpu_to_le32(TX_HDR_WRB_EVT | TX_HDR_WRB_COMPL); 123262306a36Sopenharmony_ci 123362306a36Sopenharmony_ci /* compose a dummy wrb if there are odd set of wrbs to notify */ 123462306a36Sopenharmony_ci if (!lancer_chip(adapter) && (txo->pend_wrb_cnt & 1)) { 123562306a36Sopenharmony_ci wrb_fill_dummy(queue_head_node(txq)); 123662306a36Sopenharmony_ci queue_head_inc(txq); 123762306a36Sopenharmony_ci atomic_inc(&txq->used); 123862306a36Sopenharmony_ci txo->pend_wrb_cnt++; 123962306a36Sopenharmony_ci hdr->dw[2] &= ~cpu_to_le32(TX_HDR_WRB_NUM_MASK << 124062306a36Sopenharmony_ci TX_HDR_WRB_NUM_SHIFT); 124162306a36Sopenharmony_ci hdr->dw[2] |= cpu_to_le32((txo->last_req_wrb_cnt + 1) << 124262306a36Sopenharmony_ci TX_HDR_WRB_NUM_SHIFT); 124362306a36Sopenharmony_ci } 124462306a36Sopenharmony_ci be_txq_notify(adapter, txo, txo->pend_wrb_cnt); 124562306a36Sopenharmony_ci txo->pend_wrb_cnt = 0; 124662306a36Sopenharmony_ci} 124762306a36Sopenharmony_ci 124862306a36Sopenharmony_ci/* OS2BMC related */ 124962306a36Sopenharmony_ci 125062306a36Sopenharmony_ci#define DHCP_CLIENT_PORT 68 125162306a36Sopenharmony_ci#define DHCP_SERVER_PORT 67 125262306a36Sopenharmony_ci#define NET_BIOS_PORT1 137 125362306a36Sopenharmony_ci#define NET_BIOS_PORT2 138 125462306a36Sopenharmony_ci#define DHCPV6_RAS_PORT 547 125562306a36Sopenharmony_ci 125662306a36Sopenharmony_ci#define is_mc_allowed_on_bmc(adapter, eh) \ 125762306a36Sopenharmony_ci (!is_multicast_filt_enabled(adapter) && \ 125862306a36Sopenharmony_ci is_multicast_ether_addr(eh->h_dest) && \ 125962306a36Sopenharmony_ci !is_broadcast_ether_addr(eh->h_dest)) 126062306a36Sopenharmony_ci 126162306a36Sopenharmony_ci#define is_bc_allowed_on_bmc(adapter, eh) \ 126262306a36Sopenharmony_ci (!is_broadcast_filt_enabled(adapter) && \ 126362306a36Sopenharmony_ci is_broadcast_ether_addr(eh->h_dest)) 126462306a36Sopenharmony_ci 126562306a36Sopenharmony_ci#define is_arp_allowed_on_bmc(adapter, skb) \ 126662306a36Sopenharmony_ci (is_arp(skb) && is_arp_filt_enabled(adapter)) 126762306a36Sopenharmony_ci 126862306a36Sopenharmony_ci#define is_arp(skb) (skb->protocol == htons(ETH_P_ARP)) 126962306a36Sopenharmony_ci 127062306a36Sopenharmony_ci#define is_arp_filt_enabled(adapter) \ 127162306a36Sopenharmony_ci (adapter->bmc_filt_mask & (BMC_FILT_BROADCAST_ARP)) 127262306a36Sopenharmony_ci 127362306a36Sopenharmony_ci#define is_dhcp_client_filt_enabled(adapter) \ 127462306a36Sopenharmony_ci (adapter->bmc_filt_mask & BMC_FILT_BROADCAST_DHCP_CLIENT) 127562306a36Sopenharmony_ci 127662306a36Sopenharmony_ci#define is_dhcp_srvr_filt_enabled(adapter) \ 127762306a36Sopenharmony_ci (adapter->bmc_filt_mask & BMC_FILT_BROADCAST_DHCP_SERVER) 127862306a36Sopenharmony_ci 127962306a36Sopenharmony_ci#define is_nbios_filt_enabled(adapter) \ 128062306a36Sopenharmony_ci (adapter->bmc_filt_mask & BMC_FILT_BROADCAST_NET_BIOS) 128162306a36Sopenharmony_ci 128262306a36Sopenharmony_ci#define is_ipv6_na_filt_enabled(adapter) \ 128362306a36Sopenharmony_ci (adapter->bmc_filt_mask & \ 128462306a36Sopenharmony_ci BMC_FILT_MULTICAST_IPV6_NEIGH_ADVER) 128562306a36Sopenharmony_ci 128662306a36Sopenharmony_ci#define is_ipv6_ra_filt_enabled(adapter) \ 128762306a36Sopenharmony_ci (adapter->bmc_filt_mask & BMC_FILT_MULTICAST_IPV6_RA) 128862306a36Sopenharmony_ci 128962306a36Sopenharmony_ci#define is_ipv6_ras_filt_enabled(adapter) \ 129062306a36Sopenharmony_ci (adapter->bmc_filt_mask & BMC_FILT_MULTICAST_IPV6_RAS) 129162306a36Sopenharmony_ci 129262306a36Sopenharmony_ci#define is_broadcast_filt_enabled(adapter) \ 129362306a36Sopenharmony_ci (adapter->bmc_filt_mask & BMC_FILT_BROADCAST) 129462306a36Sopenharmony_ci 129562306a36Sopenharmony_ci#define is_multicast_filt_enabled(adapter) \ 129662306a36Sopenharmony_ci (adapter->bmc_filt_mask & BMC_FILT_MULTICAST) 129762306a36Sopenharmony_ci 129862306a36Sopenharmony_cistatic bool be_send_pkt_to_bmc(struct be_adapter *adapter, 129962306a36Sopenharmony_ci struct sk_buff **skb) 130062306a36Sopenharmony_ci{ 130162306a36Sopenharmony_ci struct ethhdr *eh = (struct ethhdr *)(*skb)->data; 130262306a36Sopenharmony_ci bool os2bmc = false; 130362306a36Sopenharmony_ci 130462306a36Sopenharmony_ci if (!be_is_os2bmc_enabled(adapter)) 130562306a36Sopenharmony_ci goto done; 130662306a36Sopenharmony_ci 130762306a36Sopenharmony_ci if (!is_multicast_ether_addr(eh->h_dest)) 130862306a36Sopenharmony_ci goto done; 130962306a36Sopenharmony_ci 131062306a36Sopenharmony_ci if (is_mc_allowed_on_bmc(adapter, eh) || 131162306a36Sopenharmony_ci is_bc_allowed_on_bmc(adapter, eh) || 131262306a36Sopenharmony_ci is_arp_allowed_on_bmc(adapter, (*skb))) { 131362306a36Sopenharmony_ci os2bmc = true; 131462306a36Sopenharmony_ci goto done; 131562306a36Sopenharmony_ci } 131662306a36Sopenharmony_ci 131762306a36Sopenharmony_ci if ((*skb)->protocol == htons(ETH_P_IPV6)) { 131862306a36Sopenharmony_ci struct ipv6hdr *hdr = ipv6_hdr((*skb)); 131962306a36Sopenharmony_ci u8 nexthdr = hdr->nexthdr; 132062306a36Sopenharmony_ci 132162306a36Sopenharmony_ci if (nexthdr == IPPROTO_ICMPV6) { 132262306a36Sopenharmony_ci struct icmp6hdr *icmp6 = icmp6_hdr((*skb)); 132362306a36Sopenharmony_ci 132462306a36Sopenharmony_ci switch (icmp6->icmp6_type) { 132562306a36Sopenharmony_ci case NDISC_ROUTER_ADVERTISEMENT: 132662306a36Sopenharmony_ci os2bmc = is_ipv6_ra_filt_enabled(adapter); 132762306a36Sopenharmony_ci goto done; 132862306a36Sopenharmony_ci case NDISC_NEIGHBOUR_ADVERTISEMENT: 132962306a36Sopenharmony_ci os2bmc = is_ipv6_na_filt_enabled(adapter); 133062306a36Sopenharmony_ci goto done; 133162306a36Sopenharmony_ci default: 133262306a36Sopenharmony_ci break; 133362306a36Sopenharmony_ci } 133462306a36Sopenharmony_ci } 133562306a36Sopenharmony_ci } 133662306a36Sopenharmony_ci 133762306a36Sopenharmony_ci if (is_udp_pkt((*skb))) { 133862306a36Sopenharmony_ci struct udphdr *udp = udp_hdr((*skb)); 133962306a36Sopenharmony_ci 134062306a36Sopenharmony_ci switch (ntohs(udp->dest)) { 134162306a36Sopenharmony_ci case DHCP_CLIENT_PORT: 134262306a36Sopenharmony_ci os2bmc = is_dhcp_client_filt_enabled(adapter); 134362306a36Sopenharmony_ci goto done; 134462306a36Sopenharmony_ci case DHCP_SERVER_PORT: 134562306a36Sopenharmony_ci os2bmc = is_dhcp_srvr_filt_enabled(adapter); 134662306a36Sopenharmony_ci goto done; 134762306a36Sopenharmony_ci case NET_BIOS_PORT1: 134862306a36Sopenharmony_ci case NET_BIOS_PORT2: 134962306a36Sopenharmony_ci os2bmc = is_nbios_filt_enabled(adapter); 135062306a36Sopenharmony_ci goto done; 135162306a36Sopenharmony_ci case DHCPV6_RAS_PORT: 135262306a36Sopenharmony_ci os2bmc = is_ipv6_ras_filt_enabled(adapter); 135362306a36Sopenharmony_ci goto done; 135462306a36Sopenharmony_ci default: 135562306a36Sopenharmony_ci break; 135662306a36Sopenharmony_ci } 135762306a36Sopenharmony_ci } 135862306a36Sopenharmony_cidone: 135962306a36Sopenharmony_ci /* For packets over a vlan, which are destined 136062306a36Sopenharmony_ci * to BMC, asic expects the vlan to be inline in the packet. 136162306a36Sopenharmony_ci */ 136262306a36Sopenharmony_ci if (os2bmc) 136362306a36Sopenharmony_ci *skb = be_insert_vlan_in_pkt(adapter, *skb, NULL); 136462306a36Sopenharmony_ci 136562306a36Sopenharmony_ci return os2bmc; 136662306a36Sopenharmony_ci} 136762306a36Sopenharmony_ci 136862306a36Sopenharmony_cistatic netdev_tx_t be_xmit(struct sk_buff *skb, struct net_device *netdev) 136962306a36Sopenharmony_ci{ 137062306a36Sopenharmony_ci struct be_adapter *adapter = netdev_priv(netdev); 137162306a36Sopenharmony_ci u16 q_idx = skb_get_queue_mapping(skb); 137262306a36Sopenharmony_ci struct be_tx_obj *txo = &adapter->tx_obj[q_idx]; 137362306a36Sopenharmony_ci struct be_wrb_params wrb_params = { 0 }; 137462306a36Sopenharmony_ci bool flush = !netdev_xmit_more(); 137562306a36Sopenharmony_ci u16 wrb_cnt; 137662306a36Sopenharmony_ci 137762306a36Sopenharmony_ci skb = be_xmit_workarounds(adapter, skb, &wrb_params); 137862306a36Sopenharmony_ci if (unlikely(!skb)) 137962306a36Sopenharmony_ci goto drop; 138062306a36Sopenharmony_ci 138162306a36Sopenharmony_ci be_get_wrb_params_from_skb(adapter, skb, &wrb_params); 138262306a36Sopenharmony_ci 138362306a36Sopenharmony_ci wrb_cnt = be_xmit_enqueue(adapter, txo, skb, &wrb_params); 138462306a36Sopenharmony_ci if (unlikely(!wrb_cnt)) { 138562306a36Sopenharmony_ci dev_kfree_skb_any(skb); 138662306a36Sopenharmony_ci goto drop; 138762306a36Sopenharmony_ci } 138862306a36Sopenharmony_ci 138962306a36Sopenharmony_ci /* if os2bmc is enabled and if the pkt is destined to bmc, 139062306a36Sopenharmony_ci * enqueue the pkt a 2nd time with mgmt bit set. 139162306a36Sopenharmony_ci */ 139262306a36Sopenharmony_ci if (be_send_pkt_to_bmc(adapter, &skb)) { 139362306a36Sopenharmony_ci BE_WRB_F_SET(wrb_params.features, OS2BMC, 1); 139462306a36Sopenharmony_ci wrb_cnt = be_xmit_enqueue(adapter, txo, skb, &wrb_params); 139562306a36Sopenharmony_ci if (unlikely(!wrb_cnt)) 139662306a36Sopenharmony_ci goto drop; 139762306a36Sopenharmony_ci else 139862306a36Sopenharmony_ci skb_get(skb); 139962306a36Sopenharmony_ci } 140062306a36Sopenharmony_ci 140162306a36Sopenharmony_ci if (be_is_txq_full(txo)) { 140262306a36Sopenharmony_ci netif_stop_subqueue(netdev, q_idx); 140362306a36Sopenharmony_ci tx_stats(txo)->tx_stops++; 140462306a36Sopenharmony_ci } 140562306a36Sopenharmony_ci 140662306a36Sopenharmony_ci if (flush || __netif_subqueue_stopped(netdev, q_idx)) 140762306a36Sopenharmony_ci be_xmit_flush(adapter, txo); 140862306a36Sopenharmony_ci 140962306a36Sopenharmony_ci return NETDEV_TX_OK; 141062306a36Sopenharmony_cidrop: 141162306a36Sopenharmony_ci tx_stats(txo)->tx_drv_drops++; 141262306a36Sopenharmony_ci /* Flush the already enqueued tx requests */ 141362306a36Sopenharmony_ci if (flush && txo->pend_wrb_cnt) 141462306a36Sopenharmony_ci be_xmit_flush(adapter, txo); 141562306a36Sopenharmony_ci 141662306a36Sopenharmony_ci return NETDEV_TX_OK; 141762306a36Sopenharmony_ci} 141862306a36Sopenharmony_ci 141962306a36Sopenharmony_cistatic void be_tx_timeout(struct net_device *netdev, unsigned int txqueue) 142062306a36Sopenharmony_ci{ 142162306a36Sopenharmony_ci struct be_adapter *adapter = netdev_priv(netdev); 142262306a36Sopenharmony_ci struct device *dev = &adapter->pdev->dev; 142362306a36Sopenharmony_ci struct be_tx_obj *txo; 142462306a36Sopenharmony_ci struct sk_buff *skb; 142562306a36Sopenharmony_ci struct tcphdr *tcphdr; 142662306a36Sopenharmony_ci struct udphdr *udphdr; 142762306a36Sopenharmony_ci u32 *entry; 142862306a36Sopenharmony_ci int status; 142962306a36Sopenharmony_ci int i, j; 143062306a36Sopenharmony_ci 143162306a36Sopenharmony_ci for_all_tx_queues(adapter, txo, i) { 143262306a36Sopenharmony_ci dev_info(dev, "TXQ Dump: %d H: %d T: %d used: %d, qid: 0x%x\n", 143362306a36Sopenharmony_ci i, txo->q.head, txo->q.tail, 143462306a36Sopenharmony_ci atomic_read(&txo->q.used), txo->q.id); 143562306a36Sopenharmony_ci 143662306a36Sopenharmony_ci entry = txo->q.dma_mem.va; 143762306a36Sopenharmony_ci for (j = 0; j < TX_Q_LEN * 4; j += 4) { 143862306a36Sopenharmony_ci if (entry[j] != 0 || entry[j + 1] != 0 || 143962306a36Sopenharmony_ci entry[j + 2] != 0 || entry[j + 3] != 0) { 144062306a36Sopenharmony_ci dev_info(dev, "Entry %d 0x%x 0x%x 0x%x 0x%x\n", 144162306a36Sopenharmony_ci j, entry[j], entry[j + 1], 144262306a36Sopenharmony_ci entry[j + 2], entry[j + 3]); 144362306a36Sopenharmony_ci } 144462306a36Sopenharmony_ci } 144562306a36Sopenharmony_ci 144662306a36Sopenharmony_ci entry = txo->cq.dma_mem.va; 144762306a36Sopenharmony_ci dev_info(dev, "TXCQ Dump: %d H: %d T: %d used: %d\n", 144862306a36Sopenharmony_ci i, txo->cq.head, txo->cq.tail, 144962306a36Sopenharmony_ci atomic_read(&txo->cq.used)); 145062306a36Sopenharmony_ci for (j = 0; j < TX_CQ_LEN * 4; j += 4) { 145162306a36Sopenharmony_ci if (entry[j] != 0 || entry[j + 1] != 0 || 145262306a36Sopenharmony_ci entry[j + 2] != 0 || entry[j + 3] != 0) { 145362306a36Sopenharmony_ci dev_info(dev, "Entry %d 0x%x 0x%x 0x%x 0x%x\n", 145462306a36Sopenharmony_ci j, entry[j], entry[j + 1], 145562306a36Sopenharmony_ci entry[j + 2], entry[j + 3]); 145662306a36Sopenharmony_ci } 145762306a36Sopenharmony_ci } 145862306a36Sopenharmony_ci 145962306a36Sopenharmony_ci for (j = 0; j < TX_Q_LEN; j++) { 146062306a36Sopenharmony_ci if (txo->sent_skb_list[j]) { 146162306a36Sopenharmony_ci skb = txo->sent_skb_list[j]; 146262306a36Sopenharmony_ci if (ip_hdr(skb)->protocol == IPPROTO_TCP) { 146362306a36Sopenharmony_ci tcphdr = tcp_hdr(skb); 146462306a36Sopenharmony_ci dev_info(dev, "TCP source port %d\n", 146562306a36Sopenharmony_ci ntohs(tcphdr->source)); 146662306a36Sopenharmony_ci dev_info(dev, "TCP dest port %d\n", 146762306a36Sopenharmony_ci ntohs(tcphdr->dest)); 146862306a36Sopenharmony_ci dev_info(dev, "TCP sequence num %d\n", 146962306a36Sopenharmony_ci ntohs(tcphdr->seq)); 147062306a36Sopenharmony_ci dev_info(dev, "TCP ack_seq %d\n", 147162306a36Sopenharmony_ci ntohs(tcphdr->ack_seq)); 147262306a36Sopenharmony_ci } else if (ip_hdr(skb)->protocol == 147362306a36Sopenharmony_ci IPPROTO_UDP) { 147462306a36Sopenharmony_ci udphdr = udp_hdr(skb); 147562306a36Sopenharmony_ci dev_info(dev, "UDP source port %d\n", 147662306a36Sopenharmony_ci ntohs(udphdr->source)); 147762306a36Sopenharmony_ci dev_info(dev, "UDP dest port %d\n", 147862306a36Sopenharmony_ci ntohs(udphdr->dest)); 147962306a36Sopenharmony_ci } 148062306a36Sopenharmony_ci dev_info(dev, "skb[%d] %p len %d proto 0x%x\n", 148162306a36Sopenharmony_ci j, skb, skb->len, skb->protocol); 148262306a36Sopenharmony_ci } 148362306a36Sopenharmony_ci } 148462306a36Sopenharmony_ci } 148562306a36Sopenharmony_ci 148662306a36Sopenharmony_ci if (lancer_chip(adapter)) { 148762306a36Sopenharmony_ci dev_info(dev, "Initiating reset due to tx timeout\n"); 148862306a36Sopenharmony_ci dev_info(dev, "Resetting adapter\n"); 148962306a36Sopenharmony_ci status = lancer_physdev_ctrl(adapter, 149062306a36Sopenharmony_ci PHYSDEV_CONTROL_FW_RESET_MASK); 149162306a36Sopenharmony_ci if (status) 149262306a36Sopenharmony_ci dev_err(dev, "Reset failed .. Reboot server\n"); 149362306a36Sopenharmony_ci } 149462306a36Sopenharmony_ci} 149562306a36Sopenharmony_ci 149662306a36Sopenharmony_cistatic inline bool be_in_all_promisc(struct be_adapter *adapter) 149762306a36Sopenharmony_ci{ 149862306a36Sopenharmony_ci return (adapter->if_flags & BE_IF_FLAGS_ALL_PROMISCUOUS) == 149962306a36Sopenharmony_ci BE_IF_FLAGS_ALL_PROMISCUOUS; 150062306a36Sopenharmony_ci} 150162306a36Sopenharmony_ci 150262306a36Sopenharmony_cistatic int be_set_vlan_promisc(struct be_adapter *adapter) 150362306a36Sopenharmony_ci{ 150462306a36Sopenharmony_ci struct device *dev = &adapter->pdev->dev; 150562306a36Sopenharmony_ci int status; 150662306a36Sopenharmony_ci 150762306a36Sopenharmony_ci if (adapter->if_flags & BE_IF_FLAGS_VLAN_PROMISCUOUS) 150862306a36Sopenharmony_ci return 0; 150962306a36Sopenharmony_ci 151062306a36Sopenharmony_ci status = be_cmd_rx_filter(adapter, BE_IF_FLAGS_VLAN_PROMISCUOUS, ON); 151162306a36Sopenharmony_ci if (!status) { 151262306a36Sopenharmony_ci dev_info(dev, "Enabled VLAN promiscuous mode\n"); 151362306a36Sopenharmony_ci adapter->if_flags |= BE_IF_FLAGS_VLAN_PROMISCUOUS; 151462306a36Sopenharmony_ci } else { 151562306a36Sopenharmony_ci dev_err(dev, "Failed to enable VLAN promiscuous mode\n"); 151662306a36Sopenharmony_ci } 151762306a36Sopenharmony_ci return status; 151862306a36Sopenharmony_ci} 151962306a36Sopenharmony_ci 152062306a36Sopenharmony_cistatic int be_clear_vlan_promisc(struct be_adapter *adapter) 152162306a36Sopenharmony_ci{ 152262306a36Sopenharmony_ci struct device *dev = &adapter->pdev->dev; 152362306a36Sopenharmony_ci int status; 152462306a36Sopenharmony_ci 152562306a36Sopenharmony_ci status = be_cmd_rx_filter(adapter, BE_IF_FLAGS_VLAN_PROMISCUOUS, OFF); 152662306a36Sopenharmony_ci if (!status) { 152762306a36Sopenharmony_ci dev_info(dev, "Disabling VLAN promiscuous mode\n"); 152862306a36Sopenharmony_ci adapter->if_flags &= ~BE_IF_FLAGS_VLAN_PROMISCUOUS; 152962306a36Sopenharmony_ci } 153062306a36Sopenharmony_ci return status; 153162306a36Sopenharmony_ci} 153262306a36Sopenharmony_ci 153362306a36Sopenharmony_ci/* 153462306a36Sopenharmony_ci * A max of 64 (BE_NUM_VLANS_SUPPORTED) vlans can be configured in BE. 153562306a36Sopenharmony_ci * If the user configures more, place BE in vlan promiscuous mode. 153662306a36Sopenharmony_ci */ 153762306a36Sopenharmony_cistatic int be_vid_config(struct be_adapter *adapter) 153862306a36Sopenharmony_ci{ 153962306a36Sopenharmony_ci struct device *dev = &adapter->pdev->dev; 154062306a36Sopenharmony_ci u16 vids[BE_NUM_VLANS_SUPPORTED]; 154162306a36Sopenharmony_ci u16 num = 0, i = 0; 154262306a36Sopenharmony_ci int status = 0; 154362306a36Sopenharmony_ci 154462306a36Sopenharmony_ci /* No need to change the VLAN state if the I/F is in promiscuous */ 154562306a36Sopenharmony_ci if (adapter->netdev->flags & IFF_PROMISC) 154662306a36Sopenharmony_ci return 0; 154762306a36Sopenharmony_ci 154862306a36Sopenharmony_ci if (adapter->vlans_added > be_max_vlans(adapter)) 154962306a36Sopenharmony_ci return be_set_vlan_promisc(adapter); 155062306a36Sopenharmony_ci 155162306a36Sopenharmony_ci if (adapter->if_flags & BE_IF_FLAGS_VLAN_PROMISCUOUS) { 155262306a36Sopenharmony_ci status = be_clear_vlan_promisc(adapter); 155362306a36Sopenharmony_ci if (status) 155462306a36Sopenharmony_ci return status; 155562306a36Sopenharmony_ci } 155662306a36Sopenharmony_ci /* Construct VLAN Table to give to HW */ 155762306a36Sopenharmony_ci for_each_set_bit(i, adapter->vids, VLAN_N_VID) 155862306a36Sopenharmony_ci vids[num++] = cpu_to_le16(i); 155962306a36Sopenharmony_ci 156062306a36Sopenharmony_ci status = be_cmd_vlan_config(adapter, adapter->if_handle, vids, num, 0); 156162306a36Sopenharmony_ci if (status) { 156262306a36Sopenharmony_ci dev_err(dev, "Setting HW VLAN filtering failed\n"); 156362306a36Sopenharmony_ci /* Set to VLAN promisc mode as setting VLAN filter failed */ 156462306a36Sopenharmony_ci if (addl_status(status) == MCC_ADDL_STATUS_INSUFFICIENT_VLANS || 156562306a36Sopenharmony_ci addl_status(status) == 156662306a36Sopenharmony_ci MCC_ADDL_STATUS_INSUFFICIENT_RESOURCES) 156762306a36Sopenharmony_ci return be_set_vlan_promisc(adapter); 156862306a36Sopenharmony_ci } 156962306a36Sopenharmony_ci return status; 157062306a36Sopenharmony_ci} 157162306a36Sopenharmony_ci 157262306a36Sopenharmony_cistatic int be_vlan_add_vid(struct net_device *netdev, __be16 proto, u16 vid) 157362306a36Sopenharmony_ci{ 157462306a36Sopenharmony_ci struct be_adapter *adapter = netdev_priv(netdev); 157562306a36Sopenharmony_ci int status = 0; 157662306a36Sopenharmony_ci 157762306a36Sopenharmony_ci mutex_lock(&adapter->rx_filter_lock); 157862306a36Sopenharmony_ci 157962306a36Sopenharmony_ci /* Packets with VID 0 are always received by Lancer by default */ 158062306a36Sopenharmony_ci if (lancer_chip(adapter) && vid == 0) 158162306a36Sopenharmony_ci goto done; 158262306a36Sopenharmony_ci 158362306a36Sopenharmony_ci if (test_bit(vid, adapter->vids)) 158462306a36Sopenharmony_ci goto done; 158562306a36Sopenharmony_ci 158662306a36Sopenharmony_ci set_bit(vid, adapter->vids); 158762306a36Sopenharmony_ci adapter->vlans_added++; 158862306a36Sopenharmony_ci 158962306a36Sopenharmony_ci status = be_vid_config(adapter); 159062306a36Sopenharmony_cidone: 159162306a36Sopenharmony_ci mutex_unlock(&adapter->rx_filter_lock); 159262306a36Sopenharmony_ci return status; 159362306a36Sopenharmony_ci} 159462306a36Sopenharmony_ci 159562306a36Sopenharmony_cistatic int be_vlan_rem_vid(struct net_device *netdev, __be16 proto, u16 vid) 159662306a36Sopenharmony_ci{ 159762306a36Sopenharmony_ci struct be_adapter *adapter = netdev_priv(netdev); 159862306a36Sopenharmony_ci int status = 0; 159962306a36Sopenharmony_ci 160062306a36Sopenharmony_ci mutex_lock(&adapter->rx_filter_lock); 160162306a36Sopenharmony_ci 160262306a36Sopenharmony_ci /* Packets with VID 0 are always received by Lancer by default */ 160362306a36Sopenharmony_ci if (lancer_chip(adapter) && vid == 0) 160462306a36Sopenharmony_ci goto done; 160562306a36Sopenharmony_ci 160662306a36Sopenharmony_ci if (!test_bit(vid, adapter->vids)) 160762306a36Sopenharmony_ci goto done; 160862306a36Sopenharmony_ci 160962306a36Sopenharmony_ci clear_bit(vid, adapter->vids); 161062306a36Sopenharmony_ci adapter->vlans_added--; 161162306a36Sopenharmony_ci 161262306a36Sopenharmony_ci status = be_vid_config(adapter); 161362306a36Sopenharmony_cidone: 161462306a36Sopenharmony_ci mutex_unlock(&adapter->rx_filter_lock); 161562306a36Sopenharmony_ci return status; 161662306a36Sopenharmony_ci} 161762306a36Sopenharmony_ci 161862306a36Sopenharmony_cistatic void be_set_all_promisc(struct be_adapter *adapter) 161962306a36Sopenharmony_ci{ 162062306a36Sopenharmony_ci be_cmd_rx_filter(adapter, BE_IF_FLAGS_ALL_PROMISCUOUS, ON); 162162306a36Sopenharmony_ci adapter->if_flags |= BE_IF_FLAGS_ALL_PROMISCUOUS; 162262306a36Sopenharmony_ci} 162362306a36Sopenharmony_ci 162462306a36Sopenharmony_cistatic void be_set_mc_promisc(struct be_adapter *adapter) 162562306a36Sopenharmony_ci{ 162662306a36Sopenharmony_ci int status; 162762306a36Sopenharmony_ci 162862306a36Sopenharmony_ci if (adapter->if_flags & BE_IF_FLAGS_MCAST_PROMISCUOUS) 162962306a36Sopenharmony_ci return; 163062306a36Sopenharmony_ci 163162306a36Sopenharmony_ci status = be_cmd_rx_filter(adapter, BE_IF_FLAGS_MCAST_PROMISCUOUS, ON); 163262306a36Sopenharmony_ci if (!status) 163362306a36Sopenharmony_ci adapter->if_flags |= BE_IF_FLAGS_MCAST_PROMISCUOUS; 163462306a36Sopenharmony_ci} 163562306a36Sopenharmony_ci 163662306a36Sopenharmony_cistatic void be_set_uc_promisc(struct be_adapter *adapter) 163762306a36Sopenharmony_ci{ 163862306a36Sopenharmony_ci int status; 163962306a36Sopenharmony_ci 164062306a36Sopenharmony_ci if (adapter->if_flags & BE_IF_FLAGS_PROMISCUOUS) 164162306a36Sopenharmony_ci return; 164262306a36Sopenharmony_ci 164362306a36Sopenharmony_ci status = be_cmd_rx_filter(adapter, BE_IF_FLAGS_PROMISCUOUS, ON); 164462306a36Sopenharmony_ci if (!status) 164562306a36Sopenharmony_ci adapter->if_flags |= BE_IF_FLAGS_PROMISCUOUS; 164662306a36Sopenharmony_ci} 164762306a36Sopenharmony_ci 164862306a36Sopenharmony_cistatic void be_clear_uc_promisc(struct be_adapter *adapter) 164962306a36Sopenharmony_ci{ 165062306a36Sopenharmony_ci int status; 165162306a36Sopenharmony_ci 165262306a36Sopenharmony_ci if (!(adapter->if_flags & BE_IF_FLAGS_PROMISCUOUS)) 165362306a36Sopenharmony_ci return; 165462306a36Sopenharmony_ci 165562306a36Sopenharmony_ci status = be_cmd_rx_filter(adapter, BE_IF_FLAGS_PROMISCUOUS, OFF); 165662306a36Sopenharmony_ci if (!status) 165762306a36Sopenharmony_ci adapter->if_flags &= ~BE_IF_FLAGS_PROMISCUOUS; 165862306a36Sopenharmony_ci} 165962306a36Sopenharmony_ci 166062306a36Sopenharmony_ci/* The below 2 functions are the callback args for __dev_mc_sync/dev_uc_sync(). 166162306a36Sopenharmony_ci * We use a single callback function for both sync and unsync. We really don't 166262306a36Sopenharmony_ci * add/remove addresses through this callback. But, we use it to detect changes 166362306a36Sopenharmony_ci * to the uc/mc lists. The entire uc/mc list is programmed in be_set_rx_mode(). 166462306a36Sopenharmony_ci */ 166562306a36Sopenharmony_cistatic int be_uc_list_update(struct net_device *netdev, 166662306a36Sopenharmony_ci const unsigned char *addr) 166762306a36Sopenharmony_ci{ 166862306a36Sopenharmony_ci struct be_adapter *adapter = netdev_priv(netdev); 166962306a36Sopenharmony_ci 167062306a36Sopenharmony_ci adapter->update_uc_list = true; 167162306a36Sopenharmony_ci return 0; 167262306a36Sopenharmony_ci} 167362306a36Sopenharmony_ci 167462306a36Sopenharmony_cistatic int be_mc_list_update(struct net_device *netdev, 167562306a36Sopenharmony_ci const unsigned char *addr) 167662306a36Sopenharmony_ci{ 167762306a36Sopenharmony_ci struct be_adapter *adapter = netdev_priv(netdev); 167862306a36Sopenharmony_ci 167962306a36Sopenharmony_ci adapter->update_mc_list = true; 168062306a36Sopenharmony_ci return 0; 168162306a36Sopenharmony_ci} 168262306a36Sopenharmony_ci 168362306a36Sopenharmony_cistatic void be_set_mc_list(struct be_adapter *adapter) 168462306a36Sopenharmony_ci{ 168562306a36Sopenharmony_ci struct net_device *netdev = adapter->netdev; 168662306a36Sopenharmony_ci struct netdev_hw_addr *ha; 168762306a36Sopenharmony_ci bool mc_promisc = false; 168862306a36Sopenharmony_ci int status; 168962306a36Sopenharmony_ci 169062306a36Sopenharmony_ci netif_addr_lock_bh(netdev); 169162306a36Sopenharmony_ci __dev_mc_sync(netdev, be_mc_list_update, be_mc_list_update); 169262306a36Sopenharmony_ci 169362306a36Sopenharmony_ci if (netdev->flags & IFF_PROMISC) { 169462306a36Sopenharmony_ci adapter->update_mc_list = false; 169562306a36Sopenharmony_ci } else if (netdev->flags & IFF_ALLMULTI || 169662306a36Sopenharmony_ci netdev_mc_count(netdev) > be_max_mc(adapter)) { 169762306a36Sopenharmony_ci /* Enable multicast promisc if num configured exceeds 169862306a36Sopenharmony_ci * what we support 169962306a36Sopenharmony_ci */ 170062306a36Sopenharmony_ci mc_promisc = true; 170162306a36Sopenharmony_ci adapter->update_mc_list = false; 170262306a36Sopenharmony_ci } else if (adapter->if_flags & BE_IF_FLAGS_MCAST_PROMISCUOUS) { 170362306a36Sopenharmony_ci /* Update mc-list unconditionally if the iface was previously 170462306a36Sopenharmony_ci * in mc-promisc mode and now is out of that mode. 170562306a36Sopenharmony_ci */ 170662306a36Sopenharmony_ci adapter->update_mc_list = true; 170762306a36Sopenharmony_ci } 170862306a36Sopenharmony_ci 170962306a36Sopenharmony_ci if (adapter->update_mc_list) { 171062306a36Sopenharmony_ci int i = 0; 171162306a36Sopenharmony_ci 171262306a36Sopenharmony_ci /* cache the mc-list in adapter */ 171362306a36Sopenharmony_ci netdev_for_each_mc_addr(ha, netdev) { 171462306a36Sopenharmony_ci ether_addr_copy(adapter->mc_list[i].mac, ha->addr); 171562306a36Sopenharmony_ci i++; 171662306a36Sopenharmony_ci } 171762306a36Sopenharmony_ci adapter->mc_count = netdev_mc_count(netdev); 171862306a36Sopenharmony_ci } 171962306a36Sopenharmony_ci netif_addr_unlock_bh(netdev); 172062306a36Sopenharmony_ci 172162306a36Sopenharmony_ci if (mc_promisc) { 172262306a36Sopenharmony_ci be_set_mc_promisc(adapter); 172362306a36Sopenharmony_ci } else if (adapter->update_mc_list) { 172462306a36Sopenharmony_ci status = be_cmd_rx_filter(adapter, BE_IF_FLAGS_MULTICAST, ON); 172562306a36Sopenharmony_ci if (!status) 172662306a36Sopenharmony_ci adapter->if_flags &= ~BE_IF_FLAGS_MCAST_PROMISCUOUS; 172762306a36Sopenharmony_ci else 172862306a36Sopenharmony_ci be_set_mc_promisc(adapter); 172962306a36Sopenharmony_ci 173062306a36Sopenharmony_ci adapter->update_mc_list = false; 173162306a36Sopenharmony_ci } 173262306a36Sopenharmony_ci} 173362306a36Sopenharmony_ci 173462306a36Sopenharmony_cistatic void be_clear_mc_list(struct be_adapter *adapter) 173562306a36Sopenharmony_ci{ 173662306a36Sopenharmony_ci struct net_device *netdev = adapter->netdev; 173762306a36Sopenharmony_ci 173862306a36Sopenharmony_ci __dev_mc_unsync(netdev, NULL); 173962306a36Sopenharmony_ci be_cmd_rx_filter(adapter, BE_IF_FLAGS_MULTICAST, OFF); 174062306a36Sopenharmony_ci adapter->mc_count = 0; 174162306a36Sopenharmony_ci} 174262306a36Sopenharmony_ci 174362306a36Sopenharmony_cistatic int be_uc_mac_add(struct be_adapter *adapter, int uc_idx) 174462306a36Sopenharmony_ci{ 174562306a36Sopenharmony_ci if (ether_addr_equal(adapter->uc_list[uc_idx].mac, adapter->dev_mac)) { 174662306a36Sopenharmony_ci adapter->pmac_id[uc_idx + 1] = adapter->pmac_id[0]; 174762306a36Sopenharmony_ci return 0; 174862306a36Sopenharmony_ci } 174962306a36Sopenharmony_ci 175062306a36Sopenharmony_ci return be_cmd_pmac_add(adapter, adapter->uc_list[uc_idx].mac, 175162306a36Sopenharmony_ci adapter->if_handle, 175262306a36Sopenharmony_ci &adapter->pmac_id[uc_idx + 1], 0); 175362306a36Sopenharmony_ci} 175462306a36Sopenharmony_ci 175562306a36Sopenharmony_cistatic void be_uc_mac_del(struct be_adapter *adapter, int pmac_id) 175662306a36Sopenharmony_ci{ 175762306a36Sopenharmony_ci if (pmac_id == adapter->pmac_id[0]) 175862306a36Sopenharmony_ci return; 175962306a36Sopenharmony_ci 176062306a36Sopenharmony_ci be_cmd_pmac_del(adapter, adapter->if_handle, pmac_id, 0); 176162306a36Sopenharmony_ci} 176262306a36Sopenharmony_ci 176362306a36Sopenharmony_cistatic void be_set_uc_list(struct be_adapter *adapter) 176462306a36Sopenharmony_ci{ 176562306a36Sopenharmony_ci struct net_device *netdev = adapter->netdev; 176662306a36Sopenharmony_ci struct netdev_hw_addr *ha; 176762306a36Sopenharmony_ci bool uc_promisc = false; 176862306a36Sopenharmony_ci int curr_uc_macs = 0, i; 176962306a36Sopenharmony_ci 177062306a36Sopenharmony_ci netif_addr_lock_bh(netdev); 177162306a36Sopenharmony_ci __dev_uc_sync(netdev, be_uc_list_update, be_uc_list_update); 177262306a36Sopenharmony_ci 177362306a36Sopenharmony_ci if (netdev->flags & IFF_PROMISC) { 177462306a36Sopenharmony_ci adapter->update_uc_list = false; 177562306a36Sopenharmony_ci } else if (netdev_uc_count(netdev) > (be_max_uc(adapter) - 1)) { 177662306a36Sopenharmony_ci uc_promisc = true; 177762306a36Sopenharmony_ci adapter->update_uc_list = false; 177862306a36Sopenharmony_ci } else if (adapter->if_flags & BE_IF_FLAGS_PROMISCUOUS) { 177962306a36Sopenharmony_ci /* Update uc-list unconditionally if the iface was previously 178062306a36Sopenharmony_ci * in uc-promisc mode and now is out of that mode. 178162306a36Sopenharmony_ci */ 178262306a36Sopenharmony_ci adapter->update_uc_list = true; 178362306a36Sopenharmony_ci } 178462306a36Sopenharmony_ci 178562306a36Sopenharmony_ci if (adapter->update_uc_list) { 178662306a36Sopenharmony_ci /* cache the uc-list in adapter array */ 178762306a36Sopenharmony_ci i = 0; 178862306a36Sopenharmony_ci netdev_for_each_uc_addr(ha, netdev) { 178962306a36Sopenharmony_ci ether_addr_copy(adapter->uc_list[i].mac, ha->addr); 179062306a36Sopenharmony_ci i++; 179162306a36Sopenharmony_ci } 179262306a36Sopenharmony_ci curr_uc_macs = netdev_uc_count(netdev); 179362306a36Sopenharmony_ci } 179462306a36Sopenharmony_ci netif_addr_unlock_bh(netdev); 179562306a36Sopenharmony_ci 179662306a36Sopenharmony_ci if (uc_promisc) { 179762306a36Sopenharmony_ci be_set_uc_promisc(adapter); 179862306a36Sopenharmony_ci } else if (adapter->update_uc_list) { 179962306a36Sopenharmony_ci be_clear_uc_promisc(adapter); 180062306a36Sopenharmony_ci 180162306a36Sopenharmony_ci for (i = 0; i < adapter->uc_macs; i++) 180262306a36Sopenharmony_ci be_uc_mac_del(adapter, adapter->pmac_id[i + 1]); 180362306a36Sopenharmony_ci 180462306a36Sopenharmony_ci for (i = 0; i < curr_uc_macs; i++) 180562306a36Sopenharmony_ci be_uc_mac_add(adapter, i); 180662306a36Sopenharmony_ci adapter->uc_macs = curr_uc_macs; 180762306a36Sopenharmony_ci adapter->update_uc_list = false; 180862306a36Sopenharmony_ci } 180962306a36Sopenharmony_ci} 181062306a36Sopenharmony_ci 181162306a36Sopenharmony_cistatic void be_clear_uc_list(struct be_adapter *adapter) 181262306a36Sopenharmony_ci{ 181362306a36Sopenharmony_ci struct net_device *netdev = adapter->netdev; 181462306a36Sopenharmony_ci int i; 181562306a36Sopenharmony_ci 181662306a36Sopenharmony_ci __dev_uc_unsync(netdev, NULL); 181762306a36Sopenharmony_ci for (i = 0; i < adapter->uc_macs; i++) 181862306a36Sopenharmony_ci be_uc_mac_del(adapter, adapter->pmac_id[i + 1]); 181962306a36Sopenharmony_ci 182062306a36Sopenharmony_ci adapter->uc_macs = 0; 182162306a36Sopenharmony_ci} 182262306a36Sopenharmony_ci 182362306a36Sopenharmony_cistatic void __be_set_rx_mode(struct be_adapter *adapter) 182462306a36Sopenharmony_ci{ 182562306a36Sopenharmony_ci struct net_device *netdev = adapter->netdev; 182662306a36Sopenharmony_ci 182762306a36Sopenharmony_ci mutex_lock(&adapter->rx_filter_lock); 182862306a36Sopenharmony_ci 182962306a36Sopenharmony_ci if (netdev->flags & IFF_PROMISC) { 183062306a36Sopenharmony_ci if (!be_in_all_promisc(adapter)) 183162306a36Sopenharmony_ci be_set_all_promisc(adapter); 183262306a36Sopenharmony_ci } else if (be_in_all_promisc(adapter)) { 183362306a36Sopenharmony_ci /* We need to re-program the vlan-list or clear 183462306a36Sopenharmony_ci * vlan-promisc mode (if needed) when the interface 183562306a36Sopenharmony_ci * comes out of promisc mode. 183662306a36Sopenharmony_ci */ 183762306a36Sopenharmony_ci be_vid_config(adapter); 183862306a36Sopenharmony_ci } 183962306a36Sopenharmony_ci 184062306a36Sopenharmony_ci be_set_uc_list(adapter); 184162306a36Sopenharmony_ci be_set_mc_list(adapter); 184262306a36Sopenharmony_ci 184362306a36Sopenharmony_ci mutex_unlock(&adapter->rx_filter_lock); 184462306a36Sopenharmony_ci} 184562306a36Sopenharmony_ci 184662306a36Sopenharmony_cistatic void be_work_set_rx_mode(struct work_struct *work) 184762306a36Sopenharmony_ci{ 184862306a36Sopenharmony_ci struct be_cmd_work *cmd_work = 184962306a36Sopenharmony_ci container_of(work, struct be_cmd_work, work); 185062306a36Sopenharmony_ci 185162306a36Sopenharmony_ci __be_set_rx_mode(cmd_work->adapter); 185262306a36Sopenharmony_ci kfree(cmd_work); 185362306a36Sopenharmony_ci} 185462306a36Sopenharmony_ci 185562306a36Sopenharmony_cistatic int be_set_vf_mac(struct net_device *netdev, int vf, u8 *mac) 185662306a36Sopenharmony_ci{ 185762306a36Sopenharmony_ci struct be_adapter *adapter = netdev_priv(netdev); 185862306a36Sopenharmony_ci struct be_vf_cfg *vf_cfg = &adapter->vf_cfg[vf]; 185962306a36Sopenharmony_ci int status; 186062306a36Sopenharmony_ci 186162306a36Sopenharmony_ci if (!sriov_enabled(adapter)) 186262306a36Sopenharmony_ci return -EPERM; 186362306a36Sopenharmony_ci 186462306a36Sopenharmony_ci if (!is_valid_ether_addr(mac) || vf >= adapter->num_vfs) 186562306a36Sopenharmony_ci return -EINVAL; 186662306a36Sopenharmony_ci 186762306a36Sopenharmony_ci /* Proceed further only if user provided MAC is different 186862306a36Sopenharmony_ci * from active MAC 186962306a36Sopenharmony_ci */ 187062306a36Sopenharmony_ci if (ether_addr_equal(mac, vf_cfg->mac_addr)) 187162306a36Sopenharmony_ci return 0; 187262306a36Sopenharmony_ci 187362306a36Sopenharmony_ci if (BEx_chip(adapter)) { 187462306a36Sopenharmony_ci be_cmd_pmac_del(adapter, vf_cfg->if_handle, vf_cfg->pmac_id, 187562306a36Sopenharmony_ci vf + 1); 187662306a36Sopenharmony_ci 187762306a36Sopenharmony_ci status = be_cmd_pmac_add(adapter, mac, vf_cfg->if_handle, 187862306a36Sopenharmony_ci &vf_cfg->pmac_id, vf + 1); 187962306a36Sopenharmony_ci } else { 188062306a36Sopenharmony_ci status = be_cmd_set_mac(adapter, mac, vf_cfg->if_handle, 188162306a36Sopenharmony_ci vf + 1); 188262306a36Sopenharmony_ci } 188362306a36Sopenharmony_ci 188462306a36Sopenharmony_ci if (status) { 188562306a36Sopenharmony_ci dev_err(&adapter->pdev->dev, "MAC %pM set on VF %d Failed: %#x", 188662306a36Sopenharmony_ci mac, vf, status); 188762306a36Sopenharmony_ci return be_cmd_status(status); 188862306a36Sopenharmony_ci } 188962306a36Sopenharmony_ci 189062306a36Sopenharmony_ci ether_addr_copy(vf_cfg->mac_addr, mac); 189162306a36Sopenharmony_ci 189262306a36Sopenharmony_ci return 0; 189362306a36Sopenharmony_ci} 189462306a36Sopenharmony_ci 189562306a36Sopenharmony_cistatic int be_get_vf_config(struct net_device *netdev, int vf, 189662306a36Sopenharmony_ci struct ifla_vf_info *vi) 189762306a36Sopenharmony_ci{ 189862306a36Sopenharmony_ci struct be_adapter *adapter = netdev_priv(netdev); 189962306a36Sopenharmony_ci struct be_vf_cfg *vf_cfg = &adapter->vf_cfg[vf]; 190062306a36Sopenharmony_ci 190162306a36Sopenharmony_ci if (!sriov_enabled(adapter)) 190262306a36Sopenharmony_ci return -EPERM; 190362306a36Sopenharmony_ci 190462306a36Sopenharmony_ci if (vf >= adapter->num_vfs) 190562306a36Sopenharmony_ci return -EINVAL; 190662306a36Sopenharmony_ci 190762306a36Sopenharmony_ci vi->vf = vf; 190862306a36Sopenharmony_ci vi->max_tx_rate = vf_cfg->tx_rate; 190962306a36Sopenharmony_ci vi->min_tx_rate = 0; 191062306a36Sopenharmony_ci vi->vlan = vf_cfg->vlan_tag & VLAN_VID_MASK; 191162306a36Sopenharmony_ci vi->qos = vf_cfg->vlan_tag >> VLAN_PRIO_SHIFT; 191262306a36Sopenharmony_ci memcpy(&vi->mac, vf_cfg->mac_addr, ETH_ALEN); 191362306a36Sopenharmony_ci vi->linkstate = adapter->vf_cfg[vf].plink_tracking; 191462306a36Sopenharmony_ci vi->spoofchk = adapter->vf_cfg[vf].spoofchk; 191562306a36Sopenharmony_ci 191662306a36Sopenharmony_ci return 0; 191762306a36Sopenharmony_ci} 191862306a36Sopenharmony_ci 191962306a36Sopenharmony_cistatic int be_set_vf_tvt(struct be_adapter *adapter, int vf, u16 vlan) 192062306a36Sopenharmony_ci{ 192162306a36Sopenharmony_ci struct be_vf_cfg *vf_cfg = &adapter->vf_cfg[vf]; 192262306a36Sopenharmony_ci u16 vids[BE_NUM_VLANS_SUPPORTED]; 192362306a36Sopenharmony_ci int vf_if_id = vf_cfg->if_handle; 192462306a36Sopenharmony_ci int status; 192562306a36Sopenharmony_ci 192662306a36Sopenharmony_ci /* Enable Transparent VLAN Tagging */ 192762306a36Sopenharmony_ci status = be_cmd_set_hsw_config(adapter, vlan, vf + 1, vf_if_id, 0, 0); 192862306a36Sopenharmony_ci if (status) 192962306a36Sopenharmony_ci return status; 193062306a36Sopenharmony_ci 193162306a36Sopenharmony_ci /* Clear pre-programmed VLAN filters on VF if any, if TVT is enabled */ 193262306a36Sopenharmony_ci vids[0] = 0; 193362306a36Sopenharmony_ci status = be_cmd_vlan_config(adapter, vf_if_id, vids, 1, vf + 1); 193462306a36Sopenharmony_ci if (!status) 193562306a36Sopenharmony_ci dev_info(&adapter->pdev->dev, 193662306a36Sopenharmony_ci "Cleared guest VLANs on VF%d", vf); 193762306a36Sopenharmony_ci 193862306a36Sopenharmony_ci /* After TVT is enabled, disallow VFs to program VLAN filters */ 193962306a36Sopenharmony_ci if (vf_cfg->privileges & BE_PRIV_FILTMGMT) { 194062306a36Sopenharmony_ci status = be_cmd_set_fn_privileges(adapter, vf_cfg->privileges & 194162306a36Sopenharmony_ci ~BE_PRIV_FILTMGMT, vf + 1); 194262306a36Sopenharmony_ci if (!status) 194362306a36Sopenharmony_ci vf_cfg->privileges &= ~BE_PRIV_FILTMGMT; 194462306a36Sopenharmony_ci } 194562306a36Sopenharmony_ci return 0; 194662306a36Sopenharmony_ci} 194762306a36Sopenharmony_ci 194862306a36Sopenharmony_cistatic int be_clear_vf_tvt(struct be_adapter *adapter, int vf) 194962306a36Sopenharmony_ci{ 195062306a36Sopenharmony_ci struct be_vf_cfg *vf_cfg = &adapter->vf_cfg[vf]; 195162306a36Sopenharmony_ci struct device *dev = &adapter->pdev->dev; 195262306a36Sopenharmony_ci int status; 195362306a36Sopenharmony_ci 195462306a36Sopenharmony_ci /* Reset Transparent VLAN Tagging. */ 195562306a36Sopenharmony_ci status = be_cmd_set_hsw_config(adapter, BE_RESET_VLAN_TAG_ID, vf + 1, 195662306a36Sopenharmony_ci vf_cfg->if_handle, 0, 0); 195762306a36Sopenharmony_ci if (status) 195862306a36Sopenharmony_ci return status; 195962306a36Sopenharmony_ci 196062306a36Sopenharmony_ci /* Allow VFs to program VLAN filtering */ 196162306a36Sopenharmony_ci if (!(vf_cfg->privileges & BE_PRIV_FILTMGMT)) { 196262306a36Sopenharmony_ci status = be_cmd_set_fn_privileges(adapter, vf_cfg->privileges | 196362306a36Sopenharmony_ci BE_PRIV_FILTMGMT, vf + 1); 196462306a36Sopenharmony_ci if (!status) { 196562306a36Sopenharmony_ci vf_cfg->privileges |= BE_PRIV_FILTMGMT; 196662306a36Sopenharmony_ci dev_info(dev, "VF%d: FILTMGMT priv enabled", vf); 196762306a36Sopenharmony_ci } 196862306a36Sopenharmony_ci } 196962306a36Sopenharmony_ci 197062306a36Sopenharmony_ci dev_info(dev, 197162306a36Sopenharmony_ci "Disable/re-enable i/f in VM to clear Transparent VLAN tag"); 197262306a36Sopenharmony_ci return 0; 197362306a36Sopenharmony_ci} 197462306a36Sopenharmony_ci 197562306a36Sopenharmony_cistatic int be_set_vf_vlan(struct net_device *netdev, int vf, u16 vlan, u8 qos, 197662306a36Sopenharmony_ci __be16 vlan_proto) 197762306a36Sopenharmony_ci{ 197862306a36Sopenharmony_ci struct be_adapter *adapter = netdev_priv(netdev); 197962306a36Sopenharmony_ci struct be_vf_cfg *vf_cfg = &adapter->vf_cfg[vf]; 198062306a36Sopenharmony_ci int status; 198162306a36Sopenharmony_ci 198262306a36Sopenharmony_ci if (!sriov_enabled(adapter)) 198362306a36Sopenharmony_ci return -EPERM; 198462306a36Sopenharmony_ci 198562306a36Sopenharmony_ci if (vf >= adapter->num_vfs || vlan > 4095 || qos > 7) 198662306a36Sopenharmony_ci return -EINVAL; 198762306a36Sopenharmony_ci 198862306a36Sopenharmony_ci if (vlan_proto != htons(ETH_P_8021Q)) 198962306a36Sopenharmony_ci return -EPROTONOSUPPORT; 199062306a36Sopenharmony_ci 199162306a36Sopenharmony_ci if (vlan || qos) { 199262306a36Sopenharmony_ci vlan |= qos << VLAN_PRIO_SHIFT; 199362306a36Sopenharmony_ci status = be_set_vf_tvt(adapter, vf, vlan); 199462306a36Sopenharmony_ci } else { 199562306a36Sopenharmony_ci status = be_clear_vf_tvt(adapter, vf); 199662306a36Sopenharmony_ci } 199762306a36Sopenharmony_ci 199862306a36Sopenharmony_ci if (status) { 199962306a36Sopenharmony_ci dev_err(&adapter->pdev->dev, 200062306a36Sopenharmony_ci "VLAN %d config on VF %d failed : %#x\n", vlan, vf, 200162306a36Sopenharmony_ci status); 200262306a36Sopenharmony_ci return be_cmd_status(status); 200362306a36Sopenharmony_ci } 200462306a36Sopenharmony_ci 200562306a36Sopenharmony_ci vf_cfg->vlan_tag = vlan; 200662306a36Sopenharmony_ci return 0; 200762306a36Sopenharmony_ci} 200862306a36Sopenharmony_ci 200962306a36Sopenharmony_cistatic int be_set_vf_tx_rate(struct net_device *netdev, int vf, 201062306a36Sopenharmony_ci int min_tx_rate, int max_tx_rate) 201162306a36Sopenharmony_ci{ 201262306a36Sopenharmony_ci struct be_adapter *adapter = netdev_priv(netdev); 201362306a36Sopenharmony_ci struct device *dev = &adapter->pdev->dev; 201462306a36Sopenharmony_ci int percent_rate, status = 0; 201562306a36Sopenharmony_ci u16 link_speed = 0; 201662306a36Sopenharmony_ci u8 link_status; 201762306a36Sopenharmony_ci 201862306a36Sopenharmony_ci if (!sriov_enabled(adapter)) 201962306a36Sopenharmony_ci return -EPERM; 202062306a36Sopenharmony_ci 202162306a36Sopenharmony_ci if (vf >= adapter->num_vfs) 202262306a36Sopenharmony_ci return -EINVAL; 202362306a36Sopenharmony_ci 202462306a36Sopenharmony_ci if (min_tx_rate) 202562306a36Sopenharmony_ci return -EINVAL; 202662306a36Sopenharmony_ci 202762306a36Sopenharmony_ci if (!max_tx_rate) 202862306a36Sopenharmony_ci goto config_qos; 202962306a36Sopenharmony_ci 203062306a36Sopenharmony_ci status = be_cmd_link_status_query(adapter, &link_speed, 203162306a36Sopenharmony_ci &link_status, 0); 203262306a36Sopenharmony_ci if (status) 203362306a36Sopenharmony_ci goto err; 203462306a36Sopenharmony_ci 203562306a36Sopenharmony_ci if (!link_status) { 203662306a36Sopenharmony_ci dev_err(dev, "TX-rate setting not allowed when link is down\n"); 203762306a36Sopenharmony_ci status = -ENETDOWN; 203862306a36Sopenharmony_ci goto err; 203962306a36Sopenharmony_ci } 204062306a36Sopenharmony_ci 204162306a36Sopenharmony_ci if (max_tx_rate < 100 || max_tx_rate > link_speed) { 204262306a36Sopenharmony_ci dev_err(dev, "TX-rate must be between 100 and %d Mbps\n", 204362306a36Sopenharmony_ci link_speed); 204462306a36Sopenharmony_ci status = -EINVAL; 204562306a36Sopenharmony_ci goto err; 204662306a36Sopenharmony_ci } 204762306a36Sopenharmony_ci 204862306a36Sopenharmony_ci /* On Skyhawk the QOS setting must be done only as a % value */ 204962306a36Sopenharmony_ci percent_rate = link_speed / 100; 205062306a36Sopenharmony_ci if (skyhawk_chip(adapter) && (max_tx_rate % percent_rate)) { 205162306a36Sopenharmony_ci dev_err(dev, "TX-rate must be a multiple of %d Mbps\n", 205262306a36Sopenharmony_ci percent_rate); 205362306a36Sopenharmony_ci status = -EINVAL; 205462306a36Sopenharmony_ci goto err; 205562306a36Sopenharmony_ci } 205662306a36Sopenharmony_ci 205762306a36Sopenharmony_ciconfig_qos: 205862306a36Sopenharmony_ci status = be_cmd_config_qos(adapter, max_tx_rate, link_speed, vf + 1); 205962306a36Sopenharmony_ci if (status) 206062306a36Sopenharmony_ci goto err; 206162306a36Sopenharmony_ci 206262306a36Sopenharmony_ci adapter->vf_cfg[vf].tx_rate = max_tx_rate; 206362306a36Sopenharmony_ci return 0; 206462306a36Sopenharmony_ci 206562306a36Sopenharmony_cierr: 206662306a36Sopenharmony_ci dev_err(dev, "TX-rate setting of %dMbps on VF%d failed\n", 206762306a36Sopenharmony_ci max_tx_rate, vf); 206862306a36Sopenharmony_ci return be_cmd_status(status); 206962306a36Sopenharmony_ci} 207062306a36Sopenharmony_ci 207162306a36Sopenharmony_cistatic int be_set_vf_link_state(struct net_device *netdev, int vf, 207262306a36Sopenharmony_ci int link_state) 207362306a36Sopenharmony_ci{ 207462306a36Sopenharmony_ci struct be_adapter *adapter = netdev_priv(netdev); 207562306a36Sopenharmony_ci int status; 207662306a36Sopenharmony_ci 207762306a36Sopenharmony_ci if (!sriov_enabled(adapter)) 207862306a36Sopenharmony_ci return -EPERM; 207962306a36Sopenharmony_ci 208062306a36Sopenharmony_ci if (vf >= adapter->num_vfs) 208162306a36Sopenharmony_ci return -EINVAL; 208262306a36Sopenharmony_ci 208362306a36Sopenharmony_ci status = be_cmd_set_logical_link_config(adapter, link_state, vf+1); 208462306a36Sopenharmony_ci if (status) { 208562306a36Sopenharmony_ci dev_err(&adapter->pdev->dev, 208662306a36Sopenharmony_ci "Link state change on VF %d failed: %#x\n", vf, status); 208762306a36Sopenharmony_ci return be_cmd_status(status); 208862306a36Sopenharmony_ci } 208962306a36Sopenharmony_ci 209062306a36Sopenharmony_ci adapter->vf_cfg[vf].plink_tracking = link_state; 209162306a36Sopenharmony_ci 209262306a36Sopenharmony_ci return 0; 209362306a36Sopenharmony_ci} 209462306a36Sopenharmony_ci 209562306a36Sopenharmony_cistatic int be_set_vf_spoofchk(struct net_device *netdev, int vf, bool enable) 209662306a36Sopenharmony_ci{ 209762306a36Sopenharmony_ci struct be_adapter *adapter = netdev_priv(netdev); 209862306a36Sopenharmony_ci struct be_vf_cfg *vf_cfg = &adapter->vf_cfg[vf]; 209962306a36Sopenharmony_ci u8 spoofchk; 210062306a36Sopenharmony_ci int status; 210162306a36Sopenharmony_ci 210262306a36Sopenharmony_ci if (!sriov_enabled(adapter)) 210362306a36Sopenharmony_ci return -EPERM; 210462306a36Sopenharmony_ci 210562306a36Sopenharmony_ci if (vf >= adapter->num_vfs) 210662306a36Sopenharmony_ci return -EINVAL; 210762306a36Sopenharmony_ci 210862306a36Sopenharmony_ci if (BEx_chip(adapter)) 210962306a36Sopenharmony_ci return -EOPNOTSUPP; 211062306a36Sopenharmony_ci 211162306a36Sopenharmony_ci if (enable == vf_cfg->spoofchk) 211262306a36Sopenharmony_ci return 0; 211362306a36Sopenharmony_ci 211462306a36Sopenharmony_ci spoofchk = enable ? ENABLE_MAC_SPOOFCHK : DISABLE_MAC_SPOOFCHK; 211562306a36Sopenharmony_ci 211662306a36Sopenharmony_ci status = be_cmd_set_hsw_config(adapter, 0, vf + 1, vf_cfg->if_handle, 211762306a36Sopenharmony_ci 0, spoofchk); 211862306a36Sopenharmony_ci if (status) { 211962306a36Sopenharmony_ci dev_err(&adapter->pdev->dev, 212062306a36Sopenharmony_ci "Spoofchk change on VF %d failed: %#x\n", vf, status); 212162306a36Sopenharmony_ci return be_cmd_status(status); 212262306a36Sopenharmony_ci } 212362306a36Sopenharmony_ci 212462306a36Sopenharmony_ci vf_cfg->spoofchk = enable; 212562306a36Sopenharmony_ci return 0; 212662306a36Sopenharmony_ci} 212762306a36Sopenharmony_ci 212862306a36Sopenharmony_cistatic void be_aic_update(struct be_aic_obj *aic, u64 rx_pkts, u64 tx_pkts, 212962306a36Sopenharmony_ci ulong now) 213062306a36Sopenharmony_ci{ 213162306a36Sopenharmony_ci aic->rx_pkts_prev = rx_pkts; 213262306a36Sopenharmony_ci aic->tx_reqs_prev = tx_pkts; 213362306a36Sopenharmony_ci aic->jiffies = now; 213462306a36Sopenharmony_ci} 213562306a36Sopenharmony_ci 213662306a36Sopenharmony_cistatic int be_get_new_eqd(struct be_eq_obj *eqo) 213762306a36Sopenharmony_ci{ 213862306a36Sopenharmony_ci struct be_adapter *adapter = eqo->adapter; 213962306a36Sopenharmony_ci int eqd, start; 214062306a36Sopenharmony_ci struct be_aic_obj *aic; 214162306a36Sopenharmony_ci struct be_rx_obj *rxo; 214262306a36Sopenharmony_ci struct be_tx_obj *txo; 214362306a36Sopenharmony_ci u64 rx_pkts = 0, tx_pkts = 0; 214462306a36Sopenharmony_ci ulong now; 214562306a36Sopenharmony_ci u32 pps, delta; 214662306a36Sopenharmony_ci int i; 214762306a36Sopenharmony_ci 214862306a36Sopenharmony_ci aic = &adapter->aic_obj[eqo->idx]; 214962306a36Sopenharmony_ci if (!adapter->aic_enabled) { 215062306a36Sopenharmony_ci if (aic->jiffies) 215162306a36Sopenharmony_ci aic->jiffies = 0; 215262306a36Sopenharmony_ci eqd = aic->et_eqd; 215362306a36Sopenharmony_ci return eqd; 215462306a36Sopenharmony_ci } 215562306a36Sopenharmony_ci 215662306a36Sopenharmony_ci for_all_rx_queues_on_eq(adapter, eqo, rxo, i) { 215762306a36Sopenharmony_ci do { 215862306a36Sopenharmony_ci start = u64_stats_fetch_begin(&rxo->stats.sync); 215962306a36Sopenharmony_ci rx_pkts += rxo->stats.rx_pkts; 216062306a36Sopenharmony_ci } while (u64_stats_fetch_retry(&rxo->stats.sync, start)); 216162306a36Sopenharmony_ci } 216262306a36Sopenharmony_ci 216362306a36Sopenharmony_ci for_all_tx_queues_on_eq(adapter, eqo, txo, i) { 216462306a36Sopenharmony_ci do { 216562306a36Sopenharmony_ci start = u64_stats_fetch_begin(&txo->stats.sync); 216662306a36Sopenharmony_ci tx_pkts += txo->stats.tx_reqs; 216762306a36Sopenharmony_ci } while (u64_stats_fetch_retry(&txo->stats.sync, start)); 216862306a36Sopenharmony_ci } 216962306a36Sopenharmony_ci 217062306a36Sopenharmony_ci /* Skip, if wrapped around or first calculation */ 217162306a36Sopenharmony_ci now = jiffies; 217262306a36Sopenharmony_ci if (!aic->jiffies || time_before(now, aic->jiffies) || 217362306a36Sopenharmony_ci rx_pkts < aic->rx_pkts_prev || 217462306a36Sopenharmony_ci tx_pkts < aic->tx_reqs_prev) { 217562306a36Sopenharmony_ci be_aic_update(aic, rx_pkts, tx_pkts, now); 217662306a36Sopenharmony_ci return aic->prev_eqd; 217762306a36Sopenharmony_ci } 217862306a36Sopenharmony_ci 217962306a36Sopenharmony_ci delta = jiffies_to_msecs(now - aic->jiffies); 218062306a36Sopenharmony_ci if (delta == 0) 218162306a36Sopenharmony_ci return aic->prev_eqd; 218262306a36Sopenharmony_ci 218362306a36Sopenharmony_ci pps = (((u32)(rx_pkts - aic->rx_pkts_prev) * 1000) / delta) + 218462306a36Sopenharmony_ci (((u32)(tx_pkts - aic->tx_reqs_prev) * 1000) / delta); 218562306a36Sopenharmony_ci eqd = (pps / 15000) << 2; 218662306a36Sopenharmony_ci 218762306a36Sopenharmony_ci if (eqd < 8) 218862306a36Sopenharmony_ci eqd = 0; 218962306a36Sopenharmony_ci eqd = min_t(u32, eqd, aic->max_eqd); 219062306a36Sopenharmony_ci eqd = max_t(u32, eqd, aic->min_eqd); 219162306a36Sopenharmony_ci 219262306a36Sopenharmony_ci be_aic_update(aic, rx_pkts, tx_pkts, now); 219362306a36Sopenharmony_ci 219462306a36Sopenharmony_ci return eqd; 219562306a36Sopenharmony_ci} 219662306a36Sopenharmony_ci 219762306a36Sopenharmony_ci/* For Skyhawk-R only */ 219862306a36Sopenharmony_cistatic u32 be_get_eq_delay_mult_enc(struct be_eq_obj *eqo) 219962306a36Sopenharmony_ci{ 220062306a36Sopenharmony_ci struct be_adapter *adapter = eqo->adapter; 220162306a36Sopenharmony_ci struct be_aic_obj *aic = &adapter->aic_obj[eqo->idx]; 220262306a36Sopenharmony_ci ulong now = jiffies; 220362306a36Sopenharmony_ci int eqd; 220462306a36Sopenharmony_ci u32 mult_enc; 220562306a36Sopenharmony_ci 220662306a36Sopenharmony_ci if (!adapter->aic_enabled) 220762306a36Sopenharmony_ci return 0; 220862306a36Sopenharmony_ci 220962306a36Sopenharmony_ci if (jiffies_to_msecs(now - aic->jiffies) < 1) 221062306a36Sopenharmony_ci eqd = aic->prev_eqd; 221162306a36Sopenharmony_ci else 221262306a36Sopenharmony_ci eqd = be_get_new_eqd(eqo); 221362306a36Sopenharmony_ci 221462306a36Sopenharmony_ci if (eqd > 100) 221562306a36Sopenharmony_ci mult_enc = R2I_DLY_ENC_1; 221662306a36Sopenharmony_ci else if (eqd > 60) 221762306a36Sopenharmony_ci mult_enc = R2I_DLY_ENC_2; 221862306a36Sopenharmony_ci else if (eqd > 20) 221962306a36Sopenharmony_ci mult_enc = R2I_DLY_ENC_3; 222062306a36Sopenharmony_ci else 222162306a36Sopenharmony_ci mult_enc = R2I_DLY_ENC_0; 222262306a36Sopenharmony_ci 222362306a36Sopenharmony_ci aic->prev_eqd = eqd; 222462306a36Sopenharmony_ci 222562306a36Sopenharmony_ci return mult_enc; 222662306a36Sopenharmony_ci} 222762306a36Sopenharmony_ci 222862306a36Sopenharmony_civoid be_eqd_update(struct be_adapter *adapter, bool force_update) 222962306a36Sopenharmony_ci{ 223062306a36Sopenharmony_ci struct be_set_eqd set_eqd[MAX_EVT_QS]; 223162306a36Sopenharmony_ci struct be_aic_obj *aic; 223262306a36Sopenharmony_ci struct be_eq_obj *eqo; 223362306a36Sopenharmony_ci int i, num = 0, eqd; 223462306a36Sopenharmony_ci 223562306a36Sopenharmony_ci for_all_evt_queues(adapter, eqo, i) { 223662306a36Sopenharmony_ci aic = &adapter->aic_obj[eqo->idx]; 223762306a36Sopenharmony_ci eqd = be_get_new_eqd(eqo); 223862306a36Sopenharmony_ci if (force_update || eqd != aic->prev_eqd) { 223962306a36Sopenharmony_ci set_eqd[num].delay_multiplier = (eqd * 65)/100; 224062306a36Sopenharmony_ci set_eqd[num].eq_id = eqo->q.id; 224162306a36Sopenharmony_ci aic->prev_eqd = eqd; 224262306a36Sopenharmony_ci num++; 224362306a36Sopenharmony_ci } 224462306a36Sopenharmony_ci } 224562306a36Sopenharmony_ci 224662306a36Sopenharmony_ci if (num) 224762306a36Sopenharmony_ci be_cmd_modify_eqd(adapter, set_eqd, num); 224862306a36Sopenharmony_ci} 224962306a36Sopenharmony_ci 225062306a36Sopenharmony_cistatic void be_rx_stats_update(struct be_rx_obj *rxo, 225162306a36Sopenharmony_ci struct be_rx_compl_info *rxcp) 225262306a36Sopenharmony_ci{ 225362306a36Sopenharmony_ci struct be_rx_stats *stats = rx_stats(rxo); 225462306a36Sopenharmony_ci 225562306a36Sopenharmony_ci u64_stats_update_begin(&stats->sync); 225662306a36Sopenharmony_ci stats->rx_compl++; 225762306a36Sopenharmony_ci stats->rx_bytes += rxcp->pkt_size; 225862306a36Sopenharmony_ci stats->rx_pkts++; 225962306a36Sopenharmony_ci if (rxcp->tunneled) 226062306a36Sopenharmony_ci stats->rx_vxlan_offload_pkts++; 226162306a36Sopenharmony_ci if (rxcp->pkt_type == BE_MULTICAST_PACKET) 226262306a36Sopenharmony_ci stats->rx_mcast_pkts++; 226362306a36Sopenharmony_ci if (rxcp->err) 226462306a36Sopenharmony_ci stats->rx_compl_err++; 226562306a36Sopenharmony_ci u64_stats_update_end(&stats->sync); 226662306a36Sopenharmony_ci} 226762306a36Sopenharmony_ci 226862306a36Sopenharmony_cistatic inline bool csum_passed(struct be_rx_compl_info *rxcp) 226962306a36Sopenharmony_ci{ 227062306a36Sopenharmony_ci /* L4 checksum is not reliable for non TCP/UDP packets. 227162306a36Sopenharmony_ci * Also ignore ipcksm for ipv6 pkts 227262306a36Sopenharmony_ci */ 227362306a36Sopenharmony_ci return (rxcp->tcpf || rxcp->udpf) && rxcp->l4_csum && 227462306a36Sopenharmony_ci (rxcp->ip_csum || rxcp->ipv6) && !rxcp->err; 227562306a36Sopenharmony_ci} 227662306a36Sopenharmony_ci 227762306a36Sopenharmony_cistatic struct be_rx_page_info *get_rx_page_info(struct be_rx_obj *rxo) 227862306a36Sopenharmony_ci{ 227962306a36Sopenharmony_ci struct be_adapter *adapter = rxo->adapter; 228062306a36Sopenharmony_ci struct be_rx_page_info *rx_page_info; 228162306a36Sopenharmony_ci struct be_queue_info *rxq = &rxo->q; 228262306a36Sopenharmony_ci u32 frag_idx = rxq->tail; 228362306a36Sopenharmony_ci 228462306a36Sopenharmony_ci rx_page_info = &rxo->page_info_tbl[frag_idx]; 228562306a36Sopenharmony_ci BUG_ON(!rx_page_info->page); 228662306a36Sopenharmony_ci 228762306a36Sopenharmony_ci if (rx_page_info->last_frag) { 228862306a36Sopenharmony_ci dma_unmap_page(&adapter->pdev->dev, 228962306a36Sopenharmony_ci dma_unmap_addr(rx_page_info, bus), 229062306a36Sopenharmony_ci adapter->big_page_size, DMA_FROM_DEVICE); 229162306a36Sopenharmony_ci rx_page_info->last_frag = false; 229262306a36Sopenharmony_ci } else { 229362306a36Sopenharmony_ci dma_sync_single_for_cpu(&adapter->pdev->dev, 229462306a36Sopenharmony_ci dma_unmap_addr(rx_page_info, bus), 229562306a36Sopenharmony_ci rx_frag_size, DMA_FROM_DEVICE); 229662306a36Sopenharmony_ci } 229762306a36Sopenharmony_ci 229862306a36Sopenharmony_ci queue_tail_inc(rxq); 229962306a36Sopenharmony_ci atomic_dec(&rxq->used); 230062306a36Sopenharmony_ci return rx_page_info; 230162306a36Sopenharmony_ci} 230262306a36Sopenharmony_ci 230362306a36Sopenharmony_ci/* Throwaway the data in the Rx completion */ 230462306a36Sopenharmony_cistatic void be_rx_compl_discard(struct be_rx_obj *rxo, 230562306a36Sopenharmony_ci struct be_rx_compl_info *rxcp) 230662306a36Sopenharmony_ci{ 230762306a36Sopenharmony_ci struct be_rx_page_info *page_info; 230862306a36Sopenharmony_ci u16 i, num_rcvd = rxcp->num_rcvd; 230962306a36Sopenharmony_ci 231062306a36Sopenharmony_ci for (i = 0; i < num_rcvd; i++) { 231162306a36Sopenharmony_ci page_info = get_rx_page_info(rxo); 231262306a36Sopenharmony_ci put_page(page_info->page); 231362306a36Sopenharmony_ci memset(page_info, 0, sizeof(*page_info)); 231462306a36Sopenharmony_ci } 231562306a36Sopenharmony_ci} 231662306a36Sopenharmony_ci 231762306a36Sopenharmony_ci/* 231862306a36Sopenharmony_ci * skb_fill_rx_data forms a complete skb for an ether frame 231962306a36Sopenharmony_ci * indicated by rxcp. 232062306a36Sopenharmony_ci */ 232162306a36Sopenharmony_cistatic void skb_fill_rx_data(struct be_rx_obj *rxo, struct sk_buff *skb, 232262306a36Sopenharmony_ci struct be_rx_compl_info *rxcp) 232362306a36Sopenharmony_ci{ 232462306a36Sopenharmony_ci struct be_rx_page_info *page_info; 232562306a36Sopenharmony_ci u16 i, j; 232662306a36Sopenharmony_ci u16 hdr_len, curr_frag_len, remaining; 232762306a36Sopenharmony_ci u8 *start; 232862306a36Sopenharmony_ci 232962306a36Sopenharmony_ci page_info = get_rx_page_info(rxo); 233062306a36Sopenharmony_ci start = page_address(page_info->page) + page_info->page_offset; 233162306a36Sopenharmony_ci prefetch(start); 233262306a36Sopenharmony_ci 233362306a36Sopenharmony_ci /* Copy data in the first descriptor of this completion */ 233462306a36Sopenharmony_ci curr_frag_len = min(rxcp->pkt_size, rx_frag_size); 233562306a36Sopenharmony_ci 233662306a36Sopenharmony_ci skb->len = curr_frag_len; 233762306a36Sopenharmony_ci if (curr_frag_len <= BE_HDR_LEN) { /* tiny packet */ 233862306a36Sopenharmony_ci memcpy(skb->data, start, curr_frag_len); 233962306a36Sopenharmony_ci /* Complete packet has now been moved to data */ 234062306a36Sopenharmony_ci put_page(page_info->page); 234162306a36Sopenharmony_ci skb->data_len = 0; 234262306a36Sopenharmony_ci skb->tail += curr_frag_len; 234362306a36Sopenharmony_ci } else { 234462306a36Sopenharmony_ci hdr_len = ETH_HLEN; 234562306a36Sopenharmony_ci memcpy(skb->data, start, hdr_len); 234662306a36Sopenharmony_ci skb_shinfo(skb)->nr_frags = 1; 234762306a36Sopenharmony_ci skb_frag_fill_page_desc(&skb_shinfo(skb)->frags[0], 234862306a36Sopenharmony_ci page_info->page, 234962306a36Sopenharmony_ci page_info->page_offset + hdr_len, 235062306a36Sopenharmony_ci curr_frag_len - hdr_len); 235162306a36Sopenharmony_ci skb->data_len = curr_frag_len - hdr_len; 235262306a36Sopenharmony_ci skb->truesize += rx_frag_size; 235362306a36Sopenharmony_ci skb->tail += hdr_len; 235462306a36Sopenharmony_ci } 235562306a36Sopenharmony_ci page_info->page = NULL; 235662306a36Sopenharmony_ci 235762306a36Sopenharmony_ci if (rxcp->pkt_size <= rx_frag_size) { 235862306a36Sopenharmony_ci BUG_ON(rxcp->num_rcvd != 1); 235962306a36Sopenharmony_ci return; 236062306a36Sopenharmony_ci } 236162306a36Sopenharmony_ci 236262306a36Sopenharmony_ci /* More frags present for this completion */ 236362306a36Sopenharmony_ci remaining = rxcp->pkt_size - curr_frag_len; 236462306a36Sopenharmony_ci for (i = 1, j = 0; i < rxcp->num_rcvd; i++) { 236562306a36Sopenharmony_ci page_info = get_rx_page_info(rxo); 236662306a36Sopenharmony_ci curr_frag_len = min(remaining, rx_frag_size); 236762306a36Sopenharmony_ci 236862306a36Sopenharmony_ci /* Coalesce all frags from the same physical page in one slot */ 236962306a36Sopenharmony_ci if (page_info->page_offset == 0) { 237062306a36Sopenharmony_ci /* Fresh page */ 237162306a36Sopenharmony_ci j++; 237262306a36Sopenharmony_ci skb_frag_fill_page_desc(&skb_shinfo(skb)->frags[j], 237362306a36Sopenharmony_ci page_info->page, 237462306a36Sopenharmony_ci page_info->page_offset, 237562306a36Sopenharmony_ci curr_frag_len); 237662306a36Sopenharmony_ci skb_shinfo(skb)->nr_frags++; 237762306a36Sopenharmony_ci } else { 237862306a36Sopenharmony_ci put_page(page_info->page); 237962306a36Sopenharmony_ci skb_frag_size_add(&skb_shinfo(skb)->frags[j], 238062306a36Sopenharmony_ci curr_frag_len); 238162306a36Sopenharmony_ci } 238262306a36Sopenharmony_ci 238362306a36Sopenharmony_ci skb->len += curr_frag_len; 238462306a36Sopenharmony_ci skb->data_len += curr_frag_len; 238562306a36Sopenharmony_ci skb->truesize += rx_frag_size; 238662306a36Sopenharmony_ci remaining -= curr_frag_len; 238762306a36Sopenharmony_ci page_info->page = NULL; 238862306a36Sopenharmony_ci } 238962306a36Sopenharmony_ci BUG_ON(j > MAX_SKB_FRAGS); 239062306a36Sopenharmony_ci} 239162306a36Sopenharmony_ci 239262306a36Sopenharmony_ci/* Process the RX completion indicated by rxcp when GRO is disabled */ 239362306a36Sopenharmony_cistatic void be_rx_compl_process(struct be_rx_obj *rxo, struct napi_struct *napi, 239462306a36Sopenharmony_ci struct be_rx_compl_info *rxcp) 239562306a36Sopenharmony_ci{ 239662306a36Sopenharmony_ci struct be_adapter *adapter = rxo->adapter; 239762306a36Sopenharmony_ci struct net_device *netdev = adapter->netdev; 239862306a36Sopenharmony_ci struct sk_buff *skb; 239962306a36Sopenharmony_ci 240062306a36Sopenharmony_ci skb = netdev_alloc_skb_ip_align(netdev, BE_RX_SKB_ALLOC_SIZE); 240162306a36Sopenharmony_ci if (unlikely(!skb)) { 240262306a36Sopenharmony_ci rx_stats(rxo)->rx_drops_no_skbs++; 240362306a36Sopenharmony_ci be_rx_compl_discard(rxo, rxcp); 240462306a36Sopenharmony_ci return; 240562306a36Sopenharmony_ci } 240662306a36Sopenharmony_ci 240762306a36Sopenharmony_ci skb_fill_rx_data(rxo, skb, rxcp); 240862306a36Sopenharmony_ci 240962306a36Sopenharmony_ci if (likely((netdev->features & NETIF_F_RXCSUM) && csum_passed(rxcp))) 241062306a36Sopenharmony_ci skb->ip_summed = CHECKSUM_UNNECESSARY; 241162306a36Sopenharmony_ci else 241262306a36Sopenharmony_ci skb_checksum_none_assert(skb); 241362306a36Sopenharmony_ci 241462306a36Sopenharmony_ci skb->protocol = eth_type_trans(skb, netdev); 241562306a36Sopenharmony_ci skb_record_rx_queue(skb, rxo - &adapter->rx_obj[0]); 241662306a36Sopenharmony_ci if (netdev->features & NETIF_F_RXHASH) 241762306a36Sopenharmony_ci skb_set_hash(skb, rxcp->rss_hash, PKT_HASH_TYPE_L3); 241862306a36Sopenharmony_ci 241962306a36Sopenharmony_ci skb->csum_level = rxcp->tunneled; 242062306a36Sopenharmony_ci skb_mark_napi_id(skb, napi); 242162306a36Sopenharmony_ci 242262306a36Sopenharmony_ci if (rxcp->vlanf) 242362306a36Sopenharmony_ci __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), rxcp->vlan_tag); 242462306a36Sopenharmony_ci 242562306a36Sopenharmony_ci netif_receive_skb(skb); 242662306a36Sopenharmony_ci} 242762306a36Sopenharmony_ci 242862306a36Sopenharmony_ci/* Process the RX completion indicated by rxcp when GRO is enabled */ 242962306a36Sopenharmony_cistatic void be_rx_compl_process_gro(struct be_rx_obj *rxo, 243062306a36Sopenharmony_ci struct napi_struct *napi, 243162306a36Sopenharmony_ci struct be_rx_compl_info *rxcp) 243262306a36Sopenharmony_ci{ 243362306a36Sopenharmony_ci struct be_adapter *adapter = rxo->adapter; 243462306a36Sopenharmony_ci struct be_rx_page_info *page_info; 243562306a36Sopenharmony_ci struct sk_buff *skb = NULL; 243662306a36Sopenharmony_ci u16 remaining, curr_frag_len; 243762306a36Sopenharmony_ci u16 i, j; 243862306a36Sopenharmony_ci 243962306a36Sopenharmony_ci skb = napi_get_frags(napi); 244062306a36Sopenharmony_ci if (!skb) { 244162306a36Sopenharmony_ci be_rx_compl_discard(rxo, rxcp); 244262306a36Sopenharmony_ci return; 244362306a36Sopenharmony_ci } 244462306a36Sopenharmony_ci 244562306a36Sopenharmony_ci remaining = rxcp->pkt_size; 244662306a36Sopenharmony_ci for (i = 0, j = -1; i < rxcp->num_rcvd; i++) { 244762306a36Sopenharmony_ci page_info = get_rx_page_info(rxo); 244862306a36Sopenharmony_ci 244962306a36Sopenharmony_ci curr_frag_len = min(remaining, rx_frag_size); 245062306a36Sopenharmony_ci 245162306a36Sopenharmony_ci /* Coalesce all frags from the same physical page in one slot */ 245262306a36Sopenharmony_ci if (i == 0 || page_info->page_offset == 0) { 245362306a36Sopenharmony_ci /* First frag or Fresh page */ 245462306a36Sopenharmony_ci j++; 245562306a36Sopenharmony_ci skb_frag_fill_page_desc(&skb_shinfo(skb)->frags[j], 245662306a36Sopenharmony_ci page_info->page, 245762306a36Sopenharmony_ci page_info->page_offset, 245862306a36Sopenharmony_ci curr_frag_len); 245962306a36Sopenharmony_ci } else { 246062306a36Sopenharmony_ci put_page(page_info->page); 246162306a36Sopenharmony_ci skb_frag_size_add(&skb_shinfo(skb)->frags[j], 246262306a36Sopenharmony_ci curr_frag_len); 246362306a36Sopenharmony_ci } 246462306a36Sopenharmony_ci 246562306a36Sopenharmony_ci skb->truesize += rx_frag_size; 246662306a36Sopenharmony_ci remaining -= curr_frag_len; 246762306a36Sopenharmony_ci memset(page_info, 0, sizeof(*page_info)); 246862306a36Sopenharmony_ci } 246962306a36Sopenharmony_ci BUG_ON(j > MAX_SKB_FRAGS); 247062306a36Sopenharmony_ci 247162306a36Sopenharmony_ci skb_shinfo(skb)->nr_frags = j + 1; 247262306a36Sopenharmony_ci skb->len = rxcp->pkt_size; 247362306a36Sopenharmony_ci skb->data_len = rxcp->pkt_size; 247462306a36Sopenharmony_ci skb->ip_summed = CHECKSUM_UNNECESSARY; 247562306a36Sopenharmony_ci skb_record_rx_queue(skb, rxo - &adapter->rx_obj[0]); 247662306a36Sopenharmony_ci if (adapter->netdev->features & NETIF_F_RXHASH) 247762306a36Sopenharmony_ci skb_set_hash(skb, rxcp->rss_hash, PKT_HASH_TYPE_L3); 247862306a36Sopenharmony_ci 247962306a36Sopenharmony_ci skb->csum_level = rxcp->tunneled; 248062306a36Sopenharmony_ci 248162306a36Sopenharmony_ci if (rxcp->vlanf) 248262306a36Sopenharmony_ci __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), rxcp->vlan_tag); 248362306a36Sopenharmony_ci 248462306a36Sopenharmony_ci napi_gro_frags(napi); 248562306a36Sopenharmony_ci} 248662306a36Sopenharmony_ci 248762306a36Sopenharmony_cistatic void be_parse_rx_compl_v1(struct be_eth_rx_compl *compl, 248862306a36Sopenharmony_ci struct be_rx_compl_info *rxcp) 248962306a36Sopenharmony_ci{ 249062306a36Sopenharmony_ci rxcp->pkt_size = GET_RX_COMPL_V1_BITS(pktsize, compl); 249162306a36Sopenharmony_ci rxcp->vlanf = GET_RX_COMPL_V1_BITS(vtp, compl); 249262306a36Sopenharmony_ci rxcp->err = GET_RX_COMPL_V1_BITS(err, compl); 249362306a36Sopenharmony_ci rxcp->tcpf = GET_RX_COMPL_V1_BITS(tcpf, compl); 249462306a36Sopenharmony_ci rxcp->udpf = GET_RX_COMPL_V1_BITS(udpf, compl); 249562306a36Sopenharmony_ci rxcp->ip_csum = GET_RX_COMPL_V1_BITS(ipcksm, compl); 249662306a36Sopenharmony_ci rxcp->l4_csum = GET_RX_COMPL_V1_BITS(l4_cksm, compl); 249762306a36Sopenharmony_ci rxcp->ipv6 = GET_RX_COMPL_V1_BITS(ip_version, compl); 249862306a36Sopenharmony_ci rxcp->num_rcvd = GET_RX_COMPL_V1_BITS(numfrags, compl); 249962306a36Sopenharmony_ci rxcp->pkt_type = GET_RX_COMPL_V1_BITS(cast_enc, compl); 250062306a36Sopenharmony_ci rxcp->rss_hash = GET_RX_COMPL_V1_BITS(rsshash, compl); 250162306a36Sopenharmony_ci if (rxcp->vlanf) { 250262306a36Sopenharmony_ci rxcp->qnq = GET_RX_COMPL_V1_BITS(qnq, compl); 250362306a36Sopenharmony_ci rxcp->vlan_tag = GET_RX_COMPL_V1_BITS(vlan_tag, compl); 250462306a36Sopenharmony_ci } 250562306a36Sopenharmony_ci rxcp->port = GET_RX_COMPL_V1_BITS(port, compl); 250662306a36Sopenharmony_ci rxcp->tunneled = 250762306a36Sopenharmony_ci GET_RX_COMPL_V1_BITS(tunneled, compl); 250862306a36Sopenharmony_ci} 250962306a36Sopenharmony_ci 251062306a36Sopenharmony_cistatic void be_parse_rx_compl_v0(struct be_eth_rx_compl *compl, 251162306a36Sopenharmony_ci struct be_rx_compl_info *rxcp) 251262306a36Sopenharmony_ci{ 251362306a36Sopenharmony_ci rxcp->pkt_size = GET_RX_COMPL_V0_BITS(pktsize, compl); 251462306a36Sopenharmony_ci rxcp->vlanf = GET_RX_COMPL_V0_BITS(vtp, compl); 251562306a36Sopenharmony_ci rxcp->err = GET_RX_COMPL_V0_BITS(err, compl); 251662306a36Sopenharmony_ci rxcp->tcpf = GET_RX_COMPL_V0_BITS(tcpf, compl); 251762306a36Sopenharmony_ci rxcp->udpf = GET_RX_COMPL_V0_BITS(udpf, compl); 251862306a36Sopenharmony_ci rxcp->ip_csum = GET_RX_COMPL_V0_BITS(ipcksm, compl); 251962306a36Sopenharmony_ci rxcp->l4_csum = GET_RX_COMPL_V0_BITS(l4_cksm, compl); 252062306a36Sopenharmony_ci rxcp->ipv6 = GET_RX_COMPL_V0_BITS(ip_version, compl); 252162306a36Sopenharmony_ci rxcp->num_rcvd = GET_RX_COMPL_V0_BITS(numfrags, compl); 252262306a36Sopenharmony_ci rxcp->pkt_type = GET_RX_COMPL_V0_BITS(cast_enc, compl); 252362306a36Sopenharmony_ci rxcp->rss_hash = GET_RX_COMPL_V0_BITS(rsshash, compl); 252462306a36Sopenharmony_ci if (rxcp->vlanf) { 252562306a36Sopenharmony_ci rxcp->qnq = GET_RX_COMPL_V0_BITS(qnq, compl); 252662306a36Sopenharmony_ci rxcp->vlan_tag = GET_RX_COMPL_V0_BITS(vlan_tag, compl); 252762306a36Sopenharmony_ci } 252862306a36Sopenharmony_ci rxcp->port = GET_RX_COMPL_V0_BITS(port, compl); 252962306a36Sopenharmony_ci rxcp->ip_frag = GET_RX_COMPL_V0_BITS(ip_frag, compl); 253062306a36Sopenharmony_ci} 253162306a36Sopenharmony_ci 253262306a36Sopenharmony_cistatic struct be_rx_compl_info *be_rx_compl_get(struct be_rx_obj *rxo) 253362306a36Sopenharmony_ci{ 253462306a36Sopenharmony_ci struct be_eth_rx_compl *compl = queue_tail_node(&rxo->cq); 253562306a36Sopenharmony_ci struct be_rx_compl_info *rxcp = &rxo->rxcp; 253662306a36Sopenharmony_ci struct be_adapter *adapter = rxo->adapter; 253762306a36Sopenharmony_ci 253862306a36Sopenharmony_ci /* For checking the valid bit it is Ok to use either definition as the 253962306a36Sopenharmony_ci * valid bit is at the same position in both v0 and v1 Rx compl */ 254062306a36Sopenharmony_ci if (compl->dw[offsetof(struct amap_eth_rx_compl_v1, valid) / 32] == 0) 254162306a36Sopenharmony_ci return NULL; 254262306a36Sopenharmony_ci 254362306a36Sopenharmony_ci rmb(); 254462306a36Sopenharmony_ci be_dws_le_to_cpu(compl, sizeof(*compl)); 254562306a36Sopenharmony_ci 254662306a36Sopenharmony_ci if (adapter->be3_native) 254762306a36Sopenharmony_ci be_parse_rx_compl_v1(compl, rxcp); 254862306a36Sopenharmony_ci else 254962306a36Sopenharmony_ci be_parse_rx_compl_v0(compl, rxcp); 255062306a36Sopenharmony_ci 255162306a36Sopenharmony_ci if (rxcp->ip_frag) 255262306a36Sopenharmony_ci rxcp->l4_csum = 0; 255362306a36Sopenharmony_ci 255462306a36Sopenharmony_ci if (rxcp->vlanf) { 255562306a36Sopenharmony_ci /* In QNQ modes, if qnq bit is not set, then the packet was 255662306a36Sopenharmony_ci * tagged only with the transparent outer vlan-tag and must 255762306a36Sopenharmony_ci * not be treated as a vlan packet by host 255862306a36Sopenharmony_ci */ 255962306a36Sopenharmony_ci if (be_is_qnq_mode(adapter) && !rxcp->qnq) 256062306a36Sopenharmony_ci rxcp->vlanf = 0; 256162306a36Sopenharmony_ci 256262306a36Sopenharmony_ci if (!lancer_chip(adapter)) 256362306a36Sopenharmony_ci rxcp->vlan_tag = swab16(rxcp->vlan_tag); 256462306a36Sopenharmony_ci 256562306a36Sopenharmony_ci if (adapter->pvid == (rxcp->vlan_tag & VLAN_VID_MASK) && 256662306a36Sopenharmony_ci !test_bit(rxcp->vlan_tag, adapter->vids)) 256762306a36Sopenharmony_ci rxcp->vlanf = 0; 256862306a36Sopenharmony_ci } 256962306a36Sopenharmony_ci 257062306a36Sopenharmony_ci /* As the compl has been parsed, reset it; we wont touch it again */ 257162306a36Sopenharmony_ci compl->dw[offsetof(struct amap_eth_rx_compl_v1, valid) / 32] = 0; 257262306a36Sopenharmony_ci 257362306a36Sopenharmony_ci queue_tail_inc(&rxo->cq); 257462306a36Sopenharmony_ci return rxcp; 257562306a36Sopenharmony_ci} 257662306a36Sopenharmony_ci 257762306a36Sopenharmony_cistatic inline struct page *be_alloc_pages(u32 size, gfp_t gfp) 257862306a36Sopenharmony_ci{ 257962306a36Sopenharmony_ci u32 order = get_order(size); 258062306a36Sopenharmony_ci 258162306a36Sopenharmony_ci if (order > 0) 258262306a36Sopenharmony_ci gfp |= __GFP_COMP; 258362306a36Sopenharmony_ci return alloc_pages(gfp, order); 258462306a36Sopenharmony_ci} 258562306a36Sopenharmony_ci 258662306a36Sopenharmony_ci/* 258762306a36Sopenharmony_ci * Allocate a page, split it to fragments of size rx_frag_size and post as 258862306a36Sopenharmony_ci * receive buffers to BE 258962306a36Sopenharmony_ci */ 259062306a36Sopenharmony_cistatic void be_post_rx_frags(struct be_rx_obj *rxo, gfp_t gfp, u32 frags_needed) 259162306a36Sopenharmony_ci{ 259262306a36Sopenharmony_ci struct be_adapter *adapter = rxo->adapter; 259362306a36Sopenharmony_ci struct be_rx_page_info *page_info = NULL, *prev_page_info = NULL; 259462306a36Sopenharmony_ci struct be_queue_info *rxq = &rxo->q; 259562306a36Sopenharmony_ci struct page *pagep = NULL; 259662306a36Sopenharmony_ci struct device *dev = &adapter->pdev->dev; 259762306a36Sopenharmony_ci struct be_eth_rx_d *rxd; 259862306a36Sopenharmony_ci u64 page_dmaaddr = 0, frag_dmaaddr; 259962306a36Sopenharmony_ci u32 posted, page_offset = 0, notify = 0; 260062306a36Sopenharmony_ci 260162306a36Sopenharmony_ci page_info = &rxo->page_info_tbl[rxq->head]; 260262306a36Sopenharmony_ci for (posted = 0; posted < frags_needed && !page_info->page; posted++) { 260362306a36Sopenharmony_ci if (!pagep) { 260462306a36Sopenharmony_ci pagep = be_alloc_pages(adapter->big_page_size, gfp); 260562306a36Sopenharmony_ci if (unlikely(!pagep)) { 260662306a36Sopenharmony_ci rx_stats(rxo)->rx_post_fail++; 260762306a36Sopenharmony_ci break; 260862306a36Sopenharmony_ci } 260962306a36Sopenharmony_ci page_dmaaddr = dma_map_page(dev, pagep, 0, 261062306a36Sopenharmony_ci adapter->big_page_size, 261162306a36Sopenharmony_ci DMA_FROM_DEVICE); 261262306a36Sopenharmony_ci if (dma_mapping_error(dev, page_dmaaddr)) { 261362306a36Sopenharmony_ci put_page(pagep); 261462306a36Sopenharmony_ci pagep = NULL; 261562306a36Sopenharmony_ci adapter->drv_stats.dma_map_errors++; 261662306a36Sopenharmony_ci break; 261762306a36Sopenharmony_ci } 261862306a36Sopenharmony_ci page_offset = 0; 261962306a36Sopenharmony_ci } else { 262062306a36Sopenharmony_ci get_page(pagep); 262162306a36Sopenharmony_ci page_offset += rx_frag_size; 262262306a36Sopenharmony_ci } 262362306a36Sopenharmony_ci page_info->page_offset = page_offset; 262462306a36Sopenharmony_ci page_info->page = pagep; 262562306a36Sopenharmony_ci 262662306a36Sopenharmony_ci rxd = queue_head_node(rxq); 262762306a36Sopenharmony_ci frag_dmaaddr = page_dmaaddr + page_info->page_offset; 262862306a36Sopenharmony_ci rxd->fragpa_lo = cpu_to_le32(frag_dmaaddr & 0xFFFFFFFF); 262962306a36Sopenharmony_ci rxd->fragpa_hi = cpu_to_le32(upper_32_bits(frag_dmaaddr)); 263062306a36Sopenharmony_ci 263162306a36Sopenharmony_ci /* Any space left in the current big page for another frag? */ 263262306a36Sopenharmony_ci if ((page_offset + rx_frag_size + rx_frag_size) > 263362306a36Sopenharmony_ci adapter->big_page_size) { 263462306a36Sopenharmony_ci pagep = NULL; 263562306a36Sopenharmony_ci page_info->last_frag = true; 263662306a36Sopenharmony_ci dma_unmap_addr_set(page_info, bus, page_dmaaddr); 263762306a36Sopenharmony_ci } else { 263862306a36Sopenharmony_ci dma_unmap_addr_set(page_info, bus, frag_dmaaddr); 263962306a36Sopenharmony_ci } 264062306a36Sopenharmony_ci 264162306a36Sopenharmony_ci prev_page_info = page_info; 264262306a36Sopenharmony_ci queue_head_inc(rxq); 264362306a36Sopenharmony_ci page_info = &rxo->page_info_tbl[rxq->head]; 264462306a36Sopenharmony_ci } 264562306a36Sopenharmony_ci 264662306a36Sopenharmony_ci /* Mark the last frag of a page when we break out of the above loop 264762306a36Sopenharmony_ci * with no more slots available in the RXQ 264862306a36Sopenharmony_ci */ 264962306a36Sopenharmony_ci if (pagep) { 265062306a36Sopenharmony_ci prev_page_info->last_frag = true; 265162306a36Sopenharmony_ci dma_unmap_addr_set(prev_page_info, bus, page_dmaaddr); 265262306a36Sopenharmony_ci } 265362306a36Sopenharmony_ci 265462306a36Sopenharmony_ci if (posted) { 265562306a36Sopenharmony_ci atomic_add(posted, &rxq->used); 265662306a36Sopenharmony_ci if (rxo->rx_post_starved) 265762306a36Sopenharmony_ci rxo->rx_post_starved = false; 265862306a36Sopenharmony_ci do { 265962306a36Sopenharmony_ci notify = min(MAX_NUM_POST_ERX_DB, posted); 266062306a36Sopenharmony_ci be_rxq_notify(adapter, rxq->id, notify); 266162306a36Sopenharmony_ci posted -= notify; 266262306a36Sopenharmony_ci } while (posted); 266362306a36Sopenharmony_ci } else if (atomic_read(&rxq->used) == 0) { 266462306a36Sopenharmony_ci /* Let be_worker replenish when memory is available */ 266562306a36Sopenharmony_ci rxo->rx_post_starved = true; 266662306a36Sopenharmony_ci } 266762306a36Sopenharmony_ci} 266862306a36Sopenharmony_ci 266962306a36Sopenharmony_cistatic inline void be_update_tx_err(struct be_tx_obj *txo, u8 status) 267062306a36Sopenharmony_ci{ 267162306a36Sopenharmony_ci switch (status) { 267262306a36Sopenharmony_ci case BE_TX_COMP_HDR_PARSE_ERR: 267362306a36Sopenharmony_ci tx_stats(txo)->tx_hdr_parse_err++; 267462306a36Sopenharmony_ci break; 267562306a36Sopenharmony_ci case BE_TX_COMP_NDMA_ERR: 267662306a36Sopenharmony_ci tx_stats(txo)->tx_dma_err++; 267762306a36Sopenharmony_ci break; 267862306a36Sopenharmony_ci case BE_TX_COMP_ACL_ERR: 267962306a36Sopenharmony_ci tx_stats(txo)->tx_spoof_check_err++; 268062306a36Sopenharmony_ci break; 268162306a36Sopenharmony_ci } 268262306a36Sopenharmony_ci} 268362306a36Sopenharmony_ci 268462306a36Sopenharmony_cistatic inline void lancer_update_tx_err(struct be_tx_obj *txo, u8 status) 268562306a36Sopenharmony_ci{ 268662306a36Sopenharmony_ci switch (status) { 268762306a36Sopenharmony_ci case LANCER_TX_COMP_LSO_ERR: 268862306a36Sopenharmony_ci tx_stats(txo)->tx_tso_err++; 268962306a36Sopenharmony_ci break; 269062306a36Sopenharmony_ci case LANCER_TX_COMP_HSW_DROP_MAC_ERR: 269162306a36Sopenharmony_ci case LANCER_TX_COMP_HSW_DROP_VLAN_ERR: 269262306a36Sopenharmony_ci tx_stats(txo)->tx_spoof_check_err++; 269362306a36Sopenharmony_ci break; 269462306a36Sopenharmony_ci case LANCER_TX_COMP_QINQ_ERR: 269562306a36Sopenharmony_ci tx_stats(txo)->tx_qinq_err++; 269662306a36Sopenharmony_ci break; 269762306a36Sopenharmony_ci case LANCER_TX_COMP_PARITY_ERR: 269862306a36Sopenharmony_ci tx_stats(txo)->tx_internal_parity_err++; 269962306a36Sopenharmony_ci break; 270062306a36Sopenharmony_ci case LANCER_TX_COMP_DMA_ERR: 270162306a36Sopenharmony_ci tx_stats(txo)->tx_dma_err++; 270262306a36Sopenharmony_ci break; 270362306a36Sopenharmony_ci case LANCER_TX_COMP_SGE_ERR: 270462306a36Sopenharmony_ci tx_stats(txo)->tx_sge_err++; 270562306a36Sopenharmony_ci break; 270662306a36Sopenharmony_ci } 270762306a36Sopenharmony_ci} 270862306a36Sopenharmony_ci 270962306a36Sopenharmony_cistatic struct be_tx_compl_info *be_tx_compl_get(struct be_adapter *adapter, 271062306a36Sopenharmony_ci struct be_tx_obj *txo) 271162306a36Sopenharmony_ci{ 271262306a36Sopenharmony_ci struct be_queue_info *tx_cq = &txo->cq; 271362306a36Sopenharmony_ci struct be_tx_compl_info *txcp = &txo->txcp; 271462306a36Sopenharmony_ci struct be_eth_tx_compl *compl = queue_tail_node(tx_cq); 271562306a36Sopenharmony_ci 271662306a36Sopenharmony_ci if (compl->dw[offsetof(struct amap_eth_tx_compl, valid) / 32] == 0) 271762306a36Sopenharmony_ci return NULL; 271862306a36Sopenharmony_ci 271962306a36Sopenharmony_ci /* Ensure load ordering of valid bit dword and other dwords below */ 272062306a36Sopenharmony_ci rmb(); 272162306a36Sopenharmony_ci be_dws_le_to_cpu(compl, sizeof(*compl)); 272262306a36Sopenharmony_ci 272362306a36Sopenharmony_ci txcp->status = GET_TX_COMPL_BITS(status, compl); 272462306a36Sopenharmony_ci txcp->end_index = GET_TX_COMPL_BITS(wrb_index, compl); 272562306a36Sopenharmony_ci 272662306a36Sopenharmony_ci if (txcp->status) { 272762306a36Sopenharmony_ci if (lancer_chip(adapter)) { 272862306a36Sopenharmony_ci lancer_update_tx_err(txo, txcp->status); 272962306a36Sopenharmony_ci /* Reset the adapter incase of TSO, 273062306a36Sopenharmony_ci * SGE or Parity error 273162306a36Sopenharmony_ci */ 273262306a36Sopenharmony_ci if (txcp->status == LANCER_TX_COMP_LSO_ERR || 273362306a36Sopenharmony_ci txcp->status == LANCER_TX_COMP_PARITY_ERR || 273462306a36Sopenharmony_ci txcp->status == LANCER_TX_COMP_SGE_ERR) 273562306a36Sopenharmony_ci be_set_error(adapter, BE_ERROR_TX); 273662306a36Sopenharmony_ci } else { 273762306a36Sopenharmony_ci be_update_tx_err(txo, txcp->status); 273862306a36Sopenharmony_ci } 273962306a36Sopenharmony_ci } 274062306a36Sopenharmony_ci 274162306a36Sopenharmony_ci if (be_check_error(adapter, BE_ERROR_TX)) 274262306a36Sopenharmony_ci return NULL; 274362306a36Sopenharmony_ci 274462306a36Sopenharmony_ci compl->dw[offsetof(struct amap_eth_tx_compl, valid) / 32] = 0; 274562306a36Sopenharmony_ci queue_tail_inc(tx_cq); 274662306a36Sopenharmony_ci return txcp; 274762306a36Sopenharmony_ci} 274862306a36Sopenharmony_ci 274962306a36Sopenharmony_cistatic u16 be_tx_compl_process(struct be_adapter *adapter, 275062306a36Sopenharmony_ci struct be_tx_obj *txo, u16 last_index) 275162306a36Sopenharmony_ci{ 275262306a36Sopenharmony_ci struct sk_buff **sent_skbs = txo->sent_skb_list; 275362306a36Sopenharmony_ci struct be_queue_info *txq = &txo->q; 275462306a36Sopenharmony_ci struct sk_buff *skb = NULL; 275562306a36Sopenharmony_ci bool unmap_skb_hdr = false; 275662306a36Sopenharmony_ci struct be_eth_wrb *wrb; 275762306a36Sopenharmony_ci u16 num_wrbs = 0; 275862306a36Sopenharmony_ci u32 frag_index; 275962306a36Sopenharmony_ci 276062306a36Sopenharmony_ci do { 276162306a36Sopenharmony_ci if (sent_skbs[txq->tail]) { 276262306a36Sopenharmony_ci /* Free skb from prev req */ 276362306a36Sopenharmony_ci if (skb) 276462306a36Sopenharmony_ci dev_consume_skb_any(skb); 276562306a36Sopenharmony_ci skb = sent_skbs[txq->tail]; 276662306a36Sopenharmony_ci sent_skbs[txq->tail] = NULL; 276762306a36Sopenharmony_ci queue_tail_inc(txq); /* skip hdr wrb */ 276862306a36Sopenharmony_ci num_wrbs++; 276962306a36Sopenharmony_ci unmap_skb_hdr = true; 277062306a36Sopenharmony_ci } 277162306a36Sopenharmony_ci wrb = queue_tail_node(txq); 277262306a36Sopenharmony_ci frag_index = txq->tail; 277362306a36Sopenharmony_ci unmap_tx_frag(&adapter->pdev->dev, wrb, 277462306a36Sopenharmony_ci (unmap_skb_hdr && skb_headlen(skb))); 277562306a36Sopenharmony_ci unmap_skb_hdr = false; 277662306a36Sopenharmony_ci queue_tail_inc(txq); 277762306a36Sopenharmony_ci num_wrbs++; 277862306a36Sopenharmony_ci } while (frag_index != last_index); 277962306a36Sopenharmony_ci dev_consume_skb_any(skb); 278062306a36Sopenharmony_ci 278162306a36Sopenharmony_ci return num_wrbs; 278262306a36Sopenharmony_ci} 278362306a36Sopenharmony_ci 278462306a36Sopenharmony_ci/* Return the number of events in the event queue */ 278562306a36Sopenharmony_cistatic inline int events_get(struct be_eq_obj *eqo) 278662306a36Sopenharmony_ci{ 278762306a36Sopenharmony_ci struct be_eq_entry *eqe; 278862306a36Sopenharmony_ci int num = 0; 278962306a36Sopenharmony_ci 279062306a36Sopenharmony_ci do { 279162306a36Sopenharmony_ci eqe = queue_tail_node(&eqo->q); 279262306a36Sopenharmony_ci if (eqe->evt == 0) 279362306a36Sopenharmony_ci break; 279462306a36Sopenharmony_ci 279562306a36Sopenharmony_ci rmb(); 279662306a36Sopenharmony_ci eqe->evt = 0; 279762306a36Sopenharmony_ci num++; 279862306a36Sopenharmony_ci queue_tail_inc(&eqo->q); 279962306a36Sopenharmony_ci } while (true); 280062306a36Sopenharmony_ci 280162306a36Sopenharmony_ci return num; 280262306a36Sopenharmony_ci} 280362306a36Sopenharmony_ci 280462306a36Sopenharmony_ci/* Leaves the EQ is disarmed state */ 280562306a36Sopenharmony_cistatic void be_eq_clean(struct be_eq_obj *eqo) 280662306a36Sopenharmony_ci{ 280762306a36Sopenharmony_ci int num = events_get(eqo); 280862306a36Sopenharmony_ci 280962306a36Sopenharmony_ci be_eq_notify(eqo->adapter, eqo->q.id, false, true, num, 0); 281062306a36Sopenharmony_ci} 281162306a36Sopenharmony_ci 281262306a36Sopenharmony_ci/* Free posted rx buffers that were not used */ 281362306a36Sopenharmony_cistatic void be_rxq_clean(struct be_rx_obj *rxo) 281462306a36Sopenharmony_ci{ 281562306a36Sopenharmony_ci struct be_queue_info *rxq = &rxo->q; 281662306a36Sopenharmony_ci struct be_rx_page_info *page_info; 281762306a36Sopenharmony_ci 281862306a36Sopenharmony_ci while (atomic_read(&rxq->used) > 0) { 281962306a36Sopenharmony_ci page_info = get_rx_page_info(rxo); 282062306a36Sopenharmony_ci put_page(page_info->page); 282162306a36Sopenharmony_ci memset(page_info, 0, sizeof(*page_info)); 282262306a36Sopenharmony_ci } 282362306a36Sopenharmony_ci BUG_ON(atomic_read(&rxq->used)); 282462306a36Sopenharmony_ci rxq->tail = 0; 282562306a36Sopenharmony_ci rxq->head = 0; 282662306a36Sopenharmony_ci} 282762306a36Sopenharmony_ci 282862306a36Sopenharmony_cistatic void be_rx_cq_clean(struct be_rx_obj *rxo) 282962306a36Sopenharmony_ci{ 283062306a36Sopenharmony_ci struct be_queue_info *rx_cq = &rxo->cq; 283162306a36Sopenharmony_ci struct be_rx_compl_info *rxcp; 283262306a36Sopenharmony_ci struct be_adapter *adapter = rxo->adapter; 283362306a36Sopenharmony_ci int flush_wait = 0; 283462306a36Sopenharmony_ci 283562306a36Sopenharmony_ci /* Consume pending rx completions. 283662306a36Sopenharmony_ci * Wait for the flush completion (identified by zero num_rcvd) 283762306a36Sopenharmony_ci * to arrive. Notify CQ even when there are no more CQ entries 283862306a36Sopenharmony_ci * for HW to flush partially coalesced CQ entries. 283962306a36Sopenharmony_ci * In Lancer, there is no need to wait for flush compl. 284062306a36Sopenharmony_ci */ 284162306a36Sopenharmony_ci for (;;) { 284262306a36Sopenharmony_ci rxcp = be_rx_compl_get(rxo); 284362306a36Sopenharmony_ci if (!rxcp) { 284462306a36Sopenharmony_ci if (lancer_chip(adapter)) 284562306a36Sopenharmony_ci break; 284662306a36Sopenharmony_ci 284762306a36Sopenharmony_ci if (flush_wait++ > 50 || 284862306a36Sopenharmony_ci be_check_error(adapter, 284962306a36Sopenharmony_ci BE_ERROR_HW)) { 285062306a36Sopenharmony_ci dev_warn(&adapter->pdev->dev, 285162306a36Sopenharmony_ci "did not receive flush compl\n"); 285262306a36Sopenharmony_ci break; 285362306a36Sopenharmony_ci } 285462306a36Sopenharmony_ci be_cq_notify(adapter, rx_cq->id, true, 0); 285562306a36Sopenharmony_ci mdelay(1); 285662306a36Sopenharmony_ci } else { 285762306a36Sopenharmony_ci be_rx_compl_discard(rxo, rxcp); 285862306a36Sopenharmony_ci be_cq_notify(adapter, rx_cq->id, false, 1); 285962306a36Sopenharmony_ci if (rxcp->num_rcvd == 0) 286062306a36Sopenharmony_ci break; 286162306a36Sopenharmony_ci } 286262306a36Sopenharmony_ci } 286362306a36Sopenharmony_ci 286462306a36Sopenharmony_ci /* After cleanup, leave the CQ in unarmed state */ 286562306a36Sopenharmony_ci be_cq_notify(adapter, rx_cq->id, false, 0); 286662306a36Sopenharmony_ci} 286762306a36Sopenharmony_ci 286862306a36Sopenharmony_cistatic void be_tx_compl_clean(struct be_adapter *adapter) 286962306a36Sopenharmony_ci{ 287062306a36Sopenharmony_ci struct device *dev = &adapter->pdev->dev; 287162306a36Sopenharmony_ci u16 cmpl = 0, timeo = 0, num_wrbs = 0; 287262306a36Sopenharmony_ci struct be_tx_compl_info *txcp; 287362306a36Sopenharmony_ci struct be_queue_info *txq; 287462306a36Sopenharmony_ci u32 end_idx, notified_idx; 287562306a36Sopenharmony_ci struct be_tx_obj *txo; 287662306a36Sopenharmony_ci int i, pending_txqs; 287762306a36Sopenharmony_ci 287862306a36Sopenharmony_ci /* Stop polling for compls when HW has been silent for 10ms */ 287962306a36Sopenharmony_ci do { 288062306a36Sopenharmony_ci pending_txqs = adapter->num_tx_qs; 288162306a36Sopenharmony_ci 288262306a36Sopenharmony_ci for_all_tx_queues(adapter, txo, i) { 288362306a36Sopenharmony_ci cmpl = 0; 288462306a36Sopenharmony_ci num_wrbs = 0; 288562306a36Sopenharmony_ci txq = &txo->q; 288662306a36Sopenharmony_ci while ((txcp = be_tx_compl_get(adapter, txo))) { 288762306a36Sopenharmony_ci num_wrbs += 288862306a36Sopenharmony_ci be_tx_compl_process(adapter, txo, 288962306a36Sopenharmony_ci txcp->end_index); 289062306a36Sopenharmony_ci cmpl++; 289162306a36Sopenharmony_ci } 289262306a36Sopenharmony_ci if (cmpl) { 289362306a36Sopenharmony_ci be_cq_notify(adapter, txo->cq.id, false, cmpl); 289462306a36Sopenharmony_ci atomic_sub(num_wrbs, &txq->used); 289562306a36Sopenharmony_ci timeo = 0; 289662306a36Sopenharmony_ci } 289762306a36Sopenharmony_ci if (!be_is_tx_compl_pending(txo)) 289862306a36Sopenharmony_ci pending_txqs--; 289962306a36Sopenharmony_ci } 290062306a36Sopenharmony_ci 290162306a36Sopenharmony_ci if (pending_txqs == 0 || ++timeo > 10 || 290262306a36Sopenharmony_ci be_check_error(adapter, BE_ERROR_HW)) 290362306a36Sopenharmony_ci break; 290462306a36Sopenharmony_ci 290562306a36Sopenharmony_ci mdelay(1); 290662306a36Sopenharmony_ci } while (true); 290762306a36Sopenharmony_ci 290862306a36Sopenharmony_ci /* Free enqueued TX that was never notified to HW */ 290962306a36Sopenharmony_ci for_all_tx_queues(adapter, txo, i) { 291062306a36Sopenharmony_ci txq = &txo->q; 291162306a36Sopenharmony_ci 291262306a36Sopenharmony_ci if (atomic_read(&txq->used)) { 291362306a36Sopenharmony_ci dev_info(dev, "txq%d: cleaning %d pending tx-wrbs\n", 291462306a36Sopenharmony_ci i, atomic_read(&txq->used)); 291562306a36Sopenharmony_ci notified_idx = txq->tail; 291662306a36Sopenharmony_ci end_idx = txq->tail; 291762306a36Sopenharmony_ci index_adv(&end_idx, atomic_read(&txq->used) - 1, 291862306a36Sopenharmony_ci txq->len); 291962306a36Sopenharmony_ci /* Use the tx-compl process logic to handle requests 292062306a36Sopenharmony_ci * that were not sent to the HW. 292162306a36Sopenharmony_ci */ 292262306a36Sopenharmony_ci num_wrbs = be_tx_compl_process(adapter, txo, end_idx); 292362306a36Sopenharmony_ci atomic_sub(num_wrbs, &txq->used); 292462306a36Sopenharmony_ci BUG_ON(atomic_read(&txq->used)); 292562306a36Sopenharmony_ci txo->pend_wrb_cnt = 0; 292662306a36Sopenharmony_ci /* Since hw was never notified of these requests, 292762306a36Sopenharmony_ci * reset TXQ indices 292862306a36Sopenharmony_ci */ 292962306a36Sopenharmony_ci txq->head = notified_idx; 293062306a36Sopenharmony_ci txq->tail = notified_idx; 293162306a36Sopenharmony_ci } 293262306a36Sopenharmony_ci } 293362306a36Sopenharmony_ci} 293462306a36Sopenharmony_ci 293562306a36Sopenharmony_cistatic void be_evt_queues_destroy(struct be_adapter *adapter) 293662306a36Sopenharmony_ci{ 293762306a36Sopenharmony_ci struct be_eq_obj *eqo; 293862306a36Sopenharmony_ci int i; 293962306a36Sopenharmony_ci 294062306a36Sopenharmony_ci for_all_evt_queues(adapter, eqo, i) { 294162306a36Sopenharmony_ci if (eqo->q.created) { 294262306a36Sopenharmony_ci be_eq_clean(eqo); 294362306a36Sopenharmony_ci be_cmd_q_destroy(adapter, &eqo->q, QTYPE_EQ); 294462306a36Sopenharmony_ci netif_napi_del(&eqo->napi); 294562306a36Sopenharmony_ci free_cpumask_var(eqo->affinity_mask); 294662306a36Sopenharmony_ci } 294762306a36Sopenharmony_ci be_queue_free(adapter, &eqo->q); 294862306a36Sopenharmony_ci } 294962306a36Sopenharmony_ci} 295062306a36Sopenharmony_ci 295162306a36Sopenharmony_cistatic int be_evt_queues_create(struct be_adapter *adapter) 295262306a36Sopenharmony_ci{ 295362306a36Sopenharmony_ci struct be_queue_info *eq; 295462306a36Sopenharmony_ci struct be_eq_obj *eqo; 295562306a36Sopenharmony_ci struct be_aic_obj *aic; 295662306a36Sopenharmony_ci int i, rc; 295762306a36Sopenharmony_ci 295862306a36Sopenharmony_ci /* need enough EQs to service both RX and TX queues */ 295962306a36Sopenharmony_ci adapter->num_evt_qs = min_t(u16, num_irqs(adapter), 296062306a36Sopenharmony_ci max(adapter->cfg_num_rx_irqs, 296162306a36Sopenharmony_ci adapter->cfg_num_tx_irqs)); 296262306a36Sopenharmony_ci 296362306a36Sopenharmony_ci adapter->aic_enabled = true; 296462306a36Sopenharmony_ci 296562306a36Sopenharmony_ci for_all_evt_queues(adapter, eqo, i) { 296662306a36Sopenharmony_ci int numa_node = dev_to_node(&adapter->pdev->dev); 296762306a36Sopenharmony_ci 296862306a36Sopenharmony_ci aic = &adapter->aic_obj[i]; 296962306a36Sopenharmony_ci eqo->adapter = adapter; 297062306a36Sopenharmony_ci eqo->idx = i; 297162306a36Sopenharmony_ci aic->max_eqd = BE_MAX_EQD; 297262306a36Sopenharmony_ci 297362306a36Sopenharmony_ci eq = &eqo->q; 297462306a36Sopenharmony_ci rc = be_queue_alloc(adapter, eq, EVNT_Q_LEN, 297562306a36Sopenharmony_ci sizeof(struct be_eq_entry)); 297662306a36Sopenharmony_ci if (rc) 297762306a36Sopenharmony_ci return rc; 297862306a36Sopenharmony_ci 297962306a36Sopenharmony_ci rc = be_cmd_eq_create(adapter, eqo); 298062306a36Sopenharmony_ci if (rc) 298162306a36Sopenharmony_ci return rc; 298262306a36Sopenharmony_ci 298362306a36Sopenharmony_ci if (!zalloc_cpumask_var(&eqo->affinity_mask, GFP_KERNEL)) 298462306a36Sopenharmony_ci return -ENOMEM; 298562306a36Sopenharmony_ci cpumask_set_cpu(cpumask_local_spread(i, numa_node), 298662306a36Sopenharmony_ci eqo->affinity_mask); 298762306a36Sopenharmony_ci netif_napi_add(adapter->netdev, &eqo->napi, be_poll); 298862306a36Sopenharmony_ci } 298962306a36Sopenharmony_ci return 0; 299062306a36Sopenharmony_ci} 299162306a36Sopenharmony_ci 299262306a36Sopenharmony_cistatic void be_mcc_queues_destroy(struct be_adapter *adapter) 299362306a36Sopenharmony_ci{ 299462306a36Sopenharmony_ci struct be_queue_info *q; 299562306a36Sopenharmony_ci 299662306a36Sopenharmony_ci q = &adapter->mcc_obj.q; 299762306a36Sopenharmony_ci if (q->created) 299862306a36Sopenharmony_ci be_cmd_q_destroy(adapter, q, QTYPE_MCCQ); 299962306a36Sopenharmony_ci be_queue_free(adapter, q); 300062306a36Sopenharmony_ci 300162306a36Sopenharmony_ci q = &adapter->mcc_obj.cq; 300262306a36Sopenharmony_ci if (q->created) 300362306a36Sopenharmony_ci be_cmd_q_destroy(adapter, q, QTYPE_CQ); 300462306a36Sopenharmony_ci be_queue_free(adapter, q); 300562306a36Sopenharmony_ci} 300662306a36Sopenharmony_ci 300762306a36Sopenharmony_ci/* Must be called only after TX qs are created as MCC shares TX EQ */ 300862306a36Sopenharmony_cistatic int be_mcc_queues_create(struct be_adapter *adapter) 300962306a36Sopenharmony_ci{ 301062306a36Sopenharmony_ci struct be_queue_info *q, *cq; 301162306a36Sopenharmony_ci 301262306a36Sopenharmony_ci cq = &adapter->mcc_obj.cq; 301362306a36Sopenharmony_ci if (be_queue_alloc(adapter, cq, MCC_CQ_LEN, 301462306a36Sopenharmony_ci sizeof(struct be_mcc_compl))) 301562306a36Sopenharmony_ci goto err; 301662306a36Sopenharmony_ci 301762306a36Sopenharmony_ci /* Use the default EQ for MCC completions */ 301862306a36Sopenharmony_ci if (be_cmd_cq_create(adapter, cq, &mcc_eqo(adapter)->q, true, 0)) 301962306a36Sopenharmony_ci goto mcc_cq_free; 302062306a36Sopenharmony_ci 302162306a36Sopenharmony_ci q = &adapter->mcc_obj.q; 302262306a36Sopenharmony_ci if (be_queue_alloc(adapter, q, MCC_Q_LEN, sizeof(struct be_mcc_wrb))) 302362306a36Sopenharmony_ci goto mcc_cq_destroy; 302462306a36Sopenharmony_ci 302562306a36Sopenharmony_ci if (be_cmd_mccq_create(adapter, q, cq)) 302662306a36Sopenharmony_ci goto mcc_q_free; 302762306a36Sopenharmony_ci 302862306a36Sopenharmony_ci return 0; 302962306a36Sopenharmony_ci 303062306a36Sopenharmony_cimcc_q_free: 303162306a36Sopenharmony_ci be_queue_free(adapter, q); 303262306a36Sopenharmony_cimcc_cq_destroy: 303362306a36Sopenharmony_ci be_cmd_q_destroy(adapter, cq, QTYPE_CQ); 303462306a36Sopenharmony_cimcc_cq_free: 303562306a36Sopenharmony_ci be_queue_free(adapter, cq); 303662306a36Sopenharmony_cierr: 303762306a36Sopenharmony_ci return -1; 303862306a36Sopenharmony_ci} 303962306a36Sopenharmony_ci 304062306a36Sopenharmony_cistatic void be_tx_queues_destroy(struct be_adapter *adapter) 304162306a36Sopenharmony_ci{ 304262306a36Sopenharmony_ci struct be_queue_info *q; 304362306a36Sopenharmony_ci struct be_tx_obj *txo; 304462306a36Sopenharmony_ci u8 i; 304562306a36Sopenharmony_ci 304662306a36Sopenharmony_ci for_all_tx_queues(adapter, txo, i) { 304762306a36Sopenharmony_ci q = &txo->q; 304862306a36Sopenharmony_ci if (q->created) 304962306a36Sopenharmony_ci be_cmd_q_destroy(adapter, q, QTYPE_TXQ); 305062306a36Sopenharmony_ci be_queue_free(adapter, q); 305162306a36Sopenharmony_ci 305262306a36Sopenharmony_ci q = &txo->cq; 305362306a36Sopenharmony_ci if (q->created) 305462306a36Sopenharmony_ci be_cmd_q_destroy(adapter, q, QTYPE_CQ); 305562306a36Sopenharmony_ci be_queue_free(adapter, q); 305662306a36Sopenharmony_ci } 305762306a36Sopenharmony_ci} 305862306a36Sopenharmony_ci 305962306a36Sopenharmony_cistatic int be_tx_qs_create(struct be_adapter *adapter) 306062306a36Sopenharmony_ci{ 306162306a36Sopenharmony_ci struct be_queue_info *cq; 306262306a36Sopenharmony_ci struct be_tx_obj *txo; 306362306a36Sopenharmony_ci struct be_eq_obj *eqo; 306462306a36Sopenharmony_ci int status, i; 306562306a36Sopenharmony_ci 306662306a36Sopenharmony_ci adapter->num_tx_qs = min(adapter->num_evt_qs, adapter->cfg_num_tx_irqs); 306762306a36Sopenharmony_ci 306862306a36Sopenharmony_ci for_all_tx_queues(adapter, txo, i) { 306962306a36Sopenharmony_ci cq = &txo->cq; 307062306a36Sopenharmony_ci status = be_queue_alloc(adapter, cq, TX_CQ_LEN, 307162306a36Sopenharmony_ci sizeof(struct be_eth_tx_compl)); 307262306a36Sopenharmony_ci if (status) 307362306a36Sopenharmony_ci return status; 307462306a36Sopenharmony_ci 307562306a36Sopenharmony_ci u64_stats_init(&txo->stats.sync); 307662306a36Sopenharmony_ci u64_stats_init(&txo->stats.sync_compl); 307762306a36Sopenharmony_ci 307862306a36Sopenharmony_ci /* If num_evt_qs is less than num_tx_qs, then more than 307962306a36Sopenharmony_ci * one txq share an eq 308062306a36Sopenharmony_ci */ 308162306a36Sopenharmony_ci eqo = &adapter->eq_obj[i % adapter->num_evt_qs]; 308262306a36Sopenharmony_ci status = be_cmd_cq_create(adapter, cq, &eqo->q, false, 3); 308362306a36Sopenharmony_ci if (status) 308462306a36Sopenharmony_ci return status; 308562306a36Sopenharmony_ci 308662306a36Sopenharmony_ci status = be_queue_alloc(adapter, &txo->q, TX_Q_LEN, 308762306a36Sopenharmony_ci sizeof(struct be_eth_wrb)); 308862306a36Sopenharmony_ci if (status) 308962306a36Sopenharmony_ci return status; 309062306a36Sopenharmony_ci 309162306a36Sopenharmony_ci status = be_cmd_txq_create(adapter, txo); 309262306a36Sopenharmony_ci if (status) 309362306a36Sopenharmony_ci return status; 309462306a36Sopenharmony_ci 309562306a36Sopenharmony_ci netif_set_xps_queue(adapter->netdev, eqo->affinity_mask, 309662306a36Sopenharmony_ci eqo->idx); 309762306a36Sopenharmony_ci } 309862306a36Sopenharmony_ci 309962306a36Sopenharmony_ci dev_info(&adapter->pdev->dev, "created %d TX queue(s)\n", 310062306a36Sopenharmony_ci adapter->num_tx_qs); 310162306a36Sopenharmony_ci return 0; 310262306a36Sopenharmony_ci} 310362306a36Sopenharmony_ci 310462306a36Sopenharmony_cistatic void be_rx_cqs_destroy(struct be_adapter *adapter) 310562306a36Sopenharmony_ci{ 310662306a36Sopenharmony_ci struct be_queue_info *q; 310762306a36Sopenharmony_ci struct be_rx_obj *rxo; 310862306a36Sopenharmony_ci int i; 310962306a36Sopenharmony_ci 311062306a36Sopenharmony_ci for_all_rx_queues(adapter, rxo, i) { 311162306a36Sopenharmony_ci q = &rxo->cq; 311262306a36Sopenharmony_ci if (q->created) 311362306a36Sopenharmony_ci be_cmd_q_destroy(adapter, q, QTYPE_CQ); 311462306a36Sopenharmony_ci be_queue_free(adapter, q); 311562306a36Sopenharmony_ci } 311662306a36Sopenharmony_ci} 311762306a36Sopenharmony_ci 311862306a36Sopenharmony_cistatic int be_rx_cqs_create(struct be_adapter *adapter) 311962306a36Sopenharmony_ci{ 312062306a36Sopenharmony_ci struct be_queue_info *eq, *cq; 312162306a36Sopenharmony_ci struct be_rx_obj *rxo; 312262306a36Sopenharmony_ci int rc, i; 312362306a36Sopenharmony_ci 312462306a36Sopenharmony_ci adapter->num_rss_qs = 312562306a36Sopenharmony_ci min(adapter->num_evt_qs, adapter->cfg_num_rx_irqs); 312662306a36Sopenharmony_ci 312762306a36Sopenharmony_ci /* We'll use RSS only if atleast 2 RSS rings are supported. */ 312862306a36Sopenharmony_ci if (adapter->num_rss_qs < 2) 312962306a36Sopenharmony_ci adapter->num_rss_qs = 0; 313062306a36Sopenharmony_ci 313162306a36Sopenharmony_ci adapter->num_rx_qs = adapter->num_rss_qs + adapter->need_def_rxq; 313262306a36Sopenharmony_ci 313362306a36Sopenharmony_ci /* When the interface is not capable of RSS rings (and there is no 313462306a36Sopenharmony_ci * need to create a default RXQ) we'll still need one RXQ 313562306a36Sopenharmony_ci */ 313662306a36Sopenharmony_ci if (adapter->num_rx_qs == 0) 313762306a36Sopenharmony_ci adapter->num_rx_qs = 1; 313862306a36Sopenharmony_ci 313962306a36Sopenharmony_ci adapter->big_page_size = (1 << get_order(rx_frag_size)) * PAGE_SIZE; 314062306a36Sopenharmony_ci for_all_rx_queues(adapter, rxo, i) { 314162306a36Sopenharmony_ci rxo->adapter = adapter; 314262306a36Sopenharmony_ci cq = &rxo->cq; 314362306a36Sopenharmony_ci rc = be_queue_alloc(adapter, cq, RX_CQ_LEN, 314462306a36Sopenharmony_ci sizeof(struct be_eth_rx_compl)); 314562306a36Sopenharmony_ci if (rc) 314662306a36Sopenharmony_ci return rc; 314762306a36Sopenharmony_ci 314862306a36Sopenharmony_ci u64_stats_init(&rxo->stats.sync); 314962306a36Sopenharmony_ci eq = &adapter->eq_obj[i % adapter->num_evt_qs].q; 315062306a36Sopenharmony_ci rc = be_cmd_cq_create(adapter, cq, eq, false, 3); 315162306a36Sopenharmony_ci if (rc) 315262306a36Sopenharmony_ci return rc; 315362306a36Sopenharmony_ci } 315462306a36Sopenharmony_ci 315562306a36Sopenharmony_ci dev_info(&adapter->pdev->dev, 315662306a36Sopenharmony_ci "created %d RX queue(s)\n", adapter->num_rx_qs); 315762306a36Sopenharmony_ci return 0; 315862306a36Sopenharmony_ci} 315962306a36Sopenharmony_ci 316062306a36Sopenharmony_cistatic irqreturn_t be_intx(int irq, void *dev) 316162306a36Sopenharmony_ci{ 316262306a36Sopenharmony_ci struct be_eq_obj *eqo = dev; 316362306a36Sopenharmony_ci struct be_adapter *adapter = eqo->adapter; 316462306a36Sopenharmony_ci int num_evts = 0; 316562306a36Sopenharmony_ci 316662306a36Sopenharmony_ci /* IRQ is not expected when NAPI is scheduled as the EQ 316762306a36Sopenharmony_ci * will not be armed. 316862306a36Sopenharmony_ci * But, this can happen on Lancer INTx where it takes 316962306a36Sopenharmony_ci * a while to de-assert INTx or in BE2 where occasionaly 317062306a36Sopenharmony_ci * an interrupt may be raised even when EQ is unarmed. 317162306a36Sopenharmony_ci * If NAPI is already scheduled, then counting & notifying 317262306a36Sopenharmony_ci * events will orphan them. 317362306a36Sopenharmony_ci */ 317462306a36Sopenharmony_ci if (napi_schedule_prep(&eqo->napi)) { 317562306a36Sopenharmony_ci num_evts = events_get(eqo); 317662306a36Sopenharmony_ci __napi_schedule(&eqo->napi); 317762306a36Sopenharmony_ci if (num_evts) 317862306a36Sopenharmony_ci eqo->spurious_intr = 0; 317962306a36Sopenharmony_ci } 318062306a36Sopenharmony_ci be_eq_notify(adapter, eqo->q.id, false, true, num_evts, 0); 318162306a36Sopenharmony_ci 318262306a36Sopenharmony_ci /* Return IRQ_HANDLED only for the first spurious intr 318362306a36Sopenharmony_ci * after a valid intr to stop the kernel from branding 318462306a36Sopenharmony_ci * this irq as a bad one! 318562306a36Sopenharmony_ci */ 318662306a36Sopenharmony_ci if (num_evts || eqo->spurious_intr++ == 0) 318762306a36Sopenharmony_ci return IRQ_HANDLED; 318862306a36Sopenharmony_ci else 318962306a36Sopenharmony_ci return IRQ_NONE; 319062306a36Sopenharmony_ci} 319162306a36Sopenharmony_ci 319262306a36Sopenharmony_cistatic irqreturn_t be_msix(int irq, void *dev) 319362306a36Sopenharmony_ci{ 319462306a36Sopenharmony_ci struct be_eq_obj *eqo = dev; 319562306a36Sopenharmony_ci 319662306a36Sopenharmony_ci be_eq_notify(eqo->adapter, eqo->q.id, false, true, 0, 0); 319762306a36Sopenharmony_ci napi_schedule(&eqo->napi); 319862306a36Sopenharmony_ci return IRQ_HANDLED; 319962306a36Sopenharmony_ci} 320062306a36Sopenharmony_ci 320162306a36Sopenharmony_cistatic inline bool do_gro(struct be_rx_compl_info *rxcp) 320262306a36Sopenharmony_ci{ 320362306a36Sopenharmony_ci return (rxcp->tcpf && !rxcp->err && rxcp->l4_csum) ? true : false; 320462306a36Sopenharmony_ci} 320562306a36Sopenharmony_ci 320662306a36Sopenharmony_cistatic int be_process_rx(struct be_rx_obj *rxo, struct napi_struct *napi, 320762306a36Sopenharmony_ci int budget) 320862306a36Sopenharmony_ci{ 320962306a36Sopenharmony_ci struct be_adapter *adapter = rxo->adapter; 321062306a36Sopenharmony_ci struct be_queue_info *rx_cq = &rxo->cq; 321162306a36Sopenharmony_ci struct be_rx_compl_info *rxcp; 321262306a36Sopenharmony_ci u32 work_done; 321362306a36Sopenharmony_ci u32 frags_consumed = 0; 321462306a36Sopenharmony_ci 321562306a36Sopenharmony_ci for (work_done = 0; work_done < budget; work_done++) { 321662306a36Sopenharmony_ci rxcp = be_rx_compl_get(rxo); 321762306a36Sopenharmony_ci if (!rxcp) 321862306a36Sopenharmony_ci break; 321962306a36Sopenharmony_ci 322062306a36Sopenharmony_ci /* Is it a flush compl that has no data */ 322162306a36Sopenharmony_ci if (unlikely(rxcp->num_rcvd == 0)) 322262306a36Sopenharmony_ci goto loop_continue; 322362306a36Sopenharmony_ci 322462306a36Sopenharmony_ci /* Discard compl with partial DMA Lancer B0 */ 322562306a36Sopenharmony_ci if (unlikely(!rxcp->pkt_size)) { 322662306a36Sopenharmony_ci be_rx_compl_discard(rxo, rxcp); 322762306a36Sopenharmony_ci goto loop_continue; 322862306a36Sopenharmony_ci } 322962306a36Sopenharmony_ci 323062306a36Sopenharmony_ci /* On BE drop pkts that arrive due to imperfect filtering in 323162306a36Sopenharmony_ci * promiscuous mode on some skews 323262306a36Sopenharmony_ci */ 323362306a36Sopenharmony_ci if (unlikely(rxcp->port != adapter->port_num && 323462306a36Sopenharmony_ci !lancer_chip(adapter))) { 323562306a36Sopenharmony_ci be_rx_compl_discard(rxo, rxcp); 323662306a36Sopenharmony_ci goto loop_continue; 323762306a36Sopenharmony_ci } 323862306a36Sopenharmony_ci 323962306a36Sopenharmony_ci if (do_gro(rxcp)) 324062306a36Sopenharmony_ci be_rx_compl_process_gro(rxo, napi, rxcp); 324162306a36Sopenharmony_ci else 324262306a36Sopenharmony_ci be_rx_compl_process(rxo, napi, rxcp); 324362306a36Sopenharmony_ci 324462306a36Sopenharmony_ciloop_continue: 324562306a36Sopenharmony_ci frags_consumed += rxcp->num_rcvd; 324662306a36Sopenharmony_ci be_rx_stats_update(rxo, rxcp); 324762306a36Sopenharmony_ci } 324862306a36Sopenharmony_ci 324962306a36Sopenharmony_ci if (work_done) { 325062306a36Sopenharmony_ci be_cq_notify(adapter, rx_cq->id, true, work_done); 325162306a36Sopenharmony_ci 325262306a36Sopenharmony_ci /* When an rx-obj gets into post_starved state, just 325362306a36Sopenharmony_ci * let be_worker do the posting. 325462306a36Sopenharmony_ci */ 325562306a36Sopenharmony_ci if (atomic_read(&rxo->q.used) < RX_FRAGS_REFILL_WM && 325662306a36Sopenharmony_ci !rxo->rx_post_starved) 325762306a36Sopenharmony_ci be_post_rx_frags(rxo, GFP_ATOMIC, 325862306a36Sopenharmony_ci max_t(u32, MAX_RX_POST, 325962306a36Sopenharmony_ci frags_consumed)); 326062306a36Sopenharmony_ci } 326162306a36Sopenharmony_ci 326262306a36Sopenharmony_ci return work_done; 326362306a36Sopenharmony_ci} 326462306a36Sopenharmony_ci 326562306a36Sopenharmony_ci 326662306a36Sopenharmony_cistatic void be_process_tx(struct be_adapter *adapter, struct be_tx_obj *txo, 326762306a36Sopenharmony_ci int idx) 326862306a36Sopenharmony_ci{ 326962306a36Sopenharmony_ci int num_wrbs = 0, work_done = 0; 327062306a36Sopenharmony_ci struct be_tx_compl_info *txcp; 327162306a36Sopenharmony_ci 327262306a36Sopenharmony_ci while ((txcp = be_tx_compl_get(adapter, txo))) { 327362306a36Sopenharmony_ci num_wrbs += be_tx_compl_process(adapter, txo, txcp->end_index); 327462306a36Sopenharmony_ci work_done++; 327562306a36Sopenharmony_ci } 327662306a36Sopenharmony_ci 327762306a36Sopenharmony_ci if (work_done) { 327862306a36Sopenharmony_ci be_cq_notify(adapter, txo->cq.id, true, work_done); 327962306a36Sopenharmony_ci atomic_sub(num_wrbs, &txo->q.used); 328062306a36Sopenharmony_ci 328162306a36Sopenharmony_ci /* As Tx wrbs have been freed up, wake up netdev queue 328262306a36Sopenharmony_ci * if it was stopped due to lack of tx wrbs. */ 328362306a36Sopenharmony_ci if (__netif_subqueue_stopped(adapter->netdev, idx) && 328462306a36Sopenharmony_ci be_can_txq_wake(txo)) { 328562306a36Sopenharmony_ci netif_wake_subqueue(adapter->netdev, idx); 328662306a36Sopenharmony_ci } 328762306a36Sopenharmony_ci 328862306a36Sopenharmony_ci u64_stats_update_begin(&tx_stats(txo)->sync_compl); 328962306a36Sopenharmony_ci tx_stats(txo)->tx_compl += work_done; 329062306a36Sopenharmony_ci u64_stats_update_end(&tx_stats(txo)->sync_compl); 329162306a36Sopenharmony_ci } 329262306a36Sopenharmony_ci} 329362306a36Sopenharmony_ci 329462306a36Sopenharmony_ciint be_poll(struct napi_struct *napi, int budget) 329562306a36Sopenharmony_ci{ 329662306a36Sopenharmony_ci struct be_eq_obj *eqo = container_of(napi, struct be_eq_obj, napi); 329762306a36Sopenharmony_ci struct be_adapter *adapter = eqo->adapter; 329862306a36Sopenharmony_ci int max_work = 0, work, i, num_evts; 329962306a36Sopenharmony_ci struct be_rx_obj *rxo; 330062306a36Sopenharmony_ci struct be_tx_obj *txo; 330162306a36Sopenharmony_ci u32 mult_enc = 0; 330262306a36Sopenharmony_ci 330362306a36Sopenharmony_ci num_evts = events_get(eqo); 330462306a36Sopenharmony_ci 330562306a36Sopenharmony_ci for_all_tx_queues_on_eq(adapter, eqo, txo, i) 330662306a36Sopenharmony_ci be_process_tx(adapter, txo, i); 330762306a36Sopenharmony_ci 330862306a36Sopenharmony_ci /* This loop will iterate twice for EQ0 in which 330962306a36Sopenharmony_ci * completions of the last RXQ (default one) are also processed 331062306a36Sopenharmony_ci * For other EQs the loop iterates only once 331162306a36Sopenharmony_ci */ 331262306a36Sopenharmony_ci for_all_rx_queues_on_eq(adapter, eqo, rxo, i) { 331362306a36Sopenharmony_ci work = be_process_rx(rxo, napi, budget); 331462306a36Sopenharmony_ci max_work = max(work, max_work); 331562306a36Sopenharmony_ci } 331662306a36Sopenharmony_ci 331762306a36Sopenharmony_ci if (is_mcc_eqo(eqo)) 331862306a36Sopenharmony_ci be_process_mcc(adapter); 331962306a36Sopenharmony_ci 332062306a36Sopenharmony_ci if (max_work < budget) { 332162306a36Sopenharmony_ci napi_complete_done(napi, max_work); 332262306a36Sopenharmony_ci 332362306a36Sopenharmony_ci /* Skyhawk EQ_DB has a provision to set the rearm to interrupt 332462306a36Sopenharmony_ci * delay via a delay multiplier encoding value 332562306a36Sopenharmony_ci */ 332662306a36Sopenharmony_ci if (skyhawk_chip(adapter)) 332762306a36Sopenharmony_ci mult_enc = be_get_eq_delay_mult_enc(eqo); 332862306a36Sopenharmony_ci 332962306a36Sopenharmony_ci be_eq_notify(adapter, eqo->q.id, true, false, num_evts, 333062306a36Sopenharmony_ci mult_enc); 333162306a36Sopenharmony_ci } else { 333262306a36Sopenharmony_ci /* As we'll continue in polling mode, count and clear events */ 333362306a36Sopenharmony_ci be_eq_notify(adapter, eqo->q.id, false, false, num_evts, 0); 333462306a36Sopenharmony_ci } 333562306a36Sopenharmony_ci return max_work; 333662306a36Sopenharmony_ci} 333762306a36Sopenharmony_ci 333862306a36Sopenharmony_civoid be_detect_error(struct be_adapter *adapter) 333962306a36Sopenharmony_ci{ 334062306a36Sopenharmony_ci u32 ue_lo = 0, ue_hi = 0, ue_lo_mask = 0, ue_hi_mask = 0; 334162306a36Sopenharmony_ci u32 sliport_status = 0, sliport_err1 = 0, sliport_err2 = 0; 334262306a36Sopenharmony_ci struct device *dev = &adapter->pdev->dev; 334362306a36Sopenharmony_ci u16 val; 334462306a36Sopenharmony_ci u32 i; 334562306a36Sopenharmony_ci 334662306a36Sopenharmony_ci if (be_check_error(adapter, BE_ERROR_HW)) 334762306a36Sopenharmony_ci return; 334862306a36Sopenharmony_ci 334962306a36Sopenharmony_ci if (lancer_chip(adapter)) { 335062306a36Sopenharmony_ci sliport_status = ioread32(adapter->db + SLIPORT_STATUS_OFFSET); 335162306a36Sopenharmony_ci if (sliport_status & SLIPORT_STATUS_ERR_MASK) { 335262306a36Sopenharmony_ci be_set_error(adapter, BE_ERROR_UE); 335362306a36Sopenharmony_ci sliport_err1 = ioread32(adapter->db + 335462306a36Sopenharmony_ci SLIPORT_ERROR1_OFFSET); 335562306a36Sopenharmony_ci sliport_err2 = ioread32(adapter->db + 335662306a36Sopenharmony_ci SLIPORT_ERROR2_OFFSET); 335762306a36Sopenharmony_ci /* Do not log error messages if its a FW reset */ 335862306a36Sopenharmony_ci if (sliport_err1 == SLIPORT_ERROR_FW_RESET1 && 335962306a36Sopenharmony_ci sliport_err2 == SLIPORT_ERROR_FW_RESET2) { 336062306a36Sopenharmony_ci dev_info(dev, "Reset is in progress\n"); 336162306a36Sopenharmony_ci } else { 336262306a36Sopenharmony_ci dev_err(dev, "Error detected in the card\n"); 336362306a36Sopenharmony_ci dev_err(dev, "ERR: sliport status 0x%x\n", 336462306a36Sopenharmony_ci sliport_status); 336562306a36Sopenharmony_ci dev_err(dev, "ERR: sliport error1 0x%x\n", 336662306a36Sopenharmony_ci sliport_err1); 336762306a36Sopenharmony_ci dev_err(dev, "ERR: sliport error2 0x%x\n", 336862306a36Sopenharmony_ci sliport_err2); 336962306a36Sopenharmony_ci } 337062306a36Sopenharmony_ci } 337162306a36Sopenharmony_ci } else { 337262306a36Sopenharmony_ci ue_lo = ioread32(adapter->pcicfg + PCICFG_UE_STATUS_LOW); 337362306a36Sopenharmony_ci ue_hi = ioread32(adapter->pcicfg + PCICFG_UE_STATUS_HIGH); 337462306a36Sopenharmony_ci ue_lo_mask = ioread32(adapter->pcicfg + 337562306a36Sopenharmony_ci PCICFG_UE_STATUS_LOW_MASK); 337662306a36Sopenharmony_ci ue_hi_mask = ioread32(adapter->pcicfg + 337762306a36Sopenharmony_ci PCICFG_UE_STATUS_HI_MASK); 337862306a36Sopenharmony_ci 337962306a36Sopenharmony_ci ue_lo = (ue_lo & ~ue_lo_mask); 338062306a36Sopenharmony_ci ue_hi = (ue_hi & ~ue_hi_mask); 338162306a36Sopenharmony_ci 338262306a36Sopenharmony_ci if (ue_lo || ue_hi) { 338362306a36Sopenharmony_ci /* On certain platforms BE3 hardware can indicate 338462306a36Sopenharmony_ci * spurious UEs. In case of a UE in the chip, 338562306a36Sopenharmony_ci * the POST register correctly reports either a 338662306a36Sopenharmony_ci * FAT_LOG_START state (FW is currently dumping 338762306a36Sopenharmony_ci * FAT log data) or a ARMFW_UE state. Check for the 338862306a36Sopenharmony_ci * above states to ascertain if the UE is valid or not. 338962306a36Sopenharmony_ci */ 339062306a36Sopenharmony_ci if (BE3_chip(adapter)) { 339162306a36Sopenharmony_ci val = be_POST_stage_get(adapter); 339262306a36Sopenharmony_ci if ((val & POST_STAGE_FAT_LOG_START) 339362306a36Sopenharmony_ci != POST_STAGE_FAT_LOG_START && 339462306a36Sopenharmony_ci (val & POST_STAGE_ARMFW_UE) 339562306a36Sopenharmony_ci != POST_STAGE_ARMFW_UE && 339662306a36Sopenharmony_ci (val & POST_STAGE_RECOVERABLE_ERR) 339762306a36Sopenharmony_ci != POST_STAGE_RECOVERABLE_ERR) 339862306a36Sopenharmony_ci return; 339962306a36Sopenharmony_ci } 340062306a36Sopenharmony_ci 340162306a36Sopenharmony_ci dev_err(dev, "Error detected in the adapter"); 340262306a36Sopenharmony_ci be_set_error(adapter, BE_ERROR_UE); 340362306a36Sopenharmony_ci 340462306a36Sopenharmony_ci for (i = 0; ue_lo; ue_lo >>= 1, i++) { 340562306a36Sopenharmony_ci if (ue_lo & 1) 340662306a36Sopenharmony_ci dev_err(dev, "UE: %s bit set\n", 340762306a36Sopenharmony_ci ue_status_low_desc[i]); 340862306a36Sopenharmony_ci } 340962306a36Sopenharmony_ci for (i = 0; ue_hi; ue_hi >>= 1, i++) { 341062306a36Sopenharmony_ci if (ue_hi & 1) 341162306a36Sopenharmony_ci dev_err(dev, "UE: %s bit set\n", 341262306a36Sopenharmony_ci ue_status_hi_desc[i]); 341362306a36Sopenharmony_ci } 341462306a36Sopenharmony_ci } 341562306a36Sopenharmony_ci } 341662306a36Sopenharmony_ci} 341762306a36Sopenharmony_ci 341862306a36Sopenharmony_cistatic void be_msix_disable(struct be_adapter *adapter) 341962306a36Sopenharmony_ci{ 342062306a36Sopenharmony_ci if (msix_enabled(adapter)) { 342162306a36Sopenharmony_ci pci_disable_msix(adapter->pdev); 342262306a36Sopenharmony_ci adapter->num_msix_vec = 0; 342362306a36Sopenharmony_ci adapter->num_msix_roce_vec = 0; 342462306a36Sopenharmony_ci } 342562306a36Sopenharmony_ci} 342662306a36Sopenharmony_ci 342762306a36Sopenharmony_cistatic int be_msix_enable(struct be_adapter *adapter) 342862306a36Sopenharmony_ci{ 342962306a36Sopenharmony_ci unsigned int i, max_roce_eqs; 343062306a36Sopenharmony_ci struct device *dev = &adapter->pdev->dev; 343162306a36Sopenharmony_ci int num_vec; 343262306a36Sopenharmony_ci 343362306a36Sopenharmony_ci /* If RoCE is supported, program the max number of vectors that 343462306a36Sopenharmony_ci * could be used for NIC and RoCE, else, just program the number 343562306a36Sopenharmony_ci * we'll use initially. 343662306a36Sopenharmony_ci */ 343762306a36Sopenharmony_ci if (be_roce_supported(adapter)) { 343862306a36Sopenharmony_ci max_roce_eqs = 343962306a36Sopenharmony_ci be_max_func_eqs(adapter) - be_max_nic_eqs(adapter); 344062306a36Sopenharmony_ci max_roce_eqs = min(max_roce_eqs, num_online_cpus()); 344162306a36Sopenharmony_ci num_vec = be_max_any_irqs(adapter) + max_roce_eqs; 344262306a36Sopenharmony_ci } else { 344362306a36Sopenharmony_ci num_vec = max(adapter->cfg_num_rx_irqs, 344462306a36Sopenharmony_ci adapter->cfg_num_tx_irqs); 344562306a36Sopenharmony_ci } 344662306a36Sopenharmony_ci 344762306a36Sopenharmony_ci for (i = 0; i < num_vec; i++) 344862306a36Sopenharmony_ci adapter->msix_entries[i].entry = i; 344962306a36Sopenharmony_ci 345062306a36Sopenharmony_ci num_vec = pci_enable_msix_range(adapter->pdev, adapter->msix_entries, 345162306a36Sopenharmony_ci MIN_MSIX_VECTORS, num_vec); 345262306a36Sopenharmony_ci if (num_vec < 0) 345362306a36Sopenharmony_ci goto fail; 345462306a36Sopenharmony_ci 345562306a36Sopenharmony_ci if (be_roce_supported(adapter) && num_vec > MIN_MSIX_VECTORS) { 345662306a36Sopenharmony_ci adapter->num_msix_roce_vec = num_vec / 2; 345762306a36Sopenharmony_ci dev_info(dev, "enabled %d MSI-x vector(s) for RoCE\n", 345862306a36Sopenharmony_ci adapter->num_msix_roce_vec); 345962306a36Sopenharmony_ci } 346062306a36Sopenharmony_ci 346162306a36Sopenharmony_ci adapter->num_msix_vec = num_vec - adapter->num_msix_roce_vec; 346262306a36Sopenharmony_ci 346362306a36Sopenharmony_ci dev_info(dev, "enabled %d MSI-x vector(s) for NIC\n", 346462306a36Sopenharmony_ci adapter->num_msix_vec); 346562306a36Sopenharmony_ci return 0; 346662306a36Sopenharmony_ci 346762306a36Sopenharmony_cifail: 346862306a36Sopenharmony_ci dev_warn(dev, "MSIx enable failed\n"); 346962306a36Sopenharmony_ci 347062306a36Sopenharmony_ci /* INTx is not supported in VFs, so fail probe if enable_msix fails */ 347162306a36Sopenharmony_ci if (be_virtfn(adapter)) 347262306a36Sopenharmony_ci return num_vec; 347362306a36Sopenharmony_ci return 0; 347462306a36Sopenharmony_ci} 347562306a36Sopenharmony_ci 347662306a36Sopenharmony_cistatic inline int be_msix_vec_get(struct be_adapter *adapter, 347762306a36Sopenharmony_ci struct be_eq_obj *eqo) 347862306a36Sopenharmony_ci{ 347962306a36Sopenharmony_ci return adapter->msix_entries[eqo->msix_idx].vector; 348062306a36Sopenharmony_ci} 348162306a36Sopenharmony_ci 348262306a36Sopenharmony_cistatic int be_msix_register(struct be_adapter *adapter) 348362306a36Sopenharmony_ci{ 348462306a36Sopenharmony_ci struct net_device *netdev = adapter->netdev; 348562306a36Sopenharmony_ci struct be_eq_obj *eqo; 348662306a36Sopenharmony_ci int status, i, vec; 348762306a36Sopenharmony_ci 348862306a36Sopenharmony_ci for_all_evt_queues(adapter, eqo, i) { 348962306a36Sopenharmony_ci sprintf(eqo->desc, "%s-q%d", netdev->name, i); 349062306a36Sopenharmony_ci vec = be_msix_vec_get(adapter, eqo); 349162306a36Sopenharmony_ci status = request_irq(vec, be_msix, 0, eqo->desc, eqo); 349262306a36Sopenharmony_ci if (status) 349362306a36Sopenharmony_ci goto err_msix; 349462306a36Sopenharmony_ci 349562306a36Sopenharmony_ci irq_update_affinity_hint(vec, eqo->affinity_mask); 349662306a36Sopenharmony_ci } 349762306a36Sopenharmony_ci 349862306a36Sopenharmony_ci return 0; 349962306a36Sopenharmony_cierr_msix: 350062306a36Sopenharmony_ci for (i--; i >= 0; i--) { 350162306a36Sopenharmony_ci eqo = &adapter->eq_obj[i]; 350262306a36Sopenharmony_ci free_irq(be_msix_vec_get(adapter, eqo), eqo); 350362306a36Sopenharmony_ci } 350462306a36Sopenharmony_ci dev_warn(&adapter->pdev->dev, "MSIX Request IRQ failed - err %d\n", 350562306a36Sopenharmony_ci status); 350662306a36Sopenharmony_ci be_msix_disable(adapter); 350762306a36Sopenharmony_ci return status; 350862306a36Sopenharmony_ci} 350962306a36Sopenharmony_ci 351062306a36Sopenharmony_cistatic int be_irq_register(struct be_adapter *adapter) 351162306a36Sopenharmony_ci{ 351262306a36Sopenharmony_ci struct net_device *netdev = adapter->netdev; 351362306a36Sopenharmony_ci int status; 351462306a36Sopenharmony_ci 351562306a36Sopenharmony_ci if (msix_enabled(adapter)) { 351662306a36Sopenharmony_ci status = be_msix_register(adapter); 351762306a36Sopenharmony_ci if (status == 0) 351862306a36Sopenharmony_ci goto done; 351962306a36Sopenharmony_ci /* INTx is not supported for VF */ 352062306a36Sopenharmony_ci if (be_virtfn(adapter)) 352162306a36Sopenharmony_ci return status; 352262306a36Sopenharmony_ci } 352362306a36Sopenharmony_ci 352462306a36Sopenharmony_ci /* INTx: only the first EQ is used */ 352562306a36Sopenharmony_ci netdev->irq = adapter->pdev->irq; 352662306a36Sopenharmony_ci status = request_irq(netdev->irq, be_intx, IRQF_SHARED, netdev->name, 352762306a36Sopenharmony_ci &adapter->eq_obj[0]); 352862306a36Sopenharmony_ci if (status) { 352962306a36Sopenharmony_ci dev_err(&adapter->pdev->dev, 353062306a36Sopenharmony_ci "INTx request IRQ failed - err %d\n", status); 353162306a36Sopenharmony_ci return status; 353262306a36Sopenharmony_ci } 353362306a36Sopenharmony_cidone: 353462306a36Sopenharmony_ci adapter->isr_registered = true; 353562306a36Sopenharmony_ci return 0; 353662306a36Sopenharmony_ci} 353762306a36Sopenharmony_ci 353862306a36Sopenharmony_cistatic void be_irq_unregister(struct be_adapter *adapter) 353962306a36Sopenharmony_ci{ 354062306a36Sopenharmony_ci struct net_device *netdev = adapter->netdev; 354162306a36Sopenharmony_ci struct be_eq_obj *eqo; 354262306a36Sopenharmony_ci int i, vec; 354362306a36Sopenharmony_ci 354462306a36Sopenharmony_ci if (!adapter->isr_registered) 354562306a36Sopenharmony_ci return; 354662306a36Sopenharmony_ci 354762306a36Sopenharmony_ci /* INTx */ 354862306a36Sopenharmony_ci if (!msix_enabled(adapter)) { 354962306a36Sopenharmony_ci free_irq(netdev->irq, &adapter->eq_obj[0]); 355062306a36Sopenharmony_ci goto done; 355162306a36Sopenharmony_ci } 355262306a36Sopenharmony_ci 355362306a36Sopenharmony_ci /* MSIx */ 355462306a36Sopenharmony_ci for_all_evt_queues(adapter, eqo, i) { 355562306a36Sopenharmony_ci vec = be_msix_vec_get(adapter, eqo); 355662306a36Sopenharmony_ci irq_update_affinity_hint(vec, NULL); 355762306a36Sopenharmony_ci free_irq(vec, eqo); 355862306a36Sopenharmony_ci } 355962306a36Sopenharmony_ci 356062306a36Sopenharmony_cidone: 356162306a36Sopenharmony_ci adapter->isr_registered = false; 356262306a36Sopenharmony_ci} 356362306a36Sopenharmony_ci 356462306a36Sopenharmony_cistatic void be_rx_qs_destroy(struct be_adapter *adapter) 356562306a36Sopenharmony_ci{ 356662306a36Sopenharmony_ci struct rss_info *rss = &adapter->rss_info; 356762306a36Sopenharmony_ci struct be_queue_info *q; 356862306a36Sopenharmony_ci struct be_rx_obj *rxo; 356962306a36Sopenharmony_ci int i; 357062306a36Sopenharmony_ci 357162306a36Sopenharmony_ci for_all_rx_queues(adapter, rxo, i) { 357262306a36Sopenharmony_ci q = &rxo->q; 357362306a36Sopenharmony_ci if (q->created) { 357462306a36Sopenharmony_ci /* If RXQs are destroyed while in an "out of buffer" 357562306a36Sopenharmony_ci * state, there is a possibility of an HW stall on 357662306a36Sopenharmony_ci * Lancer. So, post 64 buffers to each queue to relieve 357762306a36Sopenharmony_ci * the "out of buffer" condition. 357862306a36Sopenharmony_ci * Make sure there's space in the RXQ before posting. 357962306a36Sopenharmony_ci */ 358062306a36Sopenharmony_ci if (lancer_chip(adapter)) { 358162306a36Sopenharmony_ci be_rx_cq_clean(rxo); 358262306a36Sopenharmony_ci if (atomic_read(&q->used) == 0) 358362306a36Sopenharmony_ci be_post_rx_frags(rxo, GFP_KERNEL, 358462306a36Sopenharmony_ci MAX_RX_POST); 358562306a36Sopenharmony_ci } 358662306a36Sopenharmony_ci 358762306a36Sopenharmony_ci be_cmd_rxq_destroy(adapter, q); 358862306a36Sopenharmony_ci be_rx_cq_clean(rxo); 358962306a36Sopenharmony_ci be_rxq_clean(rxo); 359062306a36Sopenharmony_ci } 359162306a36Sopenharmony_ci be_queue_free(adapter, q); 359262306a36Sopenharmony_ci } 359362306a36Sopenharmony_ci 359462306a36Sopenharmony_ci if (rss->rss_flags) { 359562306a36Sopenharmony_ci rss->rss_flags = RSS_ENABLE_NONE; 359662306a36Sopenharmony_ci be_cmd_rss_config(adapter, rss->rsstable, rss->rss_flags, 359762306a36Sopenharmony_ci 128, rss->rss_hkey); 359862306a36Sopenharmony_ci } 359962306a36Sopenharmony_ci} 360062306a36Sopenharmony_ci 360162306a36Sopenharmony_cistatic void be_disable_if_filters(struct be_adapter *adapter) 360262306a36Sopenharmony_ci{ 360362306a36Sopenharmony_ci /* Don't delete MAC on BE3 VFs without FILTMGMT privilege */ 360462306a36Sopenharmony_ci if (!BEx_chip(adapter) || !be_virtfn(adapter) || 360562306a36Sopenharmony_ci check_privilege(adapter, BE_PRIV_FILTMGMT)) { 360662306a36Sopenharmony_ci be_dev_mac_del(adapter, adapter->pmac_id[0]); 360762306a36Sopenharmony_ci eth_zero_addr(adapter->dev_mac); 360862306a36Sopenharmony_ci } 360962306a36Sopenharmony_ci 361062306a36Sopenharmony_ci be_clear_uc_list(adapter); 361162306a36Sopenharmony_ci be_clear_mc_list(adapter); 361262306a36Sopenharmony_ci 361362306a36Sopenharmony_ci /* The IFACE flags are enabled in the open path and cleared 361462306a36Sopenharmony_ci * in the close path. When a VF gets detached from the host and 361562306a36Sopenharmony_ci * assigned to a VM the following happens: 361662306a36Sopenharmony_ci * - VF's IFACE flags get cleared in the detach path 361762306a36Sopenharmony_ci * - IFACE create is issued by the VF in the attach path 361862306a36Sopenharmony_ci * Due to a bug in the BE3/Skyhawk-R FW 361962306a36Sopenharmony_ci * (Lancer FW doesn't have the bug), the IFACE capability flags 362062306a36Sopenharmony_ci * specified along with the IFACE create cmd issued by a VF are not 362162306a36Sopenharmony_ci * honoured by FW. As a consequence, if a *new* driver 362262306a36Sopenharmony_ci * (that enables/disables IFACE flags in open/close) 362362306a36Sopenharmony_ci * is loaded in the host and an *old* driver is * used by a VM/VF, 362462306a36Sopenharmony_ci * the IFACE gets created *without* the needed flags. 362562306a36Sopenharmony_ci * To avoid this, disable RX-filter flags only for Lancer. 362662306a36Sopenharmony_ci */ 362762306a36Sopenharmony_ci if (lancer_chip(adapter)) { 362862306a36Sopenharmony_ci be_cmd_rx_filter(adapter, BE_IF_ALL_FILT_FLAGS, OFF); 362962306a36Sopenharmony_ci adapter->if_flags &= ~BE_IF_ALL_FILT_FLAGS; 363062306a36Sopenharmony_ci } 363162306a36Sopenharmony_ci} 363262306a36Sopenharmony_ci 363362306a36Sopenharmony_cistatic int be_close(struct net_device *netdev) 363462306a36Sopenharmony_ci{ 363562306a36Sopenharmony_ci struct be_adapter *adapter = netdev_priv(netdev); 363662306a36Sopenharmony_ci struct be_eq_obj *eqo; 363762306a36Sopenharmony_ci int i; 363862306a36Sopenharmony_ci 363962306a36Sopenharmony_ci /* This protection is needed as be_close() may be called even when the 364062306a36Sopenharmony_ci * adapter is in cleared state (after eeh perm failure) 364162306a36Sopenharmony_ci */ 364262306a36Sopenharmony_ci if (!(adapter->flags & BE_FLAGS_SETUP_DONE)) 364362306a36Sopenharmony_ci return 0; 364462306a36Sopenharmony_ci 364562306a36Sopenharmony_ci /* Before attempting cleanup ensure all the pending cmds in the 364662306a36Sopenharmony_ci * config_wq have finished execution 364762306a36Sopenharmony_ci */ 364862306a36Sopenharmony_ci flush_workqueue(be_wq); 364962306a36Sopenharmony_ci 365062306a36Sopenharmony_ci be_disable_if_filters(adapter); 365162306a36Sopenharmony_ci 365262306a36Sopenharmony_ci if (adapter->flags & BE_FLAGS_NAPI_ENABLED) { 365362306a36Sopenharmony_ci for_all_evt_queues(adapter, eqo, i) { 365462306a36Sopenharmony_ci napi_disable(&eqo->napi); 365562306a36Sopenharmony_ci } 365662306a36Sopenharmony_ci adapter->flags &= ~BE_FLAGS_NAPI_ENABLED; 365762306a36Sopenharmony_ci } 365862306a36Sopenharmony_ci 365962306a36Sopenharmony_ci be_async_mcc_disable(adapter); 366062306a36Sopenharmony_ci 366162306a36Sopenharmony_ci /* Wait for all pending tx completions to arrive so that 366262306a36Sopenharmony_ci * all tx skbs are freed. 366362306a36Sopenharmony_ci */ 366462306a36Sopenharmony_ci netif_tx_disable(netdev); 366562306a36Sopenharmony_ci be_tx_compl_clean(adapter); 366662306a36Sopenharmony_ci 366762306a36Sopenharmony_ci be_rx_qs_destroy(adapter); 366862306a36Sopenharmony_ci 366962306a36Sopenharmony_ci for_all_evt_queues(adapter, eqo, i) { 367062306a36Sopenharmony_ci if (msix_enabled(adapter)) 367162306a36Sopenharmony_ci synchronize_irq(be_msix_vec_get(adapter, eqo)); 367262306a36Sopenharmony_ci else 367362306a36Sopenharmony_ci synchronize_irq(netdev->irq); 367462306a36Sopenharmony_ci be_eq_clean(eqo); 367562306a36Sopenharmony_ci } 367662306a36Sopenharmony_ci 367762306a36Sopenharmony_ci be_irq_unregister(adapter); 367862306a36Sopenharmony_ci 367962306a36Sopenharmony_ci return 0; 368062306a36Sopenharmony_ci} 368162306a36Sopenharmony_ci 368262306a36Sopenharmony_cistatic int be_rx_qs_create(struct be_adapter *adapter) 368362306a36Sopenharmony_ci{ 368462306a36Sopenharmony_ci struct rss_info *rss = &adapter->rss_info; 368562306a36Sopenharmony_ci u8 rss_key[RSS_HASH_KEY_LEN]; 368662306a36Sopenharmony_ci struct be_rx_obj *rxo; 368762306a36Sopenharmony_ci int rc, i, j; 368862306a36Sopenharmony_ci 368962306a36Sopenharmony_ci for_all_rx_queues(adapter, rxo, i) { 369062306a36Sopenharmony_ci rc = be_queue_alloc(adapter, &rxo->q, RX_Q_LEN, 369162306a36Sopenharmony_ci sizeof(struct be_eth_rx_d)); 369262306a36Sopenharmony_ci if (rc) 369362306a36Sopenharmony_ci return rc; 369462306a36Sopenharmony_ci } 369562306a36Sopenharmony_ci 369662306a36Sopenharmony_ci if (adapter->need_def_rxq || !adapter->num_rss_qs) { 369762306a36Sopenharmony_ci rxo = default_rxo(adapter); 369862306a36Sopenharmony_ci rc = be_cmd_rxq_create(adapter, &rxo->q, rxo->cq.id, 369962306a36Sopenharmony_ci rx_frag_size, adapter->if_handle, 370062306a36Sopenharmony_ci false, &rxo->rss_id); 370162306a36Sopenharmony_ci if (rc) 370262306a36Sopenharmony_ci return rc; 370362306a36Sopenharmony_ci } 370462306a36Sopenharmony_ci 370562306a36Sopenharmony_ci for_all_rss_queues(adapter, rxo, i) { 370662306a36Sopenharmony_ci rc = be_cmd_rxq_create(adapter, &rxo->q, rxo->cq.id, 370762306a36Sopenharmony_ci rx_frag_size, adapter->if_handle, 370862306a36Sopenharmony_ci true, &rxo->rss_id); 370962306a36Sopenharmony_ci if (rc) 371062306a36Sopenharmony_ci return rc; 371162306a36Sopenharmony_ci } 371262306a36Sopenharmony_ci 371362306a36Sopenharmony_ci if (be_multi_rxq(adapter)) { 371462306a36Sopenharmony_ci for (j = 0; j < RSS_INDIR_TABLE_LEN; j += adapter->num_rss_qs) { 371562306a36Sopenharmony_ci for_all_rss_queues(adapter, rxo, i) { 371662306a36Sopenharmony_ci if ((j + i) >= RSS_INDIR_TABLE_LEN) 371762306a36Sopenharmony_ci break; 371862306a36Sopenharmony_ci rss->rsstable[j + i] = rxo->rss_id; 371962306a36Sopenharmony_ci rss->rss_queue[j + i] = i; 372062306a36Sopenharmony_ci } 372162306a36Sopenharmony_ci } 372262306a36Sopenharmony_ci rss->rss_flags = RSS_ENABLE_TCP_IPV4 | RSS_ENABLE_IPV4 | 372362306a36Sopenharmony_ci RSS_ENABLE_TCP_IPV6 | RSS_ENABLE_IPV6; 372462306a36Sopenharmony_ci 372562306a36Sopenharmony_ci if (!BEx_chip(adapter)) 372662306a36Sopenharmony_ci rss->rss_flags |= RSS_ENABLE_UDP_IPV4 | 372762306a36Sopenharmony_ci RSS_ENABLE_UDP_IPV6; 372862306a36Sopenharmony_ci 372962306a36Sopenharmony_ci netdev_rss_key_fill(rss_key, RSS_HASH_KEY_LEN); 373062306a36Sopenharmony_ci rc = be_cmd_rss_config(adapter, rss->rsstable, rss->rss_flags, 373162306a36Sopenharmony_ci RSS_INDIR_TABLE_LEN, rss_key); 373262306a36Sopenharmony_ci if (rc) { 373362306a36Sopenharmony_ci rss->rss_flags = RSS_ENABLE_NONE; 373462306a36Sopenharmony_ci return rc; 373562306a36Sopenharmony_ci } 373662306a36Sopenharmony_ci 373762306a36Sopenharmony_ci memcpy(rss->rss_hkey, rss_key, RSS_HASH_KEY_LEN); 373862306a36Sopenharmony_ci } else { 373962306a36Sopenharmony_ci /* Disable RSS, if only default RX Q is created */ 374062306a36Sopenharmony_ci rss->rss_flags = RSS_ENABLE_NONE; 374162306a36Sopenharmony_ci } 374262306a36Sopenharmony_ci 374362306a36Sopenharmony_ci 374462306a36Sopenharmony_ci /* Post 1 less than RXQ-len to avoid head being equal to tail, 374562306a36Sopenharmony_ci * which is a queue empty condition 374662306a36Sopenharmony_ci */ 374762306a36Sopenharmony_ci for_all_rx_queues(adapter, rxo, i) 374862306a36Sopenharmony_ci be_post_rx_frags(rxo, GFP_KERNEL, RX_Q_LEN - 1); 374962306a36Sopenharmony_ci 375062306a36Sopenharmony_ci return 0; 375162306a36Sopenharmony_ci} 375262306a36Sopenharmony_ci 375362306a36Sopenharmony_cistatic int be_enable_if_filters(struct be_adapter *adapter) 375462306a36Sopenharmony_ci{ 375562306a36Sopenharmony_ci int status; 375662306a36Sopenharmony_ci 375762306a36Sopenharmony_ci status = be_cmd_rx_filter(adapter, BE_IF_FILT_FLAGS_BASIC, ON); 375862306a36Sopenharmony_ci if (status) 375962306a36Sopenharmony_ci return status; 376062306a36Sopenharmony_ci 376162306a36Sopenharmony_ci /* Normally this condition usually true as the ->dev_mac is zeroed. 376262306a36Sopenharmony_ci * But on BE3 VFs the initial MAC is pre-programmed by PF and 376362306a36Sopenharmony_ci * subsequent be_dev_mac_add() can fail (after fresh boot) 376462306a36Sopenharmony_ci */ 376562306a36Sopenharmony_ci if (!ether_addr_equal(adapter->dev_mac, adapter->netdev->dev_addr)) { 376662306a36Sopenharmony_ci int old_pmac_id = -1; 376762306a36Sopenharmony_ci 376862306a36Sopenharmony_ci /* Remember old programmed MAC if any - can happen on BE3 VF */ 376962306a36Sopenharmony_ci if (!is_zero_ether_addr(adapter->dev_mac)) 377062306a36Sopenharmony_ci old_pmac_id = adapter->pmac_id[0]; 377162306a36Sopenharmony_ci 377262306a36Sopenharmony_ci status = be_dev_mac_add(adapter, adapter->netdev->dev_addr); 377362306a36Sopenharmony_ci if (status) 377462306a36Sopenharmony_ci return status; 377562306a36Sopenharmony_ci 377662306a36Sopenharmony_ci /* Delete the old programmed MAC as we successfully programmed 377762306a36Sopenharmony_ci * a new MAC 377862306a36Sopenharmony_ci */ 377962306a36Sopenharmony_ci if (old_pmac_id >= 0 && old_pmac_id != adapter->pmac_id[0]) 378062306a36Sopenharmony_ci be_dev_mac_del(adapter, old_pmac_id); 378162306a36Sopenharmony_ci 378262306a36Sopenharmony_ci ether_addr_copy(adapter->dev_mac, adapter->netdev->dev_addr); 378362306a36Sopenharmony_ci } 378462306a36Sopenharmony_ci 378562306a36Sopenharmony_ci if (adapter->vlans_added) 378662306a36Sopenharmony_ci be_vid_config(adapter); 378762306a36Sopenharmony_ci 378862306a36Sopenharmony_ci __be_set_rx_mode(adapter); 378962306a36Sopenharmony_ci 379062306a36Sopenharmony_ci return 0; 379162306a36Sopenharmony_ci} 379262306a36Sopenharmony_ci 379362306a36Sopenharmony_cistatic int be_open(struct net_device *netdev) 379462306a36Sopenharmony_ci{ 379562306a36Sopenharmony_ci struct be_adapter *adapter = netdev_priv(netdev); 379662306a36Sopenharmony_ci struct be_eq_obj *eqo; 379762306a36Sopenharmony_ci struct be_rx_obj *rxo; 379862306a36Sopenharmony_ci struct be_tx_obj *txo; 379962306a36Sopenharmony_ci u8 link_status; 380062306a36Sopenharmony_ci int status, i; 380162306a36Sopenharmony_ci 380262306a36Sopenharmony_ci status = be_rx_qs_create(adapter); 380362306a36Sopenharmony_ci if (status) 380462306a36Sopenharmony_ci goto err; 380562306a36Sopenharmony_ci 380662306a36Sopenharmony_ci status = be_enable_if_filters(adapter); 380762306a36Sopenharmony_ci if (status) 380862306a36Sopenharmony_ci goto err; 380962306a36Sopenharmony_ci 381062306a36Sopenharmony_ci status = be_irq_register(adapter); 381162306a36Sopenharmony_ci if (status) 381262306a36Sopenharmony_ci goto err; 381362306a36Sopenharmony_ci 381462306a36Sopenharmony_ci for_all_rx_queues(adapter, rxo, i) 381562306a36Sopenharmony_ci be_cq_notify(adapter, rxo->cq.id, true, 0); 381662306a36Sopenharmony_ci 381762306a36Sopenharmony_ci for_all_tx_queues(adapter, txo, i) 381862306a36Sopenharmony_ci be_cq_notify(adapter, txo->cq.id, true, 0); 381962306a36Sopenharmony_ci 382062306a36Sopenharmony_ci be_async_mcc_enable(adapter); 382162306a36Sopenharmony_ci 382262306a36Sopenharmony_ci for_all_evt_queues(adapter, eqo, i) { 382362306a36Sopenharmony_ci napi_enable(&eqo->napi); 382462306a36Sopenharmony_ci be_eq_notify(adapter, eqo->q.id, true, true, 0, 0); 382562306a36Sopenharmony_ci } 382662306a36Sopenharmony_ci adapter->flags |= BE_FLAGS_NAPI_ENABLED; 382762306a36Sopenharmony_ci 382862306a36Sopenharmony_ci status = be_cmd_link_status_query(adapter, NULL, &link_status, 0); 382962306a36Sopenharmony_ci if (!status) 383062306a36Sopenharmony_ci be_link_status_update(adapter, link_status); 383162306a36Sopenharmony_ci 383262306a36Sopenharmony_ci netif_tx_start_all_queues(netdev); 383362306a36Sopenharmony_ci 383462306a36Sopenharmony_ci udp_tunnel_nic_reset_ntf(netdev); 383562306a36Sopenharmony_ci 383662306a36Sopenharmony_ci return 0; 383762306a36Sopenharmony_cierr: 383862306a36Sopenharmony_ci be_close(adapter->netdev); 383962306a36Sopenharmony_ci return -EIO; 384062306a36Sopenharmony_ci} 384162306a36Sopenharmony_ci 384262306a36Sopenharmony_cistatic void be_vf_eth_addr_generate(struct be_adapter *adapter, u8 *mac) 384362306a36Sopenharmony_ci{ 384462306a36Sopenharmony_ci u32 addr; 384562306a36Sopenharmony_ci 384662306a36Sopenharmony_ci addr = jhash(adapter->netdev->dev_addr, ETH_ALEN, 0); 384762306a36Sopenharmony_ci 384862306a36Sopenharmony_ci mac[5] = (u8)(addr & 0xFF); 384962306a36Sopenharmony_ci mac[4] = (u8)((addr >> 8) & 0xFF); 385062306a36Sopenharmony_ci mac[3] = (u8)((addr >> 16) & 0xFF); 385162306a36Sopenharmony_ci /* Use the OUI from the current MAC address */ 385262306a36Sopenharmony_ci memcpy(mac, adapter->netdev->dev_addr, 3); 385362306a36Sopenharmony_ci} 385462306a36Sopenharmony_ci 385562306a36Sopenharmony_ci/* 385662306a36Sopenharmony_ci * Generate a seed MAC address from the PF MAC Address using jhash. 385762306a36Sopenharmony_ci * MAC Address for VFs are assigned incrementally starting from the seed. 385862306a36Sopenharmony_ci * These addresses are programmed in the ASIC by the PF and the VF driver 385962306a36Sopenharmony_ci * queries for the MAC address during its probe. 386062306a36Sopenharmony_ci */ 386162306a36Sopenharmony_cistatic int be_vf_eth_addr_config(struct be_adapter *adapter) 386262306a36Sopenharmony_ci{ 386362306a36Sopenharmony_ci u32 vf; 386462306a36Sopenharmony_ci int status = 0; 386562306a36Sopenharmony_ci u8 mac[ETH_ALEN]; 386662306a36Sopenharmony_ci struct be_vf_cfg *vf_cfg; 386762306a36Sopenharmony_ci 386862306a36Sopenharmony_ci be_vf_eth_addr_generate(adapter, mac); 386962306a36Sopenharmony_ci 387062306a36Sopenharmony_ci for_all_vfs(adapter, vf_cfg, vf) { 387162306a36Sopenharmony_ci if (BEx_chip(adapter)) 387262306a36Sopenharmony_ci status = be_cmd_pmac_add(adapter, mac, 387362306a36Sopenharmony_ci vf_cfg->if_handle, 387462306a36Sopenharmony_ci &vf_cfg->pmac_id, vf + 1); 387562306a36Sopenharmony_ci else 387662306a36Sopenharmony_ci status = be_cmd_set_mac(adapter, mac, vf_cfg->if_handle, 387762306a36Sopenharmony_ci vf + 1); 387862306a36Sopenharmony_ci 387962306a36Sopenharmony_ci if (status) 388062306a36Sopenharmony_ci dev_err(&adapter->pdev->dev, 388162306a36Sopenharmony_ci "Mac address assignment failed for VF %d\n", 388262306a36Sopenharmony_ci vf); 388362306a36Sopenharmony_ci else 388462306a36Sopenharmony_ci memcpy(vf_cfg->mac_addr, mac, ETH_ALEN); 388562306a36Sopenharmony_ci 388662306a36Sopenharmony_ci mac[5] += 1; 388762306a36Sopenharmony_ci } 388862306a36Sopenharmony_ci return status; 388962306a36Sopenharmony_ci} 389062306a36Sopenharmony_ci 389162306a36Sopenharmony_cistatic int be_vfs_mac_query(struct be_adapter *adapter) 389262306a36Sopenharmony_ci{ 389362306a36Sopenharmony_ci int status, vf; 389462306a36Sopenharmony_ci u8 mac[ETH_ALEN]; 389562306a36Sopenharmony_ci struct be_vf_cfg *vf_cfg; 389662306a36Sopenharmony_ci 389762306a36Sopenharmony_ci for_all_vfs(adapter, vf_cfg, vf) { 389862306a36Sopenharmony_ci status = be_cmd_get_active_mac(adapter, vf_cfg->pmac_id, 389962306a36Sopenharmony_ci mac, vf_cfg->if_handle, 390062306a36Sopenharmony_ci false, vf+1); 390162306a36Sopenharmony_ci if (status) 390262306a36Sopenharmony_ci return status; 390362306a36Sopenharmony_ci memcpy(vf_cfg->mac_addr, mac, ETH_ALEN); 390462306a36Sopenharmony_ci } 390562306a36Sopenharmony_ci return 0; 390662306a36Sopenharmony_ci} 390762306a36Sopenharmony_ci 390862306a36Sopenharmony_cistatic void be_vf_clear(struct be_adapter *adapter) 390962306a36Sopenharmony_ci{ 391062306a36Sopenharmony_ci struct be_vf_cfg *vf_cfg; 391162306a36Sopenharmony_ci u32 vf; 391262306a36Sopenharmony_ci 391362306a36Sopenharmony_ci if (pci_vfs_assigned(adapter->pdev)) { 391462306a36Sopenharmony_ci dev_warn(&adapter->pdev->dev, 391562306a36Sopenharmony_ci "VFs are assigned to VMs: not disabling VFs\n"); 391662306a36Sopenharmony_ci goto done; 391762306a36Sopenharmony_ci } 391862306a36Sopenharmony_ci 391962306a36Sopenharmony_ci pci_disable_sriov(adapter->pdev); 392062306a36Sopenharmony_ci 392162306a36Sopenharmony_ci for_all_vfs(adapter, vf_cfg, vf) { 392262306a36Sopenharmony_ci if (BEx_chip(adapter)) 392362306a36Sopenharmony_ci be_cmd_pmac_del(adapter, vf_cfg->if_handle, 392462306a36Sopenharmony_ci vf_cfg->pmac_id, vf + 1); 392562306a36Sopenharmony_ci else 392662306a36Sopenharmony_ci be_cmd_set_mac(adapter, NULL, vf_cfg->if_handle, 392762306a36Sopenharmony_ci vf + 1); 392862306a36Sopenharmony_ci 392962306a36Sopenharmony_ci be_cmd_if_destroy(adapter, vf_cfg->if_handle, vf + 1); 393062306a36Sopenharmony_ci } 393162306a36Sopenharmony_ci 393262306a36Sopenharmony_ci if (BE3_chip(adapter)) 393362306a36Sopenharmony_ci be_cmd_set_hsw_config(adapter, 0, 0, 393462306a36Sopenharmony_ci adapter->if_handle, 393562306a36Sopenharmony_ci PORT_FWD_TYPE_PASSTHRU, 0); 393662306a36Sopenharmony_cidone: 393762306a36Sopenharmony_ci kfree(adapter->vf_cfg); 393862306a36Sopenharmony_ci adapter->num_vfs = 0; 393962306a36Sopenharmony_ci adapter->flags &= ~BE_FLAGS_SRIOV_ENABLED; 394062306a36Sopenharmony_ci} 394162306a36Sopenharmony_ci 394262306a36Sopenharmony_cistatic void be_clear_queues(struct be_adapter *adapter) 394362306a36Sopenharmony_ci{ 394462306a36Sopenharmony_ci be_mcc_queues_destroy(adapter); 394562306a36Sopenharmony_ci be_rx_cqs_destroy(adapter); 394662306a36Sopenharmony_ci be_tx_queues_destroy(adapter); 394762306a36Sopenharmony_ci be_evt_queues_destroy(adapter); 394862306a36Sopenharmony_ci} 394962306a36Sopenharmony_ci 395062306a36Sopenharmony_cistatic void be_cancel_worker(struct be_adapter *adapter) 395162306a36Sopenharmony_ci{ 395262306a36Sopenharmony_ci if (adapter->flags & BE_FLAGS_WORKER_SCHEDULED) { 395362306a36Sopenharmony_ci cancel_delayed_work_sync(&adapter->work); 395462306a36Sopenharmony_ci adapter->flags &= ~BE_FLAGS_WORKER_SCHEDULED; 395562306a36Sopenharmony_ci } 395662306a36Sopenharmony_ci} 395762306a36Sopenharmony_ci 395862306a36Sopenharmony_cistatic void be_cancel_err_detection(struct be_adapter *adapter) 395962306a36Sopenharmony_ci{ 396062306a36Sopenharmony_ci struct be_error_recovery *err_rec = &adapter->error_recovery; 396162306a36Sopenharmony_ci 396262306a36Sopenharmony_ci if (!be_err_recovery_workq) 396362306a36Sopenharmony_ci return; 396462306a36Sopenharmony_ci 396562306a36Sopenharmony_ci if (adapter->flags & BE_FLAGS_ERR_DETECTION_SCHEDULED) { 396662306a36Sopenharmony_ci cancel_delayed_work_sync(&err_rec->err_detection_work); 396762306a36Sopenharmony_ci adapter->flags &= ~BE_FLAGS_ERR_DETECTION_SCHEDULED; 396862306a36Sopenharmony_ci } 396962306a36Sopenharmony_ci} 397062306a36Sopenharmony_ci 397162306a36Sopenharmony_ci/* VxLAN offload Notes: 397262306a36Sopenharmony_ci * 397362306a36Sopenharmony_ci * The stack defines tunnel offload flags (hw_enc_features) for IP and doesn't 397462306a36Sopenharmony_ci * distinguish various types of transports (VxLAN, GRE, NVGRE ..). So, offload 397562306a36Sopenharmony_ci * is expected to work across all types of IP tunnels once exported. Skyhawk 397662306a36Sopenharmony_ci * supports offloads for either VxLAN or NVGRE, exclusively. So we export VxLAN 397762306a36Sopenharmony_ci * offloads in hw_enc_features only when a VxLAN port is added. If other (non 397862306a36Sopenharmony_ci * VxLAN) tunnels are configured while VxLAN offloads are enabled, offloads for 397962306a36Sopenharmony_ci * those other tunnels are unexported on the fly through ndo_features_check(). 398062306a36Sopenharmony_ci */ 398162306a36Sopenharmony_cistatic int be_vxlan_set_port(struct net_device *netdev, unsigned int table, 398262306a36Sopenharmony_ci unsigned int entry, struct udp_tunnel_info *ti) 398362306a36Sopenharmony_ci{ 398462306a36Sopenharmony_ci struct be_adapter *adapter = netdev_priv(netdev); 398562306a36Sopenharmony_ci struct device *dev = &adapter->pdev->dev; 398662306a36Sopenharmony_ci int status; 398762306a36Sopenharmony_ci 398862306a36Sopenharmony_ci status = be_cmd_manage_iface(adapter, adapter->if_handle, 398962306a36Sopenharmony_ci OP_CONVERT_NORMAL_TO_TUNNEL); 399062306a36Sopenharmony_ci if (status) { 399162306a36Sopenharmony_ci dev_warn(dev, "Failed to convert normal interface to tunnel\n"); 399262306a36Sopenharmony_ci return status; 399362306a36Sopenharmony_ci } 399462306a36Sopenharmony_ci adapter->flags |= BE_FLAGS_VXLAN_OFFLOADS; 399562306a36Sopenharmony_ci 399662306a36Sopenharmony_ci status = be_cmd_set_vxlan_port(adapter, ti->port); 399762306a36Sopenharmony_ci if (status) { 399862306a36Sopenharmony_ci dev_warn(dev, "Failed to add VxLAN port\n"); 399962306a36Sopenharmony_ci return status; 400062306a36Sopenharmony_ci } 400162306a36Sopenharmony_ci adapter->vxlan_port = ti->port; 400262306a36Sopenharmony_ci 400362306a36Sopenharmony_ci netdev->hw_enc_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | 400462306a36Sopenharmony_ci NETIF_F_TSO | NETIF_F_TSO6 | 400562306a36Sopenharmony_ci NETIF_F_GSO_UDP_TUNNEL; 400662306a36Sopenharmony_ci 400762306a36Sopenharmony_ci dev_info(dev, "Enabled VxLAN offloads for UDP port %d\n", 400862306a36Sopenharmony_ci be16_to_cpu(ti->port)); 400962306a36Sopenharmony_ci return 0; 401062306a36Sopenharmony_ci} 401162306a36Sopenharmony_ci 401262306a36Sopenharmony_cistatic int be_vxlan_unset_port(struct net_device *netdev, unsigned int table, 401362306a36Sopenharmony_ci unsigned int entry, struct udp_tunnel_info *ti) 401462306a36Sopenharmony_ci{ 401562306a36Sopenharmony_ci struct be_adapter *adapter = netdev_priv(netdev); 401662306a36Sopenharmony_ci 401762306a36Sopenharmony_ci if (adapter->flags & BE_FLAGS_VXLAN_OFFLOADS) 401862306a36Sopenharmony_ci be_cmd_manage_iface(adapter, adapter->if_handle, 401962306a36Sopenharmony_ci OP_CONVERT_TUNNEL_TO_NORMAL); 402062306a36Sopenharmony_ci 402162306a36Sopenharmony_ci if (adapter->vxlan_port) 402262306a36Sopenharmony_ci be_cmd_set_vxlan_port(adapter, 0); 402362306a36Sopenharmony_ci 402462306a36Sopenharmony_ci adapter->flags &= ~BE_FLAGS_VXLAN_OFFLOADS; 402562306a36Sopenharmony_ci adapter->vxlan_port = 0; 402662306a36Sopenharmony_ci 402762306a36Sopenharmony_ci netdev->hw_enc_features = 0; 402862306a36Sopenharmony_ci return 0; 402962306a36Sopenharmony_ci} 403062306a36Sopenharmony_ci 403162306a36Sopenharmony_cistatic const struct udp_tunnel_nic_info be_udp_tunnels = { 403262306a36Sopenharmony_ci .set_port = be_vxlan_set_port, 403362306a36Sopenharmony_ci .unset_port = be_vxlan_unset_port, 403462306a36Sopenharmony_ci .flags = UDP_TUNNEL_NIC_INFO_MAY_SLEEP | 403562306a36Sopenharmony_ci UDP_TUNNEL_NIC_INFO_OPEN_ONLY, 403662306a36Sopenharmony_ci .tables = { 403762306a36Sopenharmony_ci { .n_entries = 1, .tunnel_types = UDP_TUNNEL_TYPE_VXLAN, }, 403862306a36Sopenharmony_ci }, 403962306a36Sopenharmony_ci}; 404062306a36Sopenharmony_ci 404162306a36Sopenharmony_cistatic void be_calculate_vf_res(struct be_adapter *adapter, u16 num_vfs, 404262306a36Sopenharmony_ci struct be_resources *vft_res) 404362306a36Sopenharmony_ci{ 404462306a36Sopenharmony_ci struct be_resources res = adapter->pool_res; 404562306a36Sopenharmony_ci u32 vf_if_cap_flags = res.vf_if_cap_flags; 404662306a36Sopenharmony_ci struct be_resources res_mod = {0}; 404762306a36Sopenharmony_ci u16 num_vf_qs = 1; 404862306a36Sopenharmony_ci 404962306a36Sopenharmony_ci /* Distribute the queue resources among the PF and it's VFs */ 405062306a36Sopenharmony_ci if (num_vfs) { 405162306a36Sopenharmony_ci /* Divide the rx queues evenly among the VFs and the PF, capped 405262306a36Sopenharmony_ci * at VF-EQ-count. Any remainder queues belong to the PF. 405362306a36Sopenharmony_ci */ 405462306a36Sopenharmony_ci num_vf_qs = min(SH_VF_MAX_NIC_EQS, 405562306a36Sopenharmony_ci res.max_rss_qs / (num_vfs + 1)); 405662306a36Sopenharmony_ci 405762306a36Sopenharmony_ci /* Skyhawk-R chip supports only MAX_PORT_RSS_TABLES 405862306a36Sopenharmony_ci * RSS Tables per port. Provide RSS on VFs, only if number of 405962306a36Sopenharmony_ci * VFs requested is less than it's PF Pool's RSS Tables limit. 406062306a36Sopenharmony_ci */ 406162306a36Sopenharmony_ci if (num_vfs >= be_max_pf_pool_rss_tables(adapter)) 406262306a36Sopenharmony_ci num_vf_qs = 1; 406362306a36Sopenharmony_ci } 406462306a36Sopenharmony_ci 406562306a36Sopenharmony_ci /* Resource with fields set to all '1's by GET_PROFILE_CONFIG cmd, 406662306a36Sopenharmony_ci * which are modifiable using SET_PROFILE_CONFIG cmd. 406762306a36Sopenharmony_ci */ 406862306a36Sopenharmony_ci be_cmd_get_profile_config(adapter, &res_mod, NULL, ACTIVE_PROFILE_TYPE, 406962306a36Sopenharmony_ci RESOURCE_MODIFIABLE, 0); 407062306a36Sopenharmony_ci 407162306a36Sopenharmony_ci /* If RSS IFACE capability flags are modifiable for a VF, set the 407262306a36Sopenharmony_ci * capability flag as valid and set RSS and DEFQ_RSS IFACE flags if 407362306a36Sopenharmony_ci * more than 1 RSSQ is available for a VF. 407462306a36Sopenharmony_ci * Otherwise, provision only 1 queue pair for VF. 407562306a36Sopenharmony_ci */ 407662306a36Sopenharmony_ci if (res_mod.vf_if_cap_flags & BE_IF_FLAGS_RSS) { 407762306a36Sopenharmony_ci vft_res->flags |= BIT(IF_CAPS_FLAGS_VALID_SHIFT); 407862306a36Sopenharmony_ci if (num_vf_qs > 1) { 407962306a36Sopenharmony_ci vf_if_cap_flags |= BE_IF_FLAGS_RSS; 408062306a36Sopenharmony_ci if (res.if_cap_flags & BE_IF_FLAGS_DEFQ_RSS) 408162306a36Sopenharmony_ci vf_if_cap_flags |= BE_IF_FLAGS_DEFQ_RSS; 408262306a36Sopenharmony_ci } else { 408362306a36Sopenharmony_ci vf_if_cap_flags &= ~(BE_IF_FLAGS_RSS | 408462306a36Sopenharmony_ci BE_IF_FLAGS_DEFQ_RSS); 408562306a36Sopenharmony_ci } 408662306a36Sopenharmony_ci } else { 408762306a36Sopenharmony_ci num_vf_qs = 1; 408862306a36Sopenharmony_ci } 408962306a36Sopenharmony_ci 409062306a36Sopenharmony_ci if (res_mod.vf_if_cap_flags & BE_IF_FLAGS_VLAN_PROMISCUOUS) { 409162306a36Sopenharmony_ci vft_res->flags |= BIT(IF_CAPS_FLAGS_VALID_SHIFT); 409262306a36Sopenharmony_ci vf_if_cap_flags &= ~BE_IF_FLAGS_VLAN_PROMISCUOUS; 409362306a36Sopenharmony_ci } 409462306a36Sopenharmony_ci 409562306a36Sopenharmony_ci vft_res->vf_if_cap_flags = vf_if_cap_flags; 409662306a36Sopenharmony_ci vft_res->max_rx_qs = num_vf_qs; 409762306a36Sopenharmony_ci vft_res->max_rss_qs = num_vf_qs; 409862306a36Sopenharmony_ci vft_res->max_tx_qs = res.max_tx_qs / (num_vfs + 1); 409962306a36Sopenharmony_ci vft_res->max_cq_count = res.max_cq_count / (num_vfs + 1); 410062306a36Sopenharmony_ci 410162306a36Sopenharmony_ci /* Distribute unicast MACs, VLANs, IFACE count and MCCQ count equally 410262306a36Sopenharmony_ci * among the PF and it's VFs, if the fields are changeable 410362306a36Sopenharmony_ci */ 410462306a36Sopenharmony_ci if (res_mod.max_uc_mac == FIELD_MODIFIABLE) 410562306a36Sopenharmony_ci vft_res->max_uc_mac = res.max_uc_mac / (num_vfs + 1); 410662306a36Sopenharmony_ci 410762306a36Sopenharmony_ci if (res_mod.max_vlans == FIELD_MODIFIABLE) 410862306a36Sopenharmony_ci vft_res->max_vlans = res.max_vlans / (num_vfs + 1); 410962306a36Sopenharmony_ci 411062306a36Sopenharmony_ci if (res_mod.max_iface_count == FIELD_MODIFIABLE) 411162306a36Sopenharmony_ci vft_res->max_iface_count = res.max_iface_count / (num_vfs + 1); 411262306a36Sopenharmony_ci 411362306a36Sopenharmony_ci if (res_mod.max_mcc_count == FIELD_MODIFIABLE) 411462306a36Sopenharmony_ci vft_res->max_mcc_count = res.max_mcc_count / (num_vfs + 1); 411562306a36Sopenharmony_ci} 411662306a36Sopenharmony_ci 411762306a36Sopenharmony_cistatic void be_if_destroy(struct be_adapter *adapter) 411862306a36Sopenharmony_ci{ 411962306a36Sopenharmony_ci be_cmd_if_destroy(adapter, adapter->if_handle, 0); 412062306a36Sopenharmony_ci 412162306a36Sopenharmony_ci kfree(adapter->pmac_id); 412262306a36Sopenharmony_ci adapter->pmac_id = NULL; 412362306a36Sopenharmony_ci 412462306a36Sopenharmony_ci kfree(adapter->mc_list); 412562306a36Sopenharmony_ci adapter->mc_list = NULL; 412662306a36Sopenharmony_ci 412762306a36Sopenharmony_ci kfree(adapter->uc_list); 412862306a36Sopenharmony_ci adapter->uc_list = NULL; 412962306a36Sopenharmony_ci} 413062306a36Sopenharmony_ci 413162306a36Sopenharmony_cistatic int be_clear(struct be_adapter *adapter) 413262306a36Sopenharmony_ci{ 413362306a36Sopenharmony_ci struct pci_dev *pdev = adapter->pdev; 413462306a36Sopenharmony_ci struct be_resources vft_res = {0}; 413562306a36Sopenharmony_ci 413662306a36Sopenharmony_ci be_cancel_worker(adapter); 413762306a36Sopenharmony_ci 413862306a36Sopenharmony_ci flush_workqueue(be_wq); 413962306a36Sopenharmony_ci 414062306a36Sopenharmony_ci if (sriov_enabled(adapter)) 414162306a36Sopenharmony_ci be_vf_clear(adapter); 414262306a36Sopenharmony_ci 414362306a36Sopenharmony_ci /* Re-configure FW to distribute resources evenly across max-supported 414462306a36Sopenharmony_ci * number of VFs, only when VFs are not already enabled. 414562306a36Sopenharmony_ci */ 414662306a36Sopenharmony_ci if (skyhawk_chip(adapter) && be_physfn(adapter) && 414762306a36Sopenharmony_ci !pci_vfs_assigned(pdev)) { 414862306a36Sopenharmony_ci be_calculate_vf_res(adapter, 414962306a36Sopenharmony_ci pci_sriov_get_totalvfs(pdev), 415062306a36Sopenharmony_ci &vft_res); 415162306a36Sopenharmony_ci be_cmd_set_sriov_config(adapter, adapter->pool_res, 415262306a36Sopenharmony_ci pci_sriov_get_totalvfs(pdev), 415362306a36Sopenharmony_ci &vft_res); 415462306a36Sopenharmony_ci } 415562306a36Sopenharmony_ci 415662306a36Sopenharmony_ci be_vxlan_unset_port(adapter->netdev, 0, 0, NULL); 415762306a36Sopenharmony_ci 415862306a36Sopenharmony_ci be_if_destroy(adapter); 415962306a36Sopenharmony_ci 416062306a36Sopenharmony_ci be_clear_queues(adapter); 416162306a36Sopenharmony_ci 416262306a36Sopenharmony_ci be_msix_disable(adapter); 416362306a36Sopenharmony_ci adapter->flags &= ~BE_FLAGS_SETUP_DONE; 416462306a36Sopenharmony_ci return 0; 416562306a36Sopenharmony_ci} 416662306a36Sopenharmony_ci 416762306a36Sopenharmony_cistatic int be_vfs_if_create(struct be_adapter *adapter) 416862306a36Sopenharmony_ci{ 416962306a36Sopenharmony_ci struct be_resources res = {0}; 417062306a36Sopenharmony_ci u32 cap_flags, en_flags, vf; 417162306a36Sopenharmony_ci struct be_vf_cfg *vf_cfg; 417262306a36Sopenharmony_ci int status; 417362306a36Sopenharmony_ci 417462306a36Sopenharmony_ci /* If a FW profile exists, then cap_flags are updated */ 417562306a36Sopenharmony_ci cap_flags = BE_VF_IF_EN_FLAGS; 417662306a36Sopenharmony_ci 417762306a36Sopenharmony_ci for_all_vfs(adapter, vf_cfg, vf) { 417862306a36Sopenharmony_ci if (!BE3_chip(adapter)) { 417962306a36Sopenharmony_ci status = be_cmd_get_profile_config(adapter, &res, NULL, 418062306a36Sopenharmony_ci ACTIVE_PROFILE_TYPE, 418162306a36Sopenharmony_ci RESOURCE_LIMITS, 418262306a36Sopenharmony_ci vf + 1); 418362306a36Sopenharmony_ci if (!status) { 418462306a36Sopenharmony_ci cap_flags = res.if_cap_flags; 418562306a36Sopenharmony_ci /* Prevent VFs from enabling VLAN promiscuous 418662306a36Sopenharmony_ci * mode 418762306a36Sopenharmony_ci */ 418862306a36Sopenharmony_ci cap_flags &= ~BE_IF_FLAGS_VLAN_PROMISCUOUS; 418962306a36Sopenharmony_ci } 419062306a36Sopenharmony_ci } 419162306a36Sopenharmony_ci 419262306a36Sopenharmony_ci /* PF should enable IF flags during proxy if_create call */ 419362306a36Sopenharmony_ci en_flags = cap_flags & BE_VF_IF_EN_FLAGS; 419462306a36Sopenharmony_ci status = be_cmd_if_create(adapter, cap_flags, en_flags, 419562306a36Sopenharmony_ci &vf_cfg->if_handle, vf + 1); 419662306a36Sopenharmony_ci if (status) 419762306a36Sopenharmony_ci return status; 419862306a36Sopenharmony_ci } 419962306a36Sopenharmony_ci 420062306a36Sopenharmony_ci return 0; 420162306a36Sopenharmony_ci} 420262306a36Sopenharmony_ci 420362306a36Sopenharmony_cistatic int be_vf_setup_init(struct be_adapter *adapter) 420462306a36Sopenharmony_ci{ 420562306a36Sopenharmony_ci struct be_vf_cfg *vf_cfg; 420662306a36Sopenharmony_ci int vf; 420762306a36Sopenharmony_ci 420862306a36Sopenharmony_ci adapter->vf_cfg = kcalloc(adapter->num_vfs, sizeof(*vf_cfg), 420962306a36Sopenharmony_ci GFP_KERNEL); 421062306a36Sopenharmony_ci if (!adapter->vf_cfg) 421162306a36Sopenharmony_ci return -ENOMEM; 421262306a36Sopenharmony_ci 421362306a36Sopenharmony_ci for_all_vfs(adapter, vf_cfg, vf) { 421462306a36Sopenharmony_ci vf_cfg->if_handle = -1; 421562306a36Sopenharmony_ci vf_cfg->pmac_id = -1; 421662306a36Sopenharmony_ci } 421762306a36Sopenharmony_ci return 0; 421862306a36Sopenharmony_ci} 421962306a36Sopenharmony_ci 422062306a36Sopenharmony_cistatic int be_vf_setup(struct be_adapter *adapter) 422162306a36Sopenharmony_ci{ 422262306a36Sopenharmony_ci struct device *dev = &adapter->pdev->dev; 422362306a36Sopenharmony_ci struct be_vf_cfg *vf_cfg; 422462306a36Sopenharmony_ci int status, old_vfs, vf; 422562306a36Sopenharmony_ci bool spoofchk; 422662306a36Sopenharmony_ci 422762306a36Sopenharmony_ci old_vfs = pci_num_vf(adapter->pdev); 422862306a36Sopenharmony_ci 422962306a36Sopenharmony_ci status = be_vf_setup_init(adapter); 423062306a36Sopenharmony_ci if (status) 423162306a36Sopenharmony_ci goto err; 423262306a36Sopenharmony_ci 423362306a36Sopenharmony_ci if (old_vfs) { 423462306a36Sopenharmony_ci for_all_vfs(adapter, vf_cfg, vf) { 423562306a36Sopenharmony_ci status = be_cmd_get_if_id(adapter, vf_cfg, vf); 423662306a36Sopenharmony_ci if (status) 423762306a36Sopenharmony_ci goto err; 423862306a36Sopenharmony_ci } 423962306a36Sopenharmony_ci 424062306a36Sopenharmony_ci status = be_vfs_mac_query(adapter); 424162306a36Sopenharmony_ci if (status) 424262306a36Sopenharmony_ci goto err; 424362306a36Sopenharmony_ci } else { 424462306a36Sopenharmony_ci status = be_vfs_if_create(adapter); 424562306a36Sopenharmony_ci if (status) 424662306a36Sopenharmony_ci goto err; 424762306a36Sopenharmony_ci 424862306a36Sopenharmony_ci status = be_vf_eth_addr_config(adapter); 424962306a36Sopenharmony_ci if (status) 425062306a36Sopenharmony_ci goto err; 425162306a36Sopenharmony_ci } 425262306a36Sopenharmony_ci 425362306a36Sopenharmony_ci for_all_vfs(adapter, vf_cfg, vf) { 425462306a36Sopenharmony_ci /* Allow VFs to programs MAC/VLAN filters */ 425562306a36Sopenharmony_ci status = be_cmd_get_fn_privileges(adapter, &vf_cfg->privileges, 425662306a36Sopenharmony_ci vf + 1); 425762306a36Sopenharmony_ci if (!status && !(vf_cfg->privileges & BE_PRIV_FILTMGMT)) { 425862306a36Sopenharmony_ci status = be_cmd_set_fn_privileges(adapter, 425962306a36Sopenharmony_ci vf_cfg->privileges | 426062306a36Sopenharmony_ci BE_PRIV_FILTMGMT, 426162306a36Sopenharmony_ci vf + 1); 426262306a36Sopenharmony_ci if (!status) { 426362306a36Sopenharmony_ci vf_cfg->privileges |= BE_PRIV_FILTMGMT; 426462306a36Sopenharmony_ci dev_info(dev, "VF%d has FILTMGMT privilege\n", 426562306a36Sopenharmony_ci vf); 426662306a36Sopenharmony_ci } 426762306a36Sopenharmony_ci } 426862306a36Sopenharmony_ci 426962306a36Sopenharmony_ci /* Allow full available bandwidth */ 427062306a36Sopenharmony_ci if (!old_vfs) 427162306a36Sopenharmony_ci be_cmd_config_qos(adapter, 0, 0, vf + 1); 427262306a36Sopenharmony_ci 427362306a36Sopenharmony_ci status = be_cmd_get_hsw_config(adapter, NULL, vf + 1, 427462306a36Sopenharmony_ci vf_cfg->if_handle, NULL, 427562306a36Sopenharmony_ci &spoofchk); 427662306a36Sopenharmony_ci if (!status) 427762306a36Sopenharmony_ci vf_cfg->spoofchk = spoofchk; 427862306a36Sopenharmony_ci 427962306a36Sopenharmony_ci if (!old_vfs) { 428062306a36Sopenharmony_ci be_cmd_enable_vf(adapter, vf + 1); 428162306a36Sopenharmony_ci be_cmd_set_logical_link_config(adapter, 428262306a36Sopenharmony_ci IFLA_VF_LINK_STATE_AUTO, 428362306a36Sopenharmony_ci vf+1); 428462306a36Sopenharmony_ci } 428562306a36Sopenharmony_ci } 428662306a36Sopenharmony_ci 428762306a36Sopenharmony_ci if (!old_vfs) { 428862306a36Sopenharmony_ci status = pci_enable_sriov(adapter->pdev, adapter->num_vfs); 428962306a36Sopenharmony_ci if (status) { 429062306a36Sopenharmony_ci dev_err(dev, "SRIOV enable failed\n"); 429162306a36Sopenharmony_ci adapter->num_vfs = 0; 429262306a36Sopenharmony_ci goto err; 429362306a36Sopenharmony_ci } 429462306a36Sopenharmony_ci } 429562306a36Sopenharmony_ci 429662306a36Sopenharmony_ci if (BE3_chip(adapter)) { 429762306a36Sopenharmony_ci /* On BE3, enable VEB only when SRIOV is enabled */ 429862306a36Sopenharmony_ci status = be_cmd_set_hsw_config(adapter, 0, 0, 429962306a36Sopenharmony_ci adapter->if_handle, 430062306a36Sopenharmony_ci PORT_FWD_TYPE_VEB, 0); 430162306a36Sopenharmony_ci if (status) 430262306a36Sopenharmony_ci goto err; 430362306a36Sopenharmony_ci } 430462306a36Sopenharmony_ci 430562306a36Sopenharmony_ci adapter->flags |= BE_FLAGS_SRIOV_ENABLED; 430662306a36Sopenharmony_ci return 0; 430762306a36Sopenharmony_cierr: 430862306a36Sopenharmony_ci dev_err(dev, "VF setup failed\n"); 430962306a36Sopenharmony_ci be_vf_clear(adapter); 431062306a36Sopenharmony_ci return status; 431162306a36Sopenharmony_ci} 431262306a36Sopenharmony_ci 431362306a36Sopenharmony_ci/* Converting function_mode bits on BE3 to SH mc_type enums */ 431462306a36Sopenharmony_ci 431562306a36Sopenharmony_cistatic u8 be_convert_mc_type(u32 function_mode) 431662306a36Sopenharmony_ci{ 431762306a36Sopenharmony_ci if (function_mode & VNIC_MODE && function_mode & QNQ_MODE) 431862306a36Sopenharmony_ci return vNIC1; 431962306a36Sopenharmony_ci else if (function_mode & QNQ_MODE) 432062306a36Sopenharmony_ci return FLEX10; 432162306a36Sopenharmony_ci else if (function_mode & VNIC_MODE) 432262306a36Sopenharmony_ci return vNIC2; 432362306a36Sopenharmony_ci else if (function_mode & UMC_ENABLED) 432462306a36Sopenharmony_ci return UMC; 432562306a36Sopenharmony_ci else 432662306a36Sopenharmony_ci return MC_NONE; 432762306a36Sopenharmony_ci} 432862306a36Sopenharmony_ci 432962306a36Sopenharmony_ci/* On BE2/BE3 FW does not suggest the supported limits */ 433062306a36Sopenharmony_cistatic void BEx_get_resources(struct be_adapter *adapter, 433162306a36Sopenharmony_ci struct be_resources *res) 433262306a36Sopenharmony_ci{ 433362306a36Sopenharmony_ci bool use_sriov = adapter->num_vfs ? 1 : 0; 433462306a36Sopenharmony_ci 433562306a36Sopenharmony_ci if (be_physfn(adapter)) 433662306a36Sopenharmony_ci res->max_uc_mac = BE_UC_PMAC_COUNT; 433762306a36Sopenharmony_ci else 433862306a36Sopenharmony_ci res->max_uc_mac = BE_VF_UC_PMAC_COUNT; 433962306a36Sopenharmony_ci 434062306a36Sopenharmony_ci adapter->mc_type = be_convert_mc_type(adapter->function_mode); 434162306a36Sopenharmony_ci 434262306a36Sopenharmony_ci if (be_is_mc(adapter)) { 434362306a36Sopenharmony_ci /* Assuming that there are 4 channels per port, 434462306a36Sopenharmony_ci * when multi-channel is enabled 434562306a36Sopenharmony_ci */ 434662306a36Sopenharmony_ci if (be_is_qnq_mode(adapter)) 434762306a36Sopenharmony_ci res->max_vlans = BE_NUM_VLANS_SUPPORTED/8; 434862306a36Sopenharmony_ci else 434962306a36Sopenharmony_ci /* In a non-qnq multichannel mode, the pvid 435062306a36Sopenharmony_ci * takes up one vlan entry 435162306a36Sopenharmony_ci */ 435262306a36Sopenharmony_ci res->max_vlans = (BE_NUM_VLANS_SUPPORTED / 4) - 1; 435362306a36Sopenharmony_ci } else { 435462306a36Sopenharmony_ci res->max_vlans = BE_NUM_VLANS_SUPPORTED; 435562306a36Sopenharmony_ci } 435662306a36Sopenharmony_ci 435762306a36Sopenharmony_ci res->max_mcast_mac = BE_MAX_MC; 435862306a36Sopenharmony_ci 435962306a36Sopenharmony_ci /* 1) For BE3 1Gb ports, FW does not support multiple TXQs 436062306a36Sopenharmony_ci * 2) Create multiple TX rings on a BE3-R multi-channel interface 436162306a36Sopenharmony_ci * *only* if it is RSS-capable. 436262306a36Sopenharmony_ci */ 436362306a36Sopenharmony_ci if (BE2_chip(adapter) || use_sriov || (adapter->port_num > 1) || 436462306a36Sopenharmony_ci be_virtfn(adapter) || 436562306a36Sopenharmony_ci (be_is_mc(adapter) && 436662306a36Sopenharmony_ci !(adapter->function_caps & BE_FUNCTION_CAPS_RSS))) { 436762306a36Sopenharmony_ci res->max_tx_qs = 1; 436862306a36Sopenharmony_ci } else if (adapter->function_caps & BE_FUNCTION_CAPS_SUPER_NIC) { 436962306a36Sopenharmony_ci struct be_resources super_nic_res = {0}; 437062306a36Sopenharmony_ci 437162306a36Sopenharmony_ci /* On a SuperNIC profile, the driver needs to use the 437262306a36Sopenharmony_ci * GET_PROFILE_CONFIG cmd to query the per-function TXQ limits 437362306a36Sopenharmony_ci */ 437462306a36Sopenharmony_ci be_cmd_get_profile_config(adapter, &super_nic_res, NULL, 437562306a36Sopenharmony_ci ACTIVE_PROFILE_TYPE, RESOURCE_LIMITS, 437662306a36Sopenharmony_ci 0); 437762306a36Sopenharmony_ci /* Some old versions of BE3 FW don't report max_tx_qs value */ 437862306a36Sopenharmony_ci res->max_tx_qs = super_nic_res.max_tx_qs ? : BE3_MAX_TX_QS; 437962306a36Sopenharmony_ci } else { 438062306a36Sopenharmony_ci res->max_tx_qs = BE3_MAX_TX_QS; 438162306a36Sopenharmony_ci } 438262306a36Sopenharmony_ci 438362306a36Sopenharmony_ci if ((adapter->function_caps & BE_FUNCTION_CAPS_RSS) && 438462306a36Sopenharmony_ci !use_sriov && be_physfn(adapter)) 438562306a36Sopenharmony_ci res->max_rss_qs = (adapter->be3_native) ? 438662306a36Sopenharmony_ci BE3_MAX_RSS_QS : BE2_MAX_RSS_QS; 438762306a36Sopenharmony_ci res->max_rx_qs = res->max_rss_qs + 1; 438862306a36Sopenharmony_ci 438962306a36Sopenharmony_ci if (be_physfn(adapter)) 439062306a36Sopenharmony_ci res->max_evt_qs = (be_max_vfs(adapter) > 0) ? 439162306a36Sopenharmony_ci BE3_SRIOV_MAX_EVT_QS : BE3_MAX_EVT_QS; 439262306a36Sopenharmony_ci else 439362306a36Sopenharmony_ci res->max_evt_qs = 1; 439462306a36Sopenharmony_ci 439562306a36Sopenharmony_ci res->if_cap_flags = BE_IF_CAP_FLAGS_WANT; 439662306a36Sopenharmony_ci res->if_cap_flags &= ~BE_IF_FLAGS_DEFQ_RSS; 439762306a36Sopenharmony_ci if (!(adapter->function_caps & BE_FUNCTION_CAPS_RSS)) 439862306a36Sopenharmony_ci res->if_cap_flags &= ~BE_IF_FLAGS_RSS; 439962306a36Sopenharmony_ci} 440062306a36Sopenharmony_ci 440162306a36Sopenharmony_cistatic void be_setup_init(struct be_adapter *adapter) 440262306a36Sopenharmony_ci{ 440362306a36Sopenharmony_ci adapter->vlan_prio_bmap = 0xff; 440462306a36Sopenharmony_ci adapter->phy.link_speed = -1; 440562306a36Sopenharmony_ci adapter->if_handle = -1; 440662306a36Sopenharmony_ci adapter->be3_native = false; 440762306a36Sopenharmony_ci adapter->if_flags = 0; 440862306a36Sopenharmony_ci adapter->phy_state = BE_UNKNOWN_PHY_STATE; 440962306a36Sopenharmony_ci if (be_physfn(adapter)) 441062306a36Sopenharmony_ci adapter->cmd_privileges = MAX_PRIVILEGES; 441162306a36Sopenharmony_ci else 441262306a36Sopenharmony_ci adapter->cmd_privileges = MIN_PRIVILEGES; 441362306a36Sopenharmony_ci} 441462306a36Sopenharmony_ci 441562306a36Sopenharmony_ci/* HW supports only MAX_PORT_RSS_TABLES RSS Policy Tables per port. 441662306a36Sopenharmony_ci * However, this HW limitation is not exposed to the host via any SLI cmd. 441762306a36Sopenharmony_ci * As a result, in the case of SRIOV and in particular multi-partition configs 441862306a36Sopenharmony_ci * the driver needs to calcuate a proportional share of RSS Tables per PF-pool 441962306a36Sopenharmony_ci * for distribution between the VFs. This self-imposed limit will determine the 442062306a36Sopenharmony_ci * no: of VFs for which RSS can be enabled. 442162306a36Sopenharmony_ci */ 442262306a36Sopenharmony_cistatic void be_calculate_pf_pool_rss_tables(struct be_adapter *adapter) 442362306a36Sopenharmony_ci{ 442462306a36Sopenharmony_ci struct be_port_resources port_res = {0}; 442562306a36Sopenharmony_ci u8 rss_tables_on_port; 442662306a36Sopenharmony_ci u16 max_vfs = be_max_vfs(adapter); 442762306a36Sopenharmony_ci 442862306a36Sopenharmony_ci be_cmd_get_profile_config(adapter, NULL, &port_res, SAVED_PROFILE_TYPE, 442962306a36Sopenharmony_ci RESOURCE_LIMITS, 0); 443062306a36Sopenharmony_ci 443162306a36Sopenharmony_ci rss_tables_on_port = MAX_PORT_RSS_TABLES - port_res.nic_pfs; 443262306a36Sopenharmony_ci 443362306a36Sopenharmony_ci /* Each PF Pool's RSS Tables limit = 443462306a36Sopenharmony_ci * PF's Max VFs / Total_Max_VFs on Port * RSS Tables on Port 443562306a36Sopenharmony_ci */ 443662306a36Sopenharmony_ci adapter->pool_res.max_rss_tables = 443762306a36Sopenharmony_ci max_vfs * rss_tables_on_port / port_res.max_vfs; 443862306a36Sopenharmony_ci} 443962306a36Sopenharmony_ci 444062306a36Sopenharmony_cistatic int be_get_sriov_config(struct be_adapter *adapter) 444162306a36Sopenharmony_ci{ 444262306a36Sopenharmony_ci struct be_resources res = {0}; 444362306a36Sopenharmony_ci int max_vfs, old_vfs; 444462306a36Sopenharmony_ci 444562306a36Sopenharmony_ci be_cmd_get_profile_config(adapter, &res, NULL, ACTIVE_PROFILE_TYPE, 444662306a36Sopenharmony_ci RESOURCE_LIMITS, 0); 444762306a36Sopenharmony_ci 444862306a36Sopenharmony_ci /* Some old versions of BE3 FW don't report max_vfs value */ 444962306a36Sopenharmony_ci if (BE3_chip(adapter) && !res.max_vfs) { 445062306a36Sopenharmony_ci max_vfs = pci_sriov_get_totalvfs(adapter->pdev); 445162306a36Sopenharmony_ci res.max_vfs = max_vfs > 0 ? min(MAX_VFS, max_vfs) : 0; 445262306a36Sopenharmony_ci } 445362306a36Sopenharmony_ci 445462306a36Sopenharmony_ci adapter->pool_res = res; 445562306a36Sopenharmony_ci 445662306a36Sopenharmony_ci /* If during previous unload of the driver, the VFs were not disabled, 445762306a36Sopenharmony_ci * then we cannot rely on the PF POOL limits for the TotalVFs value. 445862306a36Sopenharmony_ci * Instead use the TotalVFs value stored in the pci-dev struct. 445962306a36Sopenharmony_ci */ 446062306a36Sopenharmony_ci old_vfs = pci_num_vf(adapter->pdev); 446162306a36Sopenharmony_ci if (old_vfs) { 446262306a36Sopenharmony_ci dev_info(&adapter->pdev->dev, "%d VFs are already enabled\n", 446362306a36Sopenharmony_ci old_vfs); 446462306a36Sopenharmony_ci 446562306a36Sopenharmony_ci adapter->pool_res.max_vfs = 446662306a36Sopenharmony_ci pci_sriov_get_totalvfs(adapter->pdev); 446762306a36Sopenharmony_ci adapter->num_vfs = old_vfs; 446862306a36Sopenharmony_ci } 446962306a36Sopenharmony_ci 447062306a36Sopenharmony_ci if (skyhawk_chip(adapter) && be_max_vfs(adapter) && !old_vfs) { 447162306a36Sopenharmony_ci be_calculate_pf_pool_rss_tables(adapter); 447262306a36Sopenharmony_ci dev_info(&adapter->pdev->dev, 447362306a36Sopenharmony_ci "RSS can be enabled for all VFs if num_vfs <= %d\n", 447462306a36Sopenharmony_ci be_max_pf_pool_rss_tables(adapter)); 447562306a36Sopenharmony_ci } 447662306a36Sopenharmony_ci return 0; 447762306a36Sopenharmony_ci} 447862306a36Sopenharmony_ci 447962306a36Sopenharmony_cistatic void be_alloc_sriov_res(struct be_adapter *adapter) 448062306a36Sopenharmony_ci{ 448162306a36Sopenharmony_ci int old_vfs = pci_num_vf(adapter->pdev); 448262306a36Sopenharmony_ci struct be_resources vft_res = {0}; 448362306a36Sopenharmony_ci int status; 448462306a36Sopenharmony_ci 448562306a36Sopenharmony_ci be_get_sriov_config(adapter); 448662306a36Sopenharmony_ci 448762306a36Sopenharmony_ci if (!old_vfs) 448862306a36Sopenharmony_ci pci_sriov_set_totalvfs(adapter->pdev, be_max_vfs(adapter)); 448962306a36Sopenharmony_ci 449062306a36Sopenharmony_ci /* When the HW is in SRIOV capable configuration, the PF-pool 449162306a36Sopenharmony_ci * resources are given to PF during driver load, if there are no 449262306a36Sopenharmony_ci * old VFs. This facility is not available in BE3 FW. 449362306a36Sopenharmony_ci * Also, this is done by FW in Lancer chip. 449462306a36Sopenharmony_ci */ 449562306a36Sopenharmony_ci if (skyhawk_chip(adapter) && be_max_vfs(adapter) && !old_vfs) { 449662306a36Sopenharmony_ci be_calculate_vf_res(adapter, 0, &vft_res); 449762306a36Sopenharmony_ci status = be_cmd_set_sriov_config(adapter, adapter->pool_res, 0, 449862306a36Sopenharmony_ci &vft_res); 449962306a36Sopenharmony_ci if (status) 450062306a36Sopenharmony_ci dev_err(&adapter->pdev->dev, 450162306a36Sopenharmony_ci "Failed to optimize SRIOV resources\n"); 450262306a36Sopenharmony_ci } 450362306a36Sopenharmony_ci} 450462306a36Sopenharmony_ci 450562306a36Sopenharmony_cistatic int be_get_resources(struct be_adapter *adapter) 450662306a36Sopenharmony_ci{ 450762306a36Sopenharmony_ci struct device *dev = &adapter->pdev->dev; 450862306a36Sopenharmony_ci struct be_resources res = {0}; 450962306a36Sopenharmony_ci int status; 451062306a36Sopenharmony_ci 451162306a36Sopenharmony_ci /* For Lancer, SH etc read per-function resource limits from FW. 451262306a36Sopenharmony_ci * GET_FUNC_CONFIG returns per function guaranteed limits. 451362306a36Sopenharmony_ci * GET_PROFILE_CONFIG returns PCI-E related limits PF-pool limits 451462306a36Sopenharmony_ci */ 451562306a36Sopenharmony_ci if (BEx_chip(adapter)) { 451662306a36Sopenharmony_ci BEx_get_resources(adapter, &res); 451762306a36Sopenharmony_ci } else { 451862306a36Sopenharmony_ci status = be_cmd_get_func_config(adapter, &res); 451962306a36Sopenharmony_ci if (status) 452062306a36Sopenharmony_ci return status; 452162306a36Sopenharmony_ci 452262306a36Sopenharmony_ci /* If a deafault RXQ must be created, we'll use up one RSSQ*/ 452362306a36Sopenharmony_ci if (res.max_rss_qs && res.max_rss_qs == res.max_rx_qs && 452462306a36Sopenharmony_ci !(res.if_cap_flags & BE_IF_FLAGS_DEFQ_RSS)) 452562306a36Sopenharmony_ci res.max_rss_qs -= 1; 452662306a36Sopenharmony_ci } 452762306a36Sopenharmony_ci 452862306a36Sopenharmony_ci /* If RoCE is supported stash away half the EQs for RoCE */ 452962306a36Sopenharmony_ci res.max_nic_evt_qs = be_roce_supported(adapter) ? 453062306a36Sopenharmony_ci res.max_evt_qs / 2 : res.max_evt_qs; 453162306a36Sopenharmony_ci adapter->res = res; 453262306a36Sopenharmony_ci 453362306a36Sopenharmony_ci /* If FW supports RSS default queue, then skip creating non-RSS 453462306a36Sopenharmony_ci * queue for non-IP traffic. 453562306a36Sopenharmony_ci */ 453662306a36Sopenharmony_ci adapter->need_def_rxq = (be_if_cap_flags(adapter) & 453762306a36Sopenharmony_ci BE_IF_FLAGS_DEFQ_RSS) ? 0 : 1; 453862306a36Sopenharmony_ci 453962306a36Sopenharmony_ci dev_info(dev, "Max: txqs %d, rxqs %d, rss %d, eqs %d, vfs %d\n", 454062306a36Sopenharmony_ci be_max_txqs(adapter), be_max_rxqs(adapter), 454162306a36Sopenharmony_ci be_max_rss(adapter), be_max_nic_eqs(adapter), 454262306a36Sopenharmony_ci be_max_vfs(adapter)); 454362306a36Sopenharmony_ci dev_info(dev, "Max: uc-macs %d, mc-macs %d, vlans %d\n", 454462306a36Sopenharmony_ci be_max_uc(adapter), be_max_mc(adapter), 454562306a36Sopenharmony_ci be_max_vlans(adapter)); 454662306a36Sopenharmony_ci 454762306a36Sopenharmony_ci /* Ensure RX and TX queues are created in pairs at init time */ 454862306a36Sopenharmony_ci adapter->cfg_num_rx_irqs = 454962306a36Sopenharmony_ci min_t(u16, netif_get_num_default_rss_queues(), 455062306a36Sopenharmony_ci be_max_qp_irqs(adapter)); 455162306a36Sopenharmony_ci adapter->cfg_num_tx_irqs = adapter->cfg_num_rx_irqs; 455262306a36Sopenharmony_ci return 0; 455362306a36Sopenharmony_ci} 455462306a36Sopenharmony_ci 455562306a36Sopenharmony_cistatic int be_get_config(struct be_adapter *adapter) 455662306a36Sopenharmony_ci{ 455762306a36Sopenharmony_ci int status, level; 455862306a36Sopenharmony_ci u16 profile_id; 455962306a36Sopenharmony_ci 456062306a36Sopenharmony_ci status = be_cmd_get_cntl_attributes(adapter); 456162306a36Sopenharmony_ci if (status) 456262306a36Sopenharmony_ci return status; 456362306a36Sopenharmony_ci 456462306a36Sopenharmony_ci status = be_cmd_query_fw_cfg(adapter); 456562306a36Sopenharmony_ci if (status) 456662306a36Sopenharmony_ci return status; 456762306a36Sopenharmony_ci 456862306a36Sopenharmony_ci if (!lancer_chip(adapter) && be_physfn(adapter)) 456962306a36Sopenharmony_ci be_cmd_get_fat_dump_len(adapter, &adapter->fat_dump_len); 457062306a36Sopenharmony_ci 457162306a36Sopenharmony_ci if (BEx_chip(adapter)) { 457262306a36Sopenharmony_ci level = be_cmd_get_fw_log_level(adapter); 457362306a36Sopenharmony_ci adapter->msg_enable = 457462306a36Sopenharmony_ci level <= FW_LOG_LEVEL_DEFAULT ? NETIF_MSG_HW : 0; 457562306a36Sopenharmony_ci } 457662306a36Sopenharmony_ci 457762306a36Sopenharmony_ci be_cmd_get_acpi_wol_cap(adapter); 457862306a36Sopenharmony_ci pci_enable_wake(adapter->pdev, PCI_D3hot, adapter->wol_en); 457962306a36Sopenharmony_ci pci_enable_wake(adapter->pdev, PCI_D3cold, adapter->wol_en); 458062306a36Sopenharmony_ci 458162306a36Sopenharmony_ci be_cmd_query_port_name(adapter); 458262306a36Sopenharmony_ci 458362306a36Sopenharmony_ci if (be_physfn(adapter)) { 458462306a36Sopenharmony_ci status = be_cmd_get_active_profile(adapter, &profile_id); 458562306a36Sopenharmony_ci if (!status) 458662306a36Sopenharmony_ci dev_info(&adapter->pdev->dev, 458762306a36Sopenharmony_ci "Using profile 0x%x\n", profile_id); 458862306a36Sopenharmony_ci } 458962306a36Sopenharmony_ci 459062306a36Sopenharmony_ci return 0; 459162306a36Sopenharmony_ci} 459262306a36Sopenharmony_ci 459362306a36Sopenharmony_cistatic int be_mac_setup(struct be_adapter *adapter) 459462306a36Sopenharmony_ci{ 459562306a36Sopenharmony_ci u8 mac[ETH_ALEN]; 459662306a36Sopenharmony_ci int status; 459762306a36Sopenharmony_ci 459862306a36Sopenharmony_ci if (is_zero_ether_addr(adapter->netdev->dev_addr)) { 459962306a36Sopenharmony_ci status = be_cmd_get_perm_mac(adapter, mac); 460062306a36Sopenharmony_ci if (status) 460162306a36Sopenharmony_ci return status; 460262306a36Sopenharmony_ci 460362306a36Sopenharmony_ci eth_hw_addr_set(adapter->netdev, mac); 460462306a36Sopenharmony_ci memcpy(adapter->netdev->perm_addr, mac, ETH_ALEN); 460562306a36Sopenharmony_ci 460662306a36Sopenharmony_ci /* Initial MAC for BE3 VFs is already programmed by PF */ 460762306a36Sopenharmony_ci if (BEx_chip(adapter) && be_virtfn(adapter)) 460862306a36Sopenharmony_ci memcpy(adapter->dev_mac, mac, ETH_ALEN); 460962306a36Sopenharmony_ci } 461062306a36Sopenharmony_ci 461162306a36Sopenharmony_ci return 0; 461262306a36Sopenharmony_ci} 461362306a36Sopenharmony_ci 461462306a36Sopenharmony_cistatic void be_schedule_worker(struct be_adapter *adapter) 461562306a36Sopenharmony_ci{ 461662306a36Sopenharmony_ci queue_delayed_work(be_wq, &adapter->work, msecs_to_jiffies(1000)); 461762306a36Sopenharmony_ci adapter->flags |= BE_FLAGS_WORKER_SCHEDULED; 461862306a36Sopenharmony_ci} 461962306a36Sopenharmony_ci 462062306a36Sopenharmony_cistatic void be_destroy_err_recovery_workq(void) 462162306a36Sopenharmony_ci{ 462262306a36Sopenharmony_ci if (!be_err_recovery_workq) 462362306a36Sopenharmony_ci return; 462462306a36Sopenharmony_ci 462562306a36Sopenharmony_ci destroy_workqueue(be_err_recovery_workq); 462662306a36Sopenharmony_ci be_err_recovery_workq = NULL; 462762306a36Sopenharmony_ci} 462862306a36Sopenharmony_ci 462962306a36Sopenharmony_cistatic void be_schedule_err_detection(struct be_adapter *adapter, u32 delay) 463062306a36Sopenharmony_ci{ 463162306a36Sopenharmony_ci struct be_error_recovery *err_rec = &adapter->error_recovery; 463262306a36Sopenharmony_ci 463362306a36Sopenharmony_ci if (!be_err_recovery_workq) 463462306a36Sopenharmony_ci return; 463562306a36Sopenharmony_ci 463662306a36Sopenharmony_ci queue_delayed_work(be_err_recovery_workq, &err_rec->err_detection_work, 463762306a36Sopenharmony_ci msecs_to_jiffies(delay)); 463862306a36Sopenharmony_ci adapter->flags |= BE_FLAGS_ERR_DETECTION_SCHEDULED; 463962306a36Sopenharmony_ci} 464062306a36Sopenharmony_ci 464162306a36Sopenharmony_cistatic int be_setup_queues(struct be_adapter *adapter) 464262306a36Sopenharmony_ci{ 464362306a36Sopenharmony_ci struct net_device *netdev = adapter->netdev; 464462306a36Sopenharmony_ci int status; 464562306a36Sopenharmony_ci 464662306a36Sopenharmony_ci status = be_evt_queues_create(adapter); 464762306a36Sopenharmony_ci if (status) 464862306a36Sopenharmony_ci goto err; 464962306a36Sopenharmony_ci 465062306a36Sopenharmony_ci status = be_tx_qs_create(adapter); 465162306a36Sopenharmony_ci if (status) 465262306a36Sopenharmony_ci goto err; 465362306a36Sopenharmony_ci 465462306a36Sopenharmony_ci status = be_rx_cqs_create(adapter); 465562306a36Sopenharmony_ci if (status) 465662306a36Sopenharmony_ci goto err; 465762306a36Sopenharmony_ci 465862306a36Sopenharmony_ci status = be_mcc_queues_create(adapter); 465962306a36Sopenharmony_ci if (status) 466062306a36Sopenharmony_ci goto err; 466162306a36Sopenharmony_ci 466262306a36Sopenharmony_ci status = netif_set_real_num_rx_queues(netdev, adapter->num_rx_qs); 466362306a36Sopenharmony_ci if (status) 466462306a36Sopenharmony_ci goto err; 466562306a36Sopenharmony_ci 466662306a36Sopenharmony_ci status = netif_set_real_num_tx_queues(netdev, adapter->num_tx_qs); 466762306a36Sopenharmony_ci if (status) 466862306a36Sopenharmony_ci goto err; 466962306a36Sopenharmony_ci 467062306a36Sopenharmony_ci return 0; 467162306a36Sopenharmony_cierr: 467262306a36Sopenharmony_ci dev_err(&adapter->pdev->dev, "queue_setup failed\n"); 467362306a36Sopenharmony_ci return status; 467462306a36Sopenharmony_ci} 467562306a36Sopenharmony_ci 467662306a36Sopenharmony_cistatic int be_if_create(struct be_adapter *adapter) 467762306a36Sopenharmony_ci{ 467862306a36Sopenharmony_ci u32 en_flags = BE_IF_FLAGS_RSS | BE_IF_FLAGS_DEFQ_RSS; 467962306a36Sopenharmony_ci u32 cap_flags = be_if_cap_flags(adapter); 468062306a36Sopenharmony_ci 468162306a36Sopenharmony_ci /* alloc required memory for other filtering fields */ 468262306a36Sopenharmony_ci adapter->pmac_id = kcalloc(be_max_uc(adapter), 468362306a36Sopenharmony_ci sizeof(*adapter->pmac_id), GFP_KERNEL); 468462306a36Sopenharmony_ci if (!adapter->pmac_id) 468562306a36Sopenharmony_ci return -ENOMEM; 468662306a36Sopenharmony_ci 468762306a36Sopenharmony_ci adapter->mc_list = kcalloc(be_max_mc(adapter), 468862306a36Sopenharmony_ci sizeof(*adapter->mc_list), GFP_KERNEL); 468962306a36Sopenharmony_ci if (!adapter->mc_list) 469062306a36Sopenharmony_ci return -ENOMEM; 469162306a36Sopenharmony_ci 469262306a36Sopenharmony_ci adapter->uc_list = kcalloc(be_max_uc(adapter), 469362306a36Sopenharmony_ci sizeof(*adapter->uc_list), GFP_KERNEL); 469462306a36Sopenharmony_ci if (!adapter->uc_list) 469562306a36Sopenharmony_ci return -ENOMEM; 469662306a36Sopenharmony_ci 469762306a36Sopenharmony_ci if (adapter->cfg_num_rx_irqs == 1) 469862306a36Sopenharmony_ci cap_flags &= ~(BE_IF_FLAGS_DEFQ_RSS | BE_IF_FLAGS_RSS); 469962306a36Sopenharmony_ci 470062306a36Sopenharmony_ci en_flags &= cap_flags; 470162306a36Sopenharmony_ci /* will enable all the needed filter flags in be_open() */ 470262306a36Sopenharmony_ci return be_cmd_if_create(adapter, be_if_cap_flags(adapter), en_flags, 470362306a36Sopenharmony_ci &adapter->if_handle, 0); 470462306a36Sopenharmony_ci} 470562306a36Sopenharmony_ci 470662306a36Sopenharmony_ciint be_update_queues(struct be_adapter *adapter) 470762306a36Sopenharmony_ci{ 470862306a36Sopenharmony_ci struct net_device *netdev = adapter->netdev; 470962306a36Sopenharmony_ci int status; 471062306a36Sopenharmony_ci 471162306a36Sopenharmony_ci if (netif_running(netdev)) { 471262306a36Sopenharmony_ci /* be_tx_timeout() must not run concurrently with this 471362306a36Sopenharmony_ci * function, synchronize with an already-running dev_watchdog 471462306a36Sopenharmony_ci */ 471562306a36Sopenharmony_ci netif_tx_lock_bh(netdev); 471662306a36Sopenharmony_ci /* device cannot transmit now, avoid dev_watchdog timeouts */ 471762306a36Sopenharmony_ci netif_carrier_off(netdev); 471862306a36Sopenharmony_ci netif_tx_unlock_bh(netdev); 471962306a36Sopenharmony_ci 472062306a36Sopenharmony_ci be_close(netdev); 472162306a36Sopenharmony_ci } 472262306a36Sopenharmony_ci 472362306a36Sopenharmony_ci be_cancel_worker(adapter); 472462306a36Sopenharmony_ci 472562306a36Sopenharmony_ci /* If any vectors have been shared with RoCE we cannot re-program 472662306a36Sopenharmony_ci * the MSIx table. 472762306a36Sopenharmony_ci */ 472862306a36Sopenharmony_ci if (!adapter->num_msix_roce_vec) 472962306a36Sopenharmony_ci be_msix_disable(adapter); 473062306a36Sopenharmony_ci 473162306a36Sopenharmony_ci be_clear_queues(adapter); 473262306a36Sopenharmony_ci status = be_cmd_if_destroy(adapter, adapter->if_handle, 0); 473362306a36Sopenharmony_ci if (status) 473462306a36Sopenharmony_ci return status; 473562306a36Sopenharmony_ci 473662306a36Sopenharmony_ci if (!msix_enabled(adapter)) { 473762306a36Sopenharmony_ci status = be_msix_enable(adapter); 473862306a36Sopenharmony_ci if (status) 473962306a36Sopenharmony_ci return status; 474062306a36Sopenharmony_ci } 474162306a36Sopenharmony_ci 474262306a36Sopenharmony_ci status = be_if_create(adapter); 474362306a36Sopenharmony_ci if (status) 474462306a36Sopenharmony_ci return status; 474562306a36Sopenharmony_ci 474662306a36Sopenharmony_ci status = be_setup_queues(adapter); 474762306a36Sopenharmony_ci if (status) 474862306a36Sopenharmony_ci return status; 474962306a36Sopenharmony_ci 475062306a36Sopenharmony_ci be_schedule_worker(adapter); 475162306a36Sopenharmony_ci 475262306a36Sopenharmony_ci /* The IF was destroyed and re-created. We need to clear 475362306a36Sopenharmony_ci * all promiscuous flags valid for the destroyed IF. 475462306a36Sopenharmony_ci * Without this promisc mode is not restored during 475562306a36Sopenharmony_ci * be_open() because the driver thinks that it is 475662306a36Sopenharmony_ci * already enabled in HW. 475762306a36Sopenharmony_ci */ 475862306a36Sopenharmony_ci adapter->if_flags &= ~BE_IF_FLAGS_ALL_PROMISCUOUS; 475962306a36Sopenharmony_ci 476062306a36Sopenharmony_ci if (netif_running(netdev)) 476162306a36Sopenharmony_ci status = be_open(netdev); 476262306a36Sopenharmony_ci 476362306a36Sopenharmony_ci return status; 476462306a36Sopenharmony_ci} 476562306a36Sopenharmony_ci 476662306a36Sopenharmony_cistatic inline int fw_major_num(const char *fw_ver) 476762306a36Sopenharmony_ci{ 476862306a36Sopenharmony_ci int fw_major = 0, i; 476962306a36Sopenharmony_ci 477062306a36Sopenharmony_ci i = sscanf(fw_ver, "%d.", &fw_major); 477162306a36Sopenharmony_ci if (i != 1) 477262306a36Sopenharmony_ci return 0; 477362306a36Sopenharmony_ci 477462306a36Sopenharmony_ci return fw_major; 477562306a36Sopenharmony_ci} 477662306a36Sopenharmony_ci 477762306a36Sopenharmony_ci/* If it is error recovery, FLR the PF 477862306a36Sopenharmony_ci * Else if any VFs are already enabled don't FLR the PF 477962306a36Sopenharmony_ci */ 478062306a36Sopenharmony_cistatic bool be_reset_required(struct be_adapter *adapter) 478162306a36Sopenharmony_ci{ 478262306a36Sopenharmony_ci if (be_error_recovering(adapter)) 478362306a36Sopenharmony_ci return true; 478462306a36Sopenharmony_ci else 478562306a36Sopenharmony_ci return pci_num_vf(adapter->pdev) == 0; 478662306a36Sopenharmony_ci} 478762306a36Sopenharmony_ci 478862306a36Sopenharmony_ci/* Wait for the FW to be ready and perform the required initialization */ 478962306a36Sopenharmony_cistatic int be_func_init(struct be_adapter *adapter) 479062306a36Sopenharmony_ci{ 479162306a36Sopenharmony_ci int status; 479262306a36Sopenharmony_ci 479362306a36Sopenharmony_ci status = be_fw_wait_ready(adapter); 479462306a36Sopenharmony_ci if (status) 479562306a36Sopenharmony_ci return status; 479662306a36Sopenharmony_ci 479762306a36Sopenharmony_ci /* FW is now ready; clear errors to allow cmds/doorbell */ 479862306a36Sopenharmony_ci be_clear_error(adapter, BE_CLEAR_ALL); 479962306a36Sopenharmony_ci 480062306a36Sopenharmony_ci if (be_reset_required(adapter)) { 480162306a36Sopenharmony_ci status = be_cmd_reset_function(adapter); 480262306a36Sopenharmony_ci if (status) 480362306a36Sopenharmony_ci return status; 480462306a36Sopenharmony_ci 480562306a36Sopenharmony_ci /* Wait for interrupts to quiesce after an FLR */ 480662306a36Sopenharmony_ci msleep(100); 480762306a36Sopenharmony_ci } 480862306a36Sopenharmony_ci 480962306a36Sopenharmony_ci /* Tell FW we're ready to fire cmds */ 481062306a36Sopenharmony_ci status = be_cmd_fw_init(adapter); 481162306a36Sopenharmony_ci if (status) 481262306a36Sopenharmony_ci return status; 481362306a36Sopenharmony_ci 481462306a36Sopenharmony_ci /* Allow interrupts for other ULPs running on NIC function */ 481562306a36Sopenharmony_ci be_intr_set(adapter, true); 481662306a36Sopenharmony_ci 481762306a36Sopenharmony_ci return 0; 481862306a36Sopenharmony_ci} 481962306a36Sopenharmony_ci 482062306a36Sopenharmony_cistatic int be_setup(struct be_adapter *adapter) 482162306a36Sopenharmony_ci{ 482262306a36Sopenharmony_ci struct device *dev = &adapter->pdev->dev; 482362306a36Sopenharmony_ci int status; 482462306a36Sopenharmony_ci 482562306a36Sopenharmony_ci status = be_func_init(adapter); 482662306a36Sopenharmony_ci if (status) 482762306a36Sopenharmony_ci return status; 482862306a36Sopenharmony_ci 482962306a36Sopenharmony_ci be_setup_init(adapter); 483062306a36Sopenharmony_ci 483162306a36Sopenharmony_ci if (!lancer_chip(adapter)) 483262306a36Sopenharmony_ci be_cmd_req_native_mode(adapter); 483362306a36Sopenharmony_ci 483462306a36Sopenharmony_ci /* invoke this cmd first to get pf_num and vf_num which are needed 483562306a36Sopenharmony_ci * for issuing profile related cmds 483662306a36Sopenharmony_ci */ 483762306a36Sopenharmony_ci if (!BEx_chip(adapter)) { 483862306a36Sopenharmony_ci status = be_cmd_get_func_config(adapter, NULL); 483962306a36Sopenharmony_ci if (status) 484062306a36Sopenharmony_ci return status; 484162306a36Sopenharmony_ci } 484262306a36Sopenharmony_ci 484362306a36Sopenharmony_ci status = be_get_config(adapter); 484462306a36Sopenharmony_ci if (status) 484562306a36Sopenharmony_ci goto err; 484662306a36Sopenharmony_ci 484762306a36Sopenharmony_ci if (!BE2_chip(adapter) && be_physfn(adapter)) 484862306a36Sopenharmony_ci be_alloc_sriov_res(adapter); 484962306a36Sopenharmony_ci 485062306a36Sopenharmony_ci status = be_get_resources(adapter); 485162306a36Sopenharmony_ci if (status) 485262306a36Sopenharmony_ci goto err; 485362306a36Sopenharmony_ci 485462306a36Sopenharmony_ci status = be_msix_enable(adapter); 485562306a36Sopenharmony_ci if (status) 485662306a36Sopenharmony_ci goto err; 485762306a36Sopenharmony_ci 485862306a36Sopenharmony_ci /* will enable all the needed filter flags in be_open() */ 485962306a36Sopenharmony_ci status = be_if_create(adapter); 486062306a36Sopenharmony_ci if (status) 486162306a36Sopenharmony_ci goto err; 486262306a36Sopenharmony_ci 486362306a36Sopenharmony_ci /* Updating real_num_tx/rx_queues() requires rtnl_lock() */ 486462306a36Sopenharmony_ci rtnl_lock(); 486562306a36Sopenharmony_ci status = be_setup_queues(adapter); 486662306a36Sopenharmony_ci rtnl_unlock(); 486762306a36Sopenharmony_ci if (status) 486862306a36Sopenharmony_ci goto err; 486962306a36Sopenharmony_ci 487062306a36Sopenharmony_ci be_cmd_get_fn_privileges(adapter, &adapter->cmd_privileges, 0); 487162306a36Sopenharmony_ci 487262306a36Sopenharmony_ci status = be_mac_setup(adapter); 487362306a36Sopenharmony_ci if (status) 487462306a36Sopenharmony_ci goto err; 487562306a36Sopenharmony_ci 487662306a36Sopenharmony_ci be_cmd_get_fw_ver(adapter); 487762306a36Sopenharmony_ci dev_info(dev, "FW version is %s\n", adapter->fw_ver); 487862306a36Sopenharmony_ci 487962306a36Sopenharmony_ci if (BE2_chip(adapter) && fw_major_num(adapter->fw_ver) < 4) { 488062306a36Sopenharmony_ci dev_err(dev, "Firmware on card is old(%s), IRQs may not work", 488162306a36Sopenharmony_ci adapter->fw_ver); 488262306a36Sopenharmony_ci dev_err(dev, "Please upgrade firmware to version >= 4.0\n"); 488362306a36Sopenharmony_ci } 488462306a36Sopenharmony_ci 488562306a36Sopenharmony_ci status = be_cmd_set_flow_control(adapter, adapter->tx_fc, 488662306a36Sopenharmony_ci adapter->rx_fc); 488762306a36Sopenharmony_ci if (status) 488862306a36Sopenharmony_ci be_cmd_get_flow_control(adapter, &adapter->tx_fc, 488962306a36Sopenharmony_ci &adapter->rx_fc); 489062306a36Sopenharmony_ci 489162306a36Sopenharmony_ci dev_info(&adapter->pdev->dev, "HW Flow control - TX:%d RX:%d\n", 489262306a36Sopenharmony_ci adapter->tx_fc, adapter->rx_fc); 489362306a36Sopenharmony_ci 489462306a36Sopenharmony_ci if (be_physfn(adapter)) 489562306a36Sopenharmony_ci be_cmd_set_logical_link_config(adapter, 489662306a36Sopenharmony_ci IFLA_VF_LINK_STATE_AUTO, 0); 489762306a36Sopenharmony_ci 489862306a36Sopenharmony_ci /* BE3 EVB echoes broadcast/multicast packets back to PF's vport 489962306a36Sopenharmony_ci * confusing a linux bridge or OVS that it might be connected to. 490062306a36Sopenharmony_ci * Set the EVB to PASSTHRU mode which effectively disables the EVB 490162306a36Sopenharmony_ci * when SRIOV is not enabled. 490262306a36Sopenharmony_ci */ 490362306a36Sopenharmony_ci if (BE3_chip(adapter)) 490462306a36Sopenharmony_ci be_cmd_set_hsw_config(adapter, 0, 0, adapter->if_handle, 490562306a36Sopenharmony_ci PORT_FWD_TYPE_PASSTHRU, 0); 490662306a36Sopenharmony_ci 490762306a36Sopenharmony_ci if (adapter->num_vfs) 490862306a36Sopenharmony_ci be_vf_setup(adapter); 490962306a36Sopenharmony_ci 491062306a36Sopenharmony_ci status = be_cmd_get_phy_info(adapter); 491162306a36Sopenharmony_ci if (!status && be_pause_supported(adapter)) 491262306a36Sopenharmony_ci adapter->phy.fc_autoneg = 1; 491362306a36Sopenharmony_ci 491462306a36Sopenharmony_ci if (be_physfn(adapter) && !lancer_chip(adapter)) 491562306a36Sopenharmony_ci be_cmd_set_features(adapter); 491662306a36Sopenharmony_ci 491762306a36Sopenharmony_ci be_schedule_worker(adapter); 491862306a36Sopenharmony_ci adapter->flags |= BE_FLAGS_SETUP_DONE; 491962306a36Sopenharmony_ci return 0; 492062306a36Sopenharmony_cierr: 492162306a36Sopenharmony_ci be_clear(adapter); 492262306a36Sopenharmony_ci return status; 492362306a36Sopenharmony_ci} 492462306a36Sopenharmony_ci 492562306a36Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER 492662306a36Sopenharmony_cistatic void be_netpoll(struct net_device *netdev) 492762306a36Sopenharmony_ci{ 492862306a36Sopenharmony_ci struct be_adapter *adapter = netdev_priv(netdev); 492962306a36Sopenharmony_ci struct be_eq_obj *eqo; 493062306a36Sopenharmony_ci int i; 493162306a36Sopenharmony_ci 493262306a36Sopenharmony_ci for_all_evt_queues(adapter, eqo, i) { 493362306a36Sopenharmony_ci be_eq_notify(eqo->adapter, eqo->q.id, false, true, 0, 0); 493462306a36Sopenharmony_ci napi_schedule(&eqo->napi); 493562306a36Sopenharmony_ci } 493662306a36Sopenharmony_ci} 493762306a36Sopenharmony_ci#endif 493862306a36Sopenharmony_ci 493962306a36Sopenharmony_ciint be_load_fw(struct be_adapter *adapter, u8 *fw_file) 494062306a36Sopenharmony_ci{ 494162306a36Sopenharmony_ci const struct firmware *fw; 494262306a36Sopenharmony_ci int status; 494362306a36Sopenharmony_ci 494462306a36Sopenharmony_ci if (!netif_running(adapter->netdev)) { 494562306a36Sopenharmony_ci dev_err(&adapter->pdev->dev, 494662306a36Sopenharmony_ci "Firmware load not allowed (interface is down)\n"); 494762306a36Sopenharmony_ci return -ENETDOWN; 494862306a36Sopenharmony_ci } 494962306a36Sopenharmony_ci 495062306a36Sopenharmony_ci status = request_firmware(&fw, fw_file, &adapter->pdev->dev); 495162306a36Sopenharmony_ci if (status) 495262306a36Sopenharmony_ci goto fw_exit; 495362306a36Sopenharmony_ci 495462306a36Sopenharmony_ci dev_info(&adapter->pdev->dev, "Flashing firmware file %s\n", fw_file); 495562306a36Sopenharmony_ci 495662306a36Sopenharmony_ci if (lancer_chip(adapter)) 495762306a36Sopenharmony_ci status = lancer_fw_download(adapter, fw); 495862306a36Sopenharmony_ci else 495962306a36Sopenharmony_ci status = be_fw_download(adapter, fw); 496062306a36Sopenharmony_ci 496162306a36Sopenharmony_ci if (!status) 496262306a36Sopenharmony_ci be_cmd_get_fw_ver(adapter); 496362306a36Sopenharmony_ci 496462306a36Sopenharmony_cifw_exit: 496562306a36Sopenharmony_ci release_firmware(fw); 496662306a36Sopenharmony_ci return status; 496762306a36Sopenharmony_ci} 496862306a36Sopenharmony_ci 496962306a36Sopenharmony_cistatic int be_ndo_bridge_setlink(struct net_device *dev, struct nlmsghdr *nlh, 497062306a36Sopenharmony_ci u16 flags, struct netlink_ext_ack *extack) 497162306a36Sopenharmony_ci{ 497262306a36Sopenharmony_ci struct be_adapter *adapter = netdev_priv(dev); 497362306a36Sopenharmony_ci struct nlattr *attr, *br_spec; 497462306a36Sopenharmony_ci int rem; 497562306a36Sopenharmony_ci int status = 0; 497662306a36Sopenharmony_ci u16 mode = 0; 497762306a36Sopenharmony_ci 497862306a36Sopenharmony_ci if (!sriov_enabled(adapter)) 497962306a36Sopenharmony_ci return -EOPNOTSUPP; 498062306a36Sopenharmony_ci 498162306a36Sopenharmony_ci br_spec = nlmsg_find_attr(nlh, sizeof(struct ifinfomsg), IFLA_AF_SPEC); 498262306a36Sopenharmony_ci if (!br_spec) 498362306a36Sopenharmony_ci return -EINVAL; 498462306a36Sopenharmony_ci 498562306a36Sopenharmony_ci nla_for_each_nested(attr, br_spec, rem) { 498662306a36Sopenharmony_ci if (nla_type(attr) != IFLA_BRIDGE_MODE) 498762306a36Sopenharmony_ci continue; 498862306a36Sopenharmony_ci 498962306a36Sopenharmony_ci mode = nla_get_u16(attr); 499062306a36Sopenharmony_ci if (BE3_chip(adapter) && mode == BRIDGE_MODE_VEPA) 499162306a36Sopenharmony_ci return -EOPNOTSUPP; 499262306a36Sopenharmony_ci 499362306a36Sopenharmony_ci if (mode != BRIDGE_MODE_VEPA && mode != BRIDGE_MODE_VEB) 499462306a36Sopenharmony_ci return -EINVAL; 499562306a36Sopenharmony_ci 499662306a36Sopenharmony_ci status = be_cmd_set_hsw_config(adapter, 0, 0, 499762306a36Sopenharmony_ci adapter->if_handle, 499862306a36Sopenharmony_ci mode == BRIDGE_MODE_VEPA ? 499962306a36Sopenharmony_ci PORT_FWD_TYPE_VEPA : 500062306a36Sopenharmony_ci PORT_FWD_TYPE_VEB, 0); 500162306a36Sopenharmony_ci if (status) 500262306a36Sopenharmony_ci goto err; 500362306a36Sopenharmony_ci 500462306a36Sopenharmony_ci dev_info(&adapter->pdev->dev, "enabled switch mode: %s\n", 500562306a36Sopenharmony_ci mode == BRIDGE_MODE_VEPA ? "VEPA" : "VEB"); 500662306a36Sopenharmony_ci 500762306a36Sopenharmony_ci return status; 500862306a36Sopenharmony_ci } 500962306a36Sopenharmony_cierr: 501062306a36Sopenharmony_ci dev_err(&adapter->pdev->dev, "Failed to set switch mode %s\n", 501162306a36Sopenharmony_ci mode == BRIDGE_MODE_VEPA ? "VEPA" : "VEB"); 501262306a36Sopenharmony_ci 501362306a36Sopenharmony_ci return status; 501462306a36Sopenharmony_ci} 501562306a36Sopenharmony_ci 501662306a36Sopenharmony_cistatic int be_ndo_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq, 501762306a36Sopenharmony_ci struct net_device *dev, u32 filter_mask, 501862306a36Sopenharmony_ci int nlflags) 501962306a36Sopenharmony_ci{ 502062306a36Sopenharmony_ci struct be_adapter *adapter = netdev_priv(dev); 502162306a36Sopenharmony_ci int status = 0; 502262306a36Sopenharmony_ci u8 hsw_mode; 502362306a36Sopenharmony_ci 502462306a36Sopenharmony_ci /* BE and Lancer chips support VEB mode only */ 502562306a36Sopenharmony_ci if (BEx_chip(adapter) || lancer_chip(adapter)) { 502662306a36Sopenharmony_ci /* VEB is disabled in non-SR-IOV profiles on BE3/Lancer */ 502762306a36Sopenharmony_ci if (!pci_sriov_get_totalvfs(adapter->pdev)) 502862306a36Sopenharmony_ci return 0; 502962306a36Sopenharmony_ci hsw_mode = PORT_FWD_TYPE_VEB; 503062306a36Sopenharmony_ci } else { 503162306a36Sopenharmony_ci status = be_cmd_get_hsw_config(adapter, NULL, 0, 503262306a36Sopenharmony_ci adapter->if_handle, &hsw_mode, 503362306a36Sopenharmony_ci NULL); 503462306a36Sopenharmony_ci if (status) 503562306a36Sopenharmony_ci return 0; 503662306a36Sopenharmony_ci 503762306a36Sopenharmony_ci if (hsw_mode == PORT_FWD_TYPE_PASSTHRU) 503862306a36Sopenharmony_ci return 0; 503962306a36Sopenharmony_ci } 504062306a36Sopenharmony_ci 504162306a36Sopenharmony_ci return ndo_dflt_bridge_getlink(skb, pid, seq, dev, 504262306a36Sopenharmony_ci hsw_mode == PORT_FWD_TYPE_VEPA ? 504362306a36Sopenharmony_ci BRIDGE_MODE_VEPA : BRIDGE_MODE_VEB, 504462306a36Sopenharmony_ci 0, 0, nlflags, filter_mask, NULL); 504562306a36Sopenharmony_ci} 504662306a36Sopenharmony_ci 504762306a36Sopenharmony_cistatic struct be_cmd_work *be_alloc_work(struct be_adapter *adapter, 504862306a36Sopenharmony_ci void (*func)(struct work_struct *)) 504962306a36Sopenharmony_ci{ 505062306a36Sopenharmony_ci struct be_cmd_work *work; 505162306a36Sopenharmony_ci 505262306a36Sopenharmony_ci work = kzalloc(sizeof(*work), GFP_ATOMIC); 505362306a36Sopenharmony_ci if (!work) { 505462306a36Sopenharmony_ci dev_err(&adapter->pdev->dev, 505562306a36Sopenharmony_ci "be_work memory allocation failed\n"); 505662306a36Sopenharmony_ci return NULL; 505762306a36Sopenharmony_ci } 505862306a36Sopenharmony_ci 505962306a36Sopenharmony_ci INIT_WORK(&work->work, func); 506062306a36Sopenharmony_ci work->adapter = adapter; 506162306a36Sopenharmony_ci return work; 506262306a36Sopenharmony_ci} 506362306a36Sopenharmony_ci 506462306a36Sopenharmony_cistatic netdev_features_t be_features_check(struct sk_buff *skb, 506562306a36Sopenharmony_ci struct net_device *dev, 506662306a36Sopenharmony_ci netdev_features_t features) 506762306a36Sopenharmony_ci{ 506862306a36Sopenharmony_ci struct be_adapter *adapter = netdev_priv(dev); 506962306a36Sopenharmony_ci u8 l4_hdr = 0; 507062306a36Sopenharmony_ci 507162306a36Sopenharmony_ci if (skb_is_gso(skb)) { 507262306a36Sopenharmony_ci /* IPv6 TSO requests with extension hdrs are a problem 507362306a36Sopenharmony_ci * to Lancer and BE3 HW. Disable TSO6 feature. 507462306a36Sopenharmony_ci */ 507562306a36Sopenharmony_ci if (!skyhawk_chip(adapter) && is_ipv6_ext_hdr(skb)) 507662306a36Sopenharmony_ci features &= ~NETIF_F_TSO6; 507762306a36Sopenharmony_ci 507862306a36Sopenharmony_ci /* Lancer cannot handle the packet with MSS less than 256. 507962306a36Sopenharmony_ci * Also it can't handle a TSO packet with a single segment 508062306a36Sopenharmony_ci * Disable the GSO support in such cases 508162306a36Sopenharmony_ci */ 508262306a36Sopenharmony_ci if (lancer_chip(adapter) && 508362306a36Sopenharmony_ci (skb_shinfo(skb)->gso_size < 256 || 508462306a36Sopenharmony_ci skb_shinfo(skb)->gso_segs == 1)) 508562306a36Sopenharmony_ci features &= ~NETIF_F_GSO_MASK; 508662306a36Sopenharmony_ci } 508762306a36Sopenharmony_ci 508862306a36Sopenharmony_ci /* The code below restricts offload features for some tunneled and 508962306a36Sopenharmony_ci * Q-in-Q packets. 509062306a36Sopenharmony_ci * Offload features for normal (non tunnel) packets are unchanged. 509162306a36Sopenharmony_ci */ 509262306a36Sopenharmony_ci features = vlan_features_check(skb, features); 509362306a36Sopenharmony_ci if (!skb->encapsulation || 509462306a36Sopenharmony_ci !(adapter->flags & BE_FLAGS_VXLAN_OFFLOADS)) 509562306a36Sopenharmony_ci return features; 509662306a36Sopenharmony_ci 509762306a36Sopenharmony_ci /* It's an encapsulated packet and VxLAN offloads are enabled. We 509862306a36Sopenharmony_ci * should disable tunnel offload features if it's not a VxLAN packet, 509962306a36Sopenharmony_ci * as tunnel offloads have been enabled only for VxLAN. This is done to 510062306a36Sopenharmony_ci * allow other tunneled traffic like GRE work fine while VxLAN 510162306a36Sopenharmony_ci * offloads are configured in Skyhawk-R. 510262306a36Sopenharmony_ci */ 510362306a36Sopenharmony_ci switch (vlan_get_protocol(skb)) { 510462306a36Sopenharmony_ci case htons(ETH_P_IP): 510562306a36Sopenharmony_ci l4_hdr = ip_hdr(skb)->protocol; 510662306a36Sopenharmony_ci break; 510762306a36Sopenharmony_ci case htons(ETH_P_IPV6): 510862306a36Sopenharmony_ci l4_hdr = ipv6_hdr(skb)->nexthdr; 510962306a36Sopenharmony_ci break; 511062306a36Sopenharmony_ci default: 511162306a36Sopenharmony_ci return features; 511262306a36Sopenharmony_ci } 511362306a36Sopenharmony_ci 511462306a36Sopenharmony_ci if (l4_hdr != IPPROTO_UDP || 511562306a36Sopenharmony_ci skb->inner_protocol_type != ENCAP_TYPE_ETHER || 511662306a36Sopenharmony_ci skb->inner_protocol != htons(ETH_P_TEB) || 511762306a36Sopenharmony_ci skb_inner_mac_header(skb) - skb_transport_header(skb) != 511862306a36Sopenharmony_ci sizeof(struct udphdr) + sizeof(struct vxlanhdr) || 511962306a36Sopenharmony_ci !adapter->vxlan_port || 512062306a36Sopenharmony_ci udp_hdr(skb)->dest != adapter->vxlan_port) 512162306a36Sopenharmony_ci return features & ~(NETIF_F_CSUM_MASK | NETIF_F_GSO_MASK); 512262306a36Sopenharmony_ci 512362306a36Sopenharmony_ci return features; 512462306a36Sopenharmony_ci} 512562306a36Sopenharmony_ci 512662306a36Sopenharmony_cistatic int be_get_phys_port_id(struct net_device *dev, 512762306a36Sopenharmony_ci struct netdev_phys_item_id *ppid) 512862306a36Sopenharmony_ci{ 512962306a36Sopenharmony_ci int i, id_len = CNTL_SERIAL_NUM_WORDS * CNTL_SERIAL_NUM_WORD_SZ + 1; 513062306a36Sopenharmony_ci struct be_adapter *adapter = netdev_priv(dev); 513162306a36Sopenharmony_ci u8 *id; 513262306a36Sopenharmony_ci 513362306a36Sopenharmony_ci if (MAX_PHYS_ITEM_ID_LEN < id_len) 513462306a36Sopenharmony_ci return -ENOSPC; 513562306a36Sopenharmony_ci 513662306a36Sopenharmony_ci ppid->id[0] = adapter->hba_port_num + 1; 513762306a36Sopenharmony_ci id = &ppid->id[1]; 513862306a36Sopenharmony_ci for (i = CNTL_SERIAL_NUM_WORDS - 1; i >= 0; 513962306a36Sopenharmony_ci i--, id += CNTL_SERIAL_NUM_WORD_SZ) 514062306a36Sopenharmony_ci memcpy(id, &adapter->serial_num[i], CNTL_SERIAL_NUM_WORD_SZ); 514162306a36Sopenharmony_ci 514262306a36Sopenharmony_ci ppid->id_len = id_len; 514362306a36Sopenharmony_ci 514462306a36Sopenharmony_ci return 0; 514562306a36Sopenharmony_ci} 514662306a36Sopenharmony_ci 514762306a36Sopenharmony_cistatic void be_set_rx_mode(struct net_device *dev) 514862306a36Sopenharmony_ci{ 514962306a36Sopenharmony_ci struct be_adapter *adapter = netdev_priv(dev); 515062306a36Sopenharmony_ci struct be_cmd_work *work; 515162306a36Sopenharmony_ci 515262306a36Sopenharmony_ci work = be_alloc_work(adapter, be_work_set_rx_mode); 515362306a36Sopenharmony_ci if (work) 515462306a36Sopenharmony_ci queue_work(be_wq, &work->work); 515562306a36Sopenharmony_ci} 515662306a36Sopenharmony_ci 515762306a36Sopenharmony_cistatic const struct net_device_ops be_netdev_ops = { 515862306a36Sopenharmony_ci .ndo_open = be_open, 515962306a36Sopenharmony_ci .ndo_stop = be_close, 516062306a36Sopenharmony_ci .ndo_start_xmit = be_xmit, 516162306a36Sopenharmony_ci .ndo_set_rx_mode = be_set_rx_mode, 516262306a36Sopenharmony_ci .ndo_set_mac_address = be_mac_addr_set, 516362306a36Sopenharmony_ci .ndo_get_stats64 = be_get_stats64, 516462306a36Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 516562306a36Sopenharmony_ci .ndo_vlan_rx_add_vid = be_vlan_add_vid, 516662306a36Sopenharmony_ci .ndo_vlan_rx_kill_vid = be_vlan_rem_vid, 516762306a36Sopenharmony_ci .ndo_set_vf_mac = be_set_vf_mac, 516862306a36Sopenharmony_ci .ndo_set_vf_vlan = be_set_vf_vlan, 516962306a36Sopenharmony_ci .ndo_set_vf_rate = be_set_vf_tx_rate, 517062306a36Sopenharmony_ci .ndo_get_vf_config = be_get_vf_config, 517162306a36Sopenharmony_ci .ndo_set_vf_link_state = be_set_vf_link_state, 517262306a36Sopenharmony_ci .ndo_set_vf_spoofchk = be_set_vf_spoofchk, 517362306a36Sopenharmony_ci .ndo_tx_timeout = be_tx_timeout, 517462306a36Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER 517562306a36Sopenharmony_ci .ndo_poll_controller = be_netpoll, 517662306a36Sopenharmony_ci#endif 517762306a36Sopenharmony_ci .ndo_bridge_setlink = be_ndo_bridge_setlink, 517862306a36Sopenharmony_ci .ndo_bridge_getlink = be_ndo_bridge_getlink, 517962306a36Sopenharmony_ci .ndo_features_check = be_features_check, 518062306a36Sopenharmony_ci .ndo_get_phys_port_id = be_get_phys_port_id, 518162306a36Sopenharmony_ci}; 518262306a36Sopenharmony_ci 518362306a36Sopenharmony_cistatic void be_netdev_init(struct net_device *netdev) 518462306a36Sopenharmony_ci{ 518562306a36Sopenharmony_ci struct be_adapter *adapter = netdev_priv(netdev); 518662306a36Sopenharmony_ci 518762306a36Sopenharmony_ci netdev->hw_features |= NETIF_F_SG | NETIF_F_TSO | NETIF_F_TSO6 | 518862306a36Sopenharmony_ci NETIF_F_GSO_UDP_TUNNEL | 518962306a36Sopenharmony_ci NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | NETIF_F_RXCSUM | 519062306a36Sopenharmony_ci NETIF_F_HW_VLAN_CTAG_TX; 519162306a36Sopenharmony_ci if ((be_if_cap_flags(adapter) & BE_IF_FLAGS_RSS)) 519262306a36Sopenharmony_ci netdev->hw_features |= NETIF_F_RXHASH; 519362306a36Sopenharmony_ci 519462306a36Sopenharmony_ci netdev->features |= netdev->hw_features | 519562306a36Sopenharmony_ci NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_HW_VLAN_CTAG_FILTER | 519662306a36Sopenharmony_ci NETIF_F_HIGHDMA; 519762306a36Sopenharmony_ci 519862306a36Sopenharmony_ci netdev->vlan_features |= NETIF_F_SG | NETIF_F_TSO | NETIF_F_TSO6 | 519962306a36Sopenharmony_ci NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM; 520062306a36Sopenharmony_ci 520162306a36Sopenharmony_ci netdev->priv_flags |= IFF_UNICAST_FLT; 520262306a36Sopenharmony_ci 520362306a36Sopenharmony_ci netdev->flags |= IFF_MULTICAST; 520462306a36Sopenharmony_ci 520562306a36Sopenharmony_ci netif_set_tso_max_size(netdev, BE_MAX_GSO_SIZE - ETH_HLEN); 520662306a36Sopenharmony_ci 520762306a36Sopenharmony_ci netdev->netdev_ops = &be_netdev_ops; 520862306a36Sopenharmony_ci 520962306a36Sopenharmony_ci netdev->ethtool_ops = &be_ethtool_ops; 521062306a36Sopenharmony_ci 521162306a36Sopenharmony_ci if (!lancer_chip(adapter) && !BEx_chip(adapter) && !be_is_mc(adapter)) 521262306a36Sopenharmony_ci netdev->udp_tunnel_nic_info = &be_udp_tunnels; 521362306a36Sopenharmony_ci 521462306a36Sopenharmony_ci /* MTU range: 256 - 9000 */ 521562306a36Sopenharmony_ci netdev->min_mtu = BE_MIN_MTU; 521662306a36Sopenharmony_ci netdev->max_mtu = BE_MAX_MTU; 521762306a36Sopenharmony_ci} 521862306a36Sopenharmony_ci 521962306a36Sopenharmony_cistatic void be_cleanup(struct be_adapter *adapter) 522062306a36Sopenharmony_ci{ 522162306a36Sopenharmony_ci struct net_device *netdev = adapter->netdev; 522262306a36Sopenharmony_ci 522362306a36Sopenharmony_ci rtnl_lock(); 522462306a36Sopenharmony_ci netif_device_detach(netdev); 522562306a36Sopenharmony_ci if (netif_running(netdev)) 522662306a36Sopenharmony_ci be_close(netdev); 522762306a36Sopenharmony_ci rtnl_unlock(); 522862306a36Sopenharmony_ci 522962306a36Sopenharmony_ci be_clear(adapter); 523062306a36Sopenharmony_ci} 523162306a36Sopenharmony_ci 523262306a36Sopenharmony_cistatic int be_resume(struct be_adapter *adapter) 523362306a36Sopenharmony_ci{ 523462306a36Sopenharmony_ci struct net_device *netdev = adapter->netdev; 523562306a36Sopenharmony_ci int status; 523662306a36Sopenharmony_ci 523762306a36Sopenharmony_ci status = be_setup(adapter); 523862306a36Sopenharmony_ci if (status) 523962306a36Sopenharmony_ci return status; 524062306a36Sopenharmony_ci 524162306a36Sopenharmony_ci rtnl_lock(); 524262306a36Sopenharmony_ci if (netif_running(netdev)) 524362306a36Sopenharmony_ci status = be_open(netdev); 524462306a36Sopenharmony_ci rtnl_unlock(); 524562306a36Sopenharmony_ci 524662306a36Sopenharmony_ci if (status) 524762306a36Sopenharmony_ci return status; 524862306a36Sopenharmony_ci 524962306a36Sopenharmony_ci netif_device_attach(netdev); 525062306a36Sopenharmony_ci 525162306a36Sopenharmony_ci return 0; 525262306a36Sopenharmony_ci} 525362306a36Sopenharmony_ci 525462306a36Sopenharmony_cistatic void be_soft_reset(struct be_adapter *adapter) 525562306a36Sopenharmony_ci{ 525662306a36Sopenharmony_ci u32 val; 525762306a36Sopenharmony_ci 525862306a36Sopenharmony_ci dev_info(&adapter->pdev->dev, "Initiating chip soft reset\n"); 525962306a36Sopenharmony_ci val = ioread32(adapter->pcicfg + SLIPORT_SOFTRESET_OFFSET); 526062306a36Sopenharmony_ci val |= SLIPORT_SOFTRESET_SR_MASK; 526162306a36Sopenharmony_ci iowrite32(val, adapter->pcicfg + SLIPORT_SOFTRESET_OFFSET); 526262306a36Sopenharmony_ci} 526362306a36Sopenharmony_ci 526462306a36Sopenharmony_cistatic bool be_err_is_recoverable(struct be_adapter *adapter) 526562306a36Sopenharmony_ci{ 526662306a36Sopenharmony_ci struct be_error_recovery *err_rec = &adapter->error_recovery; 526762306a36Sopenharmony_ci unsigned long initial_idle_time = 526862306a36Sopenharmony_ci msecs_to_jiffies(ERR_RECOVERY_IDLE_TIME); 526962306a36Sopenharmony_ci unsigned long recovery_interval = 527062306a36Sopenharmony_ci msecs_to_jiffies(ERR_RECOVERY_INTERVAL); 527162306a36Sopenharmony_ci u16 ue_err_code; 527262306a36Sopenharmony_ci u32 val; 527362306a36Sopenharmony_ci 527462306a36Sopenharmony_ci val = be_POST_stage_get(adapter); 527562306a36Sopenharmony_ci if ((val & POST_STAGE_RECOVERABLE_ERR) != POST_STAGE_RECOVERABLE_ERR) 527662306a36Sopenharmony_ci return false; 527762306a36Sopenharmony_ci ue_err_code = val & POST_ERR_RECOVERY_CODE_MASK; 527862306a36Sopenharmony_ci if (ue_err_code == 0) 527962306a36Sopenharmony_ci return false; 528062306a36Sopenharmony_ci 528162306a36Sopenharmony_ci dev_err(&adapter->pdev->dev, "Recoverable HW error code: 0x%x\n", 528262306a36Sopenharmony_ci ue_err_code); 528362306a36Sopenharmony_ci 528462306a36Sopenharmony_ci if (time_before_eq(jiffies - err_rec->probe_time, initial_idle_time)) { 528562306a36Sopenharmony_ci dev_err(&adapter->pdev->dev, 528662306a36Sopenharmony_ci "Cannot recover within %lu sec from driver load\n", 528762306a36Sopenharmony_ci jiffies_to_msecs(initial_idle_time) / MSEC_PER_SEC); 528862306a36Sopenharmony_ci return false; 528962306a36Sopenharmony_ci } 529062306a36Sopenharmony_ci 529162306a36Sopenharmony_ci if (err_rec->last_recovery_time && time_before_eq( 529262306a36Sopenharmony_ci jiffies - err_rec->last_recovery_time, recovery_interval)) { 529362306a36Sopenharmony_ci dev_err(&adapter->pdev->dev, 529462306a36Sopenharmony_ci "Cannot recover within %lu sec from last recovery\n", 529562306a36Sopenharmony_ci jiffies_to_msecs(recovery_interval) / MSEC_PER_SEC); 529662306a36Sopenharmony_ci return false; 529762306a36Sopenharmony_ci } 529862306a36Sopenharmony_ci 529962306a36Sopenharmony_ci if (ue_err_code == err_rec->last_err_code) { 530062306a36Sopenharmony_ci dev_err(&adapter->pdev->dev, 530162306a36Sopenharmony_ci "Cannot recover from a consecutive TPE error\n"); 530262306a36Sopenharmony_ci return false; 530362306a36Sopenharmony_ci } 530462306a36Sopenharmony_ci 530562306a36Sopenharmony_ci err_rec->last_recovery_time = jiffies; 530662306a36Sopenharmony_ci err_rec->last_err_code = ue_err_code; 530762306a36Sopenharmony_ci return true; 530862306a36Sopenharmony_ci} 530962306a36Sopenharmony_ci 531062306a36Sopenharmony_cistatic int be_tpe_recover(struct be_adapter *adapter) 531162306a36Sopenharmony_ci{ 531262306a36Sopenharmony_ci struct be_error_recovery *err_rec = &adapter->error_recovery; 531362306a36Sopenharmony_ci int status = -EAGAIN; 531462306a36Sopenharmony_ci u32 val; 531562306a36Sopenharmony_ci 531662306a36Sopenharmony_ci switch (err_rec->recovery_state) { 531762306a36Sopenharmony_ci case ERR_RECOVERY_ST_NONE: 531862306a36Sopenharmony_ci err_rec->recovery_state = ERR_RECOVERY_ST_DETECT; 531962306a36Sopenharmony_ci err_rec->resched_delay = ERR_RECOVERY_UE_DETECT_DURATION; 532062306a36Sopenharmony_ci break; 532162306a36Sopenharmony_ci 532262306a36Sopenharmony_ci case ERR_RECOVERY_ST_DETECT: 532362306a36Sopenharmony_ci val = be_POST_stage_get(adapter); 532462306a36Sopenharmony_ci if ((val & POST_STAGE_RECOVERABLE_ERR) != 532562306a36Sopenharmony_ci POST_STAGE_RECOVERABLE_ERR) { 532662306a36Sopenharmony_ci dev_err(&adapter->pdev->dev, 532762306a36Sopenharmony_ci "Unrecoverable HW error detected: 0x%x\n", val); 532862306a36Sopenharmony_ci status = -EINVAL; 532962306a36Sopenharmony_ci err_rec->resched_delay = 0; 533062306a36Sopenharmony_ci break; 533162306a36Sopenharmony_ci } 533262306a36Sopenharmony_ci 533362306a36Sopenharmony_ci dev_err(&adapter->pdev->dev, "Recoverable HW error detected\n"); 533462306a36Sopenharmony_ci 533562306a36Sopenharmony_ci /* Only PF0 initiates Chip Soft Reset. But PF0 must wait UE2SR 533662306a36Sopenharmony_ci * milliseconds before it checks for final error status in 533762306a36Sopenharmony_ci * SLIPORT_SEMAPHORE to determine if recovery criteria is met. 533862306a36Sopenharmony_ci * If it does, then PF0 initiates a Soft Reset. 533962306a36Sopenharmony_ci */ 534062306a36Sopenharmony_ci if (adapter->pf_num == 0) { 534162306a36Sopenharmony_ci err_rec->recovery_state = ERR_RECOVERY_ST_RESET; 534262306a36Sopenharmony_ci err_rec->resched_delay = err_rec->ue_to_reset_time - 534362306a36Sopenharmony_ci ERR_RECOVERY_UE_DETECT_DURATION; 534462306a36Sopenharmony_ci break; 534562306a36Sopenharmony_ci } 534662306a36Sopenharmony_ci 534762306a36Sopenharmony_ci err_rec->recovery_state = ERR_RECOVERY_ST_PRE_POLL; 534862306a36Sopenharmony_ci err_rec->resched_delay = err_rec->ue_to_poll_time - 534962306a36Sopenharmony_ci ERR_RECOVERY_UE_DETECT_DURATION; 535062306a36Sopenharmony_ci break; 535162306a36Sopenharmony_ci 535262306a36Sopenharmony_ci case ERR_RECOVERY_ST_RESET: 535362306a36Sopenharmony_ci if (!be_err_is_recoverable(adapter)) { 535462306a36Sopenharmony_ci dev_err(&adapter->pdev->dev, 535562306a36Sopenharmony_ci "Failed to meet recovery criteria\n"); 535662306a36Sopenharmony_ci status = -EIO; 535762306a36Sopenharmony_ci err_rec->resched_delay = 0; 535862306a36Sopenharmony_ci break; 535962306a36Sopenharmony_ci } 536062306a36Sopenharmony_ci be_soft_reset(adapter); 536162306a36Sopenharmony_ci err_rec->recovery_state = ERR_RECOVERY_ST_PRE_POLL; 536262306a36Sopenharmony_ci err_rec->resched_delay = err_rec->ue_to_poll_time - 536362306a36Sopenharmony_ci err_rec->ue_to_reset_time; 536462306a36Sopenharmony_ci break; 536562306a36Sopenharmony_ci 536662306a36Sopenharmony_ci case ERR_RECOVERY_ST_PRE_POLL: 536762306a36Sopenharmony_ci err_rec->recovery_state = ERR_RECOVERY_ST_REINIT; 536862306a36Sopenharmony_ci err_rec->resched_delay = 0; 536962306a36Sopenharmony_ci status = 0; /* done */ 537062306a36Sopenharmony_ci break; 537162306a36Sopenharmony_ci 537262306a36Sopenharmony_ci default: 537362306a36Sopenharmony_ci status = -EINVAL; 537462306a36Sopenharmony_ci err_rec->resched_delay = 0; 537562306a36Sopenharmony_ci break; 537662306a36Sopenharmony_ci } 537762306a36Sopenharmony_ci 537862306a36Sopenharmony_ci return status; 537962306a36Sopenharmony_ci} 538062306a36Sopenharmony_ci 538162306a36Sopenharmony_cistatic int be_err_recover(struct be_adapter *adapter) 538262306a36Sopenharmony_ci{ 538362306a36Sopenharmony_ci int status; 538462306a36Sopenharmony_ci 538562306a36Sopenharmony_ci if (!lancer_chip(adapter)) { 538662306a36Sopenharmony_ci if (!adapter->error_recovery.recovery_supported || 538762306a36Sopenharmony_ci adapter->priv_flags & BE_DISABLE_TPE_RECOVERY) 538862306a36Sopenharmony_ci return -EIO; 538962306a36Sopenharmony_ci status = be_tpe_recover(adapter); 539062306a36Sopenharmony_ci if (status) 539162306a36Sopenharmony_ci goto err; 539262306a36Sopenharmony_ci } 539362306a36Sopenharmony_ci 539462306a36Sopenharmony_ci /* Wait for adapter to reach quiescent state before 539562306a36Sopenharmony_ci * destroying queues 539662306a36Sopenharmony_ci */ 539762306a36Sopenharmony_ci status = be_fw_wait_ready(adapter); 539862306a36Sopenharmony_ci if (status) 539962306a36Sopenharmony_ci goto err; 540062306a36Sopenharmony_ci 540162306a36Sopenharmony_ci adapter->flags |= BE_FLAGS_TRY_RECOVERY; 540262306a36Sopenharmony_ci 540362306a36Sopenharmony_ci be_cleanup(adapter); 540462306a36Sopenharmony_ci 540562306a36Sopenharmony_ci status = be_resume(adapter); 540662306a36Sopenharmony_ci if (status) 540762306a36Sopenharmony_ci goto err; 540862306a36Sopenharmony_ci 540962306a36Sopenharmony_ci adapter->flags &= ~BE_FLAGS_TRY_RECOVERY; 541062306a36Sopenharmony_ci 541162306a36Sopenharmony_cierr: 541262306a36Sopenharmony_ci return status; 541362306a36Sopenharmony_ci} 541462306a36Sopenharmony_ci 541562306a36Sopenharmony_cistatic void be_err_detection_task(struct work_struct *work) 541662306a36Sopenharmony_ci{ 541762306a36Sopenharmony_ci struct be_error_recovery *err_rec = 541862306a36Sopenharmony_ci container_of(work, struct be_error_recovery, 541962306a36Sopenharmony_ci err_detection_work.work); 542062306a36Sopenharmony_ci struct be_adapter *adapter = 542162306a36Sopenharmony_ci container_of(err_rec, struct be_adapter, 542262306a36Sopenharmony_ci error_recovery); 542362306a36Sopenharmony_ci u32 resched_delay = ERR_RECOVERY_DETECTION_DELAY; 542462306a36Sopenharmony_ci struct device *dev = &adapter->pdev->dev; 542562306a36Sopenharmony_ci int recovery_status; 542662306a36Sopenharmony_ci 542762306a36Sopenharmony_ci be_detect_error(adapter); 542862306a36Sopenharmony_ci if (!be_check_error(adapter, BE_ERROR_HW)) 542962306a36Sopenharmony_ci goto reschedule_task; 543062306a36Sopenharmony_ci 543162306a36Sopenharmony_ci recovery_status = be_err_recover(adapter); 543262306a36Sopenharmony_ci if (!recovery_status) { 543362306a36Sopenharmony_ci err_rec->recovery_retries = 0; 543462306a36Sopenharmony_ci err_rec->recovery_state = ERR_RECOVERY_ST_NONE; 543562306a36Sopenharmony_ci dev_info(dev, "Adapter recovery successful\n"); 543662306a36Sopenharmony_ci goto reschedule_task; 543762306a36Sopenharmony_ci } else if (!lancer_chip(adapter) && err_rec->resched_delay) { 543862306a36Sopenharmony_ci /* BEx/SH recovery state machine */ 543962306a36Sopenharmony_ci if (adapter->pf_num == 0 && 544062306a36Sopenharmony_ci err_rec->recovery_state > ERR_RECOVERY_ST_DETECT) 544162306a36Sopenharmony_ci dev_err(&adapter->pdev->dev, 544262306a36Sopenharmony_ci "Adapter recovery in progress\n"); 544362306a36Sopenharmony_ci resched_delay = err_rec->resched_delay; 544462306a36Sopenharmony_ci goto reschedule_task; 544562306a36Sopenharmony_ci } else if (lancer_chip(adapter) && be_virtfn(adapter)) { 544662306a36Sopenharmony_ci /* For VFs, check if PF have allocated resources 544762306a36Sopenharmony_ci * every second. 544862306a36Sopenharmony_ci */ 544962306a36Sopenharmony_ci dev_err(dev, "Re-trying adapter recovery\n"); 545062306a36Sopenharmony_ci goto reschedule_task; 545162306a36Sopenharmony_ci } else if (lancer_chip(adapter) && err_rec->recovery_retries++ < 545262306a36Sopenharmony_ci ERR_RECOVERY_MAX_RETRY_COUNT) { 545362306a36Sopenharmony_ci /* In case of another error during recovery, it takes 30 sec 545462306a36Sopenharmony_ci * for adapter to come out of error. Retry error recovery after 545562306a36Sopenharmony_ci * this time interval. 545662306a36Sopenharmony_ci */ 545762306a36Sopenharmony_ci dev_err(&adapter->pdev->dev, "Re-trying adapter recovery\n"); 545862306a36Sopenharmony_ci resched_delay = ERR_RECOVERY_RETRY_DELAY; 545962306a36Sopenharmony_ci goto reschedule_task; 546062306a36Sopenharmony_ci } else { 546162306a36Sopenharmony_ci dev_err(dev, "Adapter recovery failed\n"); 546262306a36Sopenharmony_ci dev_err(dev, "Please reboot server to recover\n"); 546362306a36Sopenharmony_ci } 546462306a36Sopenharmony_ci 546562306a36Sopenharmony_ci return; 546662306a36Sopenharmony_ci 546762306a36Sopenharmony_cireschedule_task: 546862306a36Sopenharmony_ci be_schedule_err_detection(adapter, resched_delay); 546962306a36Sopenharmony_ci} 547062306a36Sopenharmony_ci 547162306a36Sopenharmony_cistatic void be_log_sfp_info(struct be_adapter *adapter) 547262306a36Sopenharmony_ci{ 547362306a36Sopenharmony_ci int status; 547462306a36Sopenharmony_ci 547562306a36Sopenharmony_ci status = be_cmd_query_sfp_info(adapter); 547662306a36Sopenharmony_ci if (!status) { 547762306a36Sopenharmony_ci dev_err(&adapter->pdev->dev, 547862306a36Sopenharmony_ci "Port %c: %s Vendor: %s part no: %s", 547962306a36Sopenharmony_ci adapter->port_name, 548062306a36Sopenharmony_ci be_misconfig_evt_port_state[adapter->phy_state], 548162306a36Sopenharmony_ci adapter->phy.vendor_name, 548262306a36Sopenharmony_ci adapter->phy.vendor_pn); 548362306a36Sopenharmony_ci } 548462306a36Sopenharmony_ci adapter->flags &= ~BE_FLAGS_PHY_MISCONFIGURED; 548562306a36Sopenharmony_ci} 548662306a36Sopenharmony_ci 548762306a36Sopenharmony_cistatic void be_worker(struct work_struct *work) 548862306a36Sopenharmony_ci{ 548962306a36Sopenharmony_ci struct be_adapter *adapter = 549062306a36Sopenharmony_ci container_of(work, struct be_adapter, work.work); 549162306a36Sopenharmony_ci struct be_rx_obj *rxo; 549262306a36Sopenharmony_ci int i; 549362306a36Sopenharmony_ci 549462306a36Sopenharmony_ci if (be_physfn(adapter) && 549562306a36Sopenharmony_ci MODULO(adapter->work_counter, adapter->be_get_temp_freq) == 0) 549662306a36Sopenharmony_ci be_cmd_get_die_temperature(adapter); 549762306a36Sopenharmony_ci 549862306a36Sopenharmony_ci /* when interrupts are not yet enabled, just reap any pending 549962306a36Sopenharmony_ci * mcc completions 550062306a36Sopenharmony_ci */ 550162306a36Sopenharmony_ci if (!netif_running(adapter->netdev)) { 550262306a36Sopenharmony_ci local_bh_disable(); 550362306a36Sopenharmony_ci be_process_mcc(adapter); 550462306a36Sopenharmony_ci local_bh_enable(); 550562306a36Sopenharmony_ci goto reschedule; 550662306a36Sopenharmony_ci } 550762306a36Sopenharmony_ci 550862306a36Sopenharmony_ci if (!adapter->stats_cmd_sent) { 550962306a36Sopenharmony_ci if (lancer_chip(adapter)) 551062306a36Sopenharmony_ci lancer_cmd_get_pport_stats(adapter, 551162306a36Sopenharmony_ci &adapter->stats_cmd); 551262306a36Sopenharmony_ci else 551362306a36Sopenharmony_ci be_cmd_get_stats(adapter, &adapter->stats_cmd); 551462306a36Sopenharmony_ci } 551562306a36Sopenharmony_ci 551662306a36Sopenharmony_ci for_all_rx_queues(adapter, rxo, i) { 551762306a36Sopenharmony_ci /* Replenish RX-queues starved due to memory 551862306a36Sopenharmony_ci * allocation failures. 551962306a36Sopenharmony_ci */ 552062306a36Sopenharmony_ci if (rxo->rx_post_starved) 552162306a36Sopenharmony_ci be_post_rx_frags(rxo, GFP_KERNEL, MAX_RX_POST); 552262306a36Sopenharmony_ci } 552362306a36Sopenharmony_ci 552462306a36Sopenharmony_ci /* EQ-delay update for Skyhawk is done while notifying EQ */ 552562306a36Sopenharmony_ci if (!skyhawk_chip(adapter)) 552662306a36Sopenharmony_ci be_eqd_update(adapter, false); 552762306a36Sopenharmony_ci 552862306a36Sopenharmony_ci if (adapter->flags & BE_FLAGS_PHY_MISCONFIGURED) 552962306a36Sopenharmony_ci be_log_sfp_info(adapter); 553062306a36Sopenharmony_ci 553162306a36Sopenharmony_cireschedule: 553262306a36Sopenharmony_ci adapter->work_counter++; 553362306a36Sopenharmony_ci queue_delayed_work(be_wq, &adapter->work, msecs_to_jiffies(1000)); 553462306a36Sopenharmony_ci} 553562306a36Sopenharmony_ci 553662306a36Sopenharmony_cistatic void be_unmap_pci_bars(struct be_adapter *adapter) 553762306a36Sopenharmony_ci{ 553862306a36Sopenharmony_ci if (adapter->csr) 553962306a36Sopenharmony_ci pci_iounmap(adapter->pdev, adapter->csr); 554062306a36Sopenharmony_ci if (adapter->db) 554162306a36Sopenharmony_ci pci_iounmap(adapter->pdev, adapter->db); 554262306a36Sopenharmony_ci if (adapter->pcicfg && adapter->pcicfg_mapped) 554362306a36Sopenharmony_ci pci_iounmap(adapter->pdev, adapter->pcicfg); 554462306a36Sopenharmony_ci} 554562306a36Sopenharmony_ci 554662306a36Sopenharmony_cistatic int db_bar(struct be_adapter *adapter) 554762306a36Sopenharmony_ci{ 554862306a36Sopenharmony_ci if (lancer_chip(adapter) || be_virtfn(adapter)) 554962306a36Sopenharmony_ci return 0; 555062306a36Sopenharmony_ci else 555162306a36Sopenharmony_ci return 4; 555262306a36Sopenharmony_ci} 555362306a36Sopenharmony_ci 555462306a36Sopenharmony_cistatic int be_roce_map_pci_bars(struct be_adapter *adapter) 555562306a36Sopenharmony_ci{ 555662306a36Sopenharmony_ci if (skyhawk_chip(adapter)) { 555762306a36Sopenharmony_ci adapter->roce_db.size = 4096; 555862306a36Sopenharmony_ci adapter->roce_db.io_addr = pci_resource_start(adapter->pdev, 555962306a36Sopenharmony_ci db_bar(adapter)); 556062306a36Sopenharmony_ci adapter->roce_db.total_size = pci_resource_len(adapter->pdev, 556162306a36Sopenharmony_ci db_bar(adapter)); 556262306a36Sopenharmony_ci } 556362306a36Sopenharmony_ci return 0; 556462306a36Sopenharmony_ci} 556562306a36Sopenharmony_ci 556662306a36Sopenharmony_cistatic int be_map_pci_bars(struct be_adapter *adapter) 556762306a36Sopenharmony_ci{ 556862306a36Sopenharmony_ci struct pci_dev *pdev = adapter->pdev; 556962306a36Sopenharmony_ci u8 __iomem *addr; 557062306a36Sopenharmony_ci u32 sli_intf; 557162306a36Sopenharmony_ci 557262306a36Sopenharmony_ci pci_read_config_dword(adapter->pdev, SLI_INTF_REG_OFFSET, &sli_intf); 557362306a36Sopenharmony_ci adapter->sli_family = (sli_intf & SLI_INTF_FAMILY_MASK) >> 557462306a36Sopenharmony_ci SLI_INTF_FAMILY_SHIFT; 557562306a36Sopenharmony_ci adapter->virtfn = (sli_intf & SLI_INTF_FT_MASK) ? 1 : 0; 557662306a36Sopenharmony_ci 557762306a36Sopenharmony_ci if (BEx_chip(adapter) && be_physfn(adapter)) { 557862306a36Sopenharmony_ci adapter->csr = pci_iomap(pdev, 2, 0); 557962306a36Sopenharmony_ci if (!adapter->csr) 558062306a36Sopenharmony_ci return -ENOMEM; 558162306a36Sopenharmony_ci } 558262306a36Sopenharmony_ci 558362306a36Sopenharmony_ci addr = pci_iomap(pdev, db_bar(adapter), 0); 558462306a36Sopenharmony_ci if (!addr) 558562306a36Sopenharmony_ci goto pci_map_err; 558662306a36Sopenharmony_ci adapter->db = addr; 558762306a36Sopenharmony_ci 558862306a36Sopenharmony_ci if (skyhawk_chip(adapter) || BEx_chip(adapter)) { 558962306a36Sopenharmony_ci if (be_physfn(adapter)) { 559062306a36Sopenharmony_ci /* PCICFG is the 2nd BAR in BE2 */ 559162306a36Sopenharmony_ci addr = pci_iomap(pdev, BE2_chip(adapter) ? 1 : 0, 0); 559262306a36Sopenharmony_ci if (!addr) 559362306a36Sopenharmony_ci goto pci_map_err; 559462306a36Sopenharmony_ci adapter->pcicfg = addr; 559562306a36Sopenharmony_ci adapter->pcicfg_mapped = true; 559662306a36Sopenharmony_ci } else { 559762306a36Sopenharmony_ci adapter->pcicfg = adapter->db + SRIOV_VF_PCICFG_OFFSET; 559862306a36Sopenharmony_ci adapter->pcicfg_mapped = false; 559962306a36Sopenharmony_ci } 560062306a36Sopenharmony_ci } 560162306a36Sopenharmony_ci 560262306a36Sopenharmony_ci be_roce_map_pci_bars(adapter); 560362306a36Sopenharmony_ci return 0; 560462306a36Sopenharmony_ci 560562306a36Sopenharmony_cipci_map_err: 560662306a36Sopenharmony_ci dev_err(&pdev->dev, "Error in mapping PCI BARs\n"); 560762306a36Sopenharmony_ci be_unmap_pci_bars(adapter); 560862306a36Sopenharmony_ci return -ENOMEM; 560962306a36Sopenharmony_ci} 561062306a36Sopenharmony_ci 561162306a36Sopenharmony_cistatic void be_drv_cleanup(struct be_adapter *adapter) 561262306a36Sopenharmony_ci{ 561362306a36Sopenharmony_ci struct be_dma_mem *mem = &adapter->mbox_mem_alloced; 561462306a36Sopenharmony_ci struct device *dev = &adapter->pdev->dev; 561562306a36Sopenharmony_ci 561662306a36Sopenharmony_ci if (mem->va) 561762306a36Sopenharmony_ci dma_free_coherent(dev, mem->size, mem->va, mem->dma); 561862306a36Sopenharmony_ci 561962306a36Sopenharmony_ci mem = &adapter->rx_filter; 562062306a36Sopenharmony_ci if (mem->va) 562162306a36Sopenharmony_ci dma_free_coherent(dev, mem->size, mem->va, mem->dma); 562262306a36Sopenharmony_ci 562362306a36Sopenharmony_ci mem = &adapter->stats_cmd; 562462306a36Sopenharmony_ci if (mem->va) 562562306a36Sopenharmony_ci dma_free_coherent(dev, mem->size, mem->va, mem->dma); 562662306a36Sopenharmony_ci} 562762306a36Sopenharmony_ci 562862306a36Sopenharmony_ci/* Allocate and initialize various fields in be_adapter struct */ 562962306a36Sopenharmony_cistatic int be_drv_init(struct be_adapter *adapter) 563062306a36Sopenharmony_ci{ 563162306a36Sopenharmony_ci struct be_dma_mem *mbox_mem_alloc = &adapter->mbox_mem_alloced; 563262306a36Sopenharmony_ci struct be_dma_mem *mbox_mem_align = &adapter->mbox_mem; 563362306a36Sopenharmony_ci struct be_dma_mem *rx_filter = &adapter->rx_filter; 563462306a36Sopenharmony_ci struct be_dma_mem *stats_cmd = &adapter->stats_cmd; 563562306a36Sopenharmony_ci struct device *dev = &adapter->pdev->dev; 563662306a36Sopenharmony_ci int status = 0; 563762306a36Sopenharmony_ci 563862306a36Sopenharmony_ci mbox_mem_alloc->size = sizeof(struct be_mcc_mailbox) + 16; 563962306a36Sopenharmony_ci mbox_mem_alloc->va = dma_alloc_coherent(dev, mbox_mem_alloc->size, 564062306a36Sopenharmony_ci &mbox_mem_alloc->dma, 564162306a36Sopenharmony_ci GFP_KERNEL); 564262306a36Sopenharmony_ci if (!mbox_mem_alloc->va) 564362306a36Sopenharmony_ci return -ENOMEM; 564462306a36Sopenharmony_ci 564562306a36Sopenharmony_ci mbox_mem_align->size = sizeof(struct be_mcc_mailbox); 564662306a36Sopenharmony_ci mbox_mem_align->va = PTR_ALIGN(mbox_mem_alloc->va, 16); 564762306a36Sopenharmony_ci mbox_mem_align->dma = PTR_ALIGN(mbox_mem_alloc->dma, 16); 564862306a36Sopenharmony_ci 564962306a36Sopenharmony_ci rx_filter->size = sizeof(struct be_cmd_req_rx_filter); 565062306a36Sopenharmony_ci rx_filter->va = dma_alloc_coherent(dev, rx_filter->size, 565162306a36Sopenharmony_ci &rx_filter->dma, GFP_KERNEL); 565262306a36Sopenharmony_ci if (!rx_filter->va) { 565362306a36Sopenharmony_ci status = -ENOMEM; 565462306a36Sopenharmony_ci goto free_mbox; 565562306a36Sopenharmony_ci } 565662306a36Sopenharmony_ci 565762306a36Sopenharmony_ci if (lancer_chip(adapter)) 565862306a36Sopenharmony_ci stats_cmd->size = sizeof(struct lancer_cmd_req_pport_stats); 565962306a36Sopenharmony_ci else if (BE2_chip(adapter)) 566062306a36Sopenharmony_ci stats_cmd->size = sizeof(struct be_cmd_req_get_stats_v0); 566162306a36Sopenharmony_ci else if (BE3_chip(adapter)) 566262306a36Sopenharmony_ci stats_cmd->size = sizeof(struct be_cmd_req_get_stats_v1); 566362306a36Sopenharmony_ci else 566462306a36Sopenharmony_ci stats_cmd->size = sizeof(struct be_cmd_req_get_stats_v2); 566562306a36Sopenharmony_ci stats_cmd->va = dma_alloc_coherent(dev, stats_cmd->size, 566662306a36Sopenharmony_ci &stats_cmd->dma, GFP_KERNEL); 566762306a36Sopenharmony_ci if (!stats_cmd->va) { 566862306a36Sopenharmony_ci status = -ENOMEM; 566962306a36Sopenharmony_ci goto free_rx_filter; 567062306a36Sopenharmony_ci } 567162306a36Sopenharmony_ci 567262306a36Sopenharmony_ci mutex_init(&adapter->mbox_lock); 567362306a36Sopenharmony_ci mutex_init(&adapter->mcc_lock); 567462306a36Sopenharmony_ci mutex_init(&adapter->rx_filter_lock); 567562306a36Sopenharmony_ci spin_lock_init(&adapter->mcc_cq_lock); 567662306a36Sopenharmony_ci init_completion(&adapter->et_cmd_compl); 567762306a36Sopenharmony_ci 567862306a36Sopenharmony_ci pci_save_state(adapter->pdev); 567962306a36Sopenharmony_ci 568062306a36Sopenharmony_ci INIT_DELAYED_WORK(&adapter->work, be_worker); 568162306a36Sopenharmony_ci 568262306a36Sopenharmony_ci adapter->error_recovery.recovery_state = ERR_RECOVERY_ST_NONE; 568362306a36Sopenharmony_ci adapter->error_recovery.resched_delay = 0; 568462306a36Sopenharmony_ci INIT_DELAYED_WORK(&adapter->error_recovery.err_detection_work, 568562306a36Sopenharmony_ci be_err_detection_task); 568662306a36Sopenharmony_ci 568762306a36Sopenharmony_ci adapter->rx_fc = true; 568862306a36Sopenharmony_ci adapter->tx_fc = true; 568962306a36Sopenharmony_ci 569062306a36Sopenharmony_ci /* Must be a power of 2 or else MODULO will BUG_ON */ 569162306a36Sopenharmony_ci adapter->be_get_temp_freq = 64; 569262306a36Sopenharmony_ci 569362306a36Sopenharmony_ci return 0; 569462306a36Sopenharmony_ci 569562306a36Sopenharmony_cifree_rx_filter: 569662306a36Sopenharmony_ci dma_free_coherent(dev, rx_filter->size, rx_filter->va, rx_filter->dma); 569762306a36Sopenharmony_cifree_mbox: 569862306a36Sopenharmony_ci dma_free_coherent(dev, mbox_mem_alloc->size, mbox_mem_alloc->va, 569962306a36Sopenharmony_ci mbox_mem_alloc->dma); 570062306a36Sopenharmony_ci return status; 570162306a36Sopenharmony_ci} 570262306a36Sopenharmony_ci 570362306a36Sopenharmony_cistatic void be_remove(struct pci_dev *pdev) 570462306a36Sopenharmony_ci{ 570562306a36Sopenharmony_ci struct be_adapter *adapter = pci_get_drvdata(pdev); 570662306a36Sopenharmony_ci 570762306a36Sopenharmony_ci if (!adapter) 570862306a36Sopenharmony_ci return; 570962306a36Sopenharmony_ci 571062306a36Sopenharmony_ci be_roce_dev_remove(adapter); 571162306a36Sopenharmony_ci be_intr_set(adapter, false); 571262306a36Sopenharmony_ci 571362306a36Sopenharmony_ci be_cancel_err_detection(adapter); 571462306a36Sopenharmony_ci 571562306a36Sopenharmony_ci unregister_netdev(adapter->netdev); 571662306a36Sopenharmony_ci 571762306a36Sopenharmony_ci be_clear(adapter); 571862306a36Sopenharmony_ci 571962306a36Sopenharmony_ci if (!pci_vfs_assigned(adapter->pdev)) 572062306a36Sopenharmony_ci be_cmd_reset_function(adapter); 572162306a36Sopenharmony_ci 572262306a36Sopenharmony_ci /* tell fw we're done with firing cmds */ 572362306a36Sopenharmony_ci be_cmd_fw_clean(adapter); 572462306a36Sopenharmony_ci 572562306a36Sopenharmony_ci be_unmap_pci_bars(adapter); 572662306a36Sopenharmony_ci be_drv_cleanup(adapter); 572762306a36Sopenharmony_ci 572862306a36Sopenharmony_ci pci_release_regions(pdev); 572962306a36Sopenharmony_ci pci_disable_device(pdev); 573062306a36Sopenharmony_ci 573162306a36Sopenharmony_ci free_netdev(adapter->netdev); 573262306a36Sopenharmony_ci} 573362306a36Sopenharmony_ci 573462306a36Sopenharmony_cistatic ssize_t be_hwmon_show_temp(struct device *dev, 573562306a36Sopenharmony_ci struct device_attribute *dev_attr, 573662306a36Sopenharmony_ci char *buf) 573762306a36Sopenharmony_ci{ 573862306a36Sopenharmony_ci struct be_adapter *adapter = dev_get_drvdata(dev); 573962306a36Sopenharmony_ci 574062306a36Sopenharmony_ci /* Unit: millidegree Celsius */ 574162306a36Sopenharmony_ci if (adapter->hwmon_info.be_on_die_temp == BE_INVALID_DIE_TEMP) 574262306a36Sopenharmony_ci return -EIO; 574362306a36Sopenharmony_ci else 574462306a36Sopenharmony_ci return sprintf(buf, "%u\n", 574562306a36Sopenharmony_ci adapter->hwmon_info.be_on_die_temp * 1000); 574662306a36Sopenharmony_ci} 574762306a36Sopenharmony_ci 574862306a36Sopenharmony_cistatic SENSOR_DEVICE_ATTR(temp1_input, 0444, 574962306a36Sopenharmony_ci be_hwmon_show_temp, NULL, 1); 575062306a36Sopenharmony_ci 575162306a36Sopenharmony_cistatic struct attribute *be_hwmon_attrs[] = { 575262306a36Sopenharmony_ci &sensor_dev_attr_temp1_input.dev_attr.attr, 575362306a36Sopenharmony_ci NULL 575462306a36Sopenharmony_ci}; 575562306a36Sopenharmony_ci 575662306a36Sopenharmony_ciATTRIBUTE_GROUPS(be_hwmon); 575762306a36Sopenharmony_ci 575862306a36Sopenharmony_cistatic char *mc_name(struct be_adapter *adapter) 575962306a36Sopenharmony_ci{ 576062306a36Sopenharmony_ci char *str = ""; /* default */ 576162306a36Sopenharmony_ci 576262306a36Sopenharmony_ci switch (adapter->mc_type) { 576362306a36Sopenharmony_ci case UMC: 576462306a36Sopenharmony_ci str = "UMC"; 576562306a36Sopenharmony_ci break; 576662306a36Sopenharmony_ci case FLEX10: 576762306a36Sopenharmony_ci str = "FLEX10"; 576862306a36Sopenharmony_ci break; 576962306a36Sopenharmony_ci case vNIC1: 577062306a36Sopenharmony_ci str = "vNIC-1"; 577162306a36Sopenharmony_ci break; 577262306a36Sopenharmony_ci case nPAR: 577362306a36Sopenharmony_ci str = "nPAR"; 577462306a36Sopenharmony_ci break; 577562306a36Sopenharmony_ci case UFP: 577662306a36Sopenharmony_ci str = "UFP"; 577762306a36Sopenharmony_ci break; 577862306a36Sopenharmony_ci case vNIC2: 577962306a36Sopenharmony_ci str = "vNIC-2"; 578062306a36Sopenharmony_ci break; 578162306a36Sopenharmony_ci default: 578262306a36Sopenharmony_ci str = ""; 578362306a36Sopenharmony_ci } 578462306a36Sopenharmony_ci 578562306a36Sopenharmony_ci return str; 578662306a36Sopenharmony_ci} 578762306a36Sopenharmony_ci 578862306a36Sopenharmony_cistatic inline char *func_name(struct be_adapter *adapter) 578962306a36Sopenharmony_ci{ 579062306a36Sopenharmony_ci return be_physfn(adapter) ? "PF" : "VF"; 579162306a36Sopenharmony_ci} 579262306a36Sopenharmony_ci 579362306a36Sopenharmony_cistatic inline char *nic_name(struct pci_dev *pdev) 579462306a36Sopenharmony_ci{ 579562306a36Sopenharmony_ci switch (pdev->device) { 579662306a36Sopenharmony_ci case OC_DEVICE_ID1: 579762306a36Sopenharmony_ci return OC_NAME; 579862306a36Sopenharmony_ci case OC_DEVICE_ID2: 579962306a36Sopenharmony_ci return OC_NAME_BE; 580062306a36Sopenharmony_ci case OC_DEVICE_ID3: 580162306a36Sopenharmony_ci case OC_DEVICE_ID4: 580262306a36Sopenharmony_ci return OC_NAME_LANCER; 580362306a36Sopenharmony_ci case BE_DEVICE_ID2: 580462306a36Sopenharmony_ci return BE3_NAME; 580562306a36Sopenharmony_ci case OC_DEVICE_ID5: 580662306a36Sopenharmony_ci case OC_DEVICE_ID6: 580762306a36Sopenharmony_ci return OC_NAME_SH; 580862306a36Sopenharmony_ci default: 580962306a36Sopenharmony_ci return BE_NAME; 581062306a36Sopenharmony_ci } 581162306a36Sopenharmony_ci} 581262306a36Sopenharmony_ci 581362306a36Sopenharmony_cistatic int be_probe(struct pci_dev *pdev, const struct pci_device_id *pdev_id) 581462306a36Sopenharmony_ci{ 581562306a36Sopenharmony_ci struct be_adapter *adapter; 581662306a36Sopenharmony_ci struct net_device *netdev; 581762306a36Sopenharmony_ci int status = 0; 581862306a36Sopenharmony_ci 581962306a36Sopenharmony_ci status = pci_enable_device(pdev); 582062306a36Sopenharmony_ci if (status) 582162306a36Sopenharmony_ci goto do_none; 582262306a36Sopenharmony_ci 582362306a36Sopenharmony_ci status = pci_request_regions(pdev, DRV_NAME); 582462306a36Sopenharmony_ci if (status) 582562306a36Sopenharmony_ci goto disable_dev; 582662306a36Sopenharmony_ci pci_set_master(pdev); 582762306a36Sopenharmony_ci 582862306a36Sopenharmony_ci netdev = alloc_etherdev_mqs(sizeof(*adapter), MAX_TX_QS, MAX_RX_QS); 582962306a36Sopenharmony_ci if (!netdev) { 583062306a36Sopenharmony_ci status = -ENOMEM; 583162306a36Sopenharmony_ci goto rel_reg; 583262306a36Sopenharmony_ci } 583362306a36Sopenharmony_ci adapter = netdev_priv(netdev); 583462306a36Sopenharmony_ci adapter->pdev = pdev; 583562306a36Sopenharmony_ci pci_set_drvdata(pdev, adapter); 583662306a36Sopenharmony_ci adapter->netdev = netdev; 583762306a36Sopenharmony_ci SET_NETDEV_DEV(netdev, &pdev->dev); 583862306a36Sopenharmony_ci 583962306a36Sopenharmony_ci status = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); 584062306a36Sopenharmony_ci if (status) { 584162306a36Sopenharmony_ci dev_err(&pdev->dev, "Could not set PCI DMA Mask\n"); 584262306a36Sopenharmony_ci goto free_netdev; 584362306a36Sopenharmony_ci } 584462306a36Sopenharmony_ci 584562306a36Sopenharmony_ci status = be_map_pci_bars(adapter); 584662306a36Sopenharmony_ci if (status) 584762306a36Sopenharmony_ci goto free_netdev; 584862306a36Sopenharmony_ci 584962306a36Sopenharmony_ci status = be_drv_init(adapter); 585062306a36Sopenharmony_ci if (status) 585162306a36Sopenharmony_ci goto unmap_bars; 585262306a36Sopenharmony_ci 585362306a36Sopenharmony_ci status = be_setup(adapter); 585462306a36Sopenharmony_ci if (status) 585562306a36Sopenharmony_ci goto drv_cleanup; 585662306a36Sopenharmony_ci 585762306a36Sopenharmony_ci be_netdev_init(netdev); 585862306a36Sopenharmony_ci status = register_netdev(netdev); 585962306a36Sopenharmony_ci if (status != 0) 586062306a36Sopenharmony_ci goto unsetup; 586162306a36Sopenharmony_ci 586262306a36Sopenharmony_ci be_roce_dev_add(adapter); 586362306a36Sopenharmony_ci 586462306a36Sopenharmony_ci be_schedule_err_detection(adapter, ERR_DETECTION_DELAY); 586562306a36Sopenharmony_ci adapter->error_recovery.probe_time = jiffies; 586662306a36Sopenharmony_ci 586762306a36Sopenharmony_ci /* On Die temperature not supported for VF. */ 586862306a36Sopenharmony_ci if (be_physfn(adapter) && IS_ENABLED(CONFIG_BE2NET_HWMON)) { 586962306a36Sopenharmony_ci adapter->hwmon_info.hwmon_dev = 587062306a36Sopenharmony_ci devm_hwmon_device_register_with_groups(&pdev->dev, 587162306a36Sopenharmony_ci DRV_NAME, 587262306a36Sopenharmony_ci adapter, 587362306a36Sopenharmony_ci be_hwmon_groups); 587462306a36Sopenharmony_ci adapter->hwmon_info.be_on_die_temp = BE_INVALID_DIE_TEMP; 587562306a36Sopenharmony_ci } 587662306a36Sopenharmony_ci 587762306a36Sopenharmony_ci dev_info(&pdev->dev, "%s: %s %s port %c\n", nic_name(pdev), 587862306a36Sopenharmony_ci func_name(adapter), mc_name(adapter), adapter->port_name); 587962306a36Sopenharmony_ci 588062306a36Sopenharmony_ci return 0; 588162306a36Sopenharmony_ci 588262306a36Sopenharmony_ciunsetup: 588362306a36Sopenharmony_ci be_clear(adapter); 588462306a36Sopenharmony_cidrv_cleanup: 588562306a36Sopenharmony_ci be_drv_cleanup(adapter); 588662306a36Sopenharmony_ciunmap_bars: 588762306a36Sopenharmony_ci be_unmap_pci_bars(adapter); 588862306a36Sopenharmony_cifree_netdev: 588962306a36Sopenharmony_ci free_netdev(netdev); 589062306a36Sopenharmony_cirel_reg: 589162306a36Sopenharmony_ci pci_release_regions(pdev); 589262306a36Sopenharmony_cidisable_dev: 589362306a36Sopenharmony_ci pci_disable_device(pdev); 589462306a36Sopenharmony_cido_none: 589562306a36Sopenharmony_ci dev_err(&pdev->dev, "%s initialization failed\n", nic_name(pdev)); 589662306a36Sopenharmony_ci return status; 589762306a36Sopenharmony_ci} 589862306a36Sopenharmony_ci 589962306a36Sopenharmony_cistatic int __maybe_unused be_suspend(struct device *dev_d) 590062306a36Sopenharmony_ci{ 590162306a36Sopenharmony_ci struct be_adapter *adapter = dev_get_drvdata(dev_d); 590262306a36Sopenharmony_ci 590362306a36Sopenharmony_ci be_intr_set(adapter, false); 590462306a36Sopenharmony_ci be_cancel_err_detection(adapter); 590562306a36Sopenharmony_ci 590662306a36Sopenharmony_ci be_cleanup(adapter); 590762306a36Sopenharmony_ci 590862306a36Sopenharmony_ci return 0; 590962306a36Sopenharmony_ci} 591062306a36Sopenharmony_ci 591162306a36Sopenharmony_cistatic int __maybe_unused be_pci_resume(struct device *dev_d) 591262306a36Sopenharmony_ci{ 591362306a36Sopenharmony_ci struct be_adapter *adapter = dev_get_drvdata(dev_d); 591462306a36Sopenharmony_ci int status = 0; 591562306a36Sopenharmony_ci 591662306a36Sopenharmony_ci status = be_resume(adapter); 591762306a36Sopenharmony_ci if (status) 591862306a36Sopenharmony_ci return status; 591962306a36Sopenharmony_ci 592062306a36Sopenharmony_ci be_schedule_err_detection(adapter, ERR_DETECTION_DELAY); 592162306a36Sopenharmony_ci 592262306a36Sopenharmony_ci return 0; 592362306a36Sopenharmony_ci} 592462306a36Sopenharmony_ci 592562306a36Sopenharmony_ci/* 592662306a36Sopenharmony_ci * An FLR will stop BE from DMAing any data. 592762306a36Sopenharmony_ci */ 592862306a36Sopenharmony_cistatic void be_shutdown(struct pci_dev *pdev) 592962306a36Sopenharmony_ci{ 593062306a36Sopenharmony_ci struct be_adapter *adapter = pci_get_drvdata(pdev); 593162306a36Sopenharmony_ci 593262306a36Sopenharmony_ci if (!adapter) 593362306a36Sopenharmony_ci return; 593462306a36Sopenharmony_ci 593562306a36Sopenharmony_ci be_roce_dev_shutdown(adapter); 593662306a36Sopenharmony_ci cancel_delayed_work_sync(&adapter->work); 593762306a36Sopenharmony_ci be_cancel_err_detection(adapter); 593862306a36Sopenharmony_ci 593962306a36Sopenharmony_ci netif_device_detach(adapter->netdev); 594062306a36Sopenharmony_ci 594162306a36Sopenharmony_ci be_cmd_reset_function(adapter); 594262306a36Sopenharmony_ci 594362306a36Sopenharmony_ci pci_disable_device(pdev); 594462306a36Sopenharmony_ci} 594562306a36Sopenharmony_ci 594662306a36Sopenharmony_cistatic pci_ers_result_t be_eeh_err_detected(struct pci_dev *pdev, 594762306a36Sopenharmony_ci pci_channel_state_t state) 594862306a36Sopenharmony_ci{ 594962306a36Sopenharmony_ci struct be_adapter *adapter = pci_get_drvdata(pdev); 595062306a36Sopenharmony_ci 595162306a36Sopenharmony_ci dev_err(&adapter->pdev->dev, "EEH error detected\n"); 595262306a36Sopenharmony_ci 595362306a36Sopenharmony_ci be_roce_dev_remove(adapter); 595462306a36Sopenharmony_ci 595562306a36Sopenharmony_ci if (!be_check_error(adapter, BE_ERROR_EEH)) { 595662306a36Sopenharmony_ci be_set_error(adapter, BE_ERROR_EEH); 595762306a36Sopenharmony_ci 595862306a36Sopenharmony_ci be_cancel_err_detection(adapter); 595962306a36Sopenharmony_ci 596062306a36Sopenharmony_ci be_cleanup(adapter); 596162306a36Sopenharmony_ci } 596262306a36Sopenharmony_ci 596362306a36Sopenharmony_ci if (state == pci_channel_io_perm_failure) 596462306a36Sopenharmony_ci return PCI_ERS_RESULT_DISCONNECT; 596562306a36Sopenharmony_ci 596662306a36Sopenharmony_ci pci_disable_device(pdev); 596762306a36Sopenharmony_ci 596862306a36Sopenharmony_ci /* The error could cause the FW to trigger a flash debug dump. 596962306a36Sopenharmony_ci * Resetting the card while flash dump is in progress 597062306a36Sopenharmony_ci * can cause it not to recover; wait for it to finish. 597162306a36Sopenharmony_ci * Wait only for first function as it is needed only once per 597262306a36Sopenharmony_ci * adapter. 597362306a36Sopenharmony_ci */ 597462306a36Sopenharmony_ci if (pdev->devfn == 0) 597562306a36Sopenharmony_ci ssleep(30); 597662306a36Sopenharmony_ci 597762306a36Sopenharmony_ci return PCI_ERS_RESULT_NEED_RESET; 597862306a36Sopenharmony_ci} 597962306a36Sopenharmony_ci 598062306a36Sopenharmony_cistatic pci_ers_result_t be_eeh_reset(struct pci_dev *pdev) 598162306a36Sopenharmony_ci{ 598262306a36Sopenharmony_ci struct be_adapter *adapter = pci_get_drvdata(pdev); 598362306a36Sopenharmony_ci int status; 598462306a36Sopenharmony_ci 598562306a36Sopenharmony_ci dev_info(&adapter->pdev->dev, "EEH reset\n"); 598662306a36Sopenharmony_ci 598762306a36Sopenharmony_ci status = pci_enable_device(pdev); 598862306a36Sopenharmony_ci if (status) 598962306a36Sopenharmony_ci return PCI_ERS_RESULT_DISCONNECT; 599062306a36Sopenharmony_ci 599162306a36Sopenharmony_ci pci_set_master(pdev); 599262306a36Sopenharmony_ci pci_restore_state(pdev); 599362306a36Sopenharmony_ci 599462306a36Sopenharmony_ci /* Check if card is ok and fw is ready */ 599562306a36Sopenharmony_ci dev_info(&adapter->pdev->dev, 599662306a36Sopenharmony_ci "Waiting for FW to be ready after EEH reset\n"); 599762306a36Sopenharmony_ci status = be_fw_wait_ready(adapter); 599862306a36Sopenharmony_ci if (status) 599962306a36Sopenharmony_ci return PCI_ERS_RESULT_DISCONNECT; 600062306a36Sopenharmony_ci 600162306a36Sopenharmony_ci be_clear_error(adapter, BE_CLEAR_ALL); 600262306a36Sopenharmony_ci return PCI_ERS_RESULT_RECOVERED; 600362306a36Sopenharmony_ci} 600462306a36Sopenharmony_ci 600562306a36Sopenharmony_cistatic void be_eeh_resume(struct pci_dev *pdev) 600662306a36Sopenharmony_ci{ 600762306a36Sopenharmony_ci int status = 0; 600862306a36Sopenharmony_ci struct be_adapter *adapter = pci_get_drvdata(pdev); 600962306a36Sopenharmony_ci 601062306a36Sopenharmony_ci dev_info(&adapter->pdev->dev, "EEH resume\n"); 601162306a36Sopenharmony_ci 601262306a36Sopenharmony_ci pci_save_state(pdev); 601362306a36Sopenharmony_ci 601462306a36Sopenharmony_ci status = be_resume(adapter); 601562306a36Sopenharmony_ci if (status) 601662306a36Sopenharmony_ci goto err; 601762306a36Sopenharmony_ci 601862306a36Sopenharmony_ci be_roce_dev_add(adapter); 601962306a36Sopenharmony_ci 602062306a36Sopenharmony_ci be_schedule_err_detection(adapter, ERR_DETECTION_DELAY); 602162306a36Sopenharmony_ci return; 602262306a36Sopenharmony_cierr: 602362306a36Sopenharmony_ci dev_err(&adapter->pdev->dev, "EEH resume failed\n"); 602462306a36Sopenharmony_ci} 602562306a36Sopenharmony_ci 602662306a36Sopenharmony_cistatic int be_pci_sriov_configure(struct pci_dev *pdev, int num_vfs) 602762306a36Sopenharmony_ci{ 602862306a36Sopenharmony_ci struct be_adapter *adapter = pci_get_drvdata(pdev); 602962306a36Sopenharmony_ci struct be_resources vft_res = {0}; 603062306a36Sopenharmony_ci int status; 603162306a36Sopenharmony_ci 603262306a36Sopenharmony_ci if (!num_vfs) 603362306a36Sopenharmony_ci be_vf_clear(adapter); 603462306a36Sopenharmony_ci 603562306a36Sopenharmony_ci adapter->num_vfs = num_vfs; 603662306a36Sopenharmony_ci 603762306a36Sopenharmony_ci if (adapter->num_vfs == 0 && pci_vfs_assigned(pdev)) { 603862306a36Sopenharmony_ci dev_warn(&pdev->dev, 603962306a36Sopenharmony_ci "Cannot disable VFs while they are assigned\n"); 604062306a36Sopenharmony_ci return -EBUSY; 604162306a36Sopenharmony_ci } 604262306a36Sopenharmony_ci 604362306a36Sopenharmony_ci /* When the HW is in SRIOV capable configuration, the PF-pool resources 604462306a36Sopenharmony_ci * are equally distributed across the max-number of VFs. The user may 604562306a36Sopenharmony_ci * request only a subset of the max-vfs to be enabled. 604662306a36Sopenharmony_ci * Based on num_vfs, redistribute the resources across num_vfs so that 604762306a36Sopenharmony_ci * each VF will have access to more number of resources. 604862306a36Sopenharmony_ci * This facility is not available in BE3 FW. 604962306a36Sopenharmony_ci * Also, this is done by FW in Lancer chip. 605062306a36Sopenharmony_ci */ 605162306a36Sopenharmony_ci if (skyhawk_chip(adapter) && !pci_num_vf(pdev)) { 605262306a36Sopenharmony_ci be_calculate_vf_res(adapter, adapter->num_vfs, 605362306a36Sopenharmony_ci &vft_res); 605462306a36Sopenharmony_ci status = be_cmd_set_sriov_config(adapter, adapter->pool_res, 605562306a36Sopenharmony_ci adapter->num_vfs, &vft_res); 605662306a36Sopenharmony_ci if (status) 605762306a36Sopenharmony_ci dev_err(&pdev->dev, 605862306a36Sopenharmony_ci "Failed to optimize SR-IOV resources\n"); 605962306a36Sopenharmony_ci } 606062306a36Sopenharmony_ci 606162306a36Sopenharmony_ci status = be_get_resources(adapter); 606262306a36Sopenharmony_ci if (status) 606362306a36Sopenharmony_ci return be_cmd_status(status); 606462306a36Sopenharmony_ci 606562306a36Sopenharmony_ci /* Updating real_num_tx/rx_queues() requires rtnl_lock() */ 606662306a36Sopenharmony_ci rtnl_lock(); 606762306a36Sopenharmony_ci status = be_update_queues(adapter); 606862306a36Sopenharmony_ci rtnl_unlock(); 606962306a36Sopenharmony_ci if (status) 607062306a36Sopenharmony_ci return be_cmd_status(status); 607162306a36Sopenharmony_ci 607262306a36Sopenharmony_ci if (adapter->num_vfs) 607362306a36Sopenharmony_ci status = be_vf_setup(adapter); 607462306a36Sopenharmony_ci 607562306a36Sopenharmony_ci if (!status) 607662306a36Sopenharmony_ci return adapter->num_vfs; 607762306a36Sopenharmony_ci 607862306a36Sopenharmony_ci return 0; 607962306a36Sopenharmony_ci} 608062306a36Sopenharmony_ci 608162306a36Sopenharmony_cistatic const struct pci_error_handlers be_eeh_handlers = { 608262306a36Sopenharmony_ci .error_detected = be_eeh_err_detected, 608362306a36Sopenharmony_ci .slot_reset = be_eeh_reset, 608462306a36Sopenharmony_ci .resume = be_eeh_resume, 608562306a36Sopenharmony_ci}; 608662306a36Sopenharmony_ci 608762306a36Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(be_pci_pm_ops, be_suspend, be_pci_resume); 608862306a36Sopenharmony_ci 608962306a36Sopenharmony_cistatic struct pci_driver be_driver = { 609062306a36Sopenharmony_ci .name = DRV_NAME, 609162306a36Sopenharmony_ci .id_table = be_dev_ids, 609262306a36Sopenharmony_ci .probe = be_probe, 609362306a36Sopenharmony_ci .remove = be_remove, 609462306a36Sopenharmony_ci .driver.pm = &be_pci_pm_ops, 609562306a36Sopenharmony_ci .shutdown = be_shutdown, 609662306a36Sopenharmony_ci .sriov_configure = be_pci_sriov_configure, 609762306a36Sopenharmony_ci .err_handler = &be_eeh_handlers 609862306a36Sopenharmony_ci}; 609962306a36Sopenharmony_ci 610062306a36Sopenharmony_cistatic int __init be_init_module(void) 610162306a36Sopenharmony_ci{ 610262306a36Sopenharmony_ci int status; 610362306a36Sopenharmony_ci 610462306a36Sopenharmony_ci if (rx_frag_size != 8192 && rx_frag_size != 4096 && 610562306a36Sopenharmony_ci rx_frag_size != 2048) { 610662306a36Sopenharmony_ci printk(KERN_WARNING DRV_NAME 610762306a36Sopenharmony_ci " : Module param rx_frag_size must be 2048/4096/8192." 610862306a36Sopenharmony_ci " Using 2048\n"); 610962306a36Sopenharmony_ci rx_frag_size = 2048; 611062306a36Sopenharmony_ci } 611162306a36Sopenharmony_ci 611262306a36Sopenharmony_ci if (num_vfs > 0) { 611362306a36Sopenharmony_ci pr_info(DRV_NAME " : Module param num_vfs is obsolete."); 611462306a36Sopenharmony_ci pr_info(DRV_NAME " : Use sysfs method to enable VFs\n"); 611562306a36Sopenharmony_ci } 611662306a36Sopenharmony_ci 611762306a36Sopenharmony_ci be_wq = create_singlethread_workqueue("be_wq"); 611862306a36Sopenharmony_ci if (!be_wq) { 611962306a36Sopenharmony_ci pr_warn(DRV_NAME "workqueue creation failed\n"); 612062306a36Sopenharmony_ci return -1; 612162306a36Sopenharmony_ci } 612262306a36Sopenharmony_ci 612362306a36Sopenharmony_ci be_err_recovery_workq = 612462306a36Sopenharmony_ci create_singlethread_workqueue("be_err_recover"); 612562306a36Sopenharmony_ci if (!be_err_recovery_workq) 612662306a36Sopenharmony_ci pr_warn(DRV_NAME "Could not create error recovery workqueue\n"); 612762306a36Sopenharmony_ci 612862306a36Sopenharmony_ci status = pci_register_driver(&be_driver); 612962306a36Sopenharmony_ci if (status) { 613062306a36Sopenharmony_ci destroy_workqueue(be_wq); 613162306a36Sopenharmony_ci be_destroy_err_recovery_workq(); 613262306a36Sopenharmony_ci } 613362306a36Sopenharmony_ci return status; 613462306a36Sopenharmony_ci} 613562306a36Sopenharmony_cimodule_init(be_init_module); 613662306a36Sopenharmony_ci 613762306a36Sopenharmony_cistatic void __exit be_exit_module(void) 613862306a36Sopenharmony_ci{ 613962306a36Sopenharmony_ci pci_unregister_driver(&be_driver); 614062306a36Sopenharmony_ci 614162306a36Sopenharmony_ci be_destroy_err_recovery_workq(); 614262306a36Sopenharmony_ci 614362306a36Sopenharmony_ci if (be_wq) 614462306a36Sopenharmony_ci destroy_workqueue(be_wq); 614562306a36Sopenharmony_ci} 614662306a36Sopenharmony_cimodule_exit(be_exit_module); 6147