18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2005 - 2016 Broadcom 48c2ecf20Sopenharmony_ci * All rights reserved. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * Contact Information: 78c2ecf20Sopenharmony_ci * linux-drivers@emulex.com 88c2ecf20Sopenharmony_ci * 98c2ecf20Sopenharmony_ci * Emulex 108c2ecf20Sopenharmony_ci * 3333 Susan Street 118c2ecf20Sopenharmony_ci * Costa Mesa, CA 92626 128c2ecf20Sopenharmony_ci */ 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include <linux/prefetch.h> 158c2ecf20Sopenharmony_ci#include <linux/module.h> 168c2ecf20Sopenharmony_ci#include "be.h" 178c2ecf20Sopenharmony_ci#include "be_cmds.h" 188c2ecf20Sopenharmony_ci#include <asm/div64.h> 198c2ecf20Sopenharmony_ci#include <linux/aer.h> 208c2ecf20Sopenharmony_ci#include <linux/if_bridge.h> 218c2ecf20Sopenharmony_ci#include <net/busy_poll.h> 228c2ecf20Sopenharmony_ci#include <net/vxlan.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(DRV_DESC); 258c2ecf20Sopenharmony_ciMODULE_AUTHOR("Emulex Corporation"); 268c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci/* num_vfs module param is obsolete. 298c2ecf20Sopenharmony_ci * Use sysfs method to enable/disable VFs. 308c2ecf20Sopenharmony_ci */ 318c2ecf20Sopenharmony_cistatic unsigned int num_vfs; 328c2ecf20Sopenharmony_cimodule_param(num_vfs, uint, 0444); 338c2ecf20Sopenharmony_ciMODULE_PARM_DESC(num_vfs, "Number of PCI VFs to initialize"); 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistatic ushort rx_frag_size = 2048; 368c2ecf20Sopenharmony_cimodule_param(rx_frag_size, ushort, 0444); 378c2ecf20Sopenharmony_ciMODULE_PARM_DESC(rx_frag_size, "Size of a fragment that holds rcvd data."); 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_ci/* Per-module error detection/recovery workq shared across all functions. 408c2ecf20Sopenharmony_ci * Each function schedules its own work request on this shared workq. 418c2ecf20Sopenharmony_ci */ 428c2ecf20Sopenharmony_cistatic struct workqueue_struct *be_err_recovery_workq; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_cistatic const struct pci_device_id be_dev_ids[] = { 458c2ecf20Sopenharmony_ci#ifdef CONFIG_BE2NET_BE2 468c2ecf20Sopenharmony_ci { PCI_DEVICE(BE_VENDOR_ID, BE_DEVICE_ID1) }, 478c2ecf20Sopenharmony_ci { PCI_DEVICE(BE_VENDOR_ID, OC_DEVICE_ID1) }, 488c2ecf20Sopenharmony_ci#endif /* CONFIG_BE2NET_BE2 */ 498c2ecf20Sopenharmony_ci#ifdef CONFIG_BE2NET_BE3 508c2ecf20Sopenharmony_ci { PCI_DEVICE(BE_VENDOR_ID, BE_DEVICE_ID2) }, 518c2ecf20Sopenharmony_ci { PCI_DEVICE(BE_VENDOR_ID, OC_DEVICE_ID2) }, 528c2ecf20Sopenharmony_ci#endif /* CONFIG_BE2NET_BE3 */ 538c2ecf20Sopenharmony_ci#ifdef CONFIG_BE2NET_LANCER 548c2ecf20Sopenharmony_ci { PCI_DEVICE(EMULEX_VENDOR_ID, OC_DEVICE_ID3)}, 558c2ecf20Sopenharmony_ci { PCI_DEVICE(EMULEX_VENDOR_ID, OC_DEVICE_ID4)}, 568c2ecf20Sopenharmony_ci#endif /* CONFIG_BE2NET_LANCER */ 578c2ecf20Sopenharmony_ci#ifdef CONFIG_BE2NET_SKYHAWK 588c2ecf20Sopenharmony_ci { PCI_DEVICE(EMULEX_VENDOR_ID, OC_DEVICE_ID5)}, 598c2ecf20Sopenharmony_ci { PCI_DEVICE(EMULEX_VENDOR_ID, OC_DEVICE_ID6)}, 608c2ecf20Sopenharmony_ci#endif /* CONFIG_BE2NET_SKYHAWK */ 618c2ecf20Sopenharmony_ci { 0 } 628c2ecf20Sopenharmony_ci}; 638c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, be_dev_ids); 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci/* Workqueue used by all functions for defering cmd calls to the adapter */ 668c2ecf20Sopenharmony_cistatic struct workqueue_struct *be_wq; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci/* UE Status Low CSR */ 698c2ecf20Sopenharmony_cistatic const char * const ue_status_low_desc[] = { 708c2ecf20Sopenharmony_ci "CEV", 718c2ecf20Sopenharmony_ci "CTX", 728c2ecf20Sopenharmony_ci "DBUF", 738c2ecf20Sopenharmony_ci "ERX", 748c2ecf20Sopenharmony_ci "Host", 758c2ecf20Sopenharmony_ci "MPU", 768c2ecf20Sopenharmony_ci "NDMA", 778c2ecf20Sopenharmony_ci "PTC ", 788c2ecf20Sopenharmony_ci "RDMA ", 798c2ecf20Sopenharmony_ci "RXF ", 808c2ecf20Sopenharmony_ci "RXIPS ", 818c2ecf20Sopenharmony_ci "RXULP0 ", 828c2ecf20Sopenharmony_ci "RXULP1 ", 838c2ecf20Sopenharmony_ci "RXULP2 ", 848c2ecf20Sopenharmony_ci "TIM ", 858c2ecf20Sopenharmony_ci "TPOST ", 868c2ecf20Sopenharmony_ci "TPRE ", 878c2ecf20Sopenharmony_ci "TXIPS ", 888c2ecf20Sopenharmony_ci "TXULP0 ", 898c2ecf20Sopenharmony_ci "TXULP1 ", 908c2ecf20Sopenharmony_ci "UC ", 918c2ecf20Sopenharmony_ci "WDMA ", 928c2ecf20Sopenharmony_ci "TXULP2 ", 938c2ecf20Sopenharmony_ci "HOST1 ", 948c2ecf20Sopenharmony_ci "P0_OB_LINK ", 958c2ecf20Sopenharmony_ci "P1_OB_LINK ", 968c2ecf20Sopenharmony_ci "HOST_GPIO ", 978c2ecf20Sopenharmony_ci "MBOX ", 988c2ecf20Sopenharmony_ci "ERX2 ", 998c2ecf20Sopenharmony_ci "SPARE ", 1008c2ecf20Sopenharmony_ci "JTAG ", 1018c2ecf20Sopenharmony_ci "MPU_INTPEND " 1028c2ecf20Sopenharmony_ci}; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci/* UE Status High CSR */ 1058c2ecf20Sopenharmony_cistatic const char * const ue_status_hi_desc[] = { 1068c2ecf20Sopenharmony_ci "LPCMEMHOST", 1078c2ecf20Sopenharmony_ci "MGMT_MAC", 1088c2ecf20Sopenharmony_ci "PCS0ONLINE", 1098c2ecf20Sopenharmony_ci "MPU_IRAM", 1108c2ecf20Sopenharmony_ci "PCS1ONLINE", 1118c2ecf20Sopenharmony_ci "PCTL0", 1128c2ecf20Sopenharmony_ci "PCTL1", 1138c2ecf20Sopenharmony_ci "PMEM", 1148c2ecf20Sopenharmony_ci "RR", 1158c2ecf20Sopenharmony_ci "TXPB", 1168c2ecf20Sopenharmony_ci "RXPP", 1178c2ecf20Sopenharmony_ci "XAUI", 1188c2ecf20Sopenharmony_ci "TXP", 1198c2ecf20Sopenharmony_ci "ARM", 1208c2ecf20Sopenharmony_ci "IPC", 1218c2ecf20Sopenharmony_ci "HOST2", 1228c2ecf20Sopenharmony_ci "HOST3", 1238c2ecf20Sopenharmony_ci "HOST4", 1248c2ecf20Sopenharmony_ci "HOST5", 1258c2ecf20Sopenharmony_ci "HOST6", 1268c2ecf20Sopenharmony_ci "HOST7", 1278c2ecf20Sopenharmony_ci "ECRC", 1288c2ecf20Sopenharmony_ci "Poison TLP", 1298c2ecf20Sopenharmony_ci "NETC", 1308c2ecf20Sopenharmony_ci "PERIPH", 1318c2ecf20Sopenharmony_ci "LLTXULP", 1328c2ecf20Sopenharmony_ci "D2P", 1338c2ecf20Sopenharmony_ci "RCON", 1348c2ecf20Sopenharmony_ci "LDMA", 1358c2ecf20Sopenharmony_ci "LLTXP", 1368c2ecf20Sopenharmony_ci "LLTXPB", 1378c2ecf20Sopenharmony_ci "Unknown" 1388c2ecf20Sopenharmony_ci}; 1398c2ecf20Sopenharmony_ci 1408c2ecf20Sopenharmony_ci#define BE_VF_IF_EN_FLAGS (BE_IF_FLAGS_UNTAGGED | \ 1418c2ecf20Sopenharmony_ci BE_IF_FLAGS_BROADCAST | \ 1428c2ecf20Sopenharmony_ci BE_IF_FLAGS_MULTICAST | \ 1438c2ecf20Sopenharmony_ci BE_IF_FLAGS_PASS_L3L4_ERRORS) 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_cistatic void be_queue_free(struct be_adapter *adapter, struct be_queue_info *q) 1468c2ecf20Sopenharmony_ci{ 1478c2ecf20Sopenharmony_ci struct be_dma_mem *mem = &q->dma_mem; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci if (mem->va) { 1508c2ecf20Sopenharmony_ci dma_free_coherent(&adapter->pdev->dev, mem->size, mem->va, 1518c2ecf20Sopenharmony_ci mem->dma); 1528c2ecf20Sopenharmony_ci mem->va = NULL; 1538c2ecf20Sopenharmony_ci } 1548c2ecf20Sopenharmony_ci} 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_cistatic int be_queue_alloc(struct be_adapter *adapter, struct be_queue_info *q, 1578c2ecf20Sopenharmony_ci u16 len, u16 entry_size) 1588c2ecf20Sopenharmony_ci{ 1598c2ecf20Sopenharmony_ci struct be_dma_mem *mem = &q->dma_mem; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci memset(q, 0, sizeof(*q)); 1628c2ecf20Sopenharmony_ci q->len = len; 1638c2ecf20Sopenharmony_ci q->entry_size = entry_size; 1648c2ecf20Sopenharmony_ci mem->size = len * entry_size; 1658c2ecf20Sopenharmony_ci mem->va = dma_alloc_coherent(&adapter->pdev->dev, mem->size, 1668c2ecf20Sopenharmony_ci &mem->dma, GFP_KERNEL); 1678c2ecf20Sopenharmony_ci if (!mem->va) 1688c2ecf20Sopenharmony_ci return -ENOMEM; 1698c2ecf20Sopenharmony_ci return 0; 1708c2ecf20Sopenharmony_ci} 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_cistatic void be_reg_intr_set(struct be_adapter *adapter, bool enable) 1738c2ecf20Sopenharmony_ci{ 1748c2ecf20Sopenharmony_ci u32 reg, enabled; 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_ci pci_read_config_dword(adapter->pdev, PCICFG_MEMBAR_CTRL_INT_CTRL_OFFSET, 1778c2ecf20Sopenharmony_ci ®); 1788c2ecf20Sopenharmony_ci enabled = reg & MEMBAR_CTRL_INT_CTRL_HOSTINTR_MASK; 1798c2ecf20Sopenharmony_ci 1808c2ecf20Sopenharmony_ci if (!enabled && enable) 1818c2ecf20Sopenharmony_ci reg |= MEMBAR_CTRL_INT_CTRL_HOSTINTR_MASK; 1828c2ecf20Sopenharmony_ci else if (enabled && !enable) 1838c2ecf20Sopenharmony_ci reg &= ~MEMBAR_CTRL_INT_CTRL_HOSTINTR_MASK; 1848c2ecf20Sopenharmony_ci else 1858c2ecf20Sopenharmony_ci return; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci pci_write_config_dword(adapter->pdev, 1888c2ecf20Sopenharmony_ci PCICFG_MEMBAR_CTRL_INT_CTRL_OFFSET, reg); 1898c2ecf20Sopenharmony_ci} 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_cistatic void be_intr_set(struct be_adapter *adapter, bool enable) 1928c2ecf20Sopenharmony_ci{ 1938c2ecf20Sopenharmony_ci int status = 0; 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci /* On lancer interrupts can't be controlled via this register */ 1968c2ecf20Sopenharmony_ci if (lancer_chip(adapter)) 1978c2ecf20Sopenharmony_ci return; 1988c2ecf20Sopenharmony_ci 1998c2ecf20Sopenharmony_ci if (be_check_error(adapter, BE_ERROR_EEH)) 2008c2ecf20Sopenharmony_ci return; 2018c2ecf20Sopenharmony_ci 2028c2ecf20Sopenharmony_ci status = be_cmd_intr_set(adapter, enable); 2038c2ecf20Sopenharmony_ci if (status) 2048c2ecf20Sopenharmony_ci be_reg_intr_set(adapter, enable); 2058c2ecf20Sopenharmony_ci} 2068c2ecf20Sopenharmony_ci 2078c2ecf20Sopenharmony_cistatic void be_rxq_notify(struct be_adapter *adapter, u16 qid, u16 posted) 2088c2ecf20Sopenharmony_ci{ 2098c2ecf20Sopenharmony_ci u32 val = 0; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci if (be_check_error(adapter, BE_ERROR_HW)) 2128c2ecf20Sopenharmony_ci return; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci val |= qid & DB_RQ_RING_ID_MASK; 2158c2ecf20Sopenharmony_ci val |= posted << DB_RQ_NUM_POSTED_SHIFT; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci wmb(); 2188c2ecf20Sopenharmony_ci iowrite32(val, adapter->db + DB_RQ_OFFSET); 2198c2ecf20Sopenharmony_ci} 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_cistatic void be_txq_notify(struct be_adapter *adapter, struct be_tx_obj *txo, 2228c2ecf20Sopenharmony_ci u16 posted) 2238c2ecf20Sopenharmony_ci{ 2248c2ecf20Sopenharmony_ci u32 val = 0; 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci if (be_check_error(adapter, BE_ERROR_HW)) 2278c2ecf20Sopenharmony_ci return; 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_ci val |= txo->q.id & DB_TXULP_RING_ID_MASK; 2308c2ecf20Sopenharmony_ci val |= (posted & DB_TXULP_NUM_POSTED_MASK) << DB_TXULP_NUM_POSTED_SHIFT; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci wmb(); 2338c2ecf20Sopenharmony_ci iowrite32(val, adapter->db + txo->db_offset); 2348c2ecf20Sopenharmony_ci} 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_cistatic void be_eq_notify(struct be_adapter *adapter, u16 qid, 2378c2ecf20Sopenharmony_ci bool arm, bool clear_int, u16 num_popped, 2388c2ecf20Sopenharmony_ci u32 eq_delay_mult_enc) 2398c2ecf20Sopenharmony_ci{ 2408c2ecf20Sopenharmony_ci u32 val = 0; 2418c2ecf20Sopenharmony_ci 2428c2ecf20Sopenharmony_ci val |= qid & DB_EQ_RING_ID_MASK; 2438c2ecf20Sopenharmony_ci val |= ((qid & DB_EQ_RING_ID_EXT_MASK) << DB_EQ_RING_ID_EXT_MASK_SHIFT); 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci if (be_check_error(adapter, BE_ERROR_HW)) 2468c2ecf20Sopenharmony_ci return; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci if (arm) 2498c2ecf20Sopenharmony_ci val |= 1 << DB_EQ_REARM_SHIFT; 2508c2ecf20Sopenharmony_ci if (clear_int) 2518c2ecf20Sopenharmony_ci val |= 1 << DB_EQ_CLR_SHIFT; 2528c2ecf20Sopenharmony_ci val |= 1 << DB_EQ_EVNT_SHIFT; 2538c2ecf20Sopenharmony_ci val |= num_popped << DB_EQ_NUM_POPPED_SHIFT; 2548c2ecf20Sopenharmony_ci val |= eq_delay_mult_enc << DB_EQ_R2I_DLY_SHIFT; 2558c2ecf20Sopenharmony_ci iowrite32(val, adapter->db + DB_EQ_OFFSET); 2568c2ecf20Sopenharmony_ci} 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_civoid be_cq_notify(struct be_adapter *adapter, u16 qid, bool arm, u16 num_popped) 2598c2ecf20Sopenharmony_ci{ 2608c2ecf20Sopenharmony_ci u32 val = 0; 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_ci val |= qid & DB_CQ_RING_ID_MASK; 2638c2ecf20Sopenharmony_ci val |= ((qid & DB_CQ_RING_ID_EXT_MASK) << 2648c2ecf20Sopenharmony_ci DB_CQ_RING_ID_EXT_MASK_SHIFT); 2658c2ecf20Sopenharmony_ci 2668c2ecf20Sopenharmony_ci if (be_check_error(adapter, BE_ERROR_HW)) 2678c2ecf20Sopenharmony_ci return; 2688c2ecf20Sopenharmony_ci 2698c2ecf20Sopenharmony_ci if (arm) 2708c2ecf20Sopenharmony_ci val |= 1 << DB_CQ_REARM_SHIFT; 2718c2ecf20Sopenharmony_ci val |= num_popped << DB_CQ_NUM_POPPED_SHIFT; 2728c2ecf20Sopenharmony_ci iowrite32(val, adapter->db + DB_CQ_OFFSET); 2738c2ecf20Sopenharmony_ci} 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_cistatic int be_dev_mac_add(struct be_adapter *adapter, u8 *mac) 2768c2ecf20Sopenharmony_ci{ 2778c2ecf20Sopenharmony_ci int i; 2788c2ecf20Sopenharmony_ci 2798c2ecf20Sopenharmony_ci /* Check if mac has already been added as part of uc-list */ 2808c2ecf20Sopenharmony_ci for (i = 0; i < adapter->uc_macs; i++) { 2818c2ecf20Sopenharmony_ci if (ether_addr_equal(adapter->uc_list[i].mac, mac)) { 2828c2ecf20Sopenharmony_ci /* mac already added, skip addition */ 2838c2ecf20Sopenharmony_ci adapter->pmac_id[0] = adapter->pmac_id[i + 1]; 2848c2ecf20Sopenharmony_ci return 0; 2858c2ecf20Sopenharmony_ci } 2868c2ecf20Sopenharmony_ci } 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci return be_cmd_pmac_add(adapter, mac, adapter->if_handle, 2898c2ecf20Sopenharmony_ci &adapter->pmac_id[0], 0); 2908c2ecf20Sopenharmony_ci} 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_cistatic void be_dev_mac_del(struct be_adapter *adapter, int pmac_id) 2938c2ecf20Sopenharmony_ci{ 2948c2ecf20Sopenharmony_ci int i; 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci /* Skip deletion if the programmed mac is 2978c2ecf20Sopenharmony_ci * being used in uc-list 2988c2ecf20Sopenharmony_ci */ 2998c2ecf20Sopenharmony_ci for (i = 0; i < adapter->uc_macs; i++) { 3008c2ecf20Sopenharmony_ci if (adapter->pmac_id[i + 1] == pmac_id) 3018c2ecf20Sopenharmony_ci return; 3028c2ecf20Sopenharmony_ci } 3038c2ecf20Sopenharmony_ci be_cmd_pmac_del(adapter, adapter->if_handle, pmac_id, 0); 3048c2ecf20Sopenharmony_ci} 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_cistatic int be_mac_addr_set(struct net_device *netdev, void *p) 3078c2ecf20Sopenharmony_ci{ 3088c2ecf20Sopenharmony_ci struct be_adapter *adapter = netdev_priv(netdev); 3098c2ecf20Sopenharmony_ci struct device *dev = &adapter->pdev->dev; 3108c2ecf20Sopenharmony_ci struct sockaddr *addr = p; 3118c2ecf20Sopenharmony_ci int status; 3128c2ecf20Sopenharmony_ci u8 mac[ETH_ALEN]; 3138c2ecf20Sopenharmony_ci u32 old_pmac_id = adapter->pmac_id[0]; 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci if (!is_valid_ether_addr(addr->sa_data)) 3168c2ecf20Sopenharmony_ci return -EADDRNOTAVAIL; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci /* Proceed further only if, User provided MAC is different 3198c2ecf20Sopenharmony_ci * from active MAC 3208c2ecf20Sopenharmony_ci */ 3218c2ecf20Sopenharmony_ci if (ether_addr_equal(addr->sa_data, adapter->dev_mac)) 3228c2ecf20Sopenharmony_ci return 0; 3238c2ecf20Sopenharmony_ci 3248c2ecf20Sopenharmony_ci /* BE3 VFs without FILTMGMT privilege are not allowed to set its MAC 3258c2ecf20Sopenharmony_ci * address 3268c2ecf20Sopenharmony_ci */ 3278c2ecf20Sopenharmony_ci if (BEx_chip(adapter) && be_virtfn(adapter) && 3288c2ecf20Sopenharmony_ci !check_privilege(adapter, BE_PRIV_FILTMGMT)) 3298c2ecf20Sopenharmony_ci return -EPERM; 3308c2ecf20Sopenharmony_ci 3318c2ecf20Sopenharmony_ci /* if device is not running, copy MAC to netdev->dev_addr */ 3328c2ecf20Sopenharmony_ci if (!netif_running(netdev)) 3338c2ecf20Sopenharmony_ci goto done; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci /* The PMAC_ADD cmd may fail if the VF doesn't have FILTMGMT 3368c2ecf20Sopenharmony_ci * privilege or if PF did not provision the new MAC address. 3378c2ecf20Sopenharmony_ci * On BE3, this cmd will always fail if the VF doesn't have the 3388c2ecf20Sopenharmony_ci * FILTMGMT privilege. This failure is OK, only if the PF programmed 3398c2ecf20Sopenharmony_ci * the MAC for the VF. 3408c2ecf20Sopenharmony_ci */ 3418c2ecf20Sopenharmony_ci mutex_lock(&adapter->rx_filter_lock); 3428c2ecf20Sopenharmony_ci status = be_dev_mac_add(adapter, (u8 *)addr->sa_data); 3438c2ecf20Sopenharmony_ci if (!status) { 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci /* Delete the old programmed MAC. This call may fail if the 3468c2ecf20Sopenharmony_ci * old MAC was already deleted by the PF driver. 3478c2ecf20Sopenharmony_ci */ 3488c2ecf20Sopenharmony_ci if (adapter->pmac_id[0] != old_pmac_id) 3498c2ecf20Sopenharmony_ci be_dev_mac_del(adapter, old_pmac_id); 3508c2ecf20Sopenharmony_ci } 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_ci mutex_unlock(&adapter->rx_filter_lock); 3538c2ecf20Sopenharmony_ci /* Decide if the new MAC is successfully activated only after 3548c2ecf20Sopenharmony_ci * querying the FW 3558c2ecf20Sopenharmony_ci */ 3568c2ecf20Sopenharmony_ci status = be_cmd_get_active_mac(adapter, adapter->pmac_id[0], mac, 3578c2ecf20Sopenharmony_ci adapter->if_handle, true, 0); 3588c2ecf20Sopenharmony_ci if (status) 3598c2ecf20Sopenharmony_ci goto err; 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci /* The MAC change did not happen, either due to lack of privilege 3628c2ecf20Sopenharmony_ci * or PF didn't pre-provision. 3638c2ecf20Sopenharmony_ci */ 3648c2ecf20Sopenharmony_ci if (!ether_addr_equal(addr->sa_data, mac)) { 3658c2ecf20Sopenharmony_ci status = -EPERM; 3668c2ecf20Sopenharmony_ci goto err; 3678c2ecf20Sopenharmony_ci } 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci /* Remember currently programmed MAC */ 3708c2ecf20Sopenharmony_ci ether_addr_copy(adapter->dev_mac, addr->sa_data); 3718c2ecf20Sopenharmony_cidone: 3728c2ecf20Sopenharmony_ci ether_addr_copy(netdev->dev_addr, addr->sa_data); 3738c2ecf20Sopenharmony_ci dev_info(dev, "MAC address changed to %pM\n", addr->sa_data); 3748c2ecf20Sopenharmony_ci return 0; 3758c2ecf20Sopenharmony_cierr: 3768c2ecf20Sopenharmony_ci dev_warn(dev, "MAC address change to %pM failed\n", addr->sa_data); 3778c2ecf20Sopenharmony_ci return status; 3788c2ecf20Sopenharmony_ci} 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci/* BE2 supports only v0 cmd */ 3818c2ecf20Sopenharmony_cistatic void *hw_stats_from_cmd(struct be_adapter *adapter) 3828c2ecf20Sopenharmony_ci{ 3838c2ecf20Sopenharmony_ci if (BE2_chip(adapter)) { 3848c2ecf20Sopenharmony_ci struct be_cmd_resp_get_stats_v0 *cmd = adapter->stats_cmd.va; 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci return &cmd->hw_stats; 3878c2ecf20Sopenharmony_ci } else if (BE3_chip(adapter)) { 3888c2ecf20Sopenharmony_ci struct be_cmd_resp_get_stats_v1 *cmd = adapter->stats_cmd.va; 3898c2ecf20Sopenharmony_ci 3908c2ecf20Sopenharmony_ci return &cmd->hw_stats; 3918c2ecf20Sopenharmony_ci } else { 3928c2ecf20Sopenharmony_ci struct be_cmd_resp_get_stats_v2 *cmd = adapter->stats_cmd.va; 3938c2ecf20Sopenharmony_ci 3948c2ecf20Sopenharmony_ci return &cmd->hw_stats; 3958c2ecf20Sopenharmony_ci } 3968c2ecf20Sopenharmony_ci} 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci/* BE2 supports only v0 cmd */ 3998c2ecf20Sopenharmony_cistatic void *be_erx_stats_from_cmd(struct be_adapter *adapter) 4008c2ecf20Sopenharmony_ci{ 4018c2ecf20Sopenharmony_ci if (BE2_chip(adapter)) { 4028c2ecf20Sopenharmony_ci struct be_hw_stats_v0 *hw_stats = hw_stats_from_cmd(adapter); 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci return &hw_stats->erx; 4058c2ecf20Sopenharmony_ci } else if (BE3_chip(adapter)) { 4068c2ecf20Sopenharmony_ci struct be_hw_stats_v1 *hw_stats = hw_stats_from_cmd(adapter); 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci return &hw_stats->erx; 4098c2ecf20Sopenharmony_ci } else { 4108c2ecf20Sopenharmony_ci struct be_hw_stats_v2 *hw_stats = hw_stats_from_cmd(adapter); 4118c2ecf20Sopenharmony_ci 4128c2ecf20Sopenharmony_ci return &hw_stats->erx; 4138c2ecf20Sopenharmony_ci } 4148c2ecf20Sopenharmony_ci} 4158c2ecf20Sopenharmony_ci 4168c2ecf20Sopenharmony_cistatic void populate_be_v0_stats(struct be_adapter *adapter) 4178c2ecf20Sopenharmony_ci{ 4188c2ecf20Sopenharmony_ci struct be_hw_stats_v0 *hw_stats = hw_stats_from_cmd(adapter); 4198c2ecf20Sopenharmony_ci struct be_pmem_stats *pmem_sts = &hw_stats->pmem; 4208c2ecf20Sopenharmony_ci struct be_rxf_stats_v0 *rxf_stats = &hw_stats->rxf; 4218c2ecf20Sopenharmony_ci struct be_port_rxf_stats_v0 *port_stats = 4228c2ecf20Sopenharmony_ci &rxf_stats->port[adapter->port_num]; 4238c2ecf20Sopenharmony_ci struct be_drv_stats *drvs = &adapter->drv_stats; 4248c2ecf20Sopenharmony_ci 4258c2ecf20Sopenharmony_ci be_dws_le_to_cpu(hw_stats, sizeof(*hw_stats)); 4268c2ecf20Sopenharmony_ci drvs->rx_pause_frames = port_stats->rx_pause_frames; 4278c2ecf20Sopenharmony_ci drvs->rx_crc_errors = port_stats->rx_crc_errors; 4288c2ecf20Sopenharmony_ci drvs->rx_control_frames = port_stats->rx_control_frames; 4298c2ecf20Sopenharmony_ci drvs->rx_in_range_errors = port_stats->rx_in_range_errors; 4308c2ecf20Sopenharmony_ci drvs->rx_frame_too_long = port_stats->rx_frame_too_long; 4318c2ecf20Sopenharmony_ci drvs->rx_dropped_runt = port_stats->rx_dropped_runt; 4328c2ecf20Sopenharmony_ci drvs->rx_ip_checksum_errs = port_stats->rx_ip_checksum_errs; 4338c2ecf20Sopenharmony_ci drvs->rx_tcp_checksum_errs = port_stats->rx_tcp_checksum_errs; 4348c2ecf20Sopenharmony_ci drvs->rx_udp_checksum_errs = port_stats->rx_udp_checksum_errs; 4358c2ecf20Sopenharmony_ci drvs->rxpp_fifo_overflow_drop = port_stats->rx_fifo_overflow; 4368c2ecf20Sopenharmony_ci drvs->rx_dropped_tcp_length = port_stats->rx_dropped_tcp_length; 4378c2ecf20Sopenharmony_ci drvs->rx_dropped_too_small = port_stats->rx_dropped_too_small; 4388c2ecf20Sopenharmony_ci drvs->rx_dropped_too_short = port_stats->rx_dropped_too_short; 4398c2ecf20Sopenharmony_ci drvs->rx_out_range_errors = port_stats->rx_out_range_errors; 4408c2ecf20Sopenharmony_ci drvs->rx_input_fifo_overflow_drop = port_stats->rx_input_fifo_overflow; 4418c2ecf20Sopenharmony_ci drvs->rx_dropped_header_too_small = 4428c2ecf20Sopenharmony_ci port_stats->rx_dropped_header_too_small; 4438c2ecf20Sopenharmony_ci drvs->rx_address_filtered = 4448c2ecf20Sopenharmony_ci port_stats->rx_address_filtered + 4458c2ecf20Sopenharmony_ci port_stats->rx_vlan_filtered; 4468c2ecf20Sopenharmony_ci drvs->rx_alignment_symbol_errors = 4478c2ecf20Sopenharmony_ci port_stats->rx_alignment_symbol_errors; 4488c2ecf20Sopenharmony_ci 4498c2ecf20Sopenharmony_ci drvs->tx_pauseframes = port_stats->tx_pauseframes; 4508c2ecf20Sopenharmony_ci drvs->tx_controlframes = port_stats->tx_controlframes; 4518c2ecf20Sopenharmony_ci 4528c2ecf20Sopenharmony_ci if (adapter->port_num) 4538c2ecf20Sopenharmony_ci drvs->jabber_events = rxf_stats->port1_jabber_events; 4548c2ecf20Sopenharmony_ci else 4558c2ecf20Sopenharmony_ci drvs->jabber_events = rxf_stats->port0_jabber_events; 4568c2ecf20Sopenharmony_ci drvs->rx_drops_no_pbuf = rxf_stats->rx_drops_no_pbuf; 4578c2ecf20Sopenharmony_ci drvs->rx_drops_no_erx_descr = rxf_stats->rx_drops_no_erx_descr; 4588c2ecf20Sopenharmony_ci drvs->forwarded_packets = rxf_stats->forwarded_packets; 4598c2ecf20Sopenharmony_ci drvs->rx_drops_mtu = rxf_stats->rx_drops_mtu; 4608c2ecf20Sopenharmony_ci drvs->rx_drops_no_tpre_descr = rxf_stats->rx_drops_no_tpre_descr; 4618c2ecf20Sopenharmony_ci drvs->rx_drops_too_many_frags = rxf_stats->rx_drops_too_many_frags; 4628c2ecf20Sopenharmony_ci adapter->drv_stats.eth_red_drops = pmem_sts->eth_red_drops; 4638c2ecf20Sopenharmony_ci} 4648c2ecf20Sopenharmony_ci 4658c2ecf20Sopenharmony_cistatic void populate_be_v1_stats(struct be_adapter *adapter) 4668c2ecf20Sopenharmony_ci{ 4678c2ecf20Sopenharmony_ci struct be_hw_stats_v1 *hw_stats = hw_stats_from_cmd(adapter); 4688c2ecf20Sopenharmony_ci struct be_pmem_stats *pmem_sts = &hw_stats->pmem; 4698c2ecf20Sopenharmony_ci struct be_rxf_stats_v1 *rxf_stats = &hw_stats->rxf; 4708c2ecf20Sopenharmony_ci struct be_port_rxf_stats_v1 *port_stats = 4718c2ecf20Sopenharmony_ci &rxf_stats->port[adapter->port_num]; 4728c2ecf20Sopenharmony_ci struct be_drv_stats *drvs = &adapter->drv_stats; 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci be_dws_le_to_cpu(hw_stats, sizeof(*hw_stats)); 4758c2ecf20Sopenharmony_ci drvs->pmem_fifo_overflow_drop = port_stats->pmem_fifo_overflow_drop; 4768c2ecf20Sopenharmony_ci drvs->rx_priority_pause_frames = port_stats->rx_priority_pause_frames; 4778c2ecf20Sopenharmony_ci drvs->rx_pause_frames = port_stats->rx_pause_frames; 4788c2ecf20Sopenharmony_ci drvs->rx_crc_errors = port_stats->rx_crc_errors; 4798c2ecf20Sopenharmony_ci drvs->rx_control_frames = port_stats->rx_control_frames; 4808c2ecf20Sopenharmony_ci drvs->rx_in_range_errors = port_stats->rx_in_range_errors; 4818c2ecf20Sopenharmony_ci drvs->rx_frame_too_long = port_stats->rx_frame_too_long; 4828c2ecf20Sopenharmony_ci drvs->rx_dropped_runt = port_stats->rx_dropped_runt; 4838c2ecf20Sopenharmony_ci drvs->rx_ip_checksum_errs = port_stats->rx_ip_checksum_errs; 4848c2ecf20Sopenharmony_ci drvs->rx_tcp_checksum_errs = port_stats->rx_tcp_checksum_errs; 4858c2ecf20Sopenharmony_ci drvs->rx_udp_checksum_errs = port_stats->rx_udp_checksum_errs; 4868c2ecf20Sopenharmony_ci drvs->rx_dropped_tcp_length = port_stats->rx_dropped_tcp_length; 4878c2ecf20Sopenharmony_ci drvs->rx_dropped_too_small = port_stats->rx_dropped_too_small; 4888c2ecf20Sopenharmony_ci drvs->rx_dropped_too_short = port_stats->rx_dropped_too_short; 4898c2ecf20Sopenharmony_ci drvs->rx_out_range_errors = port_stats->rx_out_range_errors; 4908c2ecf20Sopenharmony_ci drvs->rx_dropped_header_too_small = 4918c2ecf20Sopenharmony_ci port_stats->rx_dropped_header_too_small; 4928c2ecf20Sopenharmony_ci drvs->rx_input_fifo_overflow_drop = 4938c2ecf20Sopenharmony_ci port_stats->rx_input_fifo_overflow_drop; 4948c2ecf20Sopenharmony_ci drvs->rx_address_filtered = port_stats->rx_address_filtered; 4958c2ecf20Sopenharmony_ci drvs->rx_alignment_symbol_errors = 4968c2ecf20Sopenharmony_ci port_stats->rx_alignment_symbol_errors; 4978c2ecf20Sopenharmony_ci drvs->rxpp_fifo_overflow_drop = port_stats->rxpp_fifo_overflow_drop; 4988c2ecf20Sopenharmony_ci drvs->tx_pauseframes = port_stats->tx_pauseframes; 4998c2ecf20Sopenharmony_ci drvs->tx_controlframes = port_stats->tx_controlframes; 5008c2ecf20Sopenharmony_ci drvs->tx_priority_pauseframes = port_stats->tx_priority_pauseframes; 5018c2ecf20Sopenharmony_ci drvs->jabber_events = port_stats->jabber_events; 5028c2ecf20Sopenharmony_ci drvs->rx_drops_no_pbuf = rxf_stats->rx_drops_no_pbuf; 5038c2ecf20Sopenharmony_ci drvs->rx_drops_no_erx_descr = rxf_stats->rx_drops_no_erx_descr; 5048c2ecf20Sopenharmony_ci drvs->forwarded_packets = rxf_stats->forwarded_packets; 5058c2ecf20Sopenharmony_ci drvs->rx_drops_mtu = rxf_stats->rx_drops_mtu; 5068c2ecf20Sopenharmony_ci drvs->rx_drops_no_tpre_descr = rxf_stats->rx_drops_no_tpre_descr; 5078c2ecf20Sopenharmony_ci drvs->rx_drops_too_many_frags = rxf_stats->rx_drops_too_many_frags; 5088c2ecf20Sopenharmony_ci adapter->drv_stats.eth_red_drops = pmem_sts->eth_red_drops; 5098c2ecf20Sopenharmony_ci} 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_cistatic void populate_be_v2_stats(struct be_adapter *adapter) 5128c2ecf20Sopenharmony_ci{ 5138c2ecf20Sopenharmony_ci struct be_hw_stats_v2 *hw_stats = hw_stats_from_cmd(adapter); 5148c2ecf20Sopenharmony_ci struct be_pmem_stats *pmem_sts = &hw_stats->pmem; 5158c2ecf20Sopenharmony_ci struct be_rxf_stats_v2 *rxf_stats = &hw_stats->rxf; 5168c2ecf20Sopenharmony_ci struct be_port_rxf_stats_v2 *port_stats = 5178c2ecf20Sopenharmony_ci &rxf_stats->port[adapter->port_num]; 5188c2ecf20Sopenharmony_ci struct be_drv_stats *drvs = &adapter->drv_stats; 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci be_dws_le_to_cpu(hw_stats, sizeof(*hw_stats)); 5218c2ecf20Sopenharmony_ci drvs->pmem_fifo_overflow_drop = port_stats->pmem_fifo_overflow_drop; 5228c2ecf20Sopenharmony_ci drvs->rx_priority_pause_frames = port_stats->rx_priority_pause_frames; 5238c2ecf20Sopenharmony_ci drvs->rx_pause_frames = port_stats->rx_pause_frames; 5248c2ecf20Sopenharmony_ci drvs->rx_crc_errors = port_stats->rx_crc_errors; 5258c2ecf20Sopenharmony_ci drvs->rx_control_frames = port_stats->rx_control_frames; 5268c2ecf20Sopenharmony_ci drvs->rx_in_range_errors = port_stats->rx_in_range_errors; 5278c2ecf20Sopenharmony_ci drvs->rx_frame_too_long = port_stats->rx_frame_too_long; 5288c2ecf20Sopenharmony_ci drvs->rx_dropped_runt = port_stats->rx_dropped_runt; 5298c2ecf20Sopenharmony_ci drvs->rx_ip_checksum_errs = port_stats->rx_ip_checksum_errs; 5308c2ecf20Sopenharmony_ci drvs->rx_tcp_checksum_errs = port_stats->rx_tcp_checksum_errs; 5318c2ecf20Sopenharmony_ci drvs->rx_udp_checksum_errs = port_stats->rx_udp_checksum_errs; 5328c2ecf20Sopenharmony_ci drvs->rx_dropped_tcp_length = port_stats->rx_dropped_tcp_length; 5338c2ecf20Sopenharmony_ci drvs->rx_dropped_too_small = port_stats->rx_dropped_too_small; 5348c2ecf20Sopenharmony_ci drvs->rx_dropped_too_short = port_stats->rx_dropped_too_short; 5358c2ecf20Sopenharmony_ci drvs->rx_out_range_errors = port_stats->rx_out_range_errors; 5368c2ecf20Sopenharmony_ci drvs->rx_dropped_header_too_small = 5378c2ecf20Sopenharmony_ci port_stats->rx_dropped_header_too_small; 5388c2ecf20Sopenharmony_ci drvs->rx_input_fifo_overflow_drop = 5398c2ecf20Sopenharmony_ci port_stats->rx_input_fifo_overflow_drop; 5408c2ecf20Sopenharmony_ci drvs->rx_address_filtered = port_stats->rx_address_filtered; 5418c2ecf20Sopenharmony_ci drvs->rx_alignment_symbol_errors = 5428c2ecf20Sopenharmony_ci port_stats->rx_alignment_symbol_errors; 5438c2ecf20Sopenharmony_ci drvs->rxpp_fifo_overflow_drop = port_stats->rxpp_fifo_overflow_drop; 5448c2ecf20Sopenharmony_ci drvs->tx_pauseframes = port_stats->tx_pauseframes; 5458c2ecf20Sopenharmony_ci drvs->tx_controlframes = port_stats->tx_controlframes; 5468c2ecf20Sopenharmony_ci drvs->tx_priority_pauseframes = port_stats->tx_priority_pauseframes; 5478c2ecf20Sopenharmony_ci drvs->jabber_events = port_stats->jabber_events; 5488c2ecf20Sopenharmony_ci drvs->rx_drops_no_pbuf = rxf_stats->rx_drops_no_pbuf; 5498c2ecf20Sopenharmony_ci drvs->rx_drops_no_erx_descr = rxf_stats->rx_drops_no_erx_descr; 5508c2ecf20Sopenharmony_ci drvs->forwarded_packets = rxf_stats->forwarded_packets; 5518c2ecf20Sopenharmony_ci drvs->rx_drops_mtu = rxf_stats->rx_drops_mtu; 5528c2ecf20Sopenharmony_ci drvs->rx_drops_no_tpre_descr = rxf_stats->rx_drops_no_tpre_descr; 5538c2ecf20Sopenharmony_ci drvs->rx_drops_too_many_frags = rxf_stats->rx_drops_too_many_frags; 5548c2ecf20Sopenharmony_ci adapter->drv_stats.eth_red_drops = pmem_sts->eth_red_drops; 5558c2ecf20Sopenharmony_ci if (be_roce_supported(adapter)) { 5568c2ecf20Sopenharmony_ci drvs->rx_roce_bytes_lsd = port_stats->roce_bytes_received_lsd; 5578c2ecf20Sopenharmony_ci drvs->rx_roce_bytes_msd = port_stats->roce_bytes_received_msd; 5588c2ecf20Sopenharmony_ci drvs->rx_roce_frames = port_stats->roce_frames_received; 5598c2ecf20Sopenharmony_ci drvs->roce_drops_crc = port_stats->roce_drops_crc; 5608c2ecf20Sopenharmony_ci drvs->roce_drops_payload_len = 5618c2ecf20Sopenharmony_ci port_stats->roce_drops_payload_len; 5628c2ecf20Sopenharmony_ci } 5638c2ecf20Sopenharmony_ci} 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_cistatic void populate_lancer_stats(struct be_adapter *adapter) 5668c2ecf20Sopenharmony_ci{ 5678c2ecf20Sopenharmony_ci struct be_drv_stats *drvs = &adapter->drv_stats; 5688c2ecf20Sopenharmony_ci struct lancer_pport_stats *pport_stats = pport_stats_from_cmd(adapter); 5698c2ecf20Sopenharmony_ci 5708c2ecf20Sopenharmony_ci be_dws_le_to_cpu(pport_stats, sizeof(*pport_stats)); 5718c2ecf20Sopenharmony_ci drvs->rx_pause_frames = pport_stats->rx_pause_frames_lo; 5728c2ecf20Sopenharmony_ci drvs->rx_crc_errors = pport_stats->rx_crc_errors_lo; 5738c2ecf20Sopenharmony_ci drvs->rx_control_frames = pport_stats->rx_control_frames_lo; 5748c2ecf20Sopenharmony_ci drvs->rx_in_range_errors = pport_stats->rx_in_range_errors; 5758c2ecf20Sopenharmony_ci drvs->rx_frame_too_long = pport_stats->rx_frames_too_long_lo; 5768c2ecf20Sopenharmony_ci drvs->rx_dropped_runt = pport_stats->rx_dropped_runt; 5778c2ecf20Sopenharmony_ci drvs->rx_ip_checksum_errs = pport_stats->rx_ip_checksum_errors; 5788c2ecf20Sopenharmony_ci drvs->rx_tcp_checksum_errs = pport_stats->rx_tcp_checksum_errors; 5798c2ecf20Sopenharmony_ci drvs->rx_udp_checksum_errs = pport_stats->rx_udp_checksum_errors; 5808c2ecf20Sopenharmony_ci drvs->rx_dropped_tcp_length = 5818c2ecf20Sopenharmony_ci pport_stats->rx_dropped_invalid_tcp_length; 5828c2ecf20Sopenharmony_ci drvs->rx_dropped_too_small = pport_stats->rx_dropped_too_small; 5838c2ecf20Sopenharmony_ci drvs->rx_dropped_too_short = pport_stats->rx_dropped_too_short; 5848c2ecf20Sopenharmony_ci drvs->rx_out_range_errors = pport_stats->rx_out_of_range_errors; 5858c2ecf20Sopenharmony_ci drvs->rx_dropped_header_too_small = 5868c2ecf20Sopenharmony_ci pport_stats->rx_dropped_header_too_small; 5878c2ecf20Sopenharmony_ci drvs->rx_input_fifo_overflow_drop = pport_stats->rx_fifo_overflow; 5888c2ecf20Sopenharmony_ci drvs->rx_address_filtered = 5898c2ecf20Sopenharmony_ci pport_stats->rx_address_filtered + 5908c2ecf20Sopenharmony_ci pport_stats->rx_vlan_filtered; 5918c2ecf20Sopenharmony_ci drvs->rx_alignment_symbol_errors = pport_stats->rx_symbol_errors_lo; 5928c2ecf20Sopenharmony_ci drvs->rxpp_fifo_overflow_drop = pport_stats->rx_fifo_overflow; 5938c2ecf20Sopenharmony_ci drvs->tx_pauseframes = pport_stats->tx_pause_frames_lo; 5948c2ecf20Sopenharmony_ci drvs->tx_controlframes = pport_stats->tx_control_frames_lo; 5958c2ecf20Sopenharmony_ci drvs->jabber_events = pport_stats->rx_jabbers; 5968c2ecf20Sopenharmony_ci drvs->forwarded_packets = pport_stats->num_forwards_lo; 5978c2ecf20Sopenharmony_ci drvs->rx_drops_mtu = pport_stats->rx_drops_mtu_lo; 5988c2ecf20Sopenharmony_ci drvs->rx_drops_too_many_frags = 5998c2ecf20Sopenharmony_ci pport_stats->rx_drops_too_many_frags_lo; 6008c2ecf20Sopenharmony_ci} 6018c2ecf20Sopenharmony_ci 6028c2ecf20Sopenharmony_cistatic void accumulate_16bit_val(u32 *acc, u16 val) 6038c2ecf20Sopenharmony_ci{ 6048c2ecf20Sopenharmony_ci#define lo(x) (x & 0xFFFF) 6058c2ecf20Sopenharmony_ci#define hi(x) (x & 0xFFFF0000) 6068c2ecf20Sopenharmony_ci bool wrapped = val < lo(*acc); 6078c2ecf20Sopenharmony_ci u32 newacc = hi(*acc) + val; 6088c2ecf20Sopenharmony_ci 6098c2ecf20Sopenharmony_ci if (wrapped) 6108c2ecf20Sopenharmony_ci newacc += 65536; 6118c2ecf20Sopenharmony_ci WRITE_ONCE(*acc, newacc); 6128c2ecf20Sopenharmony_ci} 6138c2ecf20Sopenharmony_ci 6148c2ecf20Sopenharmony_cistatic void populate_erx_stats(struct be_adapter *adapter, 6158c2ecf20Sopenharmony_ci struct be_rx_obj *rxo, u32 erx_stat) 6168c2ecf20Sopenharmony_ci{ 6178c2ecf20Sopenharmony_ci if (!BEx_chip(adapter)) 6188c2ecf20Sopenharmony_ci rx_stats(rxo)->rx_drops_no_frags = erx_stat; 6198c2ecf20Sopenharmony_ci else 6208c2ecf20Sopenharmony_ci /* below erx HW counter can actually wrap around after 6218c2ecf20Sopenharmony_ci * 65535. Driver accumulates a 32-bit value 6228c2ecf20Sopenharmony_ci */ 6238c2ecf20Sopenharmony_ci accumulate_16bit_val(&rx_stats(rxo)->rx_drops_no_frags, 6248c2ecf20Sopenharmony_ci (u16)erx_stat); 6258c2ecf20Sopenharmony_ci} 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_civoid be_parse_stats(struct be_adapter *adapter) 6288c2ecf20Sopenharmony_ci{ 6298c2ecf20Sopenharmony_ci struct be_erx_stats_v2 *erx = be_erx_stats_from_cmd(adapter); 6308c2ecf20Sopenharmony_ci struct be_rx_obj *rxo; 6318c2ecf20Sopenharmony_ci int i; 6328c2ecf20Sopenharmony_ci u32 erx_stat; 6338c2ecf20Sopenharmony_ci 6348c2ecf20Sopenharmony_ci if (lancer_chip(adapter)) { 6358c2ecf20Sopenharmony_ci populate_lancer_stats(adapter); 6368c2ecf20Sopenharmony_ci } else { 6378c2ecf20Sopenharmony_ci if (BE2_chip(adapter)) 6388c2ecf20Sopenharmony_ci populate_be_v0_stats(adapter); 6398c2ecf20Sopenharmony_ci else if (BE3_chip(adapter)) 6408c2ecf20Sopenharmony_ci /* for BE3 */ 6418c2ecf20Sopenharmony_ci populate_be_v1_stats(adapter); 6428c2ecf20Sopenharmony_ci else 6438c2ecf20Sopenharmony_ci populate_be_v2_stats(adapter); 6448c2ecf20Sopenharmony_ci 6458c2ecf20Sopenharmony_ci /* erx_v2 is longer than v0, v1. use v2 for v0, v1 access */ 6468c2ecf20Sopenharmony_ci for_all_rx_queues(adapter, rxo, i) { 6478c2ecf20Sopenharmony_ci erx_stat = erx->rx_drops_no_fragments[rxo->q.id]; 6488c2ecf20Sopenharmony_ci populate_erx_stats(adapter, rxo, erx_stat); 6498c2ecf20Sopenharmony_ci } 6508c2ecf20Sopenharmony_ci } 6518c2ecf20Sopenharmony_ci} 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_cistatic void be_get_stats64(struct net_device *netdev, 6548c2ecf20Sopenharmony_ci struct rtnl_link_stats64 *stats) 6558c2ecf20Sopenharmony_ci{ 6568c2ecf20Sopenharmony_ci struct be_adapter *adapter = netdev_priv(netdev); 6578c2ecf20Sopenharmony_ci struct be_drv_stats *drvs = &adapter->drv_stats; 6588c2ecf20Sopenharmony_ci struct be_rx_obj *rxo; 6598c2ecf20Sopenharmony_ci struct be_tx_obj *txo; 6608c2ecf20Sopenharmony_ci u64 pkts, bytes; 6618c2ecf20Sopenharmony_ci unsigned int start; 6628c2ecf20Sopenharmony_ci int i; 6638c2ecf20Sopenharmony_ci 6648c2ecf20Sopenharmony_ci for_all_rx_queues(adapter, rxo, i) { 6658c2ecf20Sopenharmony_ci const struct be_rx_stats *rx_stats = rx_stats(rxo); 6668c2ecf20Sopenharmony_ci 6678c2ecf20Sopenharmony_ci do { 6688c2ecf20Sopenharmony_ci start = u64_stats_fetch_begin_irq(&rx_stats->sync); 6698c2ecf20Sopenharmony_ci pkts = rx_stats(rxo)->rx_pkts; 6708c2ecf20Sopenharmony_ci bytes = rx_stats(rxo)->rx_bytes; 6718c2ecf20Sopenharmony_ci } while (u64_stats_fetch_retry_irq(&rx_stats->sync, start)); 6728c2ecf20Sopenharmony_ci stats->rx_packets += pkts; 6738c2ecf20Sopenharmony_ci stats->rx_bytes += bytes; 6748c2ecf20Sopenharmony_ci stats->multicast += rx_stats(rxo)->rx_mcast_pkts; 6758c2ecf20Sopenharmony_ci stats->rx_dropped += rx_stats(rxo)->rx_drops_no_skbs + 6768c2ecf20Sopenharmony_ci rx_stats(rxo)->rx_drops_no_frags; 6778c2ecf20Sopenharmony_ci } 6788c2ecf20Sopenharmony_ci 6798c2ecf20Sopenharmony_ci for_all_tx_queues(adapter, txo, i) { 6808c2ecf20Sopenharmony_ci const struct be_tx_stats *tx_stats = tx_stats(txo); 6818c2ecf20Sopenharmony_ci 6828c2ecf20Sopenharmony_ci do { 6838c2ecf20Sopenharmony_ci start = u64_stats_fetch_begin_irq(&tx_stats->sync); 6848c2ecf20Sopenharmony_ci pkts = tx_stats(txo)->tx_pkts; 6858c2ecf20Sopenharmony_ci bytes = tx_stats(txo)->tx_bytes; 6868c2ecf20Sopenharmony_ci } while (u64_stats_fetch_retry_irq(&tx_stats->sync, start)); 6878c2ecf20Sopenharmony_ci stats->tx_packets += pkts; 6888c2ecf20Sopenharmony_ci stats->tx_bytes += bytes; 6898c2ecf20Sopenharmony_ci } 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci /* bad pkts received */ 6928c2ecf20Sopenharmony_ci stats->rx_errors = drvs->rx_crc_errors + 6938c2ecf20Sopenharmony_ci drvs->rx_alignment_symbol_errors + 6948c2ecf20Sopenharmony_ci drvs->rx_in_range_errors + 6958c2ecf20Sopenharmony_ci drvs->rx_out_range_errors + 6968c2ecf20Sopenharmony_ci drvs->rx_frame_too_long + 6978c2ecf20Sopenharmony_ci drvs->rx_dropped_too_small + 6988c2ecf20Sopenharmony_ci drvs->rx_dropped_too_short + 6998c2ecf20Sopenharmony_ci drvs->rx_dropped_header_too_small + 7008c2ecf20Sopenharmony_ci drvs->rx_dropped_tcp_length + 7018c2ecf20Sopenharmony_ci drvs->rx_dropped_runt; 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci /* detailed rx errors */ 7048c2ecf20Sopenharmony_ci stats->rx_length_errors = drvs->rx_in_range_errors + 7058c2ecf20Sopenharmony_ci drvs->rx_out_range_errors + 7068c2ecf20Sopenharmony_ci drvs->rx_frame_too_long; 7078c2ecf20Sopenharmony_ci 7088c2ecf20Sopenharmony_ci stats->rx_crc_errors = drvs->rx_crc_errors; 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci /* frame alignment errors */ 7118c2ecf20Sopenharmony_ci stats->rx_frame_errors = drvs->rx_alignment_symbol_errors; 7128c2ecf20Sopenharmony_ci 7138c2ecf20Sopenharmony_ci /* receiver fifo overrun */ 7148c2ecf20Sopenharmony_ci /* drops_no_pbuf is no per i/f, it's per BE card */ 7158c2ecf20Sopenharmony_ci stats->rx_fifo_errors = drvs->rxpp_fifo_overflow_drop + 7168c2ecf20Sopenharmony_ci drvs->rx_input_fifo_overflow_drop + 7178c2ecf20Sopenharmony_ci drvs->rx_drops_no_pbuf; 7188c2ecf20Sopenharmony_ci} 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_civoid be_link_status_update(struct be_adapter *adapter, u8 link_status) 7218c2ecf20Sopenharmony_ci{ 7228c2ecf20Sopenharmony_ci struct net_device *netdev = adapter->netdev; 7238c2ecf20Sopenharmony_ci 7248c2ecf20Sopenharmony_ci if (!(adapter->flags & BE_FLAGS_LINK_STATUS_INIT)) { 7258c2ecf20Sopenharmony_ci netif_carrier_off(netdev); 7268c2ecf20Sopenharmony_ci adapter->flags |= BE_FLAGS_LINK_STATUS_INIT; 7278c2ecf20Sopenharmony_ci } 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ci if (link_status) 7308c2ecf20Sopenharmony_ci netif_carrier_on(netdev); 7318c2ecf20Sopenharmony_ci else 7328c2ecf20Sopenharmony_ci netif_carrier_off(netdev); 7338c2ecf20Sopenharmony_ci 7348c2ecf20Sopenharmony_ci netdev_info(netdev, "Link is %s\n", link_status ? "Up" : "Down"); 7358c2ecf20Sopenharmony_ci} 7368c2ecf20Sopenharmony_ci 7378c2ecf20Sopenharmony_cistatic int be_gso_hdr_len(struct sk_buff *skb) 7388c2ecf20Sopenharmony_ci{ 7398c2ecf20Sopenharmony_ci if (skb->encapsulation) 7408c2ecf20Sopenharmony_ci return skb_inner_transport_offset(skb) + 7418c2ecf20Sopenharmony_ci inner_tcp_hdrlen(skb); 7428c2ecf20Sopenharmony_ci return skb_transport_offset(skb) + tcp_hdrlen(skb); 7438c2ecf20Sopenharmony_ci} 7448c2ecf20Sopenharmony_ci 7458c2ecf20Sopenharmony_cistatic void be_tx_stats_update(struct be_tx_obj *txo, struct sk_buff *skb) 7468c2ecf20Sopenharmony_ci{ 7478c2ecf20Sopenharmony_ci struct be_tx_stats *stats = tx_stats(txo); 7488c2ecf20Sopenharmony_ci u32 tx_pkts = skb_shinfo(skb)->gso_segs ? : 1; 7498c2ecf20Sopenharmony_ci /* Account for headers which get duplicated in TSO pkt */ 7508c2ecf20Sopenharmony_ci u32 dup_hdr_len = tx_pkts > 1 ? be_gso_hdr_len(skb) * (tx_pkts - 1) : 0; 7518c2ecf20Sopenharmony_ci 7528c2ecf20Sopenharmony_ci u64_stats_update_begin(&stats->sync); 7538c2ecf20Sopenharmony_ci stats->tx_reqs++; 7548c2ecf20Sopenharmony_ci stats->tx_bytes += skb->len + dup_hdr_len; 7558c2ecf20Sopenharmony_ci stats->tx_pkts += tx_pkts; 7568c2ecf20Sopenharmony_ci if (skb->encapsulation && skb->ip_summed == CHECKSUM_PARTIAL) 7578c2ecf20Sopenharmony_ci stats->tx_vxlan_offload_pkts += tx_pkts; 7588c2ecf20Sopenharmony_ci u64_stats_update_end(&stats->sync); 7598c2ecf20Sopenharmony_ci} 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci/* Returns number of WRBs needed for the skb */ 7628c2ecf20Sopenharmony_cistatic u32 skb_wrb_cnt(struct sk_buff *skb) 7638c2ecf20Sopenharmony_ci{ 7648c2ecf20Sopenharmony_ci /* +1 for the header wrb */ 7658c2ecf20Sopenharmony_ci return 1 + (skb_headlen(skb) ? 1 : 0) + skb_shinfo(skb)->nr_frags; 7668c2ecf20Sopenharmony_ci} 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_cistatic inline void wrb_fill(struct be_eth_wrb *wrb, u64 addr, int len) 7698c2ecf20Sopenharmony_ci{ 7708c2ecf20Sopenharmony_ci wrb->frag_pa_hi = cpu_to_le32(upper_32_bits(addr)); 7718c2ecf20Sopenharmony_ci wrb->frag_pa_lo = cpu_to_le32(lower_32_bits(addr)); 7728c2ecf20Sopenharmony_ci wrb->frag_len = cpu_to_le32(len & ETH_WRB_FRAG_LEN_MASK); 7738c2ecf20Sopenharmony_ci wrb->rsvd0 = 0; 7748c2ecf20Sopenharmony_ci} 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ci/* A dummy wrb is just all zeros. Using a separate routine for dummy-wrb 7778c2ecf20Sopenharmony_ci * to avoid the swap and shift/mask operations in wrb_fill(). 7788c2ecf20Sopenharmony_ci */ 7798c2ecf20Sopenharmony_cistatic inline void wrb_fill_dummy(struct be_eth_wrb *wrb) 7808c2ecf20Sopenharmony_ci{ 7818c2ecf20Sopenharmony_ci wrb->frag_pa_hi = 0; 7828c2ecf20Sopenharmony_ci wrb->frag_pa_lo = 0; 7838c2ecf20Sopenharmony_ci wrb->frag_len = 0; 7848c2ecf20Sopenharmony_ci wrb->rsvd0 = 0; 7858c2ecf20Sopenharmony_ci} 7868c2ecf20Sopenharmony_ci 7878c2ecf20Sopenharmony_cistatic inline u16 be_get_tx_vlan_tag(struct be_adapter *adapter, 7888c2ecf20Sopenharmony_ci struct sk_buff *skb) 7898c2ecf20Sopenharmony_ci{ 7908c2ecf20Sopenharmony_ci u8 vlan_prio; 7918c2ecf20Sopenharmony_ci u16 vlan_tag; 7928c2ecf20Sopenharmony_ci 7938c2ecf20Sopenharmony_ci vlan_tag = skb_vlan_tag_get(skb); 7948c2ecf20Sopenharmony_ci vlan_prio = skb_vlan_tag_get_prio(skb); 7958c2ecf20Sopenharmony_ci /* If vlan priority provided by OS is NOT in available bmap */ 7968c2ecf20Sopenharmony_ci if (!(adapter->vlan_prio_bmap & (1 << vlan_prio))) 7978c2ecf20Sopenharmony_ci vlan_tag = (vlan_tag & ~VLAN_PRIO_MASK) | 7988c2ecf20Sopenharmony_ci adapter->recommended_prio_bits; 7998c2ecf20Sopenharmony_ci 8008c2ecf20Sopenharmony_ci return vlan_tag; 8018c2ecf20Sopenharmony_ci} 8028c2ecf20Sopenharmony_ci 8038c2ecf20Sopenharmony_ci/* Used only for IP tunnel packets */ 8048c2ecf20Sopenharmony_cistatic u16 skb_inner_ip_proto(struct sk_buff *skb) 8058c2ecf20Sopenharmony_ci{ 8068c2ecf20Sopenharmony_ci return (inner_ip_hdr(skb)->version == 4) ? 8078c2ecf20Sopenharmony_ci inner_ip_hdr(skb)->protocol : inner_ipv6_hdr(skb)->nexthdr; 8088c2ecf20Sopenharmony_ci} 8098c2ecf20Sopenharmony_ci 8108c2ecf20Sopenharmony_cistatic u16 skb_ip_proto(struct sk_buff *skb) 8118c2ecf20Sopenharmony_ci{ 8128c2ecf20Sopenharmony_ci return (ip_hdr(skb)->version == 4) ? 8138c2ecf20Sopenharmony_ci ip_hdr(skb)->protocol : ipv6_hdr(skb)->nexthdr; 8148c2ecf20Sopenharmony_ci} 8158c2ecf20Sopenharmony_ci 8168c2ecf20Sopenharmony_cistatic inline bool be_is_txq_full(struct be_tx_obj *txo) 8178c2ecf20Sopenharmony_ci{ 8188c2ecf20Sopenharmony_ci return atomic_read(&txo->q.used) + BE_MAX_TX_FRAG_COUNT >= txo->q.len; 8198c2ecf20Sopenharmony_ci} 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_cistatic inline bool be_can_txq_wake(struct be_tx_obj *txo) 8228c2ecf20Sopenharmony_ci{ 8238c2ecf20Sopenharmony_ci return atomic_read(&txo->q.used) < txo->q.len / 2; 8248c2ecf20Sopenharmony_ci} 8258c2ecf20Sopenharmony_ci 8268c2ecf20Sopenharmony_cistatic inline bool be_is_tx_compl_pending(struct be_tx_obj *txo) 8278c2ecf20Sopenharmony_ci{ 8288c2ecf20Sopenharmony_ci return atomic_read(&txo->q.used) > txo->pend_wrb_cnt; 8298c2ecf20Sopenharmony_ci} 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_cistatic void be_get_wrb_params_from_skb(struct be_adapter *adapter, 8328c2ecf20Sopenharmony_ci struct sk_buff *skb, 8338c2ecf20Sopenharmony_ci struct be_wrb_params *wrb_params) 8348c2ecf20Sopenharmony_ci{ 8358c2ecf20Sopenharmony_ci u16 proto; 8368c2ecf20Sopenharmony_ci 8378c2ecf20Sopenharmony_ci if (skb_is_gso(skb)) { 8388c2ecf20Sopenharmony_ci BE_WRB_F_SET(wrb_params->features, LSO, 1); 8398c2ecf20Sopenharmony_ci wrb_params->lso_mss = skb_shinfo(skb)->gso_size; 8408c2ecf20Sopenharmony_ci if (skb_is_gso_v6(skb) && !lancer_chip(adapter)) 8418c2ecf20Sopenharmony_ci BE_WRB_F_SET(wrb_params->features, LSO6, 1); 8428c2ecf20Sopenharmony_ci } else if (skb->ip_summed == CHECKSUM_PARTIAL) { 8438c2ecf20Sopenharmony_ci if (skb->encapsulation) { 8448c2ecf20Sopenharmony_ci BE_WRB_F_SET(wrb_params->features, IPCS, 1); 8458c2ecf20Sopenharmony_ci proto = skb_inner_ip_proto(skb); 8468c2ecf20Sopenharmony_ci } else { 8478c2ecf20Sopenharmony_ci proto = skb_ip_proto(skb); 8488c2ecf20Sopenharmony_ci } 8498c2ecf20Sopenharmony_ci if (proto == IPPROTO_TCP) 8508c2ecf20Sopenharmony_ci BE_WRB_F_SET(wrb_params->features, TCPCS, 1); 8518c2ecf20Sopenharmony_ci else if (proto == IPPROTO_UDP) 8528c2ecf20Sopenharmony_ci BE_WRB_F_SET(wrb_params->features, UDPCS, 1); 8538c2ecf20Sopenharmony_ci } 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci if (skb_vlan_tag_present(skb)) { 8568c2ecf20Sopenharmony_ci BE_WRB_F_SET(wrb_params->features, VLAN, 1); 8578c2ecf20Sopenharmony_ci wrb_params->vlan_tag = be_get_tx_vlan_tag(adapter, skb); 8588c2ecf20Sopenharmony_ci } 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci BE_WRB_F_SET(wrb_params->features, CRC, 1); 8618c2ecf20Sopenharmony_ci} 8628c2ecf20Sopenharmony_ci 8638c2ecf20Sopenharmony_cistatic void wrb_fill_hdr(struct be_adapter *adapter, 8648c2ecf20Sopenharmony_ci struct be_eth_hdr_wrb *hdr, 8658c2ecf20Sopenharmony_ci struct be_wrb_params *wrb_params, 8668c2ecf20Sopenharmony_ci struct sk_buff *skb) 8678c2ecf20Sopenharmony_ci{ 8688c2ecf20Sopenharmony_ci memset(hdr, 0, sizeof(*hdr)); 8698c2ecf20Sopenharmony_ci 8708c2ecf20Sopenharmony_ci SET_TX_WRB_HDR_BITS(crc, hdr, 8718c2ecf20Sopenharmony_ci BE_WRB_F_GET(wrb_params->features, CRC)); 8728c2ecf20Sopenharmony_ci SET_TX_WRB_HDR_BITS(ipcs, hdr, 8738c2ecf20Sopenharmony_ci BE_WRB_F_GET(wrb_params->features, IPCS)); 8748c2ecf20Sopenharmony_ci SET_TX_WRB_HDR_BITS(tcpcs, hdr, 8758c2ecf20Sopenharmony_ci BE_WRB_F_GET(wrb_params->features, TCPCS)); 8768c2ecf20Sopenharmony_ci SET_TX_WRB_HDR_BITS(udpcs, hdr, 8778c2ecf20Sopenharmony_ci BE_WRB_F_GET(wrb_params->features, UDPCS)); 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_ci SET_TX_WRB_HDR_BITS(lso, hdr, 8808c2ecf20Sopenharmony_ci BE_WRB_F_GET(wrb_params->features, LSO)); 8818c2ecf20Sopenharmony_ci SET_TX_WRB_HDR_BITS(lso6, hdr, 8828c2ecf20Sopenharmony_ci BE_WRB_F_GET(wrb_params->features, LSO6)); 8838c2ecf20Sopenharmony_ci SET_TX_WRB_HDR_BITS(lso_mss, hdr, wrb_params->lso_mss); 8848c2ecf20Sopenharmony_ci 8858c2ecf20Sopenharmony_ci /* Hack to skip HW VLAN tagging needs evt = 1, compl = 0. When this 8868c2ecf20Sopenharmony_ci * hack is not needed, the evt bit is set while ringing DB. 8878c2ecf20Sopenharmony_ci */ 8888c2ecf20Sopenharmony_ci SET_TX_WRB_HDR_BITS(event, hdr, 8898c2ecf20Sopenharmony_ci BE_WRB_F_GET(wrb_params->features, VLAN_SKIP_HW)); 8908c2ecf20Sopenharmony_ci SET_TX_WRB_HDR_BITS(vlan, hdr, 8918c2ecf20Sopenharmony_ci BE_WRB_F_GET(wrb_params->features, VLAN)); 8928c2ecf20Sopenharmony_ci SET_TX_WRB_HDR_BITS(vlan_tag, hdr, wrb_params->vlan_tag); 8938c2ecf20Sopenharmony_ci 8948c2ecf20Sopenharmony_ci SET_TX_WRB_HDR_BITS(num_wrb, hdr, skb_wrb_cnt(skb)); 8958c2ecf20Sopenharmony_ci SET_TX_WRB_HDR_BITS(len, hdr, skb->len); 8968c2ecf20Sopenharmony_ci SET_TX_WRB_HDR_BITS(mgmt, hdr, 8978c2ecf20Sopenharmony_ci BE_WRB_F_GET(wrb_params->features, OS2BMC)); 8988c2ecf20Sopenharmony_ci} 8998c2ecf20Sopenharmony_ci 9008c2ecf20Sopenharmony_cistatic void unmap_tx_frag(struct device *dev, struct be_eth_wrb *wrb, 9018c2ecf20Sopenharmony_ci bool unmap_single) 9028c2ecf20Sopenharmony_ci{ 9038c2ecf20Sopenharmony_ci dma_addr_t dma; 9048c2ecf20Sopenharmony_ci u32 frag_len = le32_to_cpu(wrb->frag_len); 9058c2ecf20Sopenharmony_ci 9068c2ecf20Sopenharmony_ci 9078c2ecf20Sopenharmony_ci dma = (u64)le32_to_cpu(wrb->frag_pa_hi) << 32 | 9088c2ecf20Sopenharmony_ci (u64)le32_to_cpu(wrb->frag_pa_lo); 9098c2ecf20Sopenharmony_ci if (frag_len) { 9108c2ecf20Sopenharmony_ci if (unmap_single) 9118c2ecf20Sopenharmony_ci dma_unmap_single(dev, dma, frag_len, DMA_TO_DEVICE); 9128c2ecf20Sopenharmony_ci else 9138c2ecf20Sopenharmony_ci dma_unmap_page(dev, dma, frag_len, DMA_TO_DEVICE); 9148c2ecf20Sopenharmony_ci } 9158c2ecf20Sopenharmony_ci} 9168c2ecf20Sopenharmony_ci 9178c2ecf20Sopenharmony_ci/* Grab a WRB header for xmit */ 9188c2ecf20Sopenharmony_cistatic u32 be_tx_get_wrb_hdr(struct be_tx_obj *txo) 9198c2ecf20Sopenharmony_ci{ 9208c2ecf20Sopenharmony_ci u32 head = txo->q.head; 9218c2ecf20Sopenharmony_ci 9228c2ecf20Sopenharmony_ci queue_head_inc(&txo->q); 9238c2ecf20Sopenharmony_ci return head; 9248c2ecf20Sopenharmony_ci} 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_ci/* Set up the WRB header for xmit */ 9278c2ecf20Sopenharmony_cistatic void be_tx_setup_wrb_hdr(struct be_adapter *adapter, 9288c2ecf20Sopenharmony_ci struct be_tx_obj *txo, 9298c2ecf20Sopenharmony_ci struct be_wrb_params *wrb_params, 9308c2ecf20Sopenharmony_ci struct sk_buff *skb, u16 head) 9318c2ecf20Sopenharmony_ci{ 9328c2ecf20Sopenharmony_ci u32 num_frags = skb_wrb_cnt(skb); 9338c2ecf20Sopenharmony_ci struct be_queue_info *txq = &txo->q; 9348c2ecf20Sopenharmony_ci struct be_eth_hdr_wrb *hdr = queue_index_node(txq, head); 9358c2ecf20Sopenharmony_ci 9368c2ecf20Sopenharmony_ci wrb_fill_hdr(adapter, hdr, wrb_params, skb); 9378c2ecf20Sopenharmony_ci be_dws_cpu_to_le(hdr, sizeof(*hdr)); 9388c2ecf20Sopenharmony_ci 9398c2ecf20Sopenharmony_ci BUG_ON(txo->sent_skb_list[head]); 9408c2ecf20Sopenharmony_ci txo->sent_skb_list[head] = skb; 9418c2ecf20Sopenharmony_ci txo->last_req_hdr = head; 9428c2ecf20Sopenharmony_ci atomic_add(num_frags, &txq->used); 9438c2ecf20Sopenharmony_ci txo->last_req_wrb_cnt = num_frags; 9448c2ecf20Sopenharmony_ci txo->pend_wrb_cnt += num_frags; 9458c2ecf20Sopenharmony_ci} 9468c2ecf20Sopenharmony_ci 9478c2ecf20Sopenharmony_ci/* Setup a WRB fragment (buffer descriptor) for xmit */ 9488c2ecf20Sopenharmony_cistatic void be_tx_setup_wrb_frag(struct be_tx_obj *txo, dma_addr_t busaddr, 9498c2ecf20Sopenharmony_ci int len) 9508c2ecf20Sopenharmony_ci{ 9518c2ecf20Sopenharmony_ci struct be_eth_wrb *wrb; 9528c2ecf20Sopenharmony_ci struct be_queue_info *txq = &txo->q; 9538c2ecf20Sopenharmony_ci 9548c2ecf20Sopenharmony_ci wrb = queue_head_node(txq); 9558c2ecf20Sopenharmony_ci wrb_fill(wrb, busaddr, len); 9568c2ecf20Sopenharmony_ci queue_head_inc(txq); 9578c2ecf20Sopenharmony_ci} 9588c2ecf20Sopenharmony_ci 9598c2ecf20Sopenharmony_ci/* Bring the queue back to the state it was in before be_xmit_enqueue() routine 9608c2ecf20Sopenharmony_ci * was invoked. The producer index is restored to the previous packet and the 9618c2ecf20Sopenharmony_ci * WRBs of the current packet are unmapped. Invoked to handle tx setup errors. 9628c2ecf20Sopenharmony_ci */ 9638c2ecf20Sopenharmony_cistatic void be_xmit_restore(struct be_adapter *adapter, 9648c2ecf20Sopenharmony_ci struct be_tx_obj *txo, u32 head, bool map_single, 9658c2ecf20Sopenharmony_ci u32 copied) 9668c2ecf20Sopenharmony_ci{ 9678c2ecf20Sopenharmony_ci struct device *dev; 9688c2ecf20Sopenharmony_ci struct be_eth_wrb *wrb; 9698c2ecf20Sopenharmony_ci struct be_queue_info *txq = &txo->q; 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_ci dev = &adapter->pdev->dev; 9728c2ecf20Sopenharmony_ci txq->head = head; 9738c2ecf20Sopenharmony_ci 9748c2ecf20Sopenharmony_ci /* skip the first wrb (hdr); it's not mapped */ 9758c2ecf20Sopenharmony_ci queue_head_inc(txq); 9768c2ecf20Sopenharmony_ci while (copied) { 9778c2ecf20Sopenharmony_ci wrb = queue_head_node(txq); 9788c2ecf20Sopenharmony_ci unmap_tx_frag(dev, wrb, map_single); 9798c2ecf20Sopenharmony_ci map_single = false; 9808c2ecf20Sopenharmony_ci copied -= le32_to_cpu(wrb->frag_len); 9818c2ecf20Sopenharmony_ci queue_head_inc(txq); 9828c2ecf20Sopenharmony_ci } 9838c2ecf20Sopenharmony_ci 9848c2ecf20Sopenharmony_ci txq->head = head; 9858c2ecf20Sopenharmony_ci} 9868c2ecf20Sopenharmony_ci 9878c2ecf20Sopenharmony_ci/* Enqueue the given packet for transmit. This routine allocates WRBs for the 9888c2ecf20Sopenharmony_ci * packet, dma maps the packet buffers and sets up the WRBs. Returns the number 9898c2ecf20Sopenharmony_ci * of WRBs used up by the packet. 9908c2ecf20Sopenharmony_ci */ 9918c2ecf20Sopenharmony_cistatic u32 be_xmit_enqueue(struct be_adapter *adapter, struct be_tx_obj *txo, 9928c2ecf20Sopenharmony_ci struct sk_buff *skb, 9938c2ecf20Sopenharmony_ci struct be_wrb_params *wrb_params) 9948c2ecf20Sopenharmony_ci{ 9958c2ecf20Sopenharmony_ci u32 i, copied = 0, wrb_cnt = skb_wrb_cnt(skb); 9968c2ecf20Sopenharmony_ci struct device *dev = &adapter->pdev->dev; 9978c2ecf20Sopenharmony_ci bool map_single = false; 9988c2ecf20Sopenharmony_ci u32 head; 9998c2ecf20Sopenharmony_ci dma_addr_t busaddr; 10008c2ecf20Sopenharmony_ci int len; 10018c2ecf20Sopenharmony_ci 10028c2ecf20Sopenharmony_ci head = be_tx_get_wrb_hdr(txo); 10038c2ecf20Sopenharmony_ci 10048c2ecf20Sopenharmony_ci if (skb->len > skb->data_len) { 10058c2ecf20Sopenharmony_ci len = skb_headlen(skb); 10068c2ecf20Sopenharmony_ci 10078c2ecf20Sopenharmony_ci busaddr = dma_map_single(dev, skb->data, len, DMA_TO_DEVICE); 10088c2ecf20Sopenharmony_ci if (dma_mapping_error(dev, busaddr)) 10098c2ecf20Sopenharmony_ci goto dma_err; 10108c2ecf20Sopenharmony_ci map_single = true; 10118c2ecf20Sopenharmony_ci be_tx_setup_wrb_frag(txo, busaddr, len); 10128c2ecf20Sopenharmony_ci copied += len; 10138c2ecf20Sopenharmony_ci } 10148c2ecf20Sopenharmony_ci 10158c2ecf20Sopenharmony_ci for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) { 10168c2ecf20Sopenharmony_ci const skb_frag_t *frag = &skb_shinfo(skb)->frags[i]; 10178c2ecf20Sopenharmony_ci len = skb_frag_size(frag); 10188c2ecf20Sopenharmony_ci 10198c2ecf20Sopenharmony_ci busaddr = skb_frag_dma_map(dev, frag, 0, len, DMA_TO_DEVICE); 10208c2ecf20Sopenharmony_ci if (dma_mapping_error(dev, busaddr)) 10218c2ecf20Sopenharmony_ci goto dma_err; 10228c2ecf20Sopenharmony_ci be_tx_setup_wrb_frag(txo, busaddr, len); 10238c2ecf20Sopenharmony_ci copied += len; 10248c2ecf20Sopenharmony_ci } 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_ci be_tx_setup_wrb_hdr(adapter, txo, wrb_params, skb, head); 10278c2ecf20Sopenharmony_ci 10288c2ecf20Sopenharmony_ci be_tx_stats_update(txo, skb); 10298c2ecf20Sopenharmony_ci return wrb_cnt; 10308c2ecf20Sopenharmony_ci 10318c2ecf20Sopenharmony_cidma_err: 10328c2ecf20Sopenharmony_ci adapter->drv_stats.dma_map_errors++; 10338c2ecf20Sopenharmony_ci be_xmit_restore(adapter, txo, head, map_single, copied); 10348c2ecf20Sopenharmony_ci return 0; 10358c2ecf20Sopenharmony_ci} 10368c2ecf20Sopenharmony_ci 10378c2ecf20Sopenharmony_cistatic inline int qnq_async_evt_rcvd(struct be_adapter *adapter) 10388c2ecf20Sopenharmony_ci{ 10398c2ecf20Sopenharmony_ci return adapter->flags & BE_FLAGS_QNQ_ASYNC_EVT_RCVD; 10408c2ecf20Sopenharmony_ci} 10418c2ecf20Sopenharmony_ci 10428c2ecf20Sopenharmony_cistatic struct sk_buff *be_insert_vlan_in_pkt(struct be_adapter *adapter, 10438c2ecf20Sopenharmony_ci struct sk_buff *skb, 10448c2ecf20Sopenharmony_ci struct be_wrb_params 10458c2ecf20Sopenharmony_ci *wrb_params) 10468c2ecf20Sopenharmony_ci{ 10478c2ecf20Sopenharmony_ci bool insert_vlan = false; 10488c2ecf20Sopenharmony_ci u16 vlan_tag = 0; 10498c2ecf20Sopenharmony_ci 10508c2ecf20Sopenharmony_ci skb = skb_share_check(skb, GFP_ATOMIC); 10518c2ecf20Sopenharmony_ci if (unlikely(!skb)) 10528c2ecf20Sopenharmony_ci return skb; 10538c2ecf20Sopenharmony_ci 10548c2ecf20Sopenharmony_ci if (skb_vlan_tag_present(skb)) { 10558c2ecf20Sopenharmony_ci vlan_tag = be_get_tx_vlan_tag(adapter, skb); 10568c2ecf20Sopenharmony_ci insert_vlan = true; 10578c2ecf20Sopenharmony_ci } 10588c2ecf20Sopenharmony_ci 10598c2ecf20Sopenharmony_ci if (qnq_async_evt_rcvd(adapter) && adapter->pvid) { 10608c2ecf20Sopenharmony_ci if (!insert_vlan) { 10618c2ecf20Sopenharmony_ci vlan_tag = adapter->pvid; 10628c2ecf20Sopenharmony_ci insert_vlan = true; 10638c2ecf20Sopenharmony_ci } 10648c2ecf20Sopenharmony_ci /* f/w workaround to set skip_hw_vlan = 1, informs the F/W to 10658c2ecf20Sopenharmony_ci * skip VLAN insertion 10668c2ecf20Sopenharmony_ci */ 10678c2ecf20Sopenharmony_ci BE_WRB_F_SET(wrb_params->features, VLAN_SKIP_HW, 1); 10688c2ecf20Sopenharmony_ci } 10698c2ecf20Sopenharmony_ci 10708c2ecf20Sopenharmony_ci if (insert_vlan) { 10718c2ecf20Sopenharmony_ci skb = vlan_insert_tag_set_proto(skb, htons(ETH_P_8021Q), 10728c2ecf20Sopenharmony_ci vlan_tag); 10738c2ecf20Sopenharmony_ci if (unlikely(!skb)) 10748c2ecf20Sopenharmony_ci return skb; 10758c2ecf20Sopenharmony_ci __vlan_hwaccel_clear_tag(skb); 10768c2ecf20Sopenharmony_ci } 10778c2ecf20Sopenharmony_ci 10788c2ecf20Sopenharmony_ci /* Insert the outer VLAN, if any */ 10798c2ecf20Sopenharmony_ci if (adapter->qnq_vid) { 10808c2ecf20Sopenharmony_ci vlan_tag = adapter->qnq_vid; 10818c2ecf20Sopenharmony_ci skb = vlan_insert_tag_set_proto(skb, htons(ETH_P_8021Q), 10828c2ecf20Sopenharmony_ci vlan_tag); 10838c2ecf20Sopenharmony_ci if (unlikely(!skb)) 10848c2ecf20Sopenharmony_ci return skb; 10858c2ecf20Sopenharmony_ci BE_WRB_F_SET(wrb_params->features, VLAN_SKIP_HW, 1); 10868c2ecf20Sopenharmony_ci } 10878c2ecf20Sopenharmony_ci 10888c2ecf20Sopenharmony_ci return skb; 10898c2ecf20Sopenharmony_ci} 10908c2ecf20Sopenharmony_ci 10918c2ecf20Sopenharmony_cistatic bool be_ipv6_exthdr_check(struct sk_buff *skb) 10928c2ecf20Sopenharmony_ci{ 10938c2ecf20Sopenharmony_ci struct ethhdr *eh = (struct ethhdr *)skb->data; 10948c2ecf20Sopenharmony_ci u16 offset = ETH_HLEN; 10958c2ecf20Sopenharmony_ci 10968c2ecf20Sopenharmony_ci if (eh->h_proto == htons(ETH_P_IPV6)) { 10978c2ecf20Sopenharmony_ci struct ipv6hdr *ip6h = (struct ipv6hdr *)(skb->data + offset); 10988c2ecf20Sopenharmony_ci 10998c2ecf20Sopenharmony_ci offset += sizeof(struct ipv6hdr); 11008c2ecf20Sopenharmony_ci if (ip6h->nexthdr != NEXTHDR_TCP && 11018c2ecf20Sopenharmony_ci ip6h->nexthdr != NEXTHDR_UDP) { 11028c2ecf20Sopenharmony_ci struct ipv6_opt_hdr *ehdr = 11038c2ecf20Sopenharmony_ci (struct ipv6_opt_hdr *)(skb->data + offset); 11048c2ecf20Sopenharmony_ci 11058c2ecf20Sopenharmony_ci /* offending pkt: 2nd byte following IPv6 hdr is 0xff */ 11068c2ecf20Sopenharmony_ci if (ehdr->hdrlen == 0xff) 11078c2ecf20Sopenharmony_ci return true; 11088c2ecf20Sopenharmony_ci } 11098c2ecf20Sopenharmony_ci } 11108c2ecf20Sopenharmony_ci return false; 11118c2ecf20Sopenharmony_ci} 11128c2ecf20Sopenharmony_ci 11138c2ecf20Sopenharmony_cistatic int be_vlan_tag_tx_chk(struct be_adapter *adapter, struct sk_buff *skb) 11148c2ecf20Sopenharmony_ci{ 11158c2ecf20Sopenharmony_ci return skb_vlan_tag_present(skb) || adapter->pvid || adapter->qnq_vid; 11168c2ecf20Sopenharmony_ci} 11178c2ecf20Sopenharmony_ci 11188c2ecf20Sopenharmony_cistatic int be_ipv6_tx_stall_chk(struct be_adapter *adapter, struct sk_buff *skb) 11198c2ecf20Sopenharmony_ci{ 11208c2ecf20Sopenharmony_ci return BE3_chip(adapter) && be_ipv6_exthdr_check(skb); 11218c2ecf20Sopenharmony_ci} 11228c2ecf20Sopenharmony_ci 11238c2ecf20Sopenharmony_cistatic struct sk_buff *be_lancer_xmit_workarounds(struct be_adapter *adapter, 11248c2ecf20Sopenharmony_ci struct sk_buff *skb, 11258c2ecf20Sopenharmony_ci struct be_wrb_params 11268c2ecf20Sopenharmony_ci *wrb_params) 11278c2ecf20Sopenharmony_ci{ 11288c2ecf20Sopenharmony_ci struct vlan_ethhdr *veh = skb_vlan_eth_hdr(skb); 11298c2ecf20Sopenharmony_ci unsigned int eth_hdr_len; 11308c2ecf20Sopenharmony_ci struct iphdr *ip; 11318c2ecf20Sopenharmony_ci 11328c2ecf20Sopenharmony_ci /* For padded packets, BE HW modifies tot_len field in IP header 11338c2ecf20Sopenharmony_ci * incorrecly when VLAN tag is inserted by HW. 11348c2ecf20Sopenharmony_ci * For padded packets, Lancer computes incorrect checksum. 11358c2ecf20Sopenharmony_ci */ 11368c2ecf20Sopenharmony_ci eth_hdr_len = ntohs(skb->protocol) == ETH_P_8021Q ? 11378c2ecf20Sopenharmony_ci VLAN_ETH_HLEN : ETH_HLEN; 11388c2ecf20Sopenharmony_ci if (skb->len <= 60 && 11398c2ecf20Sopenharmony_ci (lancer_chip(adapter) || BE3_chip(adapter) || 11408c2ecf20Sopenharmony_ci skb_vlan_tag_present(skb)) && is_ipv4_pkt(skb)) { 11418c2ecf20Sopenharmony_ci ip = (struct iphdr *)ip_hdr(skb); 11428c2ecf20Sopenharmony_ci if (unlikely(pskb_trim(skb, eth_hdr_len + ntohs(ip->tot_len)))) 11438c2ecf20Sopenharmony_ci goto tx_drop; 11448c2ecf20Sopenharmony_ci } 11458c2ecf20Sopenharmony_ci 11468c2ecf20Sopenharmony_ci /* If vlan tag is already inlined in the packet, skip HW VLAN 11478c2ecf20Sopenharmony_ci * tagging in pvid-tagging mode 11488c2ecf20Sopenharmony_ci */ 11498c2ecf20Sopenharmony_ci if (be_pvid_tagging_enabled(adapter) && 11508c2ecf20Sopenharmony_ci veh->h_vlan_proto == htons(ETH_P_8021Q)) 11518c2ecf20Sopenharmony_ci BE_WRB_F_SET(wrb_params->features, VLAN_SKIP_HW, 1); 11528c2ecf20Sopenharmony_ci 11538c2ecf20Sopenharmony_ci /* HW has a bug wherein it will calculate CSUM for VLAN 11548c2ecf20Sopenharmony_ci * pkts even though it is disabled. 11558c2ecf20Sopenharmony_ci * Manually insert VLAN in pkt. 11568c2ecf20Sopenharmony_ci */ 11578c2ecf20Sopenharmony_ci if (skb->ip_summed != CHECKSUM_PARTIAL && 11588c2ecf20Sopenharmony_ci skb_vlan_tag_present(skb)) { 11598c2ecf20Sopenharmony_ci skb = be_insert_vlan_in_pkt(adapter, skb, wrb_params); 11608c2ecf20Sopenharmony_ci if (unlikely(!skb)) 11618c2ecf20Sopenharmony_ci goto err; 11628c2ecf20Sopenharmony_ci } 11638c2ecf20Sopenharmony_ci 11648c2ecf20Sopenharmony_ci /* HW may lockup when VLAN HW tagging is requested on 11658c2ecf20Sopenharmony_ci * certain ipv6 packets. Drop such pkts if the HW workaround to 11668c2ecf20Sopenharmony_ci * skip HW tagging is not enabled by FW. 11678c2ecf20Sopenharmony_ci */ 11688c2ecf20Sopenharmony_ci if (unlikely(be_ipv6_tx_stall_chk(adapter, skb) && 11698c2ecf20Sopenharmony_ci (adapter->pvid || adapter->qnq_vid) && 11708c2ecf20Sopenharmony_ci !qnq_async_evt_rcvd(adapter))) 11718c2ecf20Sopenharmony_ci goto tx_drop; 11728c2ecf20Sopenharmony_ci 11738c2ecf20Sopenharmony_ci /* Manual VLAN tag insertion to prevent: 11748c2ecf20Sopenharmony_ci * ASIC lockup when the ASIC inserts VLAN tag into 11758c2ecf20Sopenharmony_ci * certain ipv6 packets. Insert VLAN tags in driver, 11768c2ecf20Sopenharmony_ci * and set event, completion, vlan bits accordingly 11778c2ecf20Sopenharmony_ci * in the Tx WRB. 11788c2ecf20Sopenharmony_ci */ 11798c2ecf20Sopenharmony_ci if (be_ipv6_tx_stall_chk(adapter, skb) && 11808c2ecf20Sopenharmony_ci be_vlan_tag_tx_chk(adapter, skb)) { 11818c2ecf20Sopenharmony_ci skb = be_insert_vlan_in_pkt(adapter, skb, wrb_params); 11828c2ecf20Sopenharmony_ci if (unlikely(!skb)) 11838c2ecf20Sopenharmony_ci goto err; 11848c2ecf20Sopenharmony_ci } 11858c2ecf20Sopenharmony_ci 11868c2ecf20Sopenharmony_ci return skb; 11878c2ecf20Sopenharmony_citx_drop: 11888c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 11898c2ecf20Sopenharmony_cierr: 11908c2ecf20Sopenharmony_ci return NULL; 11918c2ecf20Sopenharmony_ci} 11928c2ecf20Sopenharmony_ci 11938c2ecf20Sopenharmony_cistatic struct sk_buff *be_xmit_workarounds(struct be_adapter *adapter, 11948c2ecf20Sopenharmony_ci struct sk_buff *skb, 11958c2ecf20Sopenharmony_ci struct be_wrb_params *wrb_params) 11968c2ecf20Sopenharmony_ci{ 11978c2ecf20Sopenharmony_ci int err; 11988c2ecf20Sopenharmony_ci 11998c2ecf20Sopenharmony_ci /* Lancer, SH and BE3 in SRIOV mode have a bug wherein 12008c2ecf20Sopenharmony_ci * packets that are 32b or less may cause a transmit stall 12018c2ecf20Sopenharmony_ci * on that port. The workaround is to pad such packets 12028c2ecf20Sopenharmony_ci * (len <= 32 bytes) to a minimum length of 36b. 12038c2ecf20Sopenharmony_ci */ 12048c2ecf20Sopenharmony_ci if (skb->len <= 32) { 12058c2ecf20Sopenharmony_ci if (skb_put_padto(skb, 36)) 12068c2ecf20Sopenharmony_ci return NULL; 12078c2ecf20Sopenharmony_ci } 12088c2ecf20Sopenharmony_ci 12098c2ecf20Sopenharmony_ci if (BEx_chip(adapter) || lancer_chip(adapter)) { 12108c2ecf20Sopenharmony_ci skb = be_lancer_xmit_workarounds(adapter, skb, wrb_params); 12118c2ecf20Sopenharmony_ci if (!skb) 12128c2ecf20Sopenharmony_ci return NULL; 12138c2ecf20Sopenharmony_ci } 12148c2ecf20Sopenharmony_ci 12158c2ecf20Sopenharmony_ci /* The stack can send us skbs with length greater than 12168c2ecf20Sopenharmony_ci * what the HW can handle. Trim the extra bytes. 12178c2ecf20Sopenharmony_ci */ 12188c2ecf20Sopenharmony_ci WARN_ON_ONCE(skb->len > BE_MAX_GSO_SIZE); 12198c2ecf20Sopenharmony_ci err = pskb_trim(skb, BE_MAX_GSO_SIZE); 12208c2ecf20Sopenharmony_ci WARN_ON(err); 12218c2ecf20Sopenharmony_ci 12228c2ecf20Sopenharmony_ci return skb; 12238c2ecf20Sopenharmony_ci} 12248c2ecf20Sopenharmony_ci 12258c2ecf20Sopenharmony_cistatic void be_xmit_flush(struct be_adapter *adapter, struct be_tx_obj *txo) 12268c2ecf20Sopenharmony_ci{ 12278c2ecf20Sopenharmony_ci struct be_queue_info *txq = &txo->q; 12288c2ecf20Sopenharmony_ci struct be_eth_hdr_wrb *hdr = queue_index_node(txq, txo->last_req_hdr); 12298c2ecf20Sopenharmony_ci 12308c2ecf20Sopenharmony_ci /* Mark the last request eventable if it hasn't been marked already */ 12318c2ecf20Sopenharmony_ci if (!(hdr->dw[2] & cpu_to_le32(TX_HDR_WRB_EVT))) 12328c2ecf20Sopenharmony_ci hdr->dw[2] |= cpu_to_le32(TX_HDR_WRB_EVT | TX_HDR_WRB_COMPL); 12338c2ecf20Sopenharmony_ci 12348c2ecf20Sopenharmony_ci /* compose a dummy wrb if there are odd set of wrbs to notify */ 12358c2ecf20Sopenharmony_ci if (!lancer_chip(adapter) && (txo->pend_wrb_cnt & 1)) { 12368c2ecf20Sopenharmony_ci wrb_fill_dummy(queue_head_node(txq)); 12378c2ecf20Sopenharmony_ci queue_head_inc(txq); 12388c2ecf20Sopenharmony_ci atomic_inc(&txq->used); 12398c2ecf20Sopenharmony_ci txo->pend_wrb_cnt++; 12408c2ecf20Sopenharmony_ci hdr->dw[2] &= ~cpu_to_le32(TX_HDR_WRB_NUM_MASK << 12418c2ecf20Sopenharmony_ci TX_HDR_WRB_NUM_SHIFT); 12428c2ecf20Sopenharmony_ci hdr->dw[2] |= cpu_to_le32((txo->last_req_wrb_cnt + 1) << 12438c2ecf20Sopenharmony_ci TX_HDR_WRB_NUM_SHIFT); 12448c2ecf20Sopenharmony_ci } 12458c2ecf20Sopenharmony_ci be_txq_notify(adapter, txo, txo->pend_wrb_cnt); 12468c2ecf20Sopenharmony_ci txo->pend_wrb_cnt = 0; 12478c2ecf20Sopenharmony_ci} 12488c2ecf20Sopenharmony_ci 12498c2ecf20Sopenharmony_ci/* OS2BMC related */ 12508c2ecf20Sopenharmony_ci 12518c2ecf20Sopenharmony_ci#define DHCP_CLIENT_PORT 68 12528c2ecf20Sopenharmony_ci#define DHCP_SERVER_PORT 67 12538c2ecf20Sopenharmony_ci#define NET_BIOS_PORT1 137 12548c2ecf20Sopenharmony_ci#define NET_BIOS_PORT2 138 12558c2ecf20Sopenharmony_ci#define DHCPV6_RAS_PORT 547 12568c2ecf20Sopenharmony_ci 12578c2ecf20Sopenharmony_ci#define is_mc_allowed_on_bmc(adapter, eh) \ 12588c2ecf20Sopenharmony_ci (!is_multicast_filt_enabled(adapter) && \ 12598c2ecf20Sopenharmony_ci is_multicast_ether_addr(eh->h_dest) && \ 12608c2ecf20Sopenharmony_ci !is_broadcast_ether_addr(eh->h_dest)) 12618c2ecf20Sopenharmony_ci 12628c2ecf20Sopenharmony_ci#define is_bc_allowed_on_bmc(adapter, eh) \ 12638c2ecf20Sopenharmony_ci (!is_broadcast_filt_enabled(adapter) && \ 12648c2ecf20Sopenharmony_ci is_broadcast_ether_addr(eh->h_dest)) 12658c2ecf20Sopenharmony_ci 12668c2ecf20Sopenharmony_ci#define is_arp_allowed_on_bmc(adapter, skb) \ 12678c2ecf20Sopenharmony_ci (is_arp(skb) && is_arp_filt_enabled(adapter)) 12688c2ecf20Sopenharmony_ci 12698c2ecf20Sopenharmony_ci#define is_arp(skb) (skb->protocol == htons(ETH_P_ARP)) 12708c2ecf20Sopenharmony_ci 12718c2ecf20Sopenharmony_ci#define is_arp_filt_enabled(adapter) \ 12728c2ecf20Sopenharmony_ci (adapter->bmc_filt_mask & (BMC_FILT_BROADCAST_ARP)) 12738c2ecf20Sopenharmony_ci 12748c2ecf20Sopenharmony_ci#define is_dhcp_client_filt_enabled(adapter) \ 12758c2ecf20Sopenharmony_ci (adapter->bmc_filt_mask & BMC_FILT_BROADCAST_DHCP_CLIENT) 12768c2ecf20Sopenharmony_ci 12778c2ecf20Sopenharmony_ci#define is_dhcp_srvr_filt_enabled(adapter) \ 12788c2ecf20Sopenharmony_ci (adapter->bmc_filt_mask & BMC_FILT_BROADCAST_DHCP_SERVER) 12798c2ecf20Sopenharmony_ci 12808c2ecf20Sopenharmony_ci#define is_nbios_filt_enabled(adapter) \ 12818c2ecf20Sopenharmony_ci (adapter->bmc_filt_mask & BMC_FILT_BROADCAST_NET_BIOS) 12828c2ecf20Sopenharmony_ci 12838c2ecf20Sopenharmony_ci#define is_ipv6_na_filt_enabled(adapter) \ 12848c2ecf20Sopenharmony_ci (adapter->bmc_filt_mask & \ 12858c2ecf20Sopenharmony_ci BMC_FILT_MULTICAST_IPV6_NEIGH_ADVER) 12868c2ecf20Sopenharmony_ci 12878c2ecf20Sopenharmony_ci#define is_ipv6_ra_filt_enabled(adapter) \ 12888c2ecf20Sopenharmony_ci (adapter->bmc_filt_mask & BMC_FILT_MULTICAST_IPV6_RA) 12898c2ecf20Sopenharmony_ci 12908c2ecf20Sopenharmony_ci#define is_ipv6_ras_filt_enabled(adapter) \ 12918c2ecf20Sopenharmony_ci (adapter->bmc_filt_mask & BMC_FILT_MULTICAST_IPV6_RAS) 12928c2ecf20Sopenharmony_ci 12938c2ecf20Sopenharmony_ci#define is_broadcast_filt_enabled(adapter) \ 12948c2ecf20Sopenharmony_ci (adapter->bmc_filt_mask & BMC_FILT_BROADCAST) 12958c2ecf20Sopenharmony_ci 12968c2ecf20Sopenharmony_ci#define is_multicast_filt_enabled(adapter) \ 12978c2ecf20Sopenharmony_ci (adapter->bmc_filt_mask & BMC_FILT_MULTICAST) 12988c2ecf20Sopenharmony_ci 12998c2ecf20Sopenharmony_cistatic bool be_send_pkt_to_bmc(struct be_adapter *adapter, 13008c2ecf20Sopenharmony_ci struct sk_buff **skb) 13018c2ecf20Sopenharmony_ci{ 13028c2ecf20Sopenharmony_ci struct ethhdr *eh = (struct ethhdr *)(*skb)->data; 13038c2ecf20Sopenharmony_ci bool os2bmc = false; 13048c2ecf20Sopenharmony_ci 13058c2ecf20Sopenharmony_ci if (!be_is_os2bmc_enabled(adapter)) 13068c2ecf20Sopenharmony_ci goto done; 13078c2ecf20Sopenharmony_ci 13088c2ecf20Sopenharmony_ci if (!is_multicast_ether_addr(eh->h_dest)) 13098c2ecf20Sopenharmony_ci goto done; 13108c2ecf20Sopenharmony_ci 13118c2ecf20Sopenharmony_ci if (is_mc_allowed_on_bmc(adapter, eh) || 13128c2ecf20Sopenharmony_ci is_bc_allowed_on_bmc(adapter, eh) || 13138c2ecf20Sopenharmony_ci is_arp_allowed_on_bmc(adapter, (*skb))) { 13148c2ecf20Sopenharmony_ci os2bmc = true; 13158c2ecf20Sopenharmony_ci goto done; 13168c2ecf20Sopenharmony_ci } 13178c2ecf20Sopenharmony_ci 13188c2ecf20Sopenharmony_ci if ((*skb)->protocol == htons(ETH_P_IPV6)) { 13198c2ecf20Sopenharmony_ci struct ipv6hdr *hdr = ipv6_hdr((*skb)); 13208c2ecf20Sopenharmony_ci u8 nexthdr = hdr->nexthdr; 13218c2ecf20Sopenharmony_ci 13228c2ecf20Sopenharmony_ci if (nexthdr == IPPROTO_ICMPV6) { 13238c2ecf20Sopenharmony_ci struct icmp6hdr *icmp6 = icmp6_hdr((*skb)); 13248c2ecf20Sopenharmony_ci 13258c2ecf20Sopenharmony_ci switch (icmp6->icmp6_type) { 13268c2ecf20Sopenharmony_ci case NDISC_ROUTER_ADVERTISEMENT: 13278c2ecf20Sopenharmony_ci os2bmc = is_ipv6_ra_filt_enabled(adapter); 13288c2ecf20Sopenharmony_ci goto done; 13298c2ecf20Sopenharmony_ci case NDISC_NEIGHBOUR_ADVERTISEMENT: 13308c2ecf20Sopenharmony_ci os2bmc = is_ipv6_na_filt_enabled(adapter); 13318c2ecf20Sopenharmony_ci goto done; 13328c2ecf20Sopenharmony_ci default: 13338c2ecf20Sopenharmony_ci break; 13348c2ecf20Sopenharmony_ci } 13358c2ecf20Sopenharmony_ci } 13368c2ecf20Sopenharmony_ci } 13378c2ecf20Sopenharmony_ci 13388c2ecf20Sopenharmony_ci if (is_udp_pkt((*skb))) { 13398c2ecf20Sopenharmony_ci struct udphdr *udp = udp_hdr((*skb)); 13408c2ecf20Sopenharmony_ci 13418c2ecf20Sopenharmony_ci switch (ntohs(udp->dest)) { 13428c2ecf20Sopenharmony_ci case DHCP_CLIENT_PORT: 13438c2ecf20Sopenharmony_ci os2bmc = is_dhcp_client_filt_enabled(adapter); 13448c2ecf20Sopenharmony_ci goto done; 13458c2ecf20Sopenharmony_ci case DHCP_SERVER_PORT: 13468c2ecf20Sopenharmony_ci os2bmc = is_dhcp_srvr_filt_enabled(adapter); 13478c2ecf20Sopenharmony_ci goto done; 13488c2ecf20Sopenharmony_ci case NET_BIOS_PORT1: 13498c2ecf20Sopenharmony_ci case NET_BIOS_PORT2: 13508c2ecf20Sopenharmony_ci os2bmc = is_nbios_filt_enabled(adapter); 13518c2ecf20Sopenharmony_ci goto done; 13528c2ecf20Sopenharmony_ci case DHCPV6_RAS_PORT: 13538c2ecf20Sopenharmony_ci os2bmc = is_ipv6_ras_filt_enabled(adapter); 13548c2ecf20Sopenharmony_ci goto done; 13558c2ecf20Sopenharmony_ci default: 13568c2ecf20Sopenharmony_ci break; 13578c2ecf20Sopenharmony_ci } 13588c2ecf20Sopenharmony_ci } 13598c2ecf20Sopenharmony_cidone: 13608c2ecf20Sopenharmony_ci /* For packets over a vlan, which are destined 13618c2ecf20Sopenharmony_ci * to BMC, asic expects the vlan to be inline in the packet. 13628c2ecf20Sopenharmony_ci */ 13638c2ecf20Sopenharmony_ci if (os2bmc) 13648c2ecf20Sopenharmony_ci *skb = be_insert_vlan_in_pkt(adapter, *skb, NULL); 13658c2ecf20Sopenharmony_ci 13668c2ecf20Sopenharmony_ci return os2bmc; 13678c2ecf20Sopenharmony_ci} 13688c2ecf20Sopenharmony_ci 13698c2ecf20Sopenharmony_cistatic netdev_tx_t be_xmit(struct sk_buff *skb, struct net_device *netdev) 13708c2ecf20Sopenharmony_ci{ 13718c2ecf20Sopenharmony_ci struct be_adapter *adapter = netdev_priv(netdev); 13728c2ecf20Sopenharmony_ci u16 q_idx = skb_get_queue_mapping(skb); 13738c2ecf20Sopenharmony_ci struct be_tx_obj *txo = &adapter->tx_obj[q_idx]; 13748c2ecf20Sopenharmony_ci struct be_wrb_params wrb_params = { 0 }; 13758c2ecf20Sopenharmony_ci bool flush = !netdev_xmit_more(); 13768c2ecf20Sopenharmony_ci u16 wrb_cnt; 13778c2ecf20Sopenharmony_ci 13788c2ecf20Sopenharmony_ci skb = be_xmit_workarounds(adapter, skb, &wrb_params); 13798c2ecf20Sopenharmony_ci if (unlikely(!skb)) 13808c2ecf20Sopenharmony_ci goto drop; 13818c2ecf20Sopenharmony_ci 13828c2ecf20Sopenharmony_ci be_get_wrb_params_from_skb(adapter, skb, &wrb_params); 13838c2ecf20Sopenharmony_ci 13848c2ecf20Sopenharmony_ci wrb_cnt = be_xmit_enqueue(adapter, txo, skb, &wrb_params); 13858c2ecf20Sopenharmony_ci if (unlikely(!wrb_cnt)) { 13868c2ecf20Sopenharmony_ci dev_kfree_skb_any(skb); 13878c2ecf20Sopenharmony_ci goto drop; 13888c2ecf20Sopenharmony_ci } 13898c2ecf20Sopenharmony_ci 13908c2ecf20Sopenharmony_ci /* if os2bmc is enabled and if the pkt is destined to bmc, 13918c2ecf20Sopenharmony_ci * enqueue the pkt a 2nd time with mgmt bit set. 13928c2ecf20Sopenharmony_ci */ 13938c2ecf20Sopenharmony_ci if (be_send_pkt_to_bmc(adapter, &skb)) { 13948c2ecf20Sopenharmony_ci BE_WRB_F_SET(wrb_params.features, OS2BMC, 1); 13958c2ecf20Sopenharmony_ci wrb_cnt = be_xmit_enqueue(adapter, txo, skb, &wrb_params); 13968c2ecf20Sopenharmony_ci if (unlikely(!wrb_cnt)) 13978c2ecf20Sopenharmony_ci goto drop; 13988c2ecf20Sopenharmony_ci else 13998c2ecf20Sopenharmony_ci skb_get(skb); 14008c2ecf20Sopenharmony_ci } 14018c2ecf20Sopenharmony_ci 14028c2ecf20Sopenharmony_ci if (be_is_txq_full(txo)) { 14038c2ecf20Sopenharmony_ci netif_stop_subqueue(netdev, q_idx); 14048c2ecf20Sopenharmony_ci tx_stats(txo)->tx_stops++; 14058c2ecf20Sopenharmony_ci } 14068c2ecf20Sopenharmony_ci 14078c2ecf20Sopenharmony_ci if (flush || __netif_subqueue_stopped(netdev, q_idx)) 14088c2ecf20Sopenharmony_ci be_xmit_flush(adapter, txo); 14098c2ecf20Sopenharmony_ci 14108c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 14118c2ecf20Sopenharmony_cidrop: 14128c2ecf20Sopenharmony_ci tx_stats(txo)->tx_drv_drops++; 14138c2ecf20Sopenharmony_ci /* Flush the already enqueued tx requests */ 14148c2ecf20Sopenharmony_ci if (flush && txo->pend_wrb_cnt) 14158c2ecf20Sopenharmony_ci be_xmit_flush(adapter, txo); 14168c2ecf20Sopenharmony_ci 14178c2ecf20Sopenharmony_ci return NETDEV_TX_OK; 14188c2ecf20Sopenharmony_ci} 14198c2ecf20Sopenharmony_ci 14208c2ecf20Sopenharmony_cistatic void be_tx_timeout(struct net_device *netdev, unsigned int txqueue) 14218c2ecf20Sopenharmony_ci{ 14228c2ecf20Sopenharmony_ci struct be_adapter *adapter = netdev_priv(netdev); 14238c2ecf20Sopenharmony_ci struct device *dev = &adapter->pdev->dev; 14248c2ecf20Sopenharmony_ci struct be_tx_obj *txo; 14258c2ecf20Sopenharmony_ci struct sk_buff *skb; 14268c2ecf20Sopenharmony_ci struct tcphdr *tcphdr; 14278c2ecf20Sopenharmony_ci struct udphdr *udphdr; 14288c2ecf20Sopenharmony_ci u32 *entry; 14298c2ecf20Sopenharmony_ci int status; 14308c2ecf20Sopenharmony_ci int i, j; 14318c2ecf20Sopenharmony_ci 14328c2ecf20Sopenharmony_ci for_all_tx_queues(adapter, txo, i) { 14338c2ecf20Sopenharmony_ci dev_info(dev, "TXQ Dump: %d H: %d T: %d used: %d, qid: 0x%x\n", 14348c2ecf20Sopenharmony_ci i, txo->q.head, txo->q.tail, 14358c2ecf20Sopenharmony_ci atomic_read(&txo->q.used), txo->q.id); 14368c2ecf20Sopenharmony_ci 14378c2ecf20Sopenharmony_ci entry = txo->q.dma_mem.va; 14388c2ecf20Sopenharmony_ci for (j = 0; j < TX_Q_LEN * 4; j += 4) { 14398c2ecf20Sopenharmony_ci if (entry[j] != 0 || entry[j + 1] != 0 || 14408c2ecf20Sopenharmony_ci entry[j + 2] != 0 || entry[j + 3] != 0) { 14418c2ecf20Sopenharmony_ci dev_info(dev, "Entry %d 0x%x 0x%x 0x%x 0x%x\n", 14428c2ecf20Sopenharmony_ci j, entry[j], entry[j + 1], 14438c2ecf20Sopenharmony_ci entry[j + 2], entry[j + 3]); 14448c2ecf20Sopenharmony_ci } 14458c2ecf20Sopenharmony_ci } 14468c2ecf20Sopenharmony_ci 14478c2ecf20Sopenharmony_ci entry = txo->cq.dma_mem.va; 14488c2ecf20Sopenharmony_ci dev_info(dev, "TXCQ Dump: %d H: %d T: %d used: %d\n", 14498c2ecf20Sopenharmony_ci i, txo->cq.head, txo->cq.tail, 14508c2ecf20Sopenharmony_ci atomic_read(&txo->cq.used)); 14518c2ecf20Sopenharmony_ci for (j = 0; j < TX_CQ_LEN * 4; j += 4) { 14528c2ecf20Sopenharmony_ci if (entry[j] != 0 || entry[j + 1] != 0 || 14538c2ecf20Sopenharmony_ci entry[j + 2] != 0 || entry[j + 3] != 0) { 14548c2ecf20Sopenharmony_ci dev_info(dev, "Entry %d 0x%x 0x%x 0x%x 0x%x\n", 14558c2ecf20Sopenharmony_ci j, entry[j], entry[j + 1], 14568c2ecf20Sopenharmony_ci entry[j + 2], entry[j + 3]); 14578c2ecf20Sopenharmony_ci } 14588c2ecf20Sopenharmony_ci } 14598c2ecf20Sopenharmony_ci 14608c2ecf20Sopenharmony_ci for (j = 0; j < TX_Q_LEN; j++) { 14618c2ecf20Sopenharmony_ci if (txo->sent_skb_list[j]) { 14628c2ecf20Sopenharmony_ci skb = txo->sent_skb_list[j]; 14638c2ecf20Sopenharmony_ci if (ip_hdr(skb)->protocol == IPPROTO_TCP) { 14648c2ecf20Sopenharmony_ci tcphdr = tcp_hdr(skb); 14658c2ecf20Sopenharmony_ci dev_info(dev, "TCP source port %d\n", 14668c2ecf20Sopenharmony_ci ntohs(tcphdr->source)); 14678c2ecf20Sopenharmony_ci dev_info(dev, "TCP dest port %d\n", 14688c2ecf20Sopenharmony_ci ntohs(tcphdr->dest)); 14698c2ecf20Sopenharmony_ci dev_info(dev, "TCP sequence num %d\n", 14708c2ecf20Sopenharmony_ci ntohs(tcphdr->seq)); 14718c2ecf20Sopenharmony_ci dev_info(dev, "TCP ack_seq %d\n", 14728c2ecf20Sopenharmony_ci ntohs(tcphdr->ack_seq)); 14738c2ecf20Sopenharmony_ci } else if (ip_hdr(skb)->protocol == 14748c2ecf20Sopenharmony_ci IPPROTO_UDP) { 14758c2ecf20Sopenharmony_ci udphdr = udp_hdr(skb); 14768c2ecf20Sopenharmony_ci dev_info(dev, "UDP source port %d\n", 14778c2ecf20Sopenharmony_ci ntohs(udphdr->source)); 14788c2ecf20Sopenharmony_ci dev_info(dev, "UDP dest port %d\n", 14798c2ecf20Sopenharmony_ci ntohs(udphdr->dest)); 14808c2ecf20Sopenharmony_ci } 14818c2ecf20Sopenharmony_ci dev_info(dev, "skb[%d] %p len %d proto 0x%x\n", 14828c2ecf20Sopenharmony_ci j, skb, skb->len, skb->protocol); 14838c2ecf20Sopenharmony_ci } 14848c2ecf20Sopenharmony_ci } 14858c2ecf20Sopenharmony_ci } 14868c2ecf20Sopenharmony_ci 14878c2ecf20Sopenharmony_ci if (lancer_chip(adapter)) { 14888c2ecf20Sopenharmony_ci dev_info(dev, "Initiating reset due to tx timeout\n"); 14898c2ecf20Sopenharmony_ci dev_info(dev, "Resetting adapter\n"); 14908c2ecf20Sopenharmony_ci status = lancer_physdev_ctrl(adapter, 14918c2ecf20Sopenharmony_ci PHYSDEV_CONTROL_FW_RESET_MASK); 14928c2ecf20Sopenharmony_ci if (status) 14938c2ecf20Sopenharmony_ci dev_err(dev, "Reset failed .. Reboot server\n"); 14948c2ecf20Sopenharmony_ci } 14958c2ecf20Sopenharmony_ci} 14968c2ecf20Sopenharmony_ci 14978c2ecf20Sopenharmony_cistatic inline bool be_in_all_promisc(struct be_adapter *adapter) 14988c2ecf20Sopenharmony_ci{ 14998c2ecf20Sopenharmony_ci return (adapter->if_flags & BE_IF_FLAGS_ALL_PROMISCUOUS) == 15008c2ecf20Sopenharmony_ci BE_IF_FLAGS_ALL_PROMISCUOUS; 15018c2ecf20Sopenharmony_ci} 15028c2ecf20Sopenharmony_ci 15038c2ecf20Sopenharmony_cistatic int be_set_vlan_promisc(struct be_adapter *adapter) 15048c2ecf20Sopenharmony_ci{ 15058c2ecf20Sopenharmony_ci struct device *dev = &adapter->pdev->dev; 15068c2ecf20Sopenharmony_ci int status; 15078c2ecf20Sopenharmony_ci 15088c2ecf20Sopenharmony_ci if (adapter->if_flags & BE_IF_FLAGS_VLAN_PROMISCUOUS) 15098c2ecf20Sopenharmony_ci return 0; 15108c2ecf20Sopenharmony_ci 15118c2ecf20Sopenharmony_ci status = be_cmd_rx_filter(adapter, BE_IF_FLAGS_VLAN_PROMISCUOUS, ON); 15128c2ecf20Sopenharmony_ci if (!status) { 15138c2ecf20Sopenharmony_ci dev_info(dev, "Enabled VLAN promiscuous mode\n"); 15148c2ecf20Sopenharmony_ci adapter->if_flags |= BE_IF_FLAGS_VLAN_PROMISCUOUS; 15158c2ecf20Sopenharmony_ci } else { 15168c2ecf20Sopenharmony_ci dev_err(dev, "Failed to enable VLAN promiscuous mode\n"); 15178c2ecf20Sopenharmony_ci } 15188c2ecf20Sopenharmony_ci return status; 15198c2ecf20Sopenharmony_ci} 15208c2ecf20Sopenharmony_ci 15218c2ecf20Sopenharmony_cistatic int be_clear_vlan_promisc(struct be_adapter *adapter) 15228c2ecf20Sopenharmony_ci{ 15238c2ecf20Sopenharmony_ci struct device *dev = &adapter->pdev->dev; 15248c2ecf20Sopenharmony_ci int status; 15258c2ecf20Sopenharmony_ci 15268c2ecf20Sopenharmony_ci status = be_cmd_rx_filter(adapter, BE_IF_FLAGS_VLAN_PROMISCUOUS, OFF); 15278c2ecf20Sopenharmony_ci if (!status) { 15288c2ecf20Sopenharmony_ci dev_info(dev, "Disabling VLAN promiscuous mode\n"); 15298c2ecf20Sopenharmony_ci adapter->if_flags &= ~BE_IF_FLAGS_VLAN_PROMISCUOUS; 15308c2ecf20Sopenharmony_ci } 15318c2ecf20Sopenharmony_ci return status; 15328c2ecf20Sopenharmony_ci} 15338c2ecf20Sopenharmony_ci 15348c2ecf20Sopenharmony_ci/* 15358c2ecf20Sopenharmony_ci * A max of 64 (BE_NUM_VLANS_SUPPORTED) vlans can be configured in BE. 15368c2ecf20Sopenharmony_ci * If the user configures more, place BE in vlan promiscuous mode. 15378c2ecf20Sopenharmony_ci */ 15388c2ecf20Sopenharmony_cistatic int be_vid_config(struct be_adapter *adapter) 15398c2ecf20Sopenharmony_ci{ 15408c2ecf20Sopenharmony_ci struct device *dev = &adapter->pdev->dev; 15418c2ecf20Sopenharmony_ci u16 vids[BE_NUM_VLANS_SUPPORTED]; 15428c2ecf20Sopenharmony_ci u16 num = 0, i = 0; 15438c2ecf20Sopenharmony_ci int status = 0; 15448c2ecf20Sopenharmony_ci 15458c2ecf20Sopenharmony_ci /* No need to change the VLAN state if the I/F is in promiscuous */ 15468c2ecf20Sopenharmony_ci if (adapter->netdev->flags & IFF_PROMISC) 15478c2ecf20Sopenharmony_ci return 0; 15488c2ecf20Sopenharmony_ci 15498c2ecf20Sopenharmony_ci if (adapter->vlans_added > be_max_vlans(adapter)) 15508c2ecf20Sopenharmony_ci return be_set_vlan_promisc(adapter); 15518c2ecf20Sopenharmony_ci 15528c2ecf20Sopenharmony_ci if (adapter->if_flags & BE_IF_FLAGS_VLAN_PROMISCUOUS) { 15538c2ecf20Sopenharmony_ci status = be_clear_vlan_promisc(adapter); 15548c2ecf20Sopenharmony_ci if (status) 15558c2ecf20Sopenharmony_ci return status; 15568c2ecf20Sopenharmony_ci } 15578c2ecf20Sopenharmony_ci /* Construct VLAN Table to give to HW */ 15588c2ecf20Sopenharmony_ci for_each_set_bit(i, adapter->vids, VLAN_N_VID) 15598c2ecf20Sopenharmony_ci vids[num++] = cpu_to_le16(i); 15608c2ecf20Sopenharmony_ci 15618c2ecf20Sopenharmony_ci status = be_cmd_vlan_config(adapter, adapter->if_handle, vids, num, 0); 15628c2ecf20Sopenharmony_ci if (status) { 15638c2ecf20Sopenharmony_ci dev_err(dev, "Setting HW VLAN filtering failed\n"); 15648c2ecf20Sopenharmony_ci /* Set to VLAN promisc mode as setting VLAN filter failed */ 15658c2ecf20Sopenharmony_ci if (addl_status(status) == MCC_ADDL_STATUS_INSUFFICIENT_VLANS || 15668c2ecf20Sopenharmony_ci addl_status(status) == 15678c2ecf20Sopenharmony_ci MCC_ADDL_STATUS_INSUFFICIENT_RESOURCES) 15688c2ecf20Sopenharmony_ci return be_set_vlan_promisc(adapter); 15698c2ecf20Sopenharmony_ci } 15708c2ecf20Sopenharmony_ci return status; 15718c2ecf20Sopenharmony_ci} 15728c2ecf20Sopenharmony_ci 15738c2ecf20Sopenharmony_cistatic int be_vlan_add_vid(struct net_device *netdev, __be16 proto, u16 vid) 15748c2ecf20Sopenharmony_ci{ 15758c2ecf20Sopenharmony_ci struct be_adapter *adapter = netdev_priv(netdev); 15768c2ecf20Sopenharmony_ci int status = 0; 15778c2ecf20Sopenharmony_ci 15788c2ecf20Sopenharmony_ci mutex_lock(&adapter->rx_filter_lock); 15798c2ecf20Sopenharmony_ci 15808c2ecf20Sopenharmony_ci /* Packets with VID 0 are always received by Lancer by default */ 15818c2ecf20Sopenharmony_ci if (lancer_chip(adapter) && vid == 0) 15828c2ecf20Sopenharmony_ci goto done; 15838c2ecf20Sopenharmony_ci 15848c2ecf20Sopenharmony_ci if (test_bit(vid, adapter->vids)) 15858c2ecf20Sopenharmony_ci goto done; 15868c2ecf20Sopenharmony_ci 15878c2ecf20Sopenharmony_ci set_bit(vid, adapter->vids); 15888c2ecf20Sopenharmony_ci adapter->vlans_added++; 15898c2ecf20Sopenharmony_ci 15908c2ecf20Sopenharmony_ci status = be_vid_config(adapter); 15918c2ecf20Sopenharmony_cidone: 15928c2ecf20Sopenharmony_ci mutex_unlock(&adapter->rx_filter_lock); 15938c2ecf20Sopenharmony_ci return status; 15948c2ecf20Sopenharmony_ci} 15958c2ecf20Sopenharmony_ci 15968c2ecf20Sopenharmony_cistatic int be_vlan_rem_vid(struct net_device *netdev, __be16 proto, u16 vid) 15978c2ecf20Sopenharmony_ci{ 15988c2ecf20Sopenharmony_ci struct be_adapter *adapter = netdev_priv(netdev); 15998c2ecf20Sopenharmony_ci int status = 0; 16008c2ecf20Sopenharmony_ci 16018c2ecf20Sopenharmony_ci mutex_lock(&adapter->rx_filter_lock); 16028c2ecf20Sopenharmony_ci 16038c2ecf20Sopenharmony_ci /* Packets with VID 0 are always received by Lancer by default */ 16048c2ecf20Sopenharmony_ci if (lancer_chip(adapter) && vid == 0) 16058c2ecf20Sopenharmony_ci goto done; 16068c2ecf20Sopenharmony_ci 16078c2ecf20Sopenharmony_ci if (!test_bit(vid, adapter->vids)) 16088c2ecf20Sopenharmony_ci goto done; 16098c2ecf20Sopenharmony_ci 16108c2ecf20Sopenharmony_ci clear_bit(vid, adapter->vids); 16118c2ecf20Sopenharmony_ci adapter->vlans_added--; 16128c2ecf20Sopenharmony_ci 16138c2ecf20Sopenharmony_ci status = be_vid_config(adapter); 16148c2ecf20Sopenharmony_cidone: 16158c2ecf20Sopenharmony_ci mutex_unlock(&adapter->rx_filter_lock); 16168c2ecf20Sopenharmony_ci return status; 16178c2ecf20Sopenharmony_ci} 16188c2ecf20Sopenharmony_ci 16198c2ecf20Sopenharmony_cistatic void be_set_all_promisc(struct be_adapter *adapter) 16208c2ecf20Sopenharmony_ci{ 16218c2ecf20Sopenharmony_ci be_cmd_rx_filter(adapter, BE_IF_FLAGS_ALL_PROMISCUOUS, ON); 16228c2ecf20Sopenharmony_ci adapter->if_flags |= BE_IF_FLAGS_ALL_PROMISCUOUS; 16238c2ecf20Sopenharmony_ci} 16248c2ecf20Sopenharmony_ci 16258c2ecf20Sopenharmony_cistatic void be_set_mc_promisc(struct be_adapter *adapter) 16268c2ecf20Sopenharmony_ci{ 16278c2ecf20Sopenharmony_ci int status; 16288c2ecf20Sopenharmony_ci 16298c2ecf20Sopenharmony_ci if (adapter->if_flags & BE_IF_FLAGS_MCAST_PROMISCUOUS) 16308c2ecf20Sopenharmony_ci return; 16318c2ecf20Sopenharmony_ci 16328c2ecf20Sopenharmony_ci status = be_cmd_rx_filter(adapter, BE_IF_FLAGS_MCAST_PROMISCUOUS, ON); 16338c2ecf20Sopenharmony_ci if (!status) 16348c2ecf20Sopenharmony_ci adapter->if_flags |= BE_IF_FLAGS_MCAST_PROMISCUOUS; 16358c2ecf20Sopenharmony_ci} 16368c2ecf20Sopenharmony_ci 16378c2ecf20Sopenharmony_cistatic void be_set_uc_promisc(struct be_adapter *adapter) 16388c2ecf20Sopenharmony_ci{ 16398c2ecf20Sopenharmony_ci int status; 16408c2ecf20Sopenharmony_ci 16418c2ecf20Sopenharmony_ci if (adapter->if_flags & BE_IF_FLAGS_PROMISCUOUS) 16428c2ecf20Sopenharmony_ci return; 16438c2ecf20Sopenharmony_ci 16448c2ecf20Sopenharmony_ci status = be_cmd_rx_filter(adapter, BE_IF_FLAGS_PROMISCUOUS, ON); 16458c2ecf20Sopenharmony_ci if (!status) 16468c2ecf20Sopenharmony_ci adapter->if_flags |= BE_IF_FLAGS_PROMISCUOUS; 16478c2ecf20Sopenharmony_ci} 16488c2ecf20Sopenharmony_ci 16498c2ecf20Sopenharmony_cistatic void be_clear_uc_promisc(struct be_adapter *adapter) 16508c2ecf20Sopenharmony_ci{ 16518c2ecf20Sopenharmony_ci int status; 16528c2ecf20Sopenharmony_ci 16538c2ecf20Sopenharmony_ci if (!(adapter->if_flags & BE_IF_FLAGS_PROMISCUOUS)) 16548c2ecf20Sopenharmony_ci return; 16558c2ecf20Sopenharmony_ci 16568c2ecf20Sopenharmony_ci status = be_cmd_rx_filter(adapter, BE_IF_FLAGS_PROMISCUOUS, OFF); 16578c2ecf20Sopenharmony_ci if (!status) 16588c2ecf20Sopenharmony_ci adapter->if_flags &= ~BE_IF_FLAGS_PROMISCUOUS; 16598c2ecf20Sopenharmony_ci} 16608c2ecf20Sopenharmony_ci 16618c2ecf20Sopenharmony_ci/* The below 2 functions are the callback args for __dev_mc_sync/dev_uc_sync(). 16628c2ecf20Sopenharmony_ci * We use a single callback function for both sync and unsync. We really don't 16638c2ecf20Sopenharmony_ci * add/remove addresses through this callback. But, we use it to detect changes 16648c2ecf20Sopenharmony_ci * to the uc/mc lists. The entire uc/mc list is programmed in be_set_rx_mode(). 16658c2ecf20Sopenharmony_ci */ 16668c2ecf20Sopenharmony_cistatic int be_uc_list_update(struct net_device *netdev, 16678c2ecf20Sopenharmony_ci const unsigned char *addr) 16688c2ecf20Sopenharmony_ci{ 16698c2ecf20Sopenharmony_ci struct be_adapter *adapter = netdev_priv(netdev); 16708c2ecf20Sopenharmony_ci 16718c2ecf20Sopenharmony_ci adapter->update_uc_list = true; 16728c2ecf20Sopenharmony_ci return 0; 16738c2ecf20Sopenharmony_ci} 16748c2ecf20Sopenharmony_ci 16758c2ecf20Sopenharmony_cistatic int be_mc_list_update(struct net_device *netdev, 16768c2ecf20Sopenharmony_ci const unsigned char *addr) 16778c2ecf20Sopenharmony_ci{ 16788c2ecf20Sopenharmony_ci struct be_adapter *adapter = netdev_priv(netdev); 16798c2ecf20Sopenharmony_ci 16808c2ecf20Sopenharmony_ci adapter->update_mc_list = true; 16818c2ecf20Sopenharmony_ci return 0; 16828c2ecf20Sopenharmony_ci} 16838c2ecf20Sopenharmony_ci 16848c2ecf20Sopenharmony_cistatic void be_set_mc_list(struct be_adapter *adapter) 16858c2ecf20Sopenharmony_ci{ 16868c2ecf20Sopenharmony_ci struct net_device *netdev = adapter->netdev; 16878c2ecf20Sopenharmony_ci struct netdev_hw_addr *ha; 16888c2ecf20Sopenharmony_ci bool mc_promisc = false; 16898c2ecf20Sopenharmony_ci int status; 16908c2ecf20Sopenharmony_ci 16918c2ecf20Sopenharmony_ci netif_addr_lock_bh(netdev); 16928c2ecf20Sopenharmony_ci __dev_mc_sync(netdev, be_mc_list_update, be_mc_list_update); 16938c2ecf20Sopenharmony_ci 16948c2ecf20Sopenharmony_ci if (netdev->flags & IFF_PROMISC) { 16958c2ecf20Sopenharmony_ci adapter->update_mc_list = false; 16968c2ecf20Sopenharmony_ci } else if (netdev->flags & IFF_ALLMULTI || 16978c2ecf20Sopenharmony_ci netdev_mc_count(netdev) > be_max_mc(adapter)) { 16988c2ecf20Sopenharmony_ci /* Enable multicast promisc if num configured exceeds 16998c2ecf20Sopenharmony_ci * what we support 17008c2ecf20Sopenharmony_ci */ 17018c2ecf20Sopenharmony_ci mc_promisc = true; 17028c2ecf20Sopenharmony_ci adapter->update_mc_list = false; 17038c2ecf20Sopenharmony_ci } else if (adapter->if_flags & BE_IF_FLAGS_MCAST_PROMISCUOUS) { 17048c2ecf20Sopenharmony_ci /* Update mc-list unconditionally if the iface was previously 17058c2ecf20Sopenharmony_ci * in mc-promisc mode and now is out of that mode. 17068c2ecf20Sopenharmony_ci */ 17078c2ecf20Sopenharmony_ci adapter->update_mc_list = true; 17088c2ecf20Sopenharmony_ci } 17098c2ecf20Sopenharmony_ci 17108c2ecf20Sopenharmony_ci if (adapter->update_mc_list) { 17118c2ecf20Sopenharmony_ci int i = 0; 17128c2ecf20Sopenharmony_ci 17138c2ecf20Sopenharmony_ci /* cache the mc-list in adapter */ 17148c2ecf20Sopenharmony_ci netdev_for_each_mc_addr(ha, netdev) { 17158c2ecf20Sopenharmony_ci ether_addr_copy(adapter->mc_list[i].mac, ha->addr); 17168c2ecf20Sopenharmony_ci i++; 17178c2ecf20Sopenharmony_ci } 17188c2ecf20Sopenharmony_ci adapter->mc_count = netdev_mc_count(netdev); 17198c2ecf20Sopenharmony_ci } 17208c2ecf20Sopenharmony_ci netif_addr_unlock_bh(netdev); 17218c2ecf20Sopenharmony_ci 17228c2ecf20Sopenharmony_ci if (mc_promisc) { 17238c2ecf20Sopenharmony_ci be_set_mc_promisc(adapter); 17248c2ecf20Sopenharmony_ci } else if (adapter->update_mc_list) { 17258c2ecf20Sopenharmony_ci status = be_cmd_rx_filter(adapter, BE_IF_FLAGS_MULTICAST, ON); 17268c2ecf20Sopenharmony_ci if (!status) 17278c2ecf20Sopenharmony_ci adapter->if_flags &= ~BE_IF_FLAGS_MCAST_PROMISCUOUS; 17288c2ecf20Sopenharmony_ci else 17298c2ecf20Sopenharmony_ci be_set_mc_promisc(adapter); 17308c2ecf20Sopenharmony_ci 17318c2ecf20Sopenharmony_ci adapter->update_mc_list = false; 17328c2ecf20Sopenharmony_ci } 17338c2ecf20Sopenharmony_ci} 17348c2ecf20Sopenharmony_ci 17358c2ecf20Sopenharmony_cistatic void be_clear_mc_list(struct be_adapter *adapter) 17368c2ecf20Sopenharmony_ci{ 17378c2ecf20Sopenharmony_ci struct net_device *netdev = adapter->netdev; 17388c2ecf20Sopenharmony_ci 17398c2ecf20Sopenharmony_ci __dev_mc_unsync(netdev, NULL); 17408c2ecf20Sopenharmony_ci be_cmd_rx_filter(adapter, BE_IF_FLAGS_MULTICAST, OFF); 17418c2ecf20Sopenharmony_ci adapter->mc_count = 0; 17428c2ecf20Sopenharmony_ci} 17438c2ecf20Sopenharmony_ci 17448c2ecf20Sopenharmony_cistatic int be_uc_mac_add(struct be_adapter *adapter, int uc_idx) 17458c2ecf20Sopenharmony_ci{ 17468c2ecf20Sopenharmony_ci if (ether_addr_equal(adapter->uc_list[uc_idx].mac, adapter->dev_mac)) { 17478c2ecf20Sopenharmony_ci adapter->pmac_id[uc_idx + 1] = adapter->pmac_id[0]; 17488c2ecf20Sopenharmony_ci return 0; 17498c2ecf20Sopenharmony_ci } 17508c2ecf20Sopenharmony_ci 17518c2ecf20Sopenharmony_ci return be_cmd_pmac_add(adapter, adapter->uc_list[uc_idx].mac, 17528c2ecf20Sopenharmony_ci adapter->if_handle, 17538c2ecf20Sopenharmony_ci &adapter->pmac_id[uc_idx + 1], 0); 17548c2ecf20Sopenharmony_ci} 17558c2ecf20Sopenharmony_ci 17568c2ecf20Sopenharmony_cistatic void be_uc_mac_del(struct be_adapter *adapter, int pmac_id) 17578c2ecf20Sopenharmony_ci{ 17588c2ecf20Sopenharmony_ci if (pmac_id == adapter->pmac_id[0]) 17598c2ecf20Sopenharmony_ci return; 17608c2ecf20Sopenharmony_ci 17618c2ecf20Sopenharmony_ci be_cmd_pmac_del(adapter, adapter->if_handle, pmac_id, 0); 17628c2ecf20Sopenharmony_ci} 17638c2ecf20Sopenharmony_ci 17648c2ecf20Sopenharmony_cistatic void be_set_uc_list(struct be_adapter *adapter) 17658c2ecf20Sopenharmony_ci{ 17668c2ecf20Sopenharmony_ci struct net_device *netdev = adapter->netdev; 17678c2ecf20Sopenharmony_ci struct netdev_hw_addr *ha; 17688c2ecf20Sopenharmony_ci bool uc_promisc = false; 17698c2ecf20Sopenharmony_ci int curr_uc_macs = 0, i; 17708c2ecf20Sopenharmony_ci 17718c2ecf20Sopenharmony_ci netif_addr_lock_bh(netdev); 17728c2ecf20Sopenharmony_ci __dev_uc_sync(netdev, be_uc_list_update, be_uc_list_update); 17738c2ecf20Sopenharmony_ci 17748c2ecf20Sopenharmony_ci if (netdev->flags & IFF_PROMISC) { 17758c2ecf20Sopenharmony_ci adapter->update_uc_list = false; 17768c2ecf20Sopenharmony_ci } else if (netdev_uc_count(netdev) > (be_max_uc(adapter) - 1)) { 17778c2ecf20Sopenharmony_ci uc_promisc = true; 17788c2ecf20Sopenharmony_ci adapter->update_uc_list = false; 17798c2ecf20Sopenharmony_ci } else if (adapter->if_flags & BE_IF_FLAGS_PROMISCUOUS) { 17808c2ecf20Sopenharmony_ci /* Update uc-list unconditionally if the iface was previously 17818c2ecf20Sopenharmony_ci * in uc-promisc mode and now is out of that mode. 17828c2ecf20Sopenharmony_ci */ 17838c2ecf20Sopenharmony_ci adapter->update_uc_list = true; 17848c2ecf20Sopenharmony_ci } 17858c2ecf20Sopenharmony_ci 17868c2ecf20Sopenharmony_ci if (adapter->update_uc_list) { 17878c2ecf20Sopenharmony_ci /* cache the uc-list in adapter array */ 17888c2ecf20Sopenharmony_ci i = 0; 17898c2ecf20Sopenharmony_ci netdev_for_each_uc_addr(ha, netdev) { 17908c2ecf20Sopenharmony_ci ether_addr_copy(adapter->uc_list[i].mac, ha->addr); 17918c2ecf20Sopenharmony_ci i++; 17928c2ecf20Sopenharmony_ci } 17938c2ecf20Sopenharmony_ci curr_uc_macs = netdev_uc_count(netdev); 17948c2ecf20Sopenharmony_ci } 17958c2ecf20Sopenharmony_ci netif_addr_unlock_bh(netdev); 17968c2ecf20Sopenharmony_ci 17978c2ecf20Sopenharmony_ci if (uc_promisc) { 17988c2ecf20Sopenharmony_ci be_set_uc_promisc(adapter); 17998c2ecf20Sopenharmony_ci } else if (adapter->update_uc_list) { 18008c2ecf20Sopenharmony_ci be_clear_uc_promisc(adapter); 18018c2ecf20Sopenharmony_ci 18028c2ecf20Sopenharmony_ci for (i = 0; i < adapter->uc_macs; i++) 18038c2ecf20Sopenharmony_ci be_uc_mac_del(adapter, adapter->pmac_id[i + 1]); 18048c2ecf20Sopenharmony_ci 18058c2ecf20Sopenharmony_ci for (i = 0; i < curr_uc_macs; i++) 18068c2ecf20Sopenharmony_ci be_uc_mac_add(adapter, i); 18078c2ecf20Sopenharmony_ci adapter->uc_macs = curr_uc_macs; 18088c2ecf20Sopenharmony_ci adapter->update_uc_list = false; 18098c2ecf20Sopenharmony_ci } 18108c2ecf20Sopenharmony_ci} 18118c2ecf20Sopenharmony_ci 18128c2ecf20Sopenharmony_cistatic void be_clear_uc_list(struct be_adapter *adapter) 18138c2ecf20Sopenharmony_ci{ 18148c2ecf20Sopenharmony_ci struct net_device *netdev = adapter->netdev; 18158c2ecf20Sopenharmony_ci int i; 18168c2ecf20Sopenharmony_ci 18178c2ecf20Sopenharmony_ci __dev_uc_unsync(netdev, NULL); 18188c2ecf20Sopenharmony_ci for (i = 0; i < adapter->uc_macs; i++) 18198c2ecf20Sopenharmony_ci be_uc_mac_del(adapter, adapter->pmac_id[i + 1]); 18208c2ecf20Sopenharmony_ci 18218c2ecf20Sopenharmony_ci adapter->uc_macs = 0; 18228c2ecf20Sopenharmony_ci} 18238c2ecf20Sopenharmony_ci 18248c2ecf20Sopenharmony_cistatic void __be_set_rx_mode(struct be_adapter *adapter) 18258c2ecf20Sopenharmony_ci{ 18268c2ecf20Sopenharmony_ci struct net_device *netdev = adapter->netdev; 18278c2ecf20Sopenharmony_ci 18288c2ecf20Sopenharmony_ci mutex_lock(&adapter->rx_filter_lock); 18298c2ecf20Sopenharmony_ci 18308c2ecf20Sopenharmony_ci if (netdev->flags & IFF_PROMISC) { 18318c2ecf20Sopenharmony_ci if (!be_in_all_promisc(adapter)) 18328c2ecf20Sopenharmony_ci be_set_all_promisc(adapter); 18338c2ecf20Sopenharmony_ci } else if (be_in_all_promisc(adapter)) { 18348c2ecf20Sopenharmony_ci /* We need to re-program the vlan-list or clear 18358c2ecf20Sopenharmony_ci * vlan-promisc mode (if needed) when the interface 18368c2ecf20Sopenharmony_ci * comes out of promisc mode. 18378c2ecf20Sopenharmony_ci */ 18388c2ecf20Sopenharmony_ci be_vid_config(adapter); 18398c2ecf20Sopenharmony_ci } 18408c2ecf20Sopenharmony_ci 18418c2ecf20Sopenharmony_ci be_set_uc_list(adapter); 18428c2ecf20Sopenharmony_ci be_set_mc_list(adapter); 18438c2ecf20Sopenharmony_ci 18448c2ecf20Sopenharmony_ci mutex_unlock(&adapter->rx_filter_lock); 18458c2ecf20Sopenharmony_ci} 18468c2ecf20Sopenharmony_ci 18478c2ecf20Sopenharmony_cistatic void be_work_set_rx_mode(struct work_struct *work) 18488c2ecf20Sopenharmony_ci{ 18498c2ecf20Sopenharmony_ci struct be_cmd_work *cmd_work = 18508c2ecf20Sopenharmony_ci container_of(work, struct be_cmd_work, work); 18518c2ecf20Sopenharmony_ci 18528c2ecf20Sopenharmony_ci __be_set_rx_mode(cmd_work->adapter); 18538c2ecf20Sopenharmony_ci kfree(cmd_work); 18548c2ecf20Sopenharmony_ci} 18558c2ecf20Sopenharmony_ci 18568c2ecf20Sopenharmony_cistatic int be_set_vf_mac(struct net_device *netdev, int vf, u8 *mac) 18578c2ecf20Sopenharmony_ci{ 18588c2ecf20Sopenharmony_ci struct be_adapter *adapter = netdev_priv(netdev); 18598c2ecf20Sopenharmony_ci struct be_vf_cfg *vf_cfg = &adapter->vf_cfg[vf]; 18608c2ecf20Sopenharmony_ci int status; 18618c2ecf20Sopenharmony_ci 18628c2ecf20Sopenharmony_ci if (!sriov_enabled(adapter)) 18638c2ecf20Sopenharmony_ci return -EPERM; 18648c2ecf20Sopenharmony_ci 18658c2ecf20Sopenharmony_ci if (!is_valid_ether_addr(mac) || vf >= adapter->num_vfs) 18668c2ecf20Sopenharmony_ci return -EINVAL; 18678c2ecf20Sopenharmony_ci 18688c2ecf20Sopenharmony_ci /* Proceed further only if user provided MAC is different 18698c2ecf20Sopenharmony_ci * from active MAC 18708c2ecf20Sopenharmony_ci */ 18718c2ecf20Sopenharmony_ci if (ether_addr_equal(mac, vf_cfg->mac_addr)) 18728c2ecf20Sopenharmony_ci return 0; 18738c2ecf20Sopenharmony_ci 18748c2ecf20Sopenharmony_ci if (BEx_chip(adapter)) { 18758c2ecf20Sopenharmony_ci be_cmd_pmac_del(adapter, vf_cfg->if_handle, vf_cfg->pmac_id, 18768c2ecf20Sopenharmony_ci vf + 1); 18778c2ecf20Sopenharmony_ci 18788c2ecf20Sopenharmony_ci status = be_cmd_pmac_add(adapter, mac, vf_cfg->if_handle, 18798c2ecf20Sopenharmony_ci &vf_cfg->pmac_id, vf + 1); 18808c2ecf20Sopenharmony_ci } else { 18818c2ecf20Sopenharmony_ci status = be_cmd_set_mac(adapter, mac, vf_cfg->if_handle, 18828c2ecf20Sopenharmony_ci vf + 1); 18838c2ecf20Sopenharmony_ci } 18848c2ecf20Sopenharmony_ci 18858c2ecf20Sopenharmony_ci if (status) { 18868c2ecf20Sopenharmony_ci dev_err(&adapter->pdev->dev, "MAC %pM set on VF %d Failed: %#x", 18878c2ecf20Sopenharmony_ci mac, vf, status); 18888c2ecf20Sopenharmony_ci return be_cmd_status(status); 18898c2ecf20Sopenharmony_ci } 18908c2ecf20Sopenharmony_ci 18918c2ecf20Sopenharmony_ci ether_addr_copy(vf_cfg->mac_addr, mac); 18928c2ecf20Sopenharmony_ci 18938c2ecf20Sopenharmony_ci return 0; 18948c2ecf20Sopenharmony_ci} 18958c2ecf20Sopenharmony_ci 18968c2ecf20Sopenharmony_cistatic int be_get_vf_config(struct net_device *netdev, int vf, 18978c2ecf20Sopenharmony_ci struct ifla_vf_info *vi) 18988c2ecf20Sopenharmony_ci{ 18998c2ecf20Sopenharmony_ci struct be_adapter *adapter = netdev_priv(netdev); 19008c2ecf20Sopenharmony_ci struct be_vf_cfg *vf_cfg = &adapter->vf_cfg[vf]; 19018c2ecf20Sopenharmony_ci 19028c2ecf20Sopenharmony_ci if (!sriov_enabled(adapter)) 19038c2ecf20Sopenharmony_ci return -EPERM; 19048c2ecf20Sopenharmony_ci 19058c2ecf20Sopenharmony_ci if (vf >= adapter->num_vfs) 19068c2ecf20Sopenharmony_ci return -EINVAL; 19078c2ecf20Sopenharmony_ci 19088c2ecf20Sopenharmony_ci vi->vf = vf; 19098c2ecf20Sopenharmony_ci vi->max_tx_rate = vf_cfg->tx_rate; 19108c2ecf20Sopenharmony_ci vi->min_tx_rate = 0; 19118c2ecf20Sopenharmony_ci vi->vlan = vf_cfg->vlan_tag & VLAN_VID_MASK; 19128c2ecf20Sopenharmony_ci vi->qos = vf_cfg->vlan_tag >> VLAN_PRIO_SHIFT; 19138c2ecf20Sopenharmony_ci memcpy(&vi->mac, vf_cfg->mac_addr, ETH_ALEN); 19148c2ecf20Sopenharmony_ci vi->linkstate = adapter->vf_cfg[vf].plink_tracking; 19158c2ecf20Sopenharmony_ci vi->spoofchk = adapter->vf_cfg[vf].spoofchk; 19168c2ecf20Sopenharmony_ci 19178c2ecf20Sopenharmony_ci return 0; 19188c2ecf20Sopenharmony_ci} 19198c2ecf20Sopenharmony_ci 19208c2ecf20Sopenharmony_cistatic int be_set_vf_tvt(struct be_adapter *adapter, int vf, u16 vlan) 19218c2ecf20Sopenharmony_ci{ 19228c2ecf20Sopenharmony_ci struct be_vf_cfg *vf_cfg = &adapter->vf_cfg[vf]; 19238c2ecf20Sopenharmony_ci u16 vids[BE_NUM_VLANS_SUPPORTED]; 19248c2ecf20Sopenharmony_ci int vf_if_id = vf_cfg->if_handle; 19258c2ecf20Sopenharmony_ci int status; 19268c2ecf20Sopenharmony_ci 19278c2ecf20Sopenharmony_ci /* Enable Transparent VLAN Tagging */ 19288c2ecf20Sopenharmony_ci status = be_cmd_set_hsw_config(adapter, vlan, vf + 1, vf_if_id, 0, 0); 19298c2ecf20Sopenharmony_ci if (status) 19308c2ecf20Sopenharmony_ci return status; 19318c2ecf20Sopenharmony_ci 19328c2ecf20Sopenharmony_ci /* Clear pre-programmed VLAN filters on VF if any, if TVT is enabled */ 19338c2ecf20Sopenharmony_ci vids[0] = 0; 19348c2ecf20Sopenharmony_ci status = be_cmd_vlan_config(adapter, vf_if_id, vids, 1, vf + 1); 19358c2ecf20Sopenharmony_ci if (!status) 19368c2ecf20Sopenharmony_ci dev_info(&adapter->pdev->dev, 19378c2ecf20Sopenharmony_ci "Cleared guest VLANs on VF%d", vf); 19388c2ecf20Sopenharmony_ci 19398c2ecf20Sopenharmony_ci /* After TVT is enabled, disallow VFs to program VLAN filters */ 19408c2ecf20Sopenharmony_ci if (vf_cfg->privileges & BE_PRIV_FILTMGMT) { 19418c2ecf20Sopenharmony_ci status = be_cmd_set_fn_privileges(adapter, vf_cfg->privileges & 19428c2ecf20Sopenharmony_ci ~BE_PRIV_FILTMGMT, vf + 1); 19438c2ecf20Sopenharmony_ci if (!status) 19448c2ecf20Sopenharmony_ci vf_cfg->privileges &= ~BE_PRIV_FILTMGMT; 19458c2ecf20Sopenharmony_ci } 19468c2ecf20Sopenharmony_ci return 0; 19478c2ecf20Sopenharmony_ci} 19488c2ecf20Sopenharmony_ci 19498c2ecf20Sopenharmony_cistatic int be_clear_vf_tvt(struct be_adapter *adapter, int vf) 19508c2ecf20Sopenharmony_ci{ 19518c2ecf20Sopenharmony_ci struct be_vf_cfg *vf_cfg = &adapter->vf_cfg[vf]; 19528c2ecf20Sopenharmony_ci struct device *dev = &adapter->pdev->dev; 19538c2ecf20Sopenharmony_ci int status; 19548c2ecf20Sopenharmony_ci 19558c2ecf20Sopenharmony_ci /* Reset Transparent VLAN Tagging. */ 19568c2ecf20Sopenharmony_ci status = be_cmd_set_hsw_config(adapter, BE_RESET_VLAN_TAG_ID, vf + 1, 19578c2ecf20Sopenharmony_ci vf_cfg->if_handle, 0, 0); 19588c2ecf20Sopenharmony_ci if (status) 19598c2ecf20Sopenharmony_ci return status; 19608c2ecf20Sopenharmony_ci 19618c2ecf20Sopenharmony_ci /* Allow VFs to program VLAN filtering */ 19628c2ecf20Sopenharmony_ci if (!(vf_cfg->privileges & BE_PRIV_FILTMGMT)) { 19638c2ecf20Sopenharmony_ci status = be_cmd_set_fn_privileges(adapter, vf_cfg->privileges | 19648c2ecf20Sopenharmony_ci BE_PRIV_FILTMGMT, vf + 1); 19658c2ecf20Sopenharmony_ci if (!status) { 19668c2ecf20Sopenharmony_ci vf_cfg->privileges |= BE_PRIV_FILTMGMT; 19678c2ecf20Sopenharmony_ci dev_info(dev, "VF%d: FILTMGMT priv enabled", vf); 19688c2ecf20Sopenharmony_ci } 19698c2ecf20Sopenharmony_ci } 19708c2ecf20Sopenharmony_ci 19718c2ecf20Sopenharmony_ci dev_info(dev, 19728c2ecf20Sopenharmony_ci "Disable/re-enable i/f in VM to clear Transparent VLAN tag"); 19738c2ecf20Sopenharmony_ci return 0; 19748c2ecf20Sopenharmony_ci} 19758c2ecf20Sopenharmony_ci 19768c2ecf20Sopenharmony_cistatic int be_set_vf_vlan(struct net_device *netdev, int vf, u16 vlan, u8 qos, 19778c2ecf20Sopenharmony_ci __be16 vlan_proto) 19788c2ecf20Sopenharmony_ci{ 19798c2ecf20Sopenharmony_ci struct be_adapter *adapter = netdev_priv(netdev); 19808c2ecf20Sopenharmony_ci struct be_vf_cfg *vf_cfg = &adapter->vf_cfg[vf]; 19818c2ecf20Sopenharmony_ci int status; 19828c2ecf20Sopenharmony_ci 19838c2ecf20Sopenharmony_ci if (!sriov_enabled(adapter)) 19848c2ecf20Sopenharmony_ci return -EPERM; 19858c2ecf20Sopenharmony_ci 19868c2ecf20Sopenharmony_ci if (vf >= adapter->num_vfs || vlan > 4095 || qos > 7) 19878c2ecf20Sopenharmony_ci return -EINVAL; 19888c2ecf20Sopenharmony_ci 19898c2ecf20Sopenharmony_ci if (vlan_proto != htons(ETH_P_8021Q)) 19908c2ecf20Sopenharmony_ci return -EPROTONOSUPPORT; 19918c2ecf20Sopenharmony_ci 19928c2ecf20Sopenharmony_ci if (vlan || qos) { 19938c2ecf20Sopenharmony_ci vlan |= qos << VLAN_PRIO_SHIFT; 19948c2ecf20Sopenharmony_ci status = be_set_vf_tvt(adapter, vf, vlan); 19958c2ecf20Sopenharmony_ci } else { 19968c2ecf20Sopenharmony_ci status = be_clear_vf_tvt(adapter, vf); 19978c2ecf20Sopenharmony_ci } 19988c2ecf20Sopenharmony_ci 19998c2ecf20Sopenharmony_ci if (status) { 20008c2ecf20Sopenharmony_ci dev_err(&adapter->pdev->dev, 20018c2ecf20Sopenharmony_ci "VLAN %d config on VF %d failed : %#x\n", vlan, vf, 20028c2ecf20Sopenharmony_ci status); 20038c2ecf20Sopenharmony_ci return be_cmd_status(status); 20048c2ecf20Sopenharmony_ci } 20058c2ecf20Sopenharmony_ci 20068c2ecf20Sopenharmony_ci vf_cfg->vlan_tag = vlan; 20078c2ecf20Sopenharmony_ci return 0; 20088c2ecf20Sopenharmony_ci} 20098c2ecf20Sopenharmony_ci 20108c2ecf20Sopenharmony_cistatic int be_set_vf_tx_rate(struct net_device *netdev, int vf, 20118c2ecf20Sopenharmony_ci int min_tx_rate, int max_tx_rate) 20128c2ecf20Sopenharmony_ci{ 20138c2ecf20Sopenharmony_ci struct be_adapter *adapter = netdev_priv(netdev); 20148c2ecf20Sopenharmony_ci struct device *dev = &adapter->pdev->dev; 20158c2ecf20Sopenharmony_ci int percent_rate, status = 0; 20168c2ecf20Sopenharmony_ci u16 link_speed = 0; 20178c2ecf20Sopenharmony_ci u8 link_status; 20188c2ecf20Sopenharmony_ci 20198c2ecf20Sopenharmony_ci if (!sriov_enabled(adapter)) 20208c2ecf20Sopenharmony_ci return -EPERM; 20218c2ecf20Sopenharmony_ci 20228c2ecf20Sopenharmony_ci if (vf >= adapter->num_vfs) 20238c2ecf20Sopenharmony_ci return -EINVAL; 20248c2ecf20Sopenharmony_ci 20258c2ecf20Sopenharmony_ci if (min_tx_rate) 20268c2ecf20Sopenharmony_ci return -EINVAL; 20278c2ecf20Sopenharmony_ci 20288c2ecf20Sopenharmony_ci if (!max_tx_rate) 20298c2ecf20Sopenharmony_ci goto config_qos; 20308c2ecf20Sopenharmony_ci 20318c2ecf20Sopenharmony_ci status = be_cmd_link_status_query(adapter, &link_speed, 20328c2ecf20Sopenharmony_ci &link_status, 0); 20338c2ecf20Sopenharmony_ci if (status) 20348c2ecf20Sopenharmony_ci goto err; 20358c2ecf20Sopenharmony_ci 20368c2ecf20Sopenharmony_ci if (!link_status) { 20378c2ecf20Sopenharmony_ci dev_err(dev, "TX-rate setting not allowed when link is down\n"); 20388c2ecf20Sopenharmony_ci status = -ENETDOWN; 20398c2ecf20Sopenharmony_ci goto err; 20408c2ecf20Sopenharmony_ci } 20418c2ecf20Sopenharmony_ci 20428c2ecf20Sopenharmony_ci if (max_tx_rate < 100 || max_tx_rate > link_speed) { 20438c2ecf20Sopenharmony_ci dev_err(dev, "TX-rate must be between 100 and %d Mbps\n", 20448c2ecf20Sopenharmony_ci link_speed); 20458c2ecf20Sopenharmony_ci status = -EINVAL; 20468c2ecf20Sopenharmony_ci goto err; 20478c2ecf20Sopenharmony_ci } 20488c2ecf20Sopenharmony_ci 20498c2ecf20Sopenharmony_ci /* On Skyhawk the QOS setting must be done only as a % value */ 20508c2ecf20Sopenharmony_ci percent_rate = link_speed / 100; 20518c2ecf20Sopenharmony_ci if (skyhawk_chip(adapter) && (max_tx_rate % percent_rate)) { 20528c2ecf20Sopenharmony_ci dev_err(dev, "TX-rate must be a multiple of %d Mbps\n", 20538c2ecf20Sopenharmony_ci percent_rate); 20548c2ecf20Sopenharmony_ci status = -EINVAL; 20558c2ecf20Sopenharmony_ci goto err; 20568c2ecf20Sopenharmony_ci } 20578c2ecf20Sopenharmony_ci 20588c2ecf20Sopenharmony_ciconfig_qos: 20598c2ecf20Sopenharmony_ci status = be_cmd_config_qos(adapter, max_tx_rate, link_speed, vf + 1); 20608c2ecf20Sopenharmony_ci if (status) 20618c2ecf20Sopenharmony_ci goto err; 20628c2ecf20Sopenharmony_ci 20638c2ecf20Sopenharmony_ci adapter->vf_cfg[vf].tx_rate = max_tx_rate; 20648c2ecf20Sopenharmony_ci return 0; 20658c2ecf20Sopenharmony_ci 20668c2ecf20Sopenharmony_cierr: 20678c2ecf20Sopenharmony_ci dev_err(dev, "TX-rate setting of %dMbps on VF%d failed\n", 20688c2ecf20Sopenharmony_ci max_tx_rate, vf); 20698c2ecf20Sopenharmony_ci return be_cmd_status(status); 20708c2ecf20Sopenharmony_ci} 20718c2ecf20Sopenharmony_ci 20728c2ecf20Sopenharmony_cistatic int be_set_vf_link_state(struct net_device *netdev, int vf, 20738c2ecf20Sopenharmony_ci int link_state) 20748c2ecf20Sopenharmony_ci{ 20758c2ecf20Sopenharmony_ci struct be_adapter *adapter = netdev_priv(netdev); 20768c2ecf20Sopenharmony_ci int status; 20778c2ecf20Sopenharmony_ci 20788c2ecf20Sopenharmony_ci if (!sriov_enabled(adapter)) 20798c2ecf20Sopenharmony_ci return -EPERM; 20808c2ecf20Sopenharmony_ci 20818c2ecf20Sopenharmony_ci if (vf >= adapter->num_vfs) 20828c2ecf20Sopenharmony_ci return -EINVAL; 20838c2ecf20Sopenharmony_ci 20848c2ecf20Sopenharmony_ci status = be_cmd_set_logical_link_config(adapter, link_state, vf+1); 20858c2ecf20Sopenharmony_ci if (status) { 20868c2ecf20Sopenharmony_ci dev_err(&adapter->pdev->dev, 20878c2ecf20Sopenharmony_ci "Link state change on VF %d failed: %#x\n", vf, status); 20888c2ecf20Sopenharmony_ci return be_cmd_status(status); 20898c2ecf20Sopenharmony_ci } 20908c2ecf20Sopenharmony_ci 20918c2ecf20Sopenharmony_ci adapter->vf_cfg[vf].plink_tracking = link_state; 20928c2ecf20Sopenharmony_ci 20938c2ecf20Sopenharmony_ci return 0; 20948c2ecf20Sopenharmony_ci} 20958c2ecf20Sopenharmony_ci 20968c2ecf20Sopenharmony_cistatic int be_set_vf_spoofchk(struct net_device *netdev, int vf, bool enable) 20978c2ecf20Sopenharmony_ci{ 20988c2ecf20Sopenharmony_ci struct be_adapter *adapter = netdev_priv(netdev); 20998c2ecf20Sopenharmony_ci struct be_vf_cfg *vf_cfg = &adapter->vf_cfg[vf]; 21008c2ecf20Sopenharmony_ci u8 spoofchk; 21018c2ecf20Sopenharmony_ci int status; 21028c2ecf20Sopenharmony_ci 21038c2ecf20Sopenharmony_ci if (!sriov_enabled(adapter)) 21048c2ecf20Sopenharmony_ci return -EPERM; 21058c2ecf20Sopenharmony_ci 21068c2ecf20Sopenharmony_ci if (vf >= adapter->num_vfs) 21078c2ecf20Sopenharmony_ci return -EINVAL; 21088c2ecf20Sopenharmony_ci 21098c2ecf20Sopenharmony_ci if (BEx_chip(adapter)) 21108c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 21118c2ecf20Sopenharmony_ci 21128c2ecf20Sopenharmony_ci if (enable == vf_cfg->spoofchk) 21138c2ecf20Sopenharmony_ci return 0; 21148c2ecf20Sopenharmony_ci 21158c2ecf20Sopenharmony_ci spoofchk = enable ? ENABLE_MAC_SPOOFCHK : DISABLE_MAC_SPOOFCHK; 21168c2ecf20Sopenharmony_ci 21178c2ecf20Sopenharmony_ci status = be_cmd_set_hsw_config(adapter, 0, vf + 1, vf_cfg->if_handle, 21188c2ecf20Sopenharmony_ci 0, spoofchk); 21198c2ecf20Sopenharmony_ci if (status) { 21208c2ecf20Sopenharmony_ci dev_err(&adapter->pdev->dev, 21218c2ecf20Sopenharmony_ci "Spoofchk change on VF %d failed: %#x\n", vf, status); 21228c2ecf20Sopenharmony_ci return be_cmd_status(status); 21238c2ecf20Sopenharmony_ci } 21248c2ecf20Sopenharmony_ci 21258c2ecf20Sopenharmony_ci vf_cfg->spoofchk = enable; 21268c2ecf20Sopenharmony_ci return 0; 21278c2ecf20Sopenharmony_ci} 21288c2ecf20Sopenharmony_ci 21298c2ecf20Sopenharmony_cistatic void be_aic_update(struct be_aic_obj *aic, u64 rx_pkts, u64 tx_pkts, 21308c2ecf20Sopenharmony_ci ulong now) 21318c2ecf20Sopenharmony_ci{ 21328c2ecf20Sopenharmony_ci aic->rx_pkts_prev = rx_pkts; 21338c2ecf20Sopenharmony_ci aic->tx_reqs_prev = tx_pkts; 21348c2ecf20Sopenharmony_ci aic->jiffies = now; 21358c2ecf20Sopenharmony_ci} 21368c2ecf20Sopenharmony_ci 21378c2ecf20Sopenharmony_cistatic int be_get_new_eqd(struct be_eq_obj *eqo) 21388c2ecf20Sopenharmony_ci{ 21398c2ecf20Sopenharmony_ci struct be_adapter *adapter = eqo->adapter; 21408c2ecf20Sopenharmony_ci int eqd, start; 21418c2ecf20Sopenharmony_ci struct be_aic_obj *aic; 21428c2ecf20Sopenharmony_ci struct be_rx_obj *rxo; 21438c2ecf20Sopenharmony_ci struct be_tx_obj *txo; 21448c2ecf20Sopenharmony_ci u64 rx_pkts = 0, tx_pkts = 0; 21458c2ecf20Sopenharmony_ci ulong now; 21468c2ecf20Sopenharmony_ci u32 pps, delta; 21478c2ecf20Sopenharmony_ci int i; 21488c2ecf20Sopenharmony_ci 21498c2ecf20Sopenharmony_ci aic = &adapter->aic_obj[eqo->idx]; 21508c2ecf20Sopenharmony_ci if (!adapter->aic_enabled) { 21518c2ecf20Sopenharmony_ci if (aic->jiffies) 21528c2ecf20Sopenharmony_ci aic->jiffies = 0; 21538c2ecf20Sopenharmony_ci eqd = aic->et_eqd; 21548c2ecf20Sopenharmony_ci return eqd; 21558c2ecf20Sopenharmony_ci } 21568c2ecf20Sopenharmony_ci 21578c2ecf20Sopenharmony_ci for_all_rx_queues_on_eq(adapter, eqo, rxo, i) { 21588c2ecf20Sopenharmony_ci do { 21598c2ecf20Sopenharmony_ci start = u64_stats_fetch_begin_irq(&rxo->stats.sync); 21608c2ecf20Sopenharmony_ci rx_pkts += rxo->stats.rx_pkts; 21618c2ecf20Sopenharmony_ci } while (u64_stats_fetch_retry_irq(&rxo->stats.sync, start)); 21628c2ecf20Sopenharmony_ci } 21638c2ecf20Sopenharmony_ci 21648c2ecf20Sopenharmony_ci for_all_tx_queues_on_eq(adapter, eqo, txo, i) { 21658c2ecf20Sopenharmony_ci do { 21668c2ecf20Sopenharmony_ci start = u64_stats_fetch_begin_irq(&txo->stats.sync); 21678c2ecf20Sopenharmony_ci tx_pkts += txo->stats.tx_reqs; 21688c2ecf20Sopenharmony_ci } while (u64_stats_fetch_retry_irq(&txo->stats.sync, start)); 21698c2ecf20Sopenharmony_ci } 21708c2ecf20Sopenharmony_ci 21718c2ecf20Sopenharmony_ci /* Skip, if wrapped around or first calculation */ 21728c2ecf20Sopenharmony_ci now = jiffies; 21738c2ecf20Sopenharmony_ci if (!aic->jiffies || time_before(now, aic->jiffies) || 21748c2ecf20Sopenharmony_ci rx_pkts < aic->rx_pkts_prev || 21758c2ecf20Sopenharmony_ci tx_pkts < aic->tx_reqs_prev) { 21768c2ecf20Sopenharmony_ci be_aic_update(aic, rx_pkts, tx_pkts, now); 21778c2ecf20Sopenharmony_ci return aic->prev_eqd; 21788c2ecf20Sopenharmony_ci } 21798c2ecf20Sopenharmony_ci 21808c2ecf20Sopenharmony_ci delta = jiffies_to_msecs(now - aic->jiffies); 21818c2ecf20Sopenharmony_ci if (delta == 0) 21828c2ecf20Sopenharmony_ci return aic->prev_eqd; 21838c2ecf20Sopenharmony_ci 21848c2ecf20Sopenharmony_ci pps = (((u32)(rx_pkts - aic->rx_pkts_prev) * 1000) / delta) + 21858c2ecf20Sopenharmony_ci (((u32)(tx_pkts - aic->tx_reqs_prev) * 1000) / delta); 21868c2ecf20Sopenharmony_ci eqd = (pps / 15000) << 2; 21878c2ecf20Sopenharmony_ci 21888c2ecf20Sopenharmony_ci if (eqd < 8) 21898c2ecf20Sopenharmony_ci eqd = 0; 21908c2ecf20Sopenharmony_ci eqd = min_t(u32, eqd, aic->max_eqd); 21918c2ecf20Sopenharmony_ci eqd = max_t(u32, eqd, aic->min_eqd); 21928c2ecf20Sopenharmony_ci 21938c2ecf20Sopenharmony_ci be_aic_update(aic, rx_pkts, tx_pkts, now); 21948c2ecf20Sopenharmony_ci 21958c2ecf20Sopenharmony_ci return eqd; 21968c2ecf20Sopenharmony_ci} 21978c2ecf20Sopenharmony_ci 21988c2ecf20Sopenharmony_ci/* For Skyhawk-R only */ 21998c2ecf20Sopenharmony_cistatic u32 be_get_eq_delay_mult_enc(struct be_eq_obj *eqo) 22008c2ecf20Sopenharmony_ci{ 22018c2ecf20Sopenharmony_ci struct be_adapter *adapter = eqo->adapter; 22028c2ecf20Sopenharmony_ci struct be_aic_obj *aic = &adapter->aic_obj[eqo->idx]; 22038c2ecf20Sopenharmony_ci ulong now = jiffies; 22048c2ecf20Sopenharmony_ci int eqd; 22058c2ecf20Sopenharmony_ci u32 mult_enc; 22068c2ecf20Sopenharmony_ci 22078c2ecf20Sopenharmony_ci if (!adapter->aic_enabled) 22088c2ecf20Sopenharmony_ci return 0; 22098c2ecf20Sopenharmony_ci 22108c2ecf20Sopenharmony_ci if (jiffies_to_msecs(now - aic->jiffies) < 1) 22118c2ecf20Sopenharmony_ci eqd = aic->prev_eqd; 22128c2ecf20Sopenharmony_ci else 22138c2ecf20Sopenharmony_ci eqd = be_get_new_eqd(eqo); 22148c2ecf20Sopenharmony_ci 22158c2ecf20Sopenharmony_ci if (eqd > 100) 22168c2ecf20Sopenharmony_ci mult_enc = R2I_DLY_ENC_1; 22178c2ecf20Sopenharmony_ci else if (eqd > 60) 22188c2ecf20Sopenharmony_ci mult_enc = R2I_DLY_ENC_2; 22198c2ecf20Sopenharmony_ci else if (eqd > 20) 22208c2ecf20Sopenharmony_ci mult_enc = R2I_DLY_ENC_3; 22218c2ecf20Sopenharmony_ci else 22228c2ecf20Sopenharmony_ci mult_enc = R2I_DLY_ENC_0; 22238c2ecf20Sopenharmony_ci 22248c2ecf20Sopenharmony_ci aic->prev_eqd = eqd; 22258c2ecf20Sopenharmony_ci 22268c2ecf20Sopenharmony_ci return mult_enc; 22278c2ecf20Sopenharmony_ci} 22288c2ecf20Sopenharmony_ci 22298c2ecf20Sopenharmony_civoid be_eqd_update(struct be_adapter *adapter, bool force_update) 22308c2ecf20Sopenharmony_ci{ 22318c2ecf20Sopenharmony_ci struct be_set_eqd set_eqd[MAX_EVT_QS]; 22328c2ecf20Sopenharmony_ci struct be_aic_obj *aic; 22338c2ecf20Sopenharmony_ci struct be_eq_obj *eqo; 22348c2ecf20Sopenharmony_ci int i, num = 0, eqd; 22358c2ecf20Sopenharmony_ci 22368c2ecf20Sopenharmony_ci for_all_evt_queues(adapter, eqo, i) { 22378c2ecf20Sopenharmony_ci aic = &adapter->aic_obj[eqo->idx]; 22388c2ecf20Sopenharmony_ci eqd = be_get_new_eqd(eqo); 22398c2ecf20Sopenharmony_ci if (force_update || eqd != aic->prev_eqd) { 22408c2ecf20Sopenharmony_ci set_eqd[num].delay_multiplier = (eqd * 65)/100; 22418c2ecf20Sopenharmony_ci set_eqd[num].eq_id = eqo->q.id; 22428c2ecf20Sopenharmony_ci aic->prev_eqd = eqd; 22438c2ecf20Sopenharmony_ci num++; 22448c2ecf20Sopenharmony_ci } 22458c2ecf20Sopenharmony_ci } 22468c2ecf20Sopenharmony_ci 22478c2ecf20Sopenharmony_ci if (num) 22488c2ecf20Sopenharmony_ci be_cmd_modify_eqd(adapter, set_eqd, num); 22498c2ecf20Sopenharmony_ci} 22508c2ecf20Sopenharmony_ci 22518c2ecf20Sopenharmony_cistatic void be_rx_stats_update(struct be_rx_obj *rxo, 22528c2ecf20Sopenharmony_ci struct be_rx_compl_info *rxcp) 22538c2ecf20Sopenharmony_ci{ 22548c2ecf20Sopenharmony_ci struct be_rx_stats *stats = rx_stats(rxo); 22558c2ecf20Sopenharmony_ci 22568c2ecf20Sopenharmony_ci u64_stats_update_begin(&stats->sync); 22578c2ecf20Sopenharmony_ci stats->rx_compl++; 22588c2ecf20Sopenharmony_ci stats->rx_bytes += rxcp->pkt_size; 22598c2ecf20Sopenharmony_ci stats->rx_pkts++; 22608c2ecf20Sopenharmony_ci if (rxcp->tunneled) 22618c2ecf20Sopenharmony_ci stats->rx_vxlan_offload_pkts++; 22628c2ecf20Sopenharmony_ci if (rxcp->pkt_type == BE_MULTICAST_PACKET) 22638c2ecf20Sopenharmony_ci stats->rx_mcast_pkts++; 22648c2ecf20Sopenharmony_ci if (rxcp->err) 22658c2ecf20Sopenharmony_ci stats->rx_compl_err++; 22668c2ecf20Sopenharmony_ci u64_stats_update_end(&stats->sync); 22678c2ecf20Sopenharmony_ci} 22688c2ecf20Sopenharmony_ci 22698c2ecf20Sopenharmony_cistatic inline bool csum_passed(struct be_rx_compl_info *rxcp) 22708c2ecf20Sopenharmony_ci{ 22718c2ecf20Sopenharmony_ci /* L4 checksum is not reliable for non TCP/UDP packets. 22728c2ecf20Sopenharmony_ci * Also ignore ipcksm for ipv6 pkts 22738c2ecf20Sopenharmony_ci */ 22748c2ecf20Sopenharmony_ci return (rxcp->tcpf || rxcp->udpf) && rxcp->l4_csum && 22758c2ecf20Sopenharmony_ci (rxcp->ip_csum || rxcp->ipv6) && !rxcp->err; 22768c2ecf20Sopenharmony_ci} 22778c2ecf20Sopenharmony_ci 22788c2ecf20Sopenharmony_cistatic struct be_rx_page_info *get_rx_page_info(struct be_rx_obj *rxo) 22798c2ecf20Sopenharmony_ci{ 22808c2ecf20Sopenharmony_ci struct be_adapter *adapter = rxo->adapter; 22818c2ecf20Sopenharmony_ci struct be_rx_page_info *rx_page_info; 22828c2ecf20Sopenharmony_ci struct be_queue_info *rxq = &rxo->q; 22838c2ecf20Sopenharmony_ci u32 frag_idx = rxq->tail; 22848c2ecf20Sopenharmony_ci 22858c2ecf20Sopenharmony_ci rx_page_info = &rxo->page_info_tbl[frag_idx]; 22868c2ecf20Sopenharmony_ci BUG_ON(!rx_page_info->page); 22878c2ecf20Sopenharmony_ci 22888c2ecf20Sopenharmony_ci if (rx_page_info->last_frag) { 22898c2ecf20Sopenharmony_ci dma_unmap_page(&adapter->pdev->dev, 22908c2ecf20Sopenharmony_ci dma_unmap_addr(rx_page_info, bus), 22918c2ecf20Sopenharmony_ci adapter->big_page_size, DMA_FROM_DEVICE); 22928c2ecf20Sopenharmony_ci rx_page_info->last_frag = false; 22938c2ecf20Sopenharmony_ci } else { 22948c2ecf20Sopenharmony_ci dma_sync_single_for_cpu(&adapter->pdev->dev, 22958c2ecf20Sopenharmony_ci dma_unmap_addr(rx_page_info, bus), 22968c2ecf20Sopenharmony_ci rx_frag_size, DMA_FROM_DEVICE); 22978c2ecf20Sopenharmony_ci } 22988c2ecf20Sopenharmony_ci 22998c2ecf20Sopenharmony_ci queue_tail_inc(rxq); 23008c2ecf20Sopenharmony_ci atomic_dec(&rxq->used); 23018c2ecf20Sopenharmony_ci return rx_page_info; 23028c2ecf20Sopenharmony_ci} 23038c2ecf20Sopenharmony_ci 23048c2ecf20Sopenharmony_ci/* Throwaway the data in the Rx completion */ 23058c2ecf20Sopenharmony_cistatic void be_rx_compl_discard(struct be_rx_obj *rxo, 23068c2ecf20Sopenharmony_ci struct be_rx_compl_info *rxcp) 23078c2ecf20Sopenharmony_ci{ 23088c2ecf20Sopenharmony_ci struct be_rx_page_info *page_info; 23098c2ecf20Sopenharmony_ci u16 i, num_rcvd = rxcp->num_rcvd; 23108c2ecf20Sopenharmony_ci 23118c2ecf20Sopenharmony_ci for (i = 0; i < num_rcvd; i++) { 23128c2ecf20Sopenharmony_ci page_info = get_rx_page_info(rxo); 23138c2ecf20Sopenharmony_ci put_page(page_info->page); 23148c2ecf20Sopenharmony_ci memset(page_info, 0, sizeof(*page_info)); 23158c2ecf20Sopenharmony_ci } 23168c2ecf20Sopenharmony_ci} 23178c2ecf20Sopenharmony_ci 23188c2ecf20Sopenharmony_ci/* 23198c2ecf20Sopenharmony_ci * skb_fill_rx_data forms a complete skb for an ether frame 23208c2ecf20Sopenharmony_ci * indicated by rxcp. 23218c2ecf20Sopenharmony_ci */ 23228c2ecf20Sopenharmony_cistatic void skb_fill_rx_data(struct be_rx_obj *rxo, struct sk_buff *skb, 23238c2ecf20Sopenharmony_ci struct be_rx_compl_info *rxcp) 23248c2ecf20Sopenharmony_ci{ 23258c2ecf20Sopenharmony_ci struct be_rx_page_info *page_info; 23268c2ecf20Sopenharmony_ci u16 i, j; 23278c2ecf20Sopenharmony_ci u16 hdr_len, curr_frag_len, remaining; 23288c2ecf20Sopenharmony_ci u8 *start; 23298c2ecf20Sopenharmony_ci 23308c2ecf20Sopenharmony_ci page_info = get_rx_page_info(rxo); 23318c2ecf20Sopenharmony_ci start = page_address(page_info->page) + page_info->page_offset; 23328c2ecf20Sopenharmony_ci prefetch(start); 23338c2ecf20Sopenharmony_ci 23348c2ecf20Sopenharmony_ci /* Copy data in the first descriptor of this completion */ 23358c2ecf20Sopenharmony_ci curr_frag_len = min(rxcp->pkt_size, rx_frag_size); 23368c2ecf20Sopenharmony_ci 23378c2ecf20Sopenharmony_ci skb->len = curr_frag_len; 23388c2ecf20Sopenharmony_ci if (curr_frag_len <= BE_HDR_LEN) { /* tiny packet */ 23398c2ecf20Sopenharmony_ci memcpy(skb->data, start, curr_frag_len); 23408c2ecf20Sopenharmony_ci /* Complete packet has now been moved to data */ 23418c2ecf20Sopenharmony_ci put_page(page_info->page); 23428c2ecf20Sopenharmony_ci skb->data_len = 0; 23438c2ecf20Sopenharmony_ci skb->tail += curr_frag_len; 23448c2ecf20Sopenharmony_ci } else { 23458c2ecf20Sopenharmony_ci hdr_len = ETH_HLEN; 23468c2ecf20Sopenharmony_ci memcpy(skb->data, start, hdr_len); 23478c2ecf20Sopenharmony_ci skb_shinfo(skb)->nr_frags = 1; 23488c2ecf20Sopenharmony_ci skb_frag_set_page(skb, 0, page_info->page); 23498c2ecf20Sopenharmony_ci skb_frag_off_set(&skb_shinfo(skb)->frags[0], 23508c2ecf20Sopenharmony_ci page_info->page_offset + hdr_len); 23518c2ecf20Sopenharmony_ci skb_frag_size_set(&skb_shinfo(skb)->frags[0], 23528c2ecf20Sopenharmony_ci curr_frag_len - hdr_len); 23538c2ecf20Sopenharmony_ci skb->data_len = curr_frag_len - hdr_len; 23548c2ecf20Sopenharmony_ci skb->truesize += rx_frag_size; 23558c2ecf20Sopenharmony_ci skb->tail += hdr_len; 23568c2ecf20Sopenharmony_ci } 23578c2ecf20Sopenharmony_ci page_info->page = NULL; 23588c2ecf20Sopenharmony_ci 23598c2ecf20Sopenharmony_ci if (rxcp->pkt_size <= rx_frag_size) { 23608c2ecf20Sopenharmony_ci BUG_ON(rxcp->num_rcvd != 1); 23618c2ecf20Sopenharmony_ci return; 23628c2ecf20Sopenharmony_ci } 23638c2ecf20Sopenharmony_ci 23648c2ecf20Sopenharmony_ci /* More frags present for this completion */ 23658c2ecf20Sopenharmony_ci remaining = rxcp->pkt_size - curr_frag_len; 23668c2ecf20Sopenharmony_ci for (i = 1, j = 0; i < rxcp->num_rcvd; i++) { 23678c2ecf20Sopenharmony_ci page_info = get_rx_page_info(rxo); 23688c2ecf20Sopenharmony_ci curr_frag_len = min(remaining, rx_frag_size); 23698c2ecf20Sopenharmony_ci 23708c2ecf20Sopenharmony_ci /* Coalesce all frags from the same physical page in one slot */ 23718c2ecf20Sopenharmony_ci if (page_info->page_offset == 0) { 23728c2ecf20Sopenharmony_ci /* Fresh page */ 23738c2ecf20Sopenharmony_ci j++; 23748c2ecf20Sopenharmony_ci skb_frag_set_page(skb, j, page_info->page); 23758c2ecf20Sopenharmony_ci skb_frag_off_set(&skb_shinfo(skb)->frags[j], 23768c2ecf20Sopenharmony_ci page_info->page_offset); 23778c2ecf20Sopenharmony_ci skb_frag_size_set(&skb_shinfo(skb)->frags[j], 0); 23788c2ecf20Sopenharmony_ci skb_shinfo(skb)->nr_frags++; 23798c2ecf20Sopenharmony_ci } else { 23808c2ecf20Sopenharmony_ci put_page(page_info->page); 23818c2ecf20Sopenharmony_ci } 23828c2ecf20Sopenharmony_ci 23838c2ecf20Sopenharmony_ci skb_frag_size_add(&skb_shinfo(skb)->frags[j], curr_frag_len); 23848c2ecf20Sopenharmony_ci skb->len += curr_frag_len; 23858c2ecf20Sopenharmony_ci skb->data_len += curr_frag_len; 23868c2ecf20Sopenharmony_ci skb->truesize += rx_frag_size; 23878c2ecf20Sopenharmony_ci remaining -= curr_frag_len; 23888c2ecf20Sopenharmony_ci page_info->page = NULL; 23898c2ecf20Sopenharmony_ci } 23908c2ecf20Sopenharmony_ci BUG_ON(j > MAX_SKB_FRAGS); 23918c2ecf20Sopenharmony_ci} 23928c2ecf20Sopenharmony_ci 23938c2ecf20Sopenharmony_ci/* Process the RX completion indicated by rxcp when GRO is disabled */ 23948c2ecf20Sopenharmony_cistatic void be_rx_compl_process(struct be_rx_obj *rxo, struct napi_struct *napi, 23958c2ecf20Sopenharmony_ci struct be_rx_compl_info *rxcp) 23968c2ecf20Sopenharmony_ci{ 23978c2ecf20Sopenharmony_ci struct be_adapter *adapter = rxo->adapter; 23988c2ecf20Sopenharmony_ci struct net_device *netdev = adapter->netdev; 23998c2ecf20Sopenharmony_ci struct sk_buff *skb; 24008c2ecf20Sopenharmony_ci 24018c2ecf20Sopenharmony_ci skb = netdev_alloc_skb_ip_align(netdev, BE_RX_SKB_ALLOC_SIZE); 24028c2ecf20Sopenharmony_ci if (unlikely(!skb)) { 24038c2ecf20Sopenharmony_ci rx_stats(rxo)->rx_drops_no_skbs++; 24048c2ecf20Sopenharmony_ci be_rx_compl_discard(rxo, rxcp); 24058c2ecf20Sopenharmony_ci return; 24068c2ecf20Sopenharmony_ci } 24078c2ecf20Sopenharmony_ci 24088c2ecf20Sopenharmony_ci skb_fill_rx_data(rxo, skb, rxcp); 24098c2ecf20Sopenharmony_ci 24108c2ecf20Sopenharmony_ci if (likely((netdev->features & NETIF_F_RXCSUM) && csum_passed(rxcp))) 24118c2ecf20Sopenharmony_ci skb->ip_summed = CHECKSUM_UNNECESSARY; 24128c2ecf20Sopenharmony_ci else 24138c2ecf20Sopenharmony_ci skb_checksum_none_assert(skb); 24148c2ecf20Sopenharmony_ci 24158c2ecf20Sopenharmony_ci skb->protocol = eth_type_trans(skb, netdev); 24168c2ecf20Sopenharmony_ci skb_record_rx_queue(skb, rxo - &adapter->rx_obj[0]); 24178c2ecf20Sopenharmony_ci if (netdev->features & NETIF_F_RXHASH) 24188c2ecf20Sopenharmony_ci skb_set_hash(skb, rxcp->rss_hash, PKT_HASH_TYPE_L3); 24198c2ecf20Sopenharmony_ci 24208c2ecf20Sopenharmony_ci skb->csum_level = rxcp->tunneled; 24218c2ecf20Sopenharmony_ci skb_mark_napi_id(skb, napi); 24228c2ecf20Sopenharmony_ci 24238c2ecf20Sopenharmony_ci if (rxcp->vlanf) 24248c2ecf20Sopenharmony_ci __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), rxcp->vlan_tag); 24258c2ecf20Sopenharmony_ci 24268c2ecf20Sopenharmony_ci netif_receive_skb(skb); 24278c2ecf20Sopenharmony_ci} 24288c2ecf20Sopenharmony_ci 24298c2ecf20Sopenharmony_ci/* Process the RX completion indicated by rxcp when GRO is enabled */ 24308c2ecf20Sopenharmony_cistatic void be_rx_compl_process_gro(struct be_rx_obj *rxo, 24318c2ecf20Sopenharmony_ci struct napi_struct *napi, 24328c2ecf20Sopenharmony_ci struct be_rx_compl_info *rxcp) 24338c2ecf20Sopenharmony_ci{ 24348c2ecf20Sopenharmony_ci struct be_adapter *adapter = rxo->adapter; 24358c2ecf20Sopenharmony_ci struct be_rx_page_info *page_info; 24368c2ecf20Sopenharmony_ci struct sk_buff *skb = NULL; 24378c2ecf20Sopenharmony_ci u16 remaining, curr_frag_len; 24388c2ecf20Sopenharmony_ci u16 i, j; 24398c2ecf20Sopenharmony_ci 24408c2ecf20Sopenharmony_ci skb = napi_get_frags(napi); 24418c2ecf20Sopenharmony_ci if (!skb) { 24428c2ecf20Sopenharmony_ci be_rx_compl_discard(rxo, rxcp); 24438c2ecf20Sopenharmony_ci return; 24448c2ecf20Sopenharmony_ci } 24458c2ecf20Sopenharmony_ci 24468c2ecf20Sopenharmony_ci remaining = rxcp->pkt_size; 24478c2ecf20Sopenharmony_ci for (i = 0, j = -1; i < rxcp->num_rcvd; i++) { 24488c2ecf20Sopenharmony_ci page_info = get_rx_page_info(rxo); 24498c2ecf20Sopenharmony_ci 24508c2ecf20Sopenharmony_ci curr_frag_len = min(remaining, rx_frag_size); 24518c2ecf20Sopenharmony_ci 24528c2ecf20Sopenharmony_ci /* Coalesce all frags from the same physical page in one slot */ 24538c2ecf20Sopenharmony_ci if (i == 0 || page_info->page_offset == 0) { 24548c2ecf20Sopenharmony_ci /* First frag or Fresh page */ 24558c2ecf20Sopenharmony_ci j++; 24568c2ecf20Sopenharmony_ci skb_frag_set_page(skb, j, page_info->page); 24578c2ecf20Sopenharmony_ci skb_frag_off_set(&skb_shinfo(skb)->frags[j], 24588c2ecf20Sopenharmony_ci page_info->page_offset); 24598c2ecf20Sopenharmony_ci skb_frag_size_set(&skb_shinfo(skb)->frags[j], 0); 24608c2ecf20Sopenharmony_ci } else { 24618c2ecf20Sopenharmony_ci put_page(page_info->page); 24628c2ecf20Sopenharmony_ci } 24638c2ecf20Sopenharmony_ci skb_frag_size_add(&skb_shinfo(skb)->frags[j], curr_frag_len); 24648c2ecf20Sopenharmony_ci skb->truesize += rx_frag_size; 24658c2ecf20Sopenharmony_ci remaining -= curr_frag_len; 24668c2ecf20Sopenharmony_ci memset(page_info, 0, sizeof(*page_info)); 24678c2ecf20Sopenharmony_ci } 24688c2ecf20Sopenharmony_ci BUG_ON(j > MAX_SKB_FRAGS); 24698c2ecf20Sopenharmony_ci 24708c2ecf20Sopenharmony_ci skb_shinfo(skb)->nr_frags = j + 1; 24718c2ecf20Sopenharmony_ci skb->len = rxcp->pkt_size; 24728c2ecf20Sopenharmony_ci skb->data_len = rxcp->pkt_size; 24738c2ecf20Sopenharmony_ci skb->ip_summed = CHECKSUM_UNNECESSARY; 24748c2ecf20Sopenharmony_ci skb_record_rx_queue(skb, rxo - &adapter->rx_obj[0]); 24758c2ecf20Sopenharmony_ci if (adapter->netdev->features & NETIF_F_RXHASH) 24768c2ecf20Sopenharmony_ci skb_set_hash(skb, rxcp->rss_hash, PKT_HASH_TYPE_L3); 24778c2ecf20Sopenharmony_ci 24788c2ecf20Sopenharmony_ci skb->csum_level = rxcp->tunneled; 24798c2ecf20Sopenharmony_ci 24808c2ecf20Sopenharmony_ci if (rxcp->vlanf) 24818c2ecf20Sopenharmony_ci __vlan_hwaccel_put_tag(skb, htons(ETH_P_8021Q), rxcp->vlan_tag); 24828c2ecf20Sopenharmony_ci 24838c2ecf20Sopenharmony_ci napi_gro_frags(napi); 24848c2ecf20Sopenharmony_ci} 24858c2ecf20Sopenharmony_ci 24868c2ecf20Sopenharmony_cistatic void be_parse_rx_compl_v1(struct be_eth_rx_compl *compl, 24878c2ecf20Sopenharmony_ci struct be_rx_compl_info *rxcp) 24888c2ecf20Sopenharmony_ci{ 24898c2ecf20Sopenharmony_ci rxcp->pkt_size = GET_RX_COMPL_V1_BITS(pktsize, compl); 24908c2ecf20Sopenharmony_ci rxcp->vlanf = GET_RX_COMPL_V1_BITS(vtp, compl); 24918c2ecf20Sopenharmony_ci rxcp->err = GET_RX_COMPL_V1_BITS(err, compl); 24928c2ecf20Sopenharmony_ci rxcp->tcpf = GET_RX_COMPL_V1_BITS(tcpf, compl); 24938c2ecf20Sopenharmony_ci rxcp->udpf = GET_RX_COMPL_V1_BITS(udpf, compl); 24948c2ecf20Sopenharmony_ci rxcp->ip_csum = GET_RX_COMPL_V1_BITS(ipcksm, compl); 24958c2ecf20Sopenharmony_ci rxcp->l4_csum = GET_RX_COMPL_V1_BITS(l4_cksm, compl); 24968c2ecf20Sopenharmony_ci rxcp->ipv6 = GET_RX_COMPL_V1_BITS(ip_version, compl); 24978c2ecf20Sopenharmony_ci rxcp->num_rcvd = GET_RX_COMPL_V1_BITS(numfrags, compl); 24988c2ecf20Sopenharmony_ci rxcp->pkt_type = GET_RX_COMPL_V1_BITS(cast_enc, compl); 24998c2ecf20Sopenharmony_ci rxcp->rss_hash = GET_RX_COMPL_V1_BITS(rsshash, compl); 25008c2ecf20Sopenharmony_ci if (rxcp->vlanf) { 25018c2ecf20Sopenharmony_ci rxcp->qnq = GET_RX_COMPL_V1_BITS(qnq, compl); 25028c2ecf20Sopenharmony_ci rxcp->vlan_tag = GET_RX_COMPL_V1_BITS(vlan_tag, compl); 25038c2ecf20Sopenharmony_ci } 25048c2ecf20Sopenharmony_ci rxcp->port = GET_RX_COMPL_V1_BITS(port, compl); 25058c2ecf20Sopenharmony_ci rxcp->tunneled = 25068c2ecf20Sopenharmony_ci GET_RX_COMPL_V1_BITS(tunneled, compl); 25078c2ecf20Sopenharmony_ci} 25088c2ecf20Sopenharmony_ci 25098c2ecf20Sopenharmony_cistatic void be_parse_rx_compl_v0(struct be_eth_rx_compl *compl, 25108c2ecf20Sopenharmony_ci struct be_rx_compl_info *rxcp) 25118c2ecf20Sopenharmony_ci{ 25128c2ecf20Sopenharmony_ci rxcp->pkt_size = GET_RX_COMPL_V0_BITS(pktsize, compl); 25138c2ecf20Sopenharmony_ci rxcp->vlanf = GET_RX_COMPL_V0_BITS(vtp, compl); 25148c2ecf20Sopenharmony_ci rxcp->err = GET_RX_COMPL_V0_BITS(err, compl); 25158c2ecf20Sopenharmony_ci rxcp->tcpf = GET_RX_COMPL_V0_BITS(tcpf, compl); 25168c2ecf20Sopenharmony_ci rxcp->udpf = GET_RX_COMPL_V0_BITS(udpf, compl); 25178c2ecf20Sopenharmony_ci rxcp->ip_csum = GET_RX_COMPL_V0_BITS(ipcksm, compl); 25188c2ecf20Sopenharmony_ci rxcp->l4_csum = GET_RX_COMPL_V0_BITS(l4_cksm, compl); 25198c2ecf20Sopenharmony_ci rxcp->ipv6 = GET_RX_COMPL_V0_BITS(ip_version, compl); 25208c2ecf20Sopenharmony_ci rxcp->num_rcvd = GET_RX_COMPL_V0_BITS(numfrags, compl); 25218c2ecf20Sopenharmony_ci rxcp->pkt_type = GET_RX_COMPL_V0_BITS(cast_enc, compl); 25228c2ecf20Sopenharmony_ci rxcp->rss_hash = GET_RX_COMPL_V0_BITS(rsshash, compl); 25238c2ecf20Sopenharmony_ci if (rxcp->vlanf) { 25248c2ecf20Sopenharmony_ci rxcp->qnq = GET_RX_COMPL_V0_BITS(qnq, compl); 25258c2ecf20Sopenharmony_ci rxcp->vlan_tag = GET_RX_COMPL_V0_BITS(vlan_tag, compl); 25268c2ecf20Sopenharmony_ci } 25278c2ecf20Sopenharmony_ci rxcp->port = GET_RX_COMPL_V0_BITS(port, compl); 25288c2ecf20Sopenharmony_ci rxcp->ip_frag = GET_RX_COMPL_V0_BITS(ip_frag, compl); 25298c2ecf20Sopenharmony_ci} 25308c2ecf20Sopenharmony_ci 25318c2ecf20Sopenharmony_cistatic struct be_rx_compl_info *be_rx_compl_get(struct be_rx_obj *rxo) 25328c2ecf20Sopenharmony_ci{ 25338c2ecf20Sopenharmony_ci struct be_eth_rx_compl *compl = queue_tail_node(&rxo->cq); 25348c2ecf20Sopenharmony_ci struct be_rx_compl_info *rxcp = &rxo->rxcp; 25358c2ecf20Sopenharmony_ci struct be_adapter *adapter = rxo->adapter; 25368c2ecf20Sopenharmony_ci 25378c2ecf20Sopenharmony_ci /* For checking the valid bit it is Ok to use either definition as the 25388c2ecf20Sopenharmony_ci * valid bit is at the same position in both v0 and v1 Rx compl */ 25398c2ecf20Sopenharmony_ci if (compl->dw[offsetof(struct amap_eth_rx_compl_v1, valid) / 32] == 0) 25408c2ecf20Sopenharmony_ci return NULL; 25418c2ecf20Sopenharmony_ci 25428c2ecf20Sopenharmony_ci rmb(); 25438c2ecf20Sopenharmony_ci be_dws_le_to_cpu(compl, sizeof(*compl)); 25448c2ecf20Sopenharmony_ci 25458c2ecf20Sopenharmony_ci if (adapter->be3_native) 25468c2ecf20Sopenharmony_ci be_parse_rx_compl_v1(compl, rxcp); 25478c2ecf20Sopenharmony_ci else 25488c2ecf20Sopenharmony_ci be_parse_rx_compl_v0(compl, rxcp); 25498c2ecf20Sopenharmony_ci 25508c2ecf20Sopenharmony_ci if (rxcp->ip_frag) 25518c2ecf20Sopenharmony_ci rxcp->l4_csum = 0; 25528c2ecf20Sopenharmony_ci 25538c2ecf20Sopenharmony_ci if (rxcp->vlanf) { 25548c2ecf20Sopenharmony_ci /* In QNQ modes, if qnq bit is not set, then the packet was 25558c2ecf20Sopenharmony_ci * tagged only with the transparent outer vlan-tag and must 25568c2ecf20Sopenharmony_ci * not be treated as a vlan packet by host 25578c2ecf20Sopenharmony_ci */ 25588c2ecf20Sopenharmony_ci if (be_is_qnq_mode(adapter) && !rxcp->qnq) 25598c2ecf20Sopenharmony_ci rxcp->vlanf = 0; 25608c2ecf20Sopenharmony_ci 25618c2ecf20Sopenharmony_ci if (!lancer_chip(adapter)) 25628c2ecf20Sopenharmony_ci rxcp->vlan_tag = swab16(rxcp->vlan_tag); 25638c2ecf20Sopenharmony_ci 25648c2ecf20Sopenharmony_ci if (adapter->pvid == (rxcp->vlan_tag & VLAN_VID_MASK) && 25658c2ecf20Sopenharmony_ci !test_bit(rxcp->vlan_tag, adapter->vids)) 25668c2ecf20Sopenharmony_ci rxcp->vlanf = 0; 25678c2ecf20Sopenharmony_ci } 25688c2ecf20Sopenharmony_ci 25698c2ecf20Sopenharmony_ci /* As the compl has been parsed, reset it; we wont touch it again */ 25708c2ecf20Sopenharmony_ci compl->dw[offsetof(struct amap_eth_rx_compl_v1, valid) / 32] = 0; 25718c2ecf20Sopenharmony_ci 25728c2ecf20Sopenharmony_ci queue_tail_inc(&rxo->cq); 25738c2ecf20Sopenharmony_ci return rxcp; 25748c2ecf20Sopenharmony_ci} 25758c2ecf20Sopenharmony_ci 25768c2ecf20Sopenharmony_cistatic inline struct page *be_alloc_pages(u32 size, gfp_t gfp) 25778c2ecf20Sopenharmony_ci{ 25788c2ecf20Sopenharmony_ci u32 order = get_order(size); 25798c2ecf20Sopenharmony_ci 25808c2ecf20Sopenharmony_ci if (order > 0) 25818c2ecf20Sopenharmony_ci gfp |= __GFP_COMP; 25828c2ecf20Sopenharmony_ci return alloc_pages(gfp, order); 25838c2ecf20Sopenharmony_ci} 25848c2ecf20Sopenharmony_ci 25858c2ecf20Sopenharmony_ci/* 25868c2ecf20Sopenharmony_ci * Allocate a page, split it to fragments of size rx_frag_size and post as 25878c2ecf20Sopenharmony_ci * receive buffers to BE 25888c2ecf20Sopenharmony_ci */ 25898c2ecf20Sopenharmony_cistatic void be_post_rx_frags(struct be_rx_obj *rxo, gfp_t gfp, u32 frags_needed) 25908c2ecf20Sopenharmony_ci{ 25918c2ecf20Sopenharmony_ci struct be_adapter *adapter = rxo->adapter; 25928c2ecf20Sopenharmony_ci struct be_rx_page_info *page_info = NULL, *prev_page_info = NULL; 25938c2ecf20Sopenharmony_ci struct be_queue_info *rxq = &rxo->q; 25948c2ecf20Sopenharmony_ci struct page *pagep = NULL; 25958c2ecf20Sopenharmony_ci struct device *dev = &adapter->pdev->dev; 25968c2ecf20Sopenharmony_ci struct be_eth_rx_d *rxd; 25978c2ecf20Sopenharmony_ci u64 page_dmaaddr = 0, frag_dmaaddr; 25988c2ecf20Sopenharmony_ci u32 posted, page_offset = 0, notify = 0; 25998c2ecf20Sopenharmony_ci 26008c2ecf20Sopenharmony_ci page_info = &rxo->page_info_tbl[rxq->head]; 26018c2ecf20Sopenharmony_ci for (posted = 0; posted < frags_needed && !page_info->page; posted++) { 26028c2ecf20Sopenharmony_ci if (!pagep) { 26038c2ecf20Sopenharmony_ci pagep = be_alloc_pages(adapter->big_page_size, gfp); 26048c2ecf20Sopenharmony_ci if (unlikely(!pagep)) { 26058c2ecf20Sopenharmony_ci rx_stats(rxo)->rx_post_fail++; 26068c2ecf20Sopenharmony_ci break; 26078c2ecf20Sopenharmony_ci } 26088c2ecf20Sopenharmony_ci page_dmaaddr = dma_map_page(dev, pagep, 0, 26098c2ecf20Sopenharmony_ci adapter->big_page_size, 26108c2ecf20Sopenharmony_ci DMA_FROM_DEVICE); 26118c2ecf20Sopenharmony_ci if (dma_mapping_error(dev, page_dmaaddr)) { 26128c2ecf20Sopenharmony_ci put_page(pagep); 26138c2ecf20Sopenharmony_ci pagep = NULL; 26148c2ecf20Sopenharmony_ci adapter->drv_stats.dma_map_errors++; 26158c2ecf20Sopenharmony_ci break; 26168c2ecf20Sopenharmony_ci } 26178c2ecf20Sopenharmony_ci page_offset = 0; 26188c2ecf20Sopenharmony_ci } else { 26198c2ecf20Sopenharmony_ci get_page(pagep); 26208c2ecf20Sopenharmony_ci page_offset += rx_frag_size; 26218c2ecf20Sopenharmony_ci } 26228c2ecf20Sopenharmony_ci page_info->page_offset = page_offset; 26238c2ecf20Sopenharmony_ci page_info->page = pagep; 26248c2ecf20Sopenharmony_ci 26258c2ecf20Sopenharmony_ci rxd = queue_head_node(rxq); 26268c2ecf20Sopenharmony_ci frag_dmaaddr = page_dmaaddr + page_info->page_offset; 26278c2ecf20Sopenharmony_ci rxd->fragpa_lo = cpu_to_le32(frag_dmaaddr & 0xFFFFFFFF); 26288c2ecf20Sopenharmony_ci rxd->fragpa_hi = cpu_to_le32(upper_32_bits(frag_dmaaddr)); 26298c2ecf20Sopenharmony_ci 26308c2ecf20Sopenharmony_ci /* Any space left in the current big page for another frag? */ 26318c2ecf20Sopenharmony_ci if ((page_offset + rx_frag_size + rx_frag_size) > 26328c2ecf20Sopenharmony_ci adapter->big_page_size) { 26338c2ecf20Sopenharmony_ci pagep = NULL; 26348c2ecf20Sopenharmony_ci page_info->last_frag = true; 26358c2ecf20Sopenharmony_ci dma_unmap_addr_set(page_info, bus, page_dmaaddr); 26368c2ecf20Sopenharmony_ci } else { 26378c2ecf20Sopenharmony_ci dma_unmap_addr_set(page_info, bus, frag_dmaaddr); 26388c2ecf20Sopenharmony_ci } 26398c2ecf20Sopenharmony_ci 26408c2ecf20Sopenharmony_ci prev_page_info = page_info; 26418c2ecf20Sopenharmony_ci queue_head_inc(rxq); 26428c2ecf20Sopenharmony_ci page_info = &rxo->page_info_tbl[rxq->head]; 26438c2ecf20Sopenharmony_ci } 26448c2ecf20Sopenharmony_ci 26458c2ecf20Sopenharmony_ci /* Mark the last frag of a page when we break out of the above loop 26468c2ecf20Sopenharmony_ci * with no more slots available in the RXQ 26478c2ecf20Sopenharmony_ci */ 26488c2ecf20Sopenharmony_ci if (pagep) { 26498c2ecf20Sopenharmony_ci prev_page_info->last_frag = true; 26508c2ecf20Sopenharmony_ci dma_unmap_addr_set(prev_page_info, bus, page_dmaaddr); 26518c2ecf20Sopenharmony_ci } 26528c2ecf20Sopenharmony_ci 26538c2ecf20Sopenharmony_ci if (posted) { 26548c2ecf20Sopenharmony_ci atomic_add(posted, &rxq->used); 26558c2ecf20Sopenharmony_ci if (rxo->rx_post_starved) 26568c2ecf20Sopenharmony_ci rxo->rx_post_starved = false; 26578c2ecf20Sopenharmony_ci do { 26588c2ecf20Sopenharmony_ci notify = min(MAX_NUM_POST_ERX_DB, posted); 26598c2ecf20Sopenharmony_ci be_rxq_notify(adapter, rxq->id, notify); 26608c2ecf20Sopenharmony_ci posted -= notify; 26618c2ecf20Sopenharmony_ci } while (posted); 26628c2ecf20Sopenharmony_ci } else if (atomic_read(&rxq->used) == 0) { 26638c2ecf20Sopenharmony_ci /* Let be_worker replenish when memory is available */ 26648c2ecf20Sopenharmony_ci rxo->rx_post_starved = true; 26658c2ecf20Sopenharmony_ci } 26668c2ecf20Sopenharmony_ci} 26678c2ecf20Sopenharmony_ci 26688c2ecf20Sopenharmony_cistatic inline void be_update_tx_err(struct be_tx_obj *txo, u8 status) 26698c2ecf20Sopenharmony_ci{ 26708c2ecf20Sopenharmony_ci switch (status) { 26718c2ecf20Sopenharmony_ci case BE_TX_COMP_HDR_PARSE_ERR: 26728c2ecf20Sopenharmony_ci tx_stats(txo)->tx_hdr_parse_err++; 26738c2ecf20Sopenharmony_ci break; 26748c2ecf20Sopenharmony_ci case BE_TX_COMP_NDMA_ERR: 26758c2ecf20Sopenharmony_ci tx_stats(txo)->tx_dma_err++; 26768c2ecf20Sopenharmony_ci break; 26778c2ecf20Sopenharmony_ci case BE_TX_COMP_ACL_ERR: 26788c2ecf20Sopenharmony_ci tx_stats(txo)->tx_spoof_check_err++; 26798c2ecf20Sopenharmony_ci break; 26808c2ecf20Sopenharmony_ci } 26818c2ecf20Sopenharmony_ci} 26828c2ecf20Sopenharmony_ci 26838c2ecf20Sopenharmony_cistatic inline void lancer_update_tx_err(struct be_tx_obj *txo, u8 status) 26848c2ecf20Sopenharmony_ci{ 26858c2ecf20Sopenharmony_ci switch (status) { 26868c2ecf20Sopenharmony_ci case LANCER_TX_COMP_LSO_ERR: 26878c2ecf20Sopenharmony_ci tx_stats(txo)->tx_tso_err++; 26888c2ecf20Sopenharmony_ci break; 26898c2ecf20Sopenharmony_ci case LANCER_TX_COMP_HSW_DROP_MAC_ERR: 26908c2ecf20Sopenharmony_ci case LANCER_TX_COMP_HSW_DROP_VLAN_ERR: 26918c2ecf20Sopenharmony_ci tx_stats(txo)->tx_spoof_check_err++; 26928c2ecf20Sopenharmony_ci break; 26938c2ecf20Sopenharmony_ci case LANCER_TX_COMP_QINQ_ERR: 26948c2ecf20Sopenharmony_ci tx_stats(txo)->tx_qinq_err++; 26958c2ecf20Sopenharmony_ci break; 26968c2ecf20Sopenharmony_ci case LANCER_TX_COMP_PARITY_ERR: 26978c2ecf20Sopenharmony_ci tx_stats(txo)->tx_internal_parity_err++; 26988c2ecf20Sopenharmony_ci break; 26998c2ecf20Sopenharmony_ci case LANCER_TX_COMP_DMA_ERR: 27008c2ecf20Sopenharmony_ci tx_stats(txo)->tx_dma_err++; 27018c2ecf20Sopenharmony_ci break; 27028c2ecf20Sopenharmony_ci case LANCER_TX_COMP_SGE_ERR: 27038c2ecf20Sopenharmony_ci tx_stats(txo)->tx_sge_err++; 27048c2ecf20Sopenharmony_ci break; 27058c2ecf20Sopenharmony_ci } 27068c2ecf20Sopenharmony_ci} 27078c2ecf20Sopenharmony_ci 27088c2ecf20Sopenharmony_cistatic struct be_tx_compl_info *be_tx_compl_get(struct be_adapter *adapter, 27098c2ecf20Sopenharmony_ci struct be_tx_obj *txo) 27108c2ecf20Sopenharmony_ci{ 27118c2ecf20Sopenharmony_ci struct be_queue_info *tx_cq = &txo->cq; 27128c2ecf20Sopenharmony_ci struct be_tx_compl_info *txcp = &txo->txcp; 27138c2ecf20Sopenharmony_ci struct be_eth_tx_compl *compl = queue_tail_node(tx_cq); 27148c2ecf20Sopenharmony_ci 27158c2ecf20Sopenharmony_ci if (compl->dw[offsetof(struct amap_eth_tx_compl, valid) / 32] == 0) 27168c2ecf20Sopenharmony_ci return NULL; 27178c2ecf20Sopenharmony_ci 27188c2ecf20Sopenharmony_ci /* Ensure load ordering of valid bit dword and other dwords below */ 27198c2ecf20Sopenharmony_ci rmb(); 27208c2ecf20Sopenharmony_ci be_dws_le_to_cpu(compl, sizeof(*compl)); 27218c2ecf20Sopenharmony_ci 27228c2ecf20Sopenharmony_ci txcp->status = GET_TX_COMPL_BITS(status, compl); 27238c2ecf20Sopenharmony_ci txcp->end_index = GET_TX_COMPL_BITS(wrb_index, compl); 27248c2ecf20Sopenharmony_ci 27258c2ecf20Sopenharmony_ci if (txcp->status) { 27268c2ecf20Sopenharmony_ci if (lancer_chip(adapter)) { 27278c2ecf20Sopenharmony_ci lancer_update_tx_err(txo, txcp->status); 27288c2ecf20Sopenharmony_ci /* Reset the adapter incase of TSO, 27298c2ecf20Sopenharmony_ci * SGE or Parity error 27308c2ecf20Sopenharmony_ci */ 27318c2ecf20Sopenharmony_ci if (txcp->status == LANCER_TX_COMP_LSO_ERR || 27328c2ecf20Sopenharmony_ci txcp->status == LANCER_TX_COMP_PARITY_ERR || 27338c2ecf20Sopenharmony_ci txcp->status == LANCER_TX_COMP_SGE_ERR) 27348c2ecf20Sopenharmony_ci be_set_error(adapter, BE_ERROR_TX); 27358c2ecf20Sopenharmony_ci } else { 27368c2ecf20Sopenharmony_ci be_update_tx_err(txo, txcp->status); 27378c2ecf20Sopenharmony_ci } 27388c2ecf20Sopenharmony_ci } 27398c2ecf20Sopenharmony_ci 27408c2ecf20Sopenharmony_ci if (be_check_error(adapter, BE_ERROR_TX)) 27418c2ecf20Sopenharmony_ci return NULL; 27428c2ecf20Sopenharmony_ci 27438c2ecf20Sopenharmony_ci compl->dw[offsetof(struct amap_eth_tx_compl, valid) / 32] = 0; 27448c2ecf20Sopenharmony_ci queue_tail_inc(tx_cq); 27458c2ecf20Sopenharmony_ci return txcp; 27468c2ecf20Sopenharmony_ci} 27478c2ecf20Sopenharmony_ci 27488c2ecf20Sopenharmony_cistatic u16 be_tx_compl_process(struct be_adapter *adapter, 27498c2ecf20Sopenharmony_ci struct be_tx_obj *txo, u16 last_index) 27508c2ecf20Sopenharmony_ci{ 27518c2ecf20Sopenharmony_ci struct sk_buff **sent_skbs = txo->sent_skb_list; 27528c2ecf20Sopenharmony_ci struct be_queue_info *txq = &txo->q; 27538c2ecf20Sopenharmony_ci struct sk_buff *skb = NULL; 27548c2ecf20Sopenharmony_ci bool unmap_skb_hdr = false; 27558c2ecf20Sopenharmony_ci struct be_eth_wrb *wrb; 27568c2ecf20Sopenharmony_ci u16 num_wrbs = 0; 27578c2ecf20Sopenharmony_ci u32 frag_index; 27588c2ecf20Sopenharmony_ci 27598c2ecf20Sopenharmony_ci do { 27608c2ecf20Sopenharmony_ci if (sent_skbs[txq->tail]) { 27618c2ecf20Sopenharmony_ci /* Free skb from prev req */ 27628c2ecf20Sopenharmony_ci if (skb) 27638c2ecf20Sopenharmony_ci dev_consume_skb_any(skb); 27648c2ecf20Sopenharmony_ci skb = sent_skbs[txq->tail]; 27658c2ecf20Sopenharmony_ci sent_skbs[txq->tail] = NULL; 27668c2ecf20Sopenharmony_ci queue_tail_inc(txq); /* skip hdr wrb */ 27678c2ecf20Sopenharmony_ci num_wrbs++; 27688c2ecf20Sopenharmony_ci unmap_skb_hdr = true; 27698c2ecf20Sopenharmony_ci } 27708c2ecf20Sopenharmony_ci wrb = queue_tail_node(txq); 27718c2ecf20Sopenharmony_ci frag_index = txq->tail; 27728c2ecf20Sopenharmony_ci unmap_tx_frag(&adapter->pdev->dev, wrb, 27738c2ecf20Sopenharmony_ci (unmap_skb_hdr && skb_headlen(skb))); 27748c2ecf20Sopenharmony_ci unmap_skb_hdr = false; 27758c2ecf20Sopenharmony_ci queue_tail_inc(txq); 27768c2ecf20Sopenharmony_ci num_wrbs++; 27778c2ecf20Sopenharmony_ci } while (frag_index != last_index); 27788c2ecf20Sopenharmony_ci dev_consume_skb_any(skb); 27798c2ecf20Sopenharmony_ci 27808c2ecf20Sopenharmony_ci return num_wrbs; 27818c2ecf20Sopenharmony_ci} 27828c2ecf20Sopenharmony_ci 27838c2ecf20Sopenharmony_ci/* Return the number of events in the event queue */ 27848c2ecf20Sopenharmony_cistatic inline int events_get(struct be_eq_obj *eqo) 27858c2ecf20Sopenharmony_ci{ 27868c2ecf20Sopenharmony_ci struct be_eq_entry *eqe; 27878c2ecf20Sopenharmony_ci int num = 0; 27888c2ecf20Sopenharmony_ci 27898c2ecf20Sopenharmony_ci do { 27908c2ecf20Sopenharmony_ci eqe = queue_tail_node(&eqo->q); 27918c2ecf20Sopenharmony_ci if (eqe->evt == 0) 27928c2ecf20Sopenharmony_ci break; 27938c2ecf20Sopenharmony_ci 27948c2ecf20Sopenharmony_ci rmb(); 27958c2ecf20Sopenharmony_ci eqe->evt = 0; 27968c2ecf20Sopenharmony_ci num++; 27978c2ecf20Sopenharmony_ci queue_tail_inc(&eqo->q); 27988c2ecf20Sopenharmony_ci } while (true); 27998c2ecf20Sopenharmony_ci 28008c2ecf20Sopenharmony_ci return num; 28018c2ecf20Sopenharmony_ci} 28028c2ecf20Sopenharmony_ci 28038c2ecf20Sopenharmony_ci/* Leaves the EQ is disarmed state */ 28048c2ecf20Sopenharmony_cistatic void be_eq_clean(struct be_eq_obj *eqo) 28058c2ecf20Sopenharmony_ci{ 28068c2ecf20Sopenharmony_ci int num = events_get(eqo); 28078c2ecf20Sopenharmony_ci 28088c2ecf20Sopenharmony_ci be_eq_notify(eqo->adapter, eqo->q.id, false, true, num, 0); 28098c2ecf20Sopenharmony_ci} 28108c2ecf20Sopenharmony_ci 28118c2ecf20Sopenharmony_ci/* Free posted rx buffers that were not used */ 28128c2ecf20Sopenharmony_cistatic void be_rxq_clean(struct be_rx_obj *rxo) 28138c2ecf20Sopenharmony_ci{ 28148c2ecf20Sopenharmony_ci struct be_queue_info *rxq = &rxo->q; 28158c2ecf20Sopenharmony_ci struct be_rx_page_info *page_info; 28168c2ecf20Sopenharmony_ci 28178c2ecf20Sopenharmony_ci while (atomic_read(&rxq->used) > 0) { 28188c2ecf20Sopenharmony_ci page_info = get_rx_page_info(rxo); 28198c2ecf20Sopenharmony_ci put_page(page_info->page); 28208c2ecf20Sopenharmony_ci memset(page_info, 0, sizeof(*page_info)); 28218c2ecf20Sopenharmony_ci } 28228c2ecf20Sopenharmony_ci BUG_ON(atomic_read(&rxq->used)); 28238c2ecf20Sopenharmony_ci rxq->tail = 0; 28248c2ecf20Sopenharmony_ci rxq->head = 0; 28258c2ecf20Sopenharmony_ci} 28268c2ecf20Sopenharmony_ci 28278c2ecf20Sopenharmony_cistatic void be_rx_cq_clean(struct be_rx_obj *rxo) 28288c2ecf20Sopenharmony_ci{ 28298c2ecf20Sopenharmony_ci struct be_queue_info *rx_cq = &rxo->cq; 28308c2ecf20Sopenharmony_ci struct be_rx_compl_info *rxcp; 28318c2ecf20Sopenharmony_ci struct be_adapter *adapter = rxo->adapter; 28328c2ecf20Sopenharmony_ci int flush_wait = 0; 28338c2ecf20Sopenharmony_ci 28348c2ecf20Sopenharmony_ci /* Consume pending rx completions. 28358c2ecf20Sopenharmony_ci * Wait for the flush completion (identified by zero num_rcvd) 28368c2ecf20Sopenharmony_ci * to arrive. Notify CQ even when there are no more CQ entries 28378c2ecf20Sopenharmony_ci * for HW to flush partially coalesced CQ entries. 28388c2ecf20Sopenharmony_ci * In Lancer, there is no need to wait for flush compl. 28398c2ecf20Sopenharmony_ci */ 28408c2ecf20Sopenharmony_ci for (;;) { 28418c2ecf20Sopenharmony_ci rxcp = be_rx_compl_get(rxo); 28428c2ecf20Sopenharmony_ci if (!rxcp) { 28438c2ecf20Sopenharmony_ci if (lancer_chip(adapter)) 28448c2ecf20Sopenharmony_ci break; 28458c2ecf20Sopenharmony_ci 28468c2ecf20Sopenharmony_ci if (flush_wait++ > 50 || 28478c2ecf20Sopenharmony_ci be_check_error(adapter, 28488c2ecf20Sopenharmony_ci BE_ERROR_HW)) { 28498c2ecf20Sopenharmony_ci dev_warn(&adapter->pdev->dev, 28508c2ecf20Sopenharmony_ci "did not receive flush compl\n"); 28518c2ecf20Sopenharmony_ci break; 28528c2ecf20Sopenharmony_ci } 28538c2ecf20Sopenharmony_ci be_cq_notify(adapter, rx_cq->id, true, 0); 28548c2ecf20Sopenharmony_ci mdelay(1); 28558c2ecf20Sopenharmony_ci } else { 28568c2ecf20Sopenharmony_ci be_rx_compl_discard(rxo, rxcp); 28578c2ecf20Sopenharmony_ci be_cq_notify(adapter, rx_cq->id, false, 1); 28588c2ecf20Sopenharmony_ci if (rxcp->num_rcvd == 0) 28598c2ecf20Sopenharmony_ci break; 28608c2ecf20Sopenharmony_ci } 28618c2ecf20Sopenharmony_ci } 28628c2ecf20Sopenharmony_ci 28638c2ecf20Sopenharmony_ci /* After cleanup, leave the CQ in unarmed state */ 28648c2ecf20Sopenharmony_ci be_cq_notify(adapter, rx_cq->id, false, 0); 28658c2ecf20Sopenharmony_ci} 28668c2ecf20Sopenharmony_ci 28678c2ecf20Sopenharmony_cistatic void be_tx_compl_clean(struct be_adapter *adapter) 28688c2ecf20Sopenharmony_ci{ 28698c2ecf20Sopenharmony_ci struct device *dev = &adapter->pdev->dev; 28708c2ecf20Sopenharmony_ci u16 cmpl = 0, timeo = 0, num_wrbs = 0; 28718c2ecf20Sopenharmony_ci struct be_tx_compl_info *txcp; 28728c2ecf20Sopenharmony_ci struct be_queue_info *txq; 28738c2ecf20Sopenharmony_ci u32 end_idx, notified_idx; 28748c2ecf20Sopenharmony_ci struct be_tx_obj *txo; 28758c2ecf20Sopenharmony_ci int i, pending_txqs; 28768c2ecf20Sopenharmony_ci 28778c2ecf20Sopenharmony_ci /* Stop polling for compls when HW has been silent for 10ms */ 28788c2ecf20Sopenharmony_ci do { 28798c2ecf20Sopenharmony_ci pending_txqs = adapter->num_tx_qs; 28808c2ecf20Sopenharmony_ci 28818c2ecf20Sopenharmony_ci for_all_tx_queues(adapter, txo, i) { 28828c2ecf20Sopenharmony_ci cmpl = 0; 28838c2ecf20Sopenharmony_ci num_wrbs = 0; 28848c2ecf20Sopenharmony_ci txq = &txo->q; 28858c2ecf20Sopenharmony_ci while ((txcp = be_tx_compl_get(adapter, txo))) { 28868c2ecf20Sopenharmony_ci num_wrbs += 28878c2ecf20Sopenharmony_ci be_tx_compl_process(adapter, txo, 28888c2ecf20Sopenharmony_ci txcp->end_index); 28898c2ecf20Sopenharmony_ci cmpl++; 28908c2ecf20Sopenharmony_ci } 28918c2ecf20Sopenharmony_ci if (cmpl) { 28928c2ecf20Sopenharmony_ci be_cq_notify(adapter, txo->cq.id, false, cmpl); 28938c2ecf20Sopenharmony_ci atomic_sub(num_wrbs, &txq->used); 28948c2ecf20Sopenharmony_ci timeo = 0; 28958c2ecf20Sopenharmony_ci } 28968c2ecf20Sopenharmony_ci if (!be_is_tx_compl_pending(txo)) 28978c2ecf20Sopenharmony_ci pending_txqs--; 28988c2ecf20Sopenharmony_ci } 28998c2ecf20Sopenharmony_ci 29008c2ecf20Sopenharmony_ci if (pending_txqs == 0 || ++timeo > 10 || 29018c2ecf20Sopenharmony_ci be_check_error(adapter, BE_ERROR_HW)) 29028c2ecf20Sopenharmony_ci break; 29038c2ecf20Sopenharmony_ci 29048c2ecf20Sopenharmony_ci mdelay(1); 29058c2ecf20Sopenharmony_ci } while (true); 29068c2ecf20Sopenharmony_ci 29078c2ecf20Sopenharmony_ci /* Free enqueued TX that was never notified to HW */ 29088c2ecf20Sopenharmony_ci for_all_tx_queues(adapter, txo, i) { 29098c2ecf20Sopenharmony_ci txq = &txo->q; 29108c2ecf20Sopenharmony_ci 29118c2ecf20Sopenharmony_ci if (atomic_read(&txq->used)) { 29128c2ecf20Sopenharmony_ci dev_info(dev, "txq%d: cleaning %d pending tx-wrbs\n", 29138c2ecf20Sopenharmony_ci i, atomic_read(&txq->used)); 29148c2ecf20Sopenharmony_ci notified_idx = txq->tail; 29158c2ecf20Sopenharmony_ci end_idx = txq->tail; 29168c2ecf20Sopenharmony_ci index_adv(&end_idx, atomic_read(&txq->used) - 1, 29178c2ecf20Sopenharmony_ci txq->len); 29188c2ecf20Sopenharmony_ci /* Use the tx-compl process logic to handle requests 29198c2ecf20Sopenharmony_ci * that were not sent to the HW. 29208c2ecf20Sopenharmony_ci */ 29218c2ecf20Sopenharmony_ci num_wrbs = be_tx_compl_process(adapter, txo, end_idx); 29228c2ecf20Sopenharmony_ci atomic_sub(num_wrbs, &txq->used); 29238c2ecf20Sopenharmony_ci BUG_ON(atomic_read(&txq->used)); 29248c2ecf20Sopenharmony_ci txo->pend_wrb_cnt = 0; 29258c2ecf20Sopenharmony_ci /* Since hw was never notified of these requests, 29268c2ecf20Sopenharmony_ci * reset TXQ indices 29278c2ecf20Sopenharmony_ci */ 29288c2ecf20Sopenharmony_ci txq->head = notified_idx; 29298c2ecf20Sopenharmony_ci txq->tail = notified_idx; 29308c2ecf20Sopenharmony_ci } 29318c2ecf20Sopenharmony_ci } 29328c2ecf20Sopenharmony_ci} 29338c2ecf20Sopenharmony_ci 29348c2ecf20Sopenharmony_cistatic void be_evt_queues_destroy(struct be_adapter *adapter) 29358c2ecf20Sopenharmony_ci{ 29368c2ecf20Sopenharmony_ci struct be_eq_obj *eqo; 29378c2ecf20Sopenharmony_ci int i; 29388c2ecf20Sopenharmony_ci 29398c2ecf20Sopenharmony_ci for_all_evt_queues(adapter, eqo, i) { 29408c2ecf20Sopenharmony_ci if (eqo->q.created) { 29418c2ecf20Sopenharmony_ci be_eq_clean(eqo); 29428c2ecf20Sopenharmony_ci be_cmd_q_destroy(adapter, &eqo->q, QTYPE_EQ); 29438c2ecf20Sopenharmony_ci netif_napi_del(&eqo->napi); 29448c2ecf20Sopenharmony_ci free_cpumask_var(eqo->affinity_mask); 29458c2ecf20Sopenharmony_ci } 29468c2ecf20Sopenharmony_ci be_queue_free(adapter, &eqo->q); 29478c2ecf20Sopenharmony_ci } 29488c2ecf20Sopenharmony_ci} 29498c2ecf20Sopenharmony_ci 29508c2ecf20Sopenharmony_cistatic int be_evt_queues_create(struct be_adapter *adapter) 29518c2ecf20Sopenharmony_ci{ 29528c2ecf20Sopenharmony_ci struct be_queue_info *eq; 29538c2ecf20Sopenharmony_ci struct be_eq_obj *eqo; 29548c2ecf20Sopenharmony_ci struct be_aic_obj *aic; 29558c2ecf20Sopenharmony_ci int i, rc; 29568c2ecf20Sopenharmony_ci 29578c2ecf20Sopenharmony_ci /* need enough EQs to service both RX and TX queues */ 29588c2ecf20Sopenharmony_ci adapter->num_evt_qs = min_t(u16, num_irqs(adapter), 29598c2ecf20Sopenharmony_ci max(adapter->cfg_num_rx_irqs, 29608c2ecf20Sopenharmony_ci adapter->cfg_num_tx_irqs)); 29618c2ecf20Sopenharmony_ci 29628c2ecf20Sopenharmony_ci adapter->aic_enabled = true; 29638c2ecf20Sopenharmony_ci 29648c2ecf20Sopenharmony_ci for_all_evt_queues(adapter, eqo, i) { 29658c2ecf20Sopenharmony_ci int numa_node = dev_to_node(&adapter->pdev->dev); 29668c2ecf20Sopenharmony_ci 29678c2ecf20Sopenharmony_ci aic = &adapter->aic_obj[i]; 29688c2ecf20Sopenharmony_ci eqo->adapter = adapter; 29698c2ecf20Sopenharmony_ci eqo->idx = i; 29708c2ecf20Sopenharmony_ci aic->max_eqd = BE_MAX_EQD; 29718c2ecf20Sopenharmony_ci 29728c2ecf20Sopenharmony_ci eq = &eqo->q; 29738c2ecf20Sopenharmony_ci rc = be_queue_alloc(adapter, eq, EVNT_Q_LEN, 29748c2ecf20Sopenharmony_ci sizeof(struct be_eq_entry)); 29758c2ecf20Sopenharmony_ci if (rc) 29768c2ecf20Sopenharmony_ci return rc; 29778c2ecf20Sopenharmony_ci 29788c2ecf20Sopenharmony_ci rc = be_cmd_eq_create(adapter, eqo); 29798c2ecf20Sopenharmony_ci if (rc) 29808c2ecf20Sopenharmony_ci return rc; 29818c2ecf20Sopenharmony_ci 29828c2ecf20Sopenharmony_ci if (!zalloc_cpumask_var(&eqo->affinity_mask, GFP_KERNEL)) 29838c2ecf20Sopenharmony_ci return -ENOMEM; 29848c2ecf20Sopenharmony_ci cpumask_set_cpu(cpumask_local_spread(i, numa_node), 29858c2ecf20Sopenharmony_ci eqo->affinity_mask); 29868c2ecf20Sopenharmony_ci netif_napi_add(adapter->netdev, &eqo->napi, be_poll, 29878c2ecf20Sopenharmony_ci BE_NAPI_WEIGHT); 29888c2ecf20Sopenharmony_ci } 29898c2ecf20Sopenharmony_ci return 0; 29908c2ecf20Sopenharmony_ci} 29918c2ecf20Sopenharmony_ci 29928c2ecf20Sopenharmony_cistatic void be_mcc_queues_destroy(struct be_adapter *adapter) 29938c2ecf20Sopenharmony_ci{ 29948c2ecf20Sopenharmony_ci struct be_queue_info *q; 29958c2ecf20Sopenharmony_ci 29968c2ecf20Sopenharmony_ci q = &adapter->mcc_obj.q; 29978c2ecf20Sopenharmony_ci if (q->created) 29988c2ecf20Sopenharmony_ci be_cmd_q_destroy(adapter, q, QTYPE_MCCQ); 29998c2ecf20Sopenharmony_ci be_queue_free(adapter, q); 30008c2ecf20Sopenharmony_ci 30018c2ecf20Sopenharmony_ci q = &adapter->mcc_obj.cq; 30028c2ecf20Sopenharmony_ci if (q->created) 30038c2ecf20Sopenharmony_ci be_cmd_q_destroy(adapter, q, QTYPE_CQ); 30048c2ecf20Sopenharmony_ci be_queue_free(adapter, q); 30058c2ecf20Sopenharmony_ci} 30068c2ecf20Sopenharmony_ci 30078c2ecf20Sopenharmony_ci/* Must be called only after TX qs are created as MCC shares TX EQ */ 30088c2ecf20Sopenharmony_cistatic int be_mcc_queues_create(struct be_adapter *adapter) 30098c2ecf20Sopenharmony_ci{ 30108c2ecf20Sopenharmony_ci struct be_queue_info *q, *cq; 30118c2ecf20Sopenharmony_ci 30128c2ecf20Sopenharmony_ci cq = &adapter->mcc_obj.cq; 30138c2ecf20Sopenharmony_ci if (be_queue_alloc(adapter, cq, MCC_CQ_LEN, 30148c2ecf20Sopenharmony_ci sizeof(struct be_mcc_compl))) 30158c2ecf20Sopenharmony_ci goto err; 30168c2ecf20Sopenharmony_ci 30178c2ecf20Sopenharmony_ci /* Use the default EQ for MCC completions */ 30188c2ecf20Sopenharmony_ci if (be_cmd_cq_create(adapter, cq, &mcc_eqo(adapter)->q, true, 0)) 30198c2ecf20Sopenharmony_ci goto mcc_cq_free; 30208c2ecf20Sopenharmony_ci 30218c2ecf20Sopenharmony_ci q = &adapter->mcc_obj.q; 30228c2ecf20Sopenharmony_ci if (be_queue_alloc(adapter, q, MCC_Q_LEN, sizeof(struct be_mcc_wrb))) 30238c2ecf20Sopenharmony_ci goto mcc_cq_destroy; 30248c2ecf20Sopenharmony_ci 30258c2ecf20Sopenharmony_ci if (be_cmd_mccq_create(adapter, q, cq)) 30268c2ecf20Sopenharmony_ci goto mcc_q_free; 30278c2ecf20Sopenharmony_ci 30288c2ecf20Sopenharmony_ci return 0; 30298c2ecf20Sopenharmony_ci 30308c2ecf20Sopenharmony_cimcc_q_free: 30318c2ecf20Sopenharmony_ci be_queue_free(adapter, q); 30328c2ecf20Sopenharmony_cimcc_cq_destroy: 30338c2ecf20Sopenharmony_ci be_cmd_q_destroy(adapter, cq, QTYPE_CQ); 30348c2ecf20Sopenharmony_cimcc_cq_free: 30358c2ecf20Sopenharmony_ci be_queue_free(adapter, cq); 30368c2ecf20Sopenharmony_cierr: 30378c2ecf20Sopenharmony_ci return -1; 30388c2ecf20Sopenharmony_ci} 30398c2ecf20Sopenharmony_ci 30408c2ecf20Sopenharmony_cistatic void be_tx_queues_destroy(struct be_adapter *adapter) 30418c2ecf20Sopenharmony_ci{ 30428c2ecf20Sopenharmony_ci struct be_queue_info *q; 30438c2ecf20Sopenharmony_ci struct be_tx_obj *txo; 30448c2ecf20Sopenharmony_ci u8 i; 30458c2ecf20Sopenharmony_ci 30468c2ecf20Sopenharmony_ci for_all_tx_queues(adapter, txo, i) { 30478c2ecf20Sopenharmony_ci q = &txo->q; 30488c2ecf20Sopenharmony_ci if (q->created) 30498c2ecf20Sopenharmony_ci be_cmd_q_destroy(adapter, q, QTYPE_TXQ); 30508c2ecf20Sopenharmony_ci be_queue_free(adapter, q); 30518c2ecf20Sopenharmony_ci 30528c2ecf20Sopenharmony_ci q = &txo->cq; 30538c2ecf20Sopenharmony_ci if (q->created) 30548c2ecf20Sopenharmony_ci be_cmd_q_destroy(adapter, q, QTYPE_CQ); 30558c2ecf20Sopenharmony_ci be_queue_free(adapter, q); 30568c2ecf20Sopenharmony_ci } 30578c2ecf20Sopenharmony_ci} 30588c2ecf20Sopenharmony_ci 30598c2ecf20Sopenharmony_cistatic int be_tx_qs_create(struct be_adapter *adapter) 30608c2ecf20Sopenharmony_ci{ 30618c2ecf20Sopenharmony_ci struct be_queue_info *cq; 30628c2ecf20Sopenharmony_ci struct be_tx_obj *txo; 30638c2ecf20Sopenharmony_ci struct be_eq_obj *eqo; 30648c2ecf20Sopenharmony_ci int status, i; 30658c2ecf20Sopenharmony_ci 30668c2ecf20Sopenharmony_ci adapter->num_tx_qs = min(adapter->num_evt_qs, adapter->cfg_num_tx_irqs); 30678c2ecf20Sopenharmony_ci 30688c2ecf20Sopenharmony_ci for_all_tx_queues(adapter, txo, i) { 30698c2ecf20Sopenharmony_ci cq = &txo->cq; 30708c2ecf20Sopenharmony_ci status = be_queue_alloc(adapter, cq, TX_CQ_LEN, 30718c2ecf20Sopenharmony_ci sizeof(struct be_eth_tx_compl)); 30728c2ecf20Sopenharmony_ci if (status) 30738c2ecf20Sopenharmony_ci return status; 30748c2ecf20Sopenharmony_ci 30758c2ecf20Sopenharmony_ci u64_stats_init(&txo->stats.sync); 30768c2ecf20Sopenharmony_ci u64_stats_init(&txo->stats.sync_compl); 30778c2ecf20Sopenharmony_ci 30788c2ecf20Sopenharmony_ci /* If num_evt_qs is less than num_tx_qs, then more than 30798c2ecf20Sopenharmony_ci * one txq share an eq 30808c2ecf20Sopenharmony_ci */ 30818c2ecf20Sopenharmony_ci eqo = &adapter->eq_obj[i % adapter->num_evt_qs]; 30828c2ecf20Sopenharmony_ci status = be_cmd_cq_create(adapter, cq, &eqo->q, false, 3); 30838c2ecf20Sopenharmony_ci if (status) 30848c2ecf20Sopenharmony_ci return status; 30858c2ecf20Sopenharmony_ci 30868c2ecf20Sopenharmony_ci status = be_queue_alloc(adapter, &txo->q, TX_Q_LEN, 30878c2ecf20Sopenharmony_ci sizeof(struct be_eth_wrb)); 30888c2ecf20Sopenharmony_ci if (status) 30898c2ecf20Sopenharmony_ci return status; 30908c2ecf20Sopenharmony_ci 30918c2ecf20Sopenharmony_ci status = be_cmd_txq_create(adapter, txo); 30928c2ecf20Sopenharmony_ci if (status) 30938c2ecf20Sopenharmony_ci return status; 30948c2ecf20Sopenharmony_ci 30958c2ecf20Sopenharmony_ci netif_set_xps_queue(adapter->netdev, eqo->affinity_mask, 30968c2ecf20Sopenharmony_ci eqo->idx); 30978c2ecf20Sopenharmony_ci } 30988c2ecf20Sopenharmony_ci 30998c2ecf20Sopenharmony_ci dev_info(&adapter->pdev->dev, "created %d TX queue(s)\n", 31008c2ecf20Sopenharmony_ci adapter->num_tx_qs); 31018c2ecf20Sopenharmony_ci return 0; 31028c2ecf20Sopenharmony_ci} 31038c2ecf20Sopenharmony_ci 31048c2ecf20Sopenharmony_cistatic void be_rx_cqs_destroy(struct be_adapter *adapter) 31058c2ecf20Sopenharmony_ci{ 31068c2ecf20Sopenharmony_ci struct be_queue_info *q; 31078c2ecf20Sopenharmony_ci struct be_rx_obj *rxo; 31088c2ecf20Sopenharmony_ci int i; 31098c2ecf20Sopenharmony_ci 31108c2ecf20Sopenharmony_ci for_all_rx_queues(adapter, rxo, i) { 31118c2ecf20Sopenharmony_ci q = &rxo->cq; 31128c2ecf20Sopenharmony_ci if (q->created) 31138c2ecf20Sopenharmony_ci be_cmd_q_destroy(adapter, q, QTYPE_CQ); 31148c2ecf20Sopenharmony_ci be_queue_free(adapter, q); 31158c2ecf20Sopenharmony_ci } 31168c2ecf20Sopenharmony_ci} 31178c2ecf20Sopenharmony_ci 31188c2ecf20Sopenharmony_cistatic int be_rx_cqs_create(struct be_adapter *adapter) 31198c2ecf20Sopenharmony_ci{ 31208c2ecf20Sopenharmony_ci struct be_queue_info *eq, *cq; 31218c2ecf20Sopenharmony_ci struct be_rx_obj *rxo; 31228c2ecf20Sopenharmony_ci int rc, i; 31238c2ecf20Sopenharmony_ci 31248c2ecf20Sopenharmony_ci adapter->num_rss_qs = 31258c2ecf20Sopenharmony_ci min(adapter->num_evt_qs, adapter->cfg_num_rx_irqs); 31268c2ecf20Sopenharmony_ci 31278c2ecf20Sopenharmony_ci /* We'll use RSS only if atleast 2 RSS rings are supported. */ 31288c2ecf20Sopenharmony_ci if (adapter->num_rss_qs < 2) 31298c2ecf20Sopenharmony_ci adapter->num_rss_qs = 0; 31308c2ecf20Sopenharmony_ci 31318c2ecf20Sopenharmony_ci adapter->num_rx_qs = adapter->num_rss_qs + adapter->need_def_rxq; 31328c2ecf20Sopenharmony_ci 31338c2ecf20Sopenharmony_ci /* When the interface is not capable of RSS rings (and there is no 31348c2ecf20Sopenharmony_ci * need to create a default RXQ) we'll still need one RXQ 31358c2ecf20Sopenharmony_ci */ 31368c2ecf20Sopenharmony_ci if (adapter->num_rx_qs == 0) 31378c2ecf20Sopenharmony_ci adapter->num_rx_qs = 1; 31388c2ecf20Sopenharmony_ci 31398c2ecf20Sopenharmony_ci adapter->big_page_size = (1 << get_order(rx_frag_size)) * PAGE_SIZE; 31408c2ecf20Sopenharmony_ci for_all_rx_queues(adapter, rxo, i) { 31418c2ecf20Sopenharmony_ci rxo->adapter = adapter; 31428c2ecf20Sopenharmony_ci cq = &rxo->cq; 31438c2ecf20Sopenharmony_ci rc = be_queue_alloc(adapter, cq, RX_CQ_LEN, 31448c2ecf20Sopenharmony_ci sizeof(struct be_eth_rx_compl)); 31458c2ecf20Sopenharmony_ci if (rc) 31468c2ecf20Sopenharmony_ci return rc; 31478c2ecf20Sopenharmony_ci 31488c2ecf20Sopenharmony_ci u64_stats_init(&rxo->stats.sync); 31498c2ecf20Sopenharmony_ci eq = &adapter->eq_obj[i % adapter->num_evt_qs].q; 31508c2ecf20Sopenharmony_ci rc = be_cmd_cq_create(adapter, cq, eq, false, 3); 31518c2ecf20Sopenharmony_ci if (rc) 31528c2ecf20Sopenharmony_ci return rc; 31538c2ecf20Sopenharmony_ci } 31548c2ecf20Sopenharmony_ci 31558c2ecf20Sopenharmony_ci dev_info(&adapter->pdev->dev, 31568c2ecf20Sopenharmony_ci "created %d RX queue(s)\n", adapter->num_rx_qs); 31578c2ecf20Sopenharmony_ci return 0; 31588c2ecf20Sopenharmony_ci} 31598c2ecf20Sopenharmony_ci 31608c2ecf20Sopenharmony_cistatic irqreturn_t be_intx(int irq, void *dev) 31618c2ecf20Sopenharmony_ci{ 31628c2ecf20Sopenharmony_ci struct be_eq_obj *eqo = dev; 31638c2ecf20Sopenharmony_ci struct be_adapter *adapter = eqo->adapter; 31648c2ecf20Sopenharmony_ci int num_evts = 0; 31658c2ecf20Sopenharmony_ci 31668c2ecf20Sopenharmony_ci /* IRQ is not expected when NAPI is scheduled as the EQ 31678c2ecf20Sopenharmony_ci * will not be armed. 31688c2ecf20Sopenharmony_ci * But, this can happen on Lancer INTx where it takes 31698c2ecf20Sopenharmony_ci * a while to de-assert INTx or in BE2 where occasionaly 31708c2ecf20Sopenharmony_ci * an interrupt may be raised even when EQ is unarmed. 31718c2ecf20Sopenharmony_ci * If NAPI is already scheduled, then counting & notifying 31728c2ecf20Sopenharmony_ci * events will orphan them. 31738c2ecf20Sopenharmony_ci */ 31748c2ecf20Sopenharmony_ci if (napi_schedule_prep(&eqo->napi)) { 31758c2ecf20Sopenharmony_ci num_evts = events_get(eqo); 31768c2ecf20Sopenharmony_ci __napi_schedule(&eqo->napi); 31778c2ecf20Sopenharmony_ci if (num_evts) 31788c2ecf20Sopenharmony_ci eqo->spurious_intr = 0; 31798c2ecf20Sopenharmony_ci } 31808c2ecf20Sopenharmony_ci be_eq_notify(adapter, eqo->q.id, false, true, num_evts, 0); 31818c2ecf20Sopenharmony_ci 31828c2ecf20Sopenharmony_ci /* Return IRQ_HANDLED only for the the first spurious intr 31838c2ecf20Sopenharmony_ci * after a valid intr to stop the kernel from branding 31848c2ecf20Sopenharmony_ci * this irq as a bad one! 31858c2ecf20Sopenharmony_ci */ 31868c2ecf20Sopenharmony_ci if (num_evts || eqo->spurious_intr++ == 0) 31878c2ecf20Sopenharmony_ci return IRQ_HANDLED; 31888c2ecf20Sopenharmony_ci else 31898c2ecf20Sopenharmony_ci return IRQ_NONE; 31908c2ecf20Sopenharmony_ci} 31918c2ecf20Sopenharmony_ci 31928c2ecf20Sopenharmony_cistatic irqreturn_t be_msix(int irq, void *dev) 31938c2ecf20Sopenharmony_ci{ 31948c2ecf20Sopenharmony_ci struct be_eq_obj *eqo = dev; 31958c2ecf20Sopenharmony_ci 31968c2ecf20Sopenharmony_ci be_eq_notify(eqo->adapter, eqo->q.id, false, true, 0, 0); 31978c2ecf20Sopenharmony_ci napi_schedule(&eqo->napi); 31988c2ecf20Sopenharmony_ci return IRQ_HANDLED; 31998c2ecf20Sopenharmony_ci} 32008c2ecf20Sopenharmony_ci 32018c2ecf20Sopenharmony_cistatic inline bool do_gro(struct be_rx_compl_info *rxcp) 32028c2ecf20Sopenharmony_ci{ 32038c2ecf20Sopenharmony_ci return (rxcp->tcpf && !rxcp->err && rxcp->l4_csum) ? true : false; 32048c2ecf20Sopenharmony_ci} 32058c2ecf20Sopenharmony_ci 32068c2ecf20Sopenharmony_cistatic int be_process_rx(struct be_rx_obj *rxo, struct napi_struct *napi, 32078c2ecf20Sopenharmony_ci int budget) 32088c2ecf20Sopenharmony_ci{ 32098c2ecf20Sopenharmony_ci struct be_adapter *adapter = rxo->adapter; 32108c2ecf20Sopenharmony_ci struct be_queue_info *rx_cq = &rxo->cq; 32118c2ecf20Sopenharmony_ci struct be_rx_compl_info *rxcp; 32128c2ecf20Sopenharmony_ci u32 work_done; 32138c2ecf20Sopenharmony_ci u32 frags_consumed = 0; 32148c2ecf20Sopenharmony_ci 32158c2ecf20Sopenharmony_ci for (work_done = 0; work_done < budget; work_done++) { 32168c2ecf20Sopenharmony_ci rxcp = be_rx_compl_get(rxo); 32178c2ecf20Sopenharmony_ci if (!rxcp) 32188c2ecf20Sopenharmony_ci break; 32198c2ecf20Sopenharmony_ci 32208c2ecf20Sopenharmony_ci /* Is it a flush compl that has no data */ 32218c2ecf20Sopenharmony_ci if (unlikely(rxcp->num_rcvd == 0)) 32228c2ecf20Sopenharmony_ci goto loop_continue; 32238c2ecf20Sopenharmony_ci 32248c2ecf20Sopenharmony_ci /* Discard compl with partial DMA Lancer B0 */ 32258c2ecf20Sopenharmony_ci if (unlikely(!rxcp->pkt_size)) { 32268c2ecf20Sopenharmony_ci be_rx_compl_discard(rxo, rxcp); 32278c2ecf20Sopenharmony_ci goto loop_continue; 32288c2ecf20Sopenharmony_ci } 32298c2ecf20Sopenharmony_ci 32308c2ecf20Sopenharmony_ci /* On BE drop pkts that arrive due to imperfect filtering in 32318c2ecf20Sopenharmony_ci * promiscuous mode on some skews 32328c2ecf20Sopenharmony_ci */ 32338c2ecf20Sopenharmony_ci if (unlikely(rxcp->port != adapter->port_num && 32348c2ecf20Sopenharmony_ci !lancer_chip(adapter))) { 32358c2ecf20Sopenharmony_ci be_rx_compl_discard(rxo, rxcp); 32368c2ecf20Sopenharmony_ci goto loop_continue; 32378c2ecf20Sopenharmony_ci } 32388c2ecf20Sopenharmony_ci 32398c2ecf20Sopenharmony_ci if (do_gro(rxcp)) 32408c2ecf20Sopenharmony_ci be_rx_compl_process_gro(rxo, napi, rxcp); 32418c2ecf20Sopenharmony_ci else 32428c2ecf20Sopenharmony_ci be_rx_compl_process(rxo, napi, rxcp); 32438c2ecf20Sopenharmony_ci 32448c2ecf20Sopenharmony_ciloop_continue: 32458c2ecf20Sopenharmony_ci frags_consumed += rxcp->num_rcvd; 32468c2ecf20Sopenharmony_ci be_rx_stats_update(rxo, rxcp); 32478c2ecf20Sopenharmony_ci } 32488c2ecf20Sopenharmony_ci 32498c2ecf20Sopenharmony_ci if (work_done) { 32508c2ecf20Sopenharmony_ci be_cq_notify(adapter, rx_cq->id, true, work_done); 32518c2ecf20Sopenharmony_ci 32528c2ecf20Sopenharmony_ci /* When an rx-obj gets into post_starved state, just 32538c2ecf20Sopenharmony_ci * let be_worker do the posting. 32548c2ecf20Sopenharmony_ci */ 32558c2ecf20Sopenharmony_ci if (atomic_read(&rxo->q.used) < RX_FRAGS_REFILL_WM && 32568c2ecf20Sopenharmony_ci !rxo->rx_post_starved) 32578c2ecf20Sopenharmony_ci be_post_rx_frags(rxo, GFP_ATOMIC, 32588c2ecf20Sopenharmony_ci max_t(u32, MAX_RX_POST, 32598c2ecf20Sopenharmony_ci frags_consumed)); 32608c2ecf20Sopenharmony_ci } 32618c2ecf20Sopenharmony_ci 32628c2ecf20Sopenharmony_ci return work_done; 32638c2ecf20Sopenharmony_ci} 32648c2ecf20Sopenharmony_ci 32658c2ecf20Sopenharmony_ci 32668c2ecf20Sopenharmony_cistatic void be_process_tx(struct be_adapter *adapter, struct be_tx_obj *txo, 32678c2ecf20Sopenharmony_ci int idx) 32688c2ecf20Sopenharmony_ci{ 32698c2ecf20Sopenharmony_ci int num_wrbs = 0, work_done = 0; 32708c2ecf20Sopenharmony_ci struct be_tx_compl_info *txcp; 32718c2ecf20Sopenharmony_ci 32728c2ecf20Sopenharmony_ci while ((txcp = be_tx_compl_get(adapter, txo))) { 32738c2ecf20Sopenharmony_ci num_wrbs += be_tx_compl_process(adapter, txo, txcp->end_index); 32748c2ecf20Sopenharmony_ci work_done++; 32758c2ecf20Sopenharmony_ci } 32768c2ecf20Sopenharmony_ci 32778c2ecf20Sopenharmony_ci if (work_done) { 32788c2ecf20Sopenharmony_ci be_cq_notify(adapter, txo->cq.id, true, work_done); 32798c2ecf20Sopenharmony_ci atomic_sub(num_wrbs, &txo->q.used); 32808c2ecf20Sopenharmony_ci 32818c2ecf20Sopenharmony_ci /* As Tx wrbs have been freed up, wake up netdev queue 32828c2ecf20Sopenharmony_ci * if it was stopped due to lack of tx wrbs. */ 32838c2ecf20Sopenharmony_ci if (__netif_subqueue_stopped(adapter->netdev, idx) && 32848c2ecf20Sopenharmony_ci be_can_txq_wake(txo)) { 32858c2ecf20Sopenharmony_ci netif_wake_subqueue(adapter->netdev, idx); 32868c2ecf20Sopenharmony_ci } 32878c2ecf20Sopenharmony_ci 32888c2ecf20Sopenharmony_ci u64_stats_update_begin(&tx_stats(txo)->sync_compl); 32898c2ecf20Sopenharmony_ci tx_stats(txo)->tx_compl += work_done; 32908c2ecf20Sopenharmony_ci u64_stats_update_end(&tx_stats(txo)->sync_compl); 32918c2ecf20Sopenharmony_ci } 32928c2ecf20Sopenharmony_ci} 32938c2ecf20Sopenharmony_ci 32948c2ecf20Sopenharmony_ciint be_poll(struct napi_struct *napi, int budget) 32958c2ecf20Sopenharmony_ci{ 32968c2ecf20Sopenharmony_ci struct be_eq_obj *eqo = container_of(napi, struct be_eq_obj, napi); 32978c2ecf20Sopenharmony_ci struct be_adapter *adapter = eqo->adapter; 32988c2ecf20Sopenharmony_ci int max_work = 0, work, i, num_evts; 32998c2ecf20Sopenharmony_ci struct be_rx_obj *rxo; 33008c2ecf20Sopenharmony_ci struct be_tx_obj *txo; 33018c2ecf20Sopenharmony_ci u32 mult_enc = 0; 33028c2ecf20Sopenharmony_ci 33038c2ecf20Sopenharmony_ci num_evts = events_get(eqo); 33048c2ecf20Sopenharmony_ci 33058c2ecf20Sopenharmony_ci for_all_tx_queues_on_eq(adapter, eqo, txo, i) 33068c2ecf20Sopenharmony_ci be_process_tx(adapter, txo, i); 33078c2ecf20Sopenharmony_ci 33088c2ecf20Sopenharmony_ci /* This loop will iterate twice for EQ0 in which 33098c2ecf20Sopenharmony_ci * completions of the last RXQ (default one) are also processed 33108c2ecf20Sopenharmony_ci * For other EQs the loop iterates only once 33118c2ecf20Sopenharmony_ci */ 33128c2ecf20Sopenharmony_ci for_all_rx_queues_on_eq(adapter, eqo, rxo, i) { 33138c2ecf20Sopenharmony_ci work = be_process_rx(rxo, napi, budget); 33148c2ecf20Sopenharmony_ci max_work = max(work, max_work); 33158c2ecf20Sopenharmony_ci } 33168c2ecf20Sopenharmony_ci 33178c2ecf20Sopenharmony_ci if (is_mcc_eqo(eqo)) 33188c2ecf20Sopenharmony_ci be_process_mcc(adapter); 33198c2ecf20Sopenharmony_ci 33208c2ecf20Sopenharmony_ci if (max_work < budget) { 33218c2ecf20Sopenharmony_ci napi_complete_done(napi, max_work); 33228c2ecf20Sopenharmony_ci 33238c2ecf20Sopenharmony_ci /* Skyhawk EQ_DB has a provision to set the rearm to interrupt 33248c2ecf20Sopenharmony_ci * delay via a delay multiplier encoding value 33258c2ecf20Sopenharmony_ci */ 33268c2ecf20Sopenharmony_ci if (skyhawk_chip(adapter)) 33278c2ecf20Sopenharmony_ci mult_enc = be_get_eq_delay_mult_enc(eqo); 33288c2ecf20Sopenharmony_ci 33298c2ecf20Sopenharmony_ci be_eq_notify(adapter, eqo->q.id, true, false, num_evts, 33308c2ecf20Sopenharmony_ci mult_enc); 33318c2ecf20Sopenharmony_ci } else { 33328c2ecf20Sopenharmony_ci /* As we'll continue in polling mode, count and clear events */ 33338c2ecf20Sopenharmony_ci be_eq_notify(adapter, eqo->q.id, false, false, num_evts, 0); 33348c2ecf20Sopenharmony_ci } 33358c2ecf20Sopenharmony_ci return max_work; 33368c2ecf20Sopenharmony_ci} 33378c2ecf20Sopenharmony_ci 33388c2ecf20Sopenharmony_civoid be_detect_error(struct be_adapter *adapter) 33398c2ecf20Sopenharmony_ci{ 33408c2ecf20Sopenharmony_ci u32 ue_lo = 0, ue_hi = 0, ue_lo_mask = 0, ue_hi_mask = 0; 33418c2ecf20Sopenharmony_ci u32 sliport_status = 0, sliport_err1 = 0, sliport_err2 = 0; 33428c2ecf20Sopenharmony_ci struct device *dev = &adapter->pdev->dev; 33438c2ecf20Sopenharmony_ci u16 val; 33448c2ecf20Sopenharmony_ci u32 i; 33458c2ecf20Sopenharmony_ci 33468c2ecf20Sopenharmony_ci if (be_check_error(adapter, BE_ERROR_HW)) 33478c2ecf20Sopenharmony_ci return; 33488c2ecf20Sopenharmony_ci 33498c2ecf20Sopenharmony_ci if (lancer_chip(adapter)) { 33508c2ecf20Sopenharmony_ci sliport_status = ioread32(adapter->db + SLIPORT_STATUS_OFFSET); 33518c2ecf20Sopenharmony_ci if (sliport_status & SLIPORT_STATUS_ERR_MASK) { 33528c2ecf20Sopenharmony_ci be_set_error(adapter, BE_ERROR_UE); 33538c2ecf20Sopenharmony_ci sliport_err1 = ioread32(adapter->db + 33548c2ecf20Sopenharmony_ci SLIPORT_ERROR1_OFFSET); 33558c2ecf20Sopenharmony_ci sliport_err2 = ioread32(adapter->db + 33568c2ecf20Sopenharmony_ci SLIPORT_ERROR2_OFFSET); 33578c2ecf20Sopenharmony_ci /* Do not log error messages if its a FW reset */ 33588c2ecf20Sopenharmony_ci if (sliport_err1 == SLIPORT_ERROR_FW_RESET1 && 33598c2ecf20Sopenharmony_ci sliport_err2 == SLIPORT_ERROR_FW_RESET2) { 33608c2ecf20Sopenharmony_ci dev_info(dev, "Reset is in progress\n"); 33618c2ecf20Sopenharmony_ci } else { 33628c2ecf20Sopenharmony_ci dev_err(dev, "Error detected in the card\n"); 33638c2ecf20Sopenharmony_ci dev_err(dev, "ERR: sliport status 0x%x\n", 33648c2ecf20Sopenharmony_ci sliport_status); 33658c2ecf20Sopenharmony_ci dev_err(dev, "ERR: sliport error1 0x%x\n", 33668c2ecf20Sopenharmony_ci sliport_err1); 33678c2ecf20Sopenharmony_ci dev_err(dev, "ERR: sliport error2 0x%x\n", 33688c2ecf20Sopenharmony_ci sliport_err2); 33698c2ecf20Sopenharmony_ci } 33708c2ecf20Sopenharmony_ci } 33718c2ecf20Sopenharmony_ci } else { 33728c2ecf20Sopenharmony_ci ue_lo = ioread32(adapter->pcicfg + PCICFG_UE_STATUS_LOW); 33738c2ecf20Sopenharmony_ci ue_hi = ioread32(adapter->pcicfg + PCICFG_UE_STATUS_HIGH); 33748c2ecf20Sopenharmony_ci ue_lo_mask = ioread32(adapter->pcicfg + 33758c2ecf20Sopenharmony_ci PCICFG_UE_STATUS_LOW_MASK); 33768c2ecf20Sopenharmony_ci ue_hi_mask = ioread32(adapter->pcicfg + 33778c2ecf20Sopenharmony_ci PCICFG_UE_STATUS_HI_MASK); 33788c2ecf20Sopenharmony_ci 33798c2ecf20Sopenharmony_ci ue_lo = (ue_lo & ~ue_lo_mask); 33808c2ecf20Sopenharmony_ci ue_hi = (ue_hi & ~ue_hi_mask); 33818c2ecf20Sopenharmony_ci 33828c2ecf20Sopenharmony_ci if (ue_lo || ue_hi) { 33838c2ecf20Sopenharmony_ci /* On certain platforms BE3 hardware can indicate 33848c2ecf20Sopenharmony_ci * spurious UEs. In case of a UE in the chip, 33858c2ecf20Sopenharmony_ci * the POST register correctly reports either a 33868c2ecf20Sopenharmony_ci * FAT_LOG_START state (FW is currently dumping 33878c2ecf20Sopenharmony_ci * FAT log data) or a ARMFW_UE state. Check for the 33888c2ecf20Sopenharmony_ci * above states to ascertain if the UE is valid or not. 33898c2ecf20Sopenharmony_ci */ 33908c2ecf20Sopenharmony_ci if (BE3_chip(adapter)) { 33918c2ecf20Sopenharmony_ci val = be_POST_stage_get(adapter); 33928c2ecf20Sopenharmony_ci if ((val & POST_STAGE_FAT_LOG_START) 33938c2ecf20Sopenharmony_ci != POST_STAGE_FAT_LOG_START && 33948c2ecf20Sopenharmony_ci (val & POST_STAGE_ARMFW_UE) 33958c2ecf20Sopenharmony_ci != POST_STAGE_ARMFW_UE && 33968c2ecf20Sopenharmony_ci (val & POST_STAGE_RECOVERABLE_ERR) 33978c2ecf20Sopenharmony_ci != POST_STAGE_RECOVERABLE_ERR) 33988c2ecf20Sopenharmony_ci return; 33998c2ecf20Sopenharmony_ci } 34008c2ecf20Sopenharmony_ci 34018c2ecf20Sopenharmony_ci dev_err(dev, "Error detected in the adapter"); 34028c2ecf20Sopenharmony_ci be_set_error(adapter, BE_ERROR_UE); 34038c2ecf20Sopenharmony_ci 34048c2ecf20Sopenharmony_ci for (i = 0; ue_lo; ue_lo >>= 1, i++) { 34058c2ecf20Sopenharmony_ci if (ue_lo & 1) 34068c2ecf20Sopenharmony_ci dev_err(dev, "UE: %s bit set\n", 34078c2ecf20Sopenharmony_ci ue_status_low_desc[i]); 34088c2ecf20Sopenharmony_ci } 34098c2ecf20Sopenharmony_ci for (i = 0; ue_hi; ue_hi >>= 1, i++) { 34108c2ecf20Sopenharmony_ci if (ue_hi & 1) 34118c2ecf20Sopenharmony_ci dev_err(dev, "UE: %s bit set\n", 34128c2ecf20Sopenharmony_ci ue_status_hi_desc[i]); 34138c2ecf20Sopenharmony_ci } 34148c2ecf20Sopenharmony_ci } 34158c2ecf20Sopenharmony_ci } 34168c2ecf20Sopenharmony_ci} 34178c2ecf20Sopenharmony_ci 34188c2ecf20Sopenharmony_cistatic void be_msix_disable(struct be_adapter *adapter) 34198c2ecf20Sopenharmony_ci{ 34208c2ecf20Sopenharmony_ci if (msix_enabled(adapter)) { 34218c2ecf20Sopenharmony_ci pci_disable_msix(adapter->pdev); 34228c2ecf20Sopenharmony_ci adapter->num_msix_vec = 0; 34238c2ecf20Sopenharmony_ci adapter->num_msix_roce_vec = 0; 34248c2ecf20Sopenharmony_ci } 34258c2ecf20Sopenharmony_ci} 34268c2ecf20Sopenharmony_ci 34278c2ecf20Sopenharmony_cistatic int be_msix_enable(struct be_adapter *adapter) 34288c2ecf20Sopenharmony_ci{ 34298c2ecf20Sopenharmony_ci unsigned int i, max_roce_eqs; 34308c2ecf20Sopenharmony_ci struct device *dev = &adapter->pdev->dev; 34318c2ecf20Sopenharmony_ci int num_vec; 34328c2ecf20Sopenharmony_ci 34338c2ecf20Sopenharmony_ci /* If RoCE is supported, program the max number of vectors that 34348c2ecf20Sopenharmony_ci * could be used for NIC and RoCE, else, just program the number 34358c2ecf20Sopenharmony_ci * we'll use initially. 34368c2ecf20Sopenharmony_ci */ 34378c2ecf20Sopenharmony_ci if (be_roce_supported(adapter)) { 34388c2ecf20Sopenharmony_ci max_roce_eqs = 34398c2ecf20Sopenharmony_ci be_max_func_eqs(adapter) - be_max_nic_eqs(adapter); 34408c2ecf20Sopenharmony_ci max_roce_eqs = min(max_roce_eqs, num_online_cpus()); 34418c2ecf20Sopenharmony_ci num_vec = be_max_any_irqs(adapter) + max_roce_eqs; 34428c2ecf20Sopenharmony_ci } else { 34438c2ecf20Sopenharmony_ci num_vec = max(adapter->cfg_num_rx_irqs, 34448c2ecf20Sopenharmony_ci adapter->cfg_num_tx_irqs); 34458c2ecf20Sopenharmony_ci } 34468c2ecf20Sopenharmony_ci 34478c2ecf20Sopenharmony_ci for (i = 0; i < num_vec; i++) 34488c2ecf20Sopenharmony_ci adapter->msix_entries[i].entry = i; 34498c2ecf20Sopenharmony_ci 34508c2ecf20Sopenharmony_ci num_vec = pci_enable_msix_range(adapter->pdev, adapter->msix_entries, 34518c2ecf20Sopenharmony_ci MIN_MSIX_VECTORS, num_vec); 34528c2ecf20Sopenharmony_ci if (num_vec < 0) 34538c2ecf20Sopenharmony_ci goto fail; 34548c2ecf20Sopenharmony_ci 34558c2ecf20Sopenharmony_ci if (be_roce_supported(adapter) && num_vec > MIN_MSIX_VECTORS) { 34568c2ecf20Sopenharmony_ci adapter->num_msix_roce_vec = num_vec / 2; 34578c2ecf20Sopenharmony_ci dev_info(dev, "enabled %d MSI-x vector(s) for RoCE\n", 34588c2ecf20Sopenharmony_ci adapter->num_msix_roce_vec); 34598c2ecf20Sopenharmony_ci } 34608c2ecf20Sopenharmony_ci 34618c2ecf20Sopenharmony_ci adapter->num_msix_vec = num_vec - adapter->num_msix_roce_vec; 34628c2ecf20Sopenharmony_ci 34638c2ecf20Sopenharmony_ci dev_info(dev, "enabled %d MSI-x vector(s) for NIC\n", 34648c2ecf20Sopenharmony_ci adapter->num_msix_vec); 34658c2ecf20Sopenharmony_ci return 0; 34668c2ecf20Sopenharmony_ci 34678c2ecf20Sopenharmony_cifail: 34688c2ecf20Sopenharmony_ci dev_warn(dev, "MSIx enable failed\n"); 34698c2ecf20Sopenharmony_ci 34708c2ecf20Sopenharmony_ci /* INTx is not supported in VFs, so fail probe if enable_msix fails */ 34718c2ecf20Sopenharmony_ci if (be_virtfn(adapter)) 34728c2ecf20Sopenharmony_ci return num_vec; 34738c2ecf20Sopenharmony_ci return 0; 34748c2ecf20Sopenharmony_ci} 34758c2ecf20Sopenharmony_ci 34768c2ecf20Sopenharmony_cistatic inline int be_msix_vec_get(struct be_adapter *adapter, 34778c2ecf20Sopenharmony_ci struct be_eq_obj *eqo) 34788c2ecf20Sopenharmony_ci{ 34798c2ecf20Sopenharmony_ci return adapter->msix_entries[eqo->msix_idx].vector; 34808c2ecf20Sopenharmony_ci} 34818c2ecf20Sopenharmony_ci 34828c2ecf20Sopenharmony_cistatic int be_msix_register(struct be_adapter *adapter) 34838c2ecf20Sopenharmony_ci{ 34848c2ecf20Sopenharmony_ci struct net_device *netdev = adapter->netdev; 34858c2ecf20Sopenharmony_ci struct be_eq_obj *eqo; 34868c2ecf20Sopenharmony_ci int status, i, vec; 34878c2ecf20Sopenharmony_ci 34888c2ecf20Sopenharmony_ci for_all_evt_queues(adapter, eqo, i) { 34898c2ecf20Sopenharmony_ci sprintf(eqo->desc, "%s-q%d", netdev->name, i); 34908c2ecf20Sopenharmony_ci vec = be_msix_vec_get(adapter, eqo); 34918c2ecf20Sopenharmony_ci status = request_irq(vec, be_msix, 0, eqo->desc, eqo); 34928c2ecf20Sopenharmony_ci if (status) 34938c2ecf20Sopenharmony_ci goto err_msix; 34948c2ecf20Sopenharmony_ci 34958c2ecf20Sopenharmony_ci irq_set_affinity_hint(vec, eqo->affinity_mask); 34968c2ecf20Sopenharmony_ci } 34978c2ecf20Sopenharmony_ci 34988c2ecf20Sopenharmony_ci return 0; 34998c2ecf20Sopenharmony_cierr_msix: 35008c2ecf20Sopenharmony_ci for (i--; i >= 0; i--) { 35018c2ecf20Sopenharmony_ci eqo = &adapter->eq_obj[i]; 35028c2ecf20Sopenharmony_ci free_irq(be_msix_vec_get(adapter, eqo), eqo); 35038c2ecf20Sopenharmony_ci } 35048c2ecf20Sopenharmony_ci dev_warn(&adapter->pdev->dev, "MSIX Request IRQ failed - err %d\n", 35058c2ecf20Sopenharmony_ci status); 35068c2ecf20Sopenharmony_ci be_msix_disable(adapter); 35078c2ecf20Sopenharmony_ci return status; 35088c2ecf20Sopenharmony_ci} 35098c2ecf20Sopenharmony_ci 35108c2ecf20Sopenharmony_cistatic int be_irq_register(struct be_adapter *adapter) 35118c2ecf20Sopenharmony_ci{ 35128c2ecf20Sopenharmony_ci struct net_device *netdev = adapter->netdev; 35138c2ecf20Sopenharmony_ci int status; 35148c2ecf20Sopenharmony_ci 35158c2ecf20Sopenharmony_ci if (msix_enabled(adapter)) { 35168c2ecf20Sopenharmony_ci status = be_msix_register(adapter); 35178c2ecf20Sopenharmony_ci if (status == 0) 35188c2ecf20Sopenharmony_ci goto done; 35198c2ecf20Sopenharmony_ci /* INTx is not supported for VF */ 35208c2ecf20Sopenharmony_ci if (be_virtfn(adapter)) 35218c2ecf20Sopenharmony_ci return status; 35228c2ecf20Sopenharmony_ci } 35238c2ecf20Sopenharmony_ci 35248c2ecf20Sopenharmony_ci /* INTx: only the first EQ is used */ 35258c2ecf20Sopenharmony_ci netdev->irq = adapter->pdev->irq; 35268c2ecf20Sopenharmony_ci status = request_irq(netdev->irq, be_intx, IRQF_SHARED, netdev->name, 35278c2ecf20Sopenharmony_ci &adapter->eq_obj[0]); 35288c2ecf20Sopenharmony_ci if (status) { 35298c2ecf20Sopenharmony_ci dev_err(&adapter->pdev->dev, 35308c2ecf20Sopenharmony_ci "INTx request IRQ failed - err %d\n", status); 35318c2ecf20Sopenharmony_ci return status; 35328c2ecf20Sopenharmony_ci } 35338c2ecf20Sopenharmony_cidone: 35348c2ecf20Sopenharmony_ci adapter->isr_registered = true; 35358c2ecf20Sopenharmony_ci return 0; 35368c2ecf20Sopenharmony_ci} 35378c2ecf20Sopenharmony_ci 35388c2ecf20Sopenharmony_cistatic void be_irq_unregister(struct be_adapter *adapter) 35398c2ecf20Sopenharmony_ci{ 35408c2ecf20Sopenharmony_ci struct net_device *netdev = adapter->netdev; 35418c2ecf20Sopenharmony_ci struct be_eq_obj *eqo; 35428c2ecf20Sopenharmony_ci int i, vec; 35438c2ecf20Sopenharmony_ci 35448c2ecf20Sopenharmony_ci if (!adapter->isr_registered) 35458c2ecf20Sopenharmony_ci return; 35468c2ecf20Sopenharmony_ci 35478c2ecf20Sopenharmony_ci /* INTx */ 35488c2ecf20Sopenharmony_ci if (!msix_enabled(adapter)) { 35498c2ecf20Sopenharmony_ci free_irq(netdev->irq, &adapter->eq_obj[0]); 35508c2ecf20Sopenharmony_ci goto done; 35518c2ecf20Sopenharmony_ci } 35528c2ecf20Sopenharmony_ci 35538c2ecf20Sopenharmony_ci /* MSIx */ 35548c2ecf20Sopenharmony_ci for_all_evt_queues(adapter, eqo, i) { 35558c2ecf20Sopenharmony_ci vec = be_msix_vec_get(adapter, eqo); 35568c2ecf20Sopenharmony_ci irq_set_affinity_hint(vec, NULL); 35578c2ecf20Sopenharmony_ci free_irq(vec, eqo); 35588c2ecf20Sopenharmony_ci } 35598c2ecf20Sopenharmony_ci 35608c2ecf20Sopenharmony_cidone: 35618c2ecf20Sopenharmony_ci adapter->isr_registered = false; 35628c2ecf20Sopenharmony_ci} 35638c2ecf20Sopenharmony_ci 35648c2ecf20Sopenharmony_cistatic void be_rx_qs_destroy(struct be_adapter *adapter) 35658c2ecf20Sopenharmony_ci{ 35668c2ecf20Sopenharmony_ci struct rss_info *rss = &adapter->rss_info; 35678c2ecf20Sopenharmony_ci struct be_queue_info *q; 35688c2ecf20Sopenharmony_ci struct be_rx_obj *rxo; 35698c2ecf20Sopenharmony_ci int i; 35708c2ecf20Sopenharmony_ci 35718c2ecf20Sopenharmony_ci for_all_rx_queues(adapter, rxo, i) { 35728c2ecf20Sopenharmony_ci q = &rxo->q; 35738c2ecf20Sopenharmony_ci if (q->created) { 35748c2ecf20Sopenharmony_ci /* If RXQs are destroyed while in an "out of buffer" 35758c2ecf20Sopenharmony_ci * state, there is a possibility of an HW stall on 35768c2ecf20Sopenharmony_ci * Lancer. So, post 64 buffers to each queue to relieve 35778c2ecf20Sopenharmony_ci * the "out of buffer" condition. 35788c2ecf20Sopenharmony_ci * Make sure there's space in the RXQ before posting. 35798c2ecf20Sopenharmony_ci */ 35808c2ecf20Sopenharmony_ci if (lancer_chip(adapter)) { 35818c2ecf20Sopenharmony_ci be_rx_cq_clean(rxo); 35828c2ecf20Sopenharmony_ci if (atomic_read(&q->used) == 0) 35838c2ecf20Sopenharmony_ci be_post_rx_frags(rxo, GFP_KERNEL, 35848c2ecf20Sopenharmony_ci MAX_RX_POST); 35858c2ecf20Sopenharmony_ci } 35868c2ecf20Sopenharmony_ci 35878c2ecf20Sopenharmony_ci be_cmd_rxq_destroy(adapter, q); 35888c2ecf20Sopenharmony_ci be_rx_cq_clean(rxo); 35898c2ecf20Sopenharmony_ci be_rxq_clean(rxo); 35908c2ecf20Sopenharmony_ci } 35918c2ecf20Sopenharmony_ci be_queue_free(adapter, q); 35928c2ecf20Sopenharmony_ci } 35938c2ecf20Sopenharmony_ci 35948c2ecf20Sopenharmony_ci if (rss->rss_flags) { 35958c2ecf20Sopenharmony_ci rss->rss_flags = RSS_ENABLE_NONE; 35968c2ecf20Sopenharmony_ci be_cmd_rss_config(adapter, rss->rsstable, rss->rss_flags, 35978c2ecf20Sopenharmony_ci 128, rss->rss_hkey); 35988c2ecf20Sopenharmony_ci } 35998c2ecf20Sopenharmony_ci} 36008c2ecf20Sopenharmony_ci 36018c2ecf20Sopenharmony_cistatic void be_disable_if_filters(struct be_adapter *adapter) 36028c2ecf20Sopenharmony_ci{ 36038c2ecf20Sopenharmony_ci /* Don't delete MAC on BE3 VFs without FILTMGMT privilege */ 36048c2ecf20Sopenharmony_ci if (!BEx_chip(adapter) || !be_virtfn(adapter) || 36058c2ecf20Sopenharmony_ci check_privilege(adapter, BE_PRIV_FILTMGMT)) { 36068c2ecf20Sopenharmony_ci be_dev_mac_del(adapter, adapter->pmac_id[0]); 36078c2ecf20Sopenharmony_ci eth_zero_addr(adapter->dev_mac); 36088c2ecf20Sopenharmony_ci } 36098c2ecf20Sopenharmony_ci 36108c2ecf20Sopenharmony_ci be_clear_uc_list(adapter); 36118c2ecf20Sopenharmony_ci be_clear_mc_list(adapter); 36128c2ecf20Sopenharmony_ci 36138c2ecf20Sopenharmony_ci /* The IFACE flags are enabled in the open path and cleared 36148c2ecf20Sopenharmony_ci * in the close path. When a VF gets detached from the host and 36158c2ecf20Sopenharmony_ci * assigned to a VM the following happens: 36168c2ecf20Sopenharmony_ci * - VF's IFACE flags get cleared in the detach path 36178c2ecf20Sopenharmony_ci * - IFACE create is issued by the VF in the attach path 36188c2ecf20Sopenharmony_ci * Due to a bug in the BE3/Skyhawk-R FW 36198c2ecf20Sopenharmony_ci * (Lancer FW doesn't have the bug), the IFACE capability flags 36208c2ecf20Sopenharmony_ci * specified along with the IFACE create cmd issued by a VF are not 36218c2ecf20Sopenharmony_ci * honoured by FW. As a consequence, if a *new* driver 36228c2ecf20Sopenharmony_ci * (that enables/disables IFACE flags in open/close) 36238c2ecf20Sopenharmony_ci * is loaded in the host and an *old* driver is * used by a VM/VF, 36248c2ecf20Sopenharmony_ci * the IFACE gets created *without* the needed flags. 36258c2ecf20Sopenharmony_ci * To avoid this, disable RX-filter flags only for Lancer. 36268c2ecf20Sopenharmony_ci */ 36278c2ecf20Sopenharmony_ci if (lancer_chip(adapter)) { 36288c2ecf20Sopenharmony_ci be_cmd_rx_filter(adapter, BE_IF_ALL_FILT_FLAGS, OFF); 36298c2ecf20Sopenharmony_ci adapter->if_flags &= ~BE_IF_ALL_FILT_FLAGS; 36308c2ecf20Sopenharmony_ci } 36318c2ecf20Sopenharmony_ci} 36328c2ecf20Sopenharmony_ci 36338c2ecf20Sopenharmony_cistatic int be_close(struct net_device *netdev) 36348c2ecf20Sopenharmony_ci{ 36358c2ecf20Sopenharmony_ci struct be_adapter *adapter = netdev_priv(netdev); 36368c2ecf20Sopenharmony_ci struct be_eq_obj *eqo; 36378c2ecf20Sopenharmony_ci int i; 36388c2ecf20Sopenharmony_ci 36398c2ecf20Sopenharmony_ci /* This protection is needed as be_close() may be called even when the 36408c2ecf20Sopenharmony_ci * adapter is in cleared state (after eeh perm failure) 36418c2ecf20Sopenharmony_ci */ 36428c2ecf20Sopenharmony_ci if (!(adapter->flags & BE_FLAGS_SETUP_DONE)) 36438c2ecf20Sopenharmony_ci return 0; 36448c2ecf20Sopenharmony_ci 36458c2ecf20Sopenharmony_ci /* Before attempting cleanup ensure all the pending cmds in the 36468c2ecf20Sopenharmony_ci * config_wq have finished execution 36478c2ecf20Sopenharmony_ci */ 36488c2ecf20Sopenharmony_ci flush_workqueue(be_wq); 36498c2ecf20Sopenharmony_ci 36508c2ecf20Sopenharmony_ci be_disable_if_filters(adapter); 36518c2ecf20Sopenharmony_ci 36528c2ecf20Sopenharmony_ci if (adapter->flags & BE_FLAGS_NAPI_ENABLED) { 36538c2ecf20Sopenharmony_ci for_all_evt_queues(adapter, eqo, i) { 36548c2ecf20Sopenharmony_ci napi_disable(&eqo->napi); 36558c2ecf20Sopenharmony_ci } 36568c2ecf20Sopenharmony_ci adapter->flags &= ~BE_FLAGS_NAPI_ENABLED; 36578c2ecf20Sopenharmony_ci } 36588c2ecf20Sopenharmony_ci 36598c2ecf20Sopenharmony_ci be_async_mcc_disable(adapter); 36608c2ecf20Sopenharmony_ci 36618c2ecf20Sopenharmony_ci /* Wait for all pending tx completions to arrive so that 36628c2ecf20Sopenharmony_ci * all tx skbs are freed. 36638c2ecf20Sopenharmony_ci */ 36648c2ecf20Sopenharmony_ci netif_tx_disable(netdev); 36658c2ecf20Sopenharmony_ci be_tx_compl_clean(adapter); 36668c2ecf20Sopenharmony_ci 36678c2ecf20Sopenharmony_ci be_rx_qs_destroy(adapter); 36688c2ecf20Sopenharmony_ci 36698c2ecf20Sopenharmony_ci for_all_evt_queues(adapter, eqo, i) { 36708c2ecf20Sopenharmony_ci if (msix_enabled(adapter)) 36718c2ecf20Sopenharmony_ci synchronize_irq(be_msix_vec_get(adapter, eqo)); 36728c2ecf20Sopenharmony_ci else 36738c2ecf20Sopenharmony_ci synchronize_irq(netdev->irq); 36748c2ecf20Sopenharmony_ci be_eq_clean(eqo); 36758c2ecf20Sopenharmony_ci } 36768c2ecf20Sopenharmony_ci 36778c2ecf20Sopenharmony_ci be_irq_unregister(adapter); 36788c2ecf20Sopenharmony_ci 36798c2ecf20Sopenharmony_ci return 0; 36808c2ecf20Sopenharmony_ci} 36818c2ecf20Sopenharmony_ci 36828c2ecf20Sopenharmony_cistatic int be_rx_qs_create(struct be_adapter *adapter) 36838c2ecf20Sopenharmony_ci{ 36848c2ecf20Sopenharmony_ci struct rss_info *rss = &adapter->rss_info; 36858c2ecf20Sopenharmony_ci u8 rss_key[RSS_HASH_KEY_LEN]; 36868c2ecf20Sopenharmony_ci struct be_rx_obj *rxo; 36878c2ecf20Sopenharmony_ci int rc, i, j; 36888c2ecf20Sopenharmony_ci 36898c2ecf20Sopenharmony_ci for_all_rx_queues(adapter, rxo, i) { 36908c2ecf20Sopenharmony_ci rc = be_queue_alloc(adapter, &rxo->q, RX_Q_LEN, 36918c2ecf20Sopenharmony_ci sizeof(struct be_eth_rx_d)); 36928c2ecf20Sopenharmony_ci if (rc) 36938c2ecf20Sopenharmony_ci return rc; 36948c2ecf20Sopenharmony_ci } 36958c2ecf20Sopenharmony_ci 36968c2ecf20Sopenharmony_ci if (adapter->need_def_rxq || !adapter->num_rss_qs) { 36978c2ecf20Sopenharmony_ci rxo = default_rxo(adapter); 36988c2ecf20Sopenharmony_ci rc = be_cmd_rxq_create(adapter, &rxo->q, rxo->cq.id, 36998c2ecf20Sopenharmony_ci rx_frag_size, adapter->if_handle, 37008c2ecf20Sopenharmony_ci false, &rxo->rss_id); 37018c2ecf20Sopenharmony_ci if (rc) 37028c2ecf20Sopenharmony_ci return rc; 37038c2ecf20Sopenharmony_ci } 37048c2ecf20Sopenharmony_ci 37058c2ecf20Sopenharmony_ci for_all_rss_queues(adapter, rxo, i) { 37068c2ecf20Sopenharmony_ci rc = be_cmd_rxq_create(adapter, &rxo->q, rxo->cq.id, 37078c2ecf20Sopenharmony_ci rx_frag_size, adapter->if_handle, 37088c2ecf20Sopenharmony_ci true, &rxo->rss_id); 37098c2ecf20Sopenharmony_ci if (rc) 37108c2ecf20Sopenharmony_ci return rc; 37118c2ecf20Sopenharmony_ci } 37128c2ecf20Sopenharmony_ci 37138c2ecf20Sopenharmony_ci if (be_multi_rxq(adapter)) { 37148c2ecf20Sopenharmony_ci for (j = 0; j < RSS_INDIR_TABLE_LEN; j += adapter->num_rss_qs) { 37158c2ecf20Sopenharmony_ci for_all_rss_queues(adapter, rxo, i) { 37168c2ecf20Sopenharmony_ci if ((j + i) >= RSS_INDIR_TABLE_LEN) 37178c2ecf20Sopenharmony_ci break; 37188c2ecf20Sopenharmony_ci rss->rsstable[j + i] = rxo->rss_id; 37198c2ecf20Sopenharmony_ci rss->rss_queue[j + i] = i; 37208c2ecf20Sopenharmony_ci } 37218c2ecf20Sopenharmony_ci } 37228c2ecf20Sopenharmony_ci rss->rss_flags = RSS_ENABLE_TCP_IPV4 | RSS_ENABLE_IPV4 | 37238c2ecf20Sopenharmony_ci RSS_ENABLE_TCP_IPV6 | RSS_ENABLE_IPV6; 37248c2ecf20Sopenharmony_ci 37258c2ecf20Sopenharmony_ci if (!BEx_chip(adapter)) 37268c2ecf20Sopenharmony_ci rss->rss_flags |= RSS_ENABLE_UDP_IPV4 | 37278c2ecf20Sopenharmony_ci RSS_ENABLE_UDP_IPV6; 37288c2ecf20Sopenharmony_ci 37298c2ecf20Sopenharmony_ci netdev_rss_key_fill(rss_key, RSS_HASH_KEY_LEN); 37308c2ecf20Sopenharmony_ci rc = be_cmd_rss_config(adapter, rss->rsstable, rss->rss_flags, 37318c2ecf20Sopenharmony_ci RSS_INDIR_TABLE_LEN, rss_key); 37328c2ecf20Sopenharmony_ci if (rc) { 37338c2ecf20Sopenharmony_ci rss->rss_flags = RSS_ENABLE_NONE; 37348c2ecf20Sopenharmony_ci return rc; 37358c2ecf20Sopenharmony_ci } 37368c2ecf20Sopenharmony_ci 37378c2ecf20Sopenharmony_ci memcpy(rss->rss_hkey, rss_key, RSS_HASH_KEY_LEN); 37388c2ecf20Sopenharmony_ci } else { 37398c2ecf20Sopenharmony_ci /* Disable RSS, if only default RX Q is created */ 37408c2ecf20Sopenharmony_ci rss->rss_flags = RSS_ENABLE_NONE; 37418c2ecf20Sopenharmony_ci } 37428c2ecf20Sopenharmony_ci 37438c2ecf20Sopenharmony_ci 37448c2ecf20Sopenharmony_ci /* Post 1 less than RXQ-len to avoid head being equal to tail, 37458c2ecf20Sopenharmony_ci * which is a queue empty condition 37468c2ecf20Sopenharmony_ci */ 37478c2ecf20Sopenharmony_ci for_all_rx_queues(adapter, rxo, i) 37488c2ecf20Sopenharmony_ci be_post_rx_frags(rxo, GFP_KERNEL, RX_Q_LEN - 1); 37498c2ecf20Sopenharmony_ci 37508c2ecf20Sopenharmony_ci return 0; 37518c2ecf20Sopenharmony_ci} 37528c2ecf20Sopenharmony_ci 37538c2ecf20Sopenharmony_cistatic int be_enable_if_filters(struct be_adapter *adapter) 37548c2ecf20Sopenharmony_ci{ 37558c2ecf20Sopenharmony_ci int status; 37568c2ecf20Sopenharmony_ci 37578c2ecf20Sopenharmony_ci status = be_cmd_rx_filter(adapter, BE_IF_FILT_FLAGS_BASIC, ON); 37588c2ecf20Sopenharmony_ci if (status) 37598c2ecf20Sopenharmony_ci return status; 37608c2ecf20Sopenharmony_ci 37618c2ecf20Sopenharmony_ci /* Normally this condition usually true as the ->dev_mac is zeroed. 37628c2ecf20Sopenharmony_ci * But on BE3 VFs the initial MAC is pre-programmed by PF and 37638c2ecf20Sopenharmony_ci * subsequent be_dev_mac_add() can fail (after fresh boot) 37648c2ecf20Sopenharmony_ci */ 37658c2ecf20Sopenharmony_ci if (!ether_addr_equal(adapter->dev_mac, adapter->netdev->dev_addr)) { 37668c2ecf20Sopenharmony_ci int old_pmac_id = -1; 37678c2ecf20Sopenharmony_ci 37688c2ecf20Sopenharmony_ci /* Remember old programmed MAC if any - can happen on BE3 VF */ 37698c2ecf20Sopenharmony_ci if (!is_zero_ether_addr(adapter->dev_mac)) 37708c2ecf20Sopenharmony_ci old_pmac_id = adapter->pmac_id[0]; 37718c2ecf20Sopenharmony_ci 37728c2ecf20Sopenharmony_ci status = be_dev_mac_add(adapter, adapter->netdev->dev_addr); 37738c2ecf20Sopenharmony_ci if (status) 37748c2ecf20Sopenharmony_ci return status; 37758c2ecf20Sopenharmony_ci 37768c2ecf20Sopenharmony_ci /* Delete the old programmed MAC as we successfully programmed 37778c2ecf20Sopenharmony_ci * a new MAC 37788c2ecf20Sopenharmony_ci */ 37798c2ecf20Sopenharmony_ci if (old_pmac_id >= 0 && old_pmac_id != adapter->pmac_id[0]) 37808c2ecf20Sopenharmony_ci be_dev_mac_del(adapter, old_pmac_id); 37818c2ecf20Sopenharmony_ci 37828c2ecf20Sopenharmony_ci ether_addr_copy(adapter->dev_mac, adapter->netdev->dev_addr); 37838c2ecf20Sopenharmony_ci } 37848c2ecf20Sopenharmony_ci 37858c2ecf20Sopenharmony_ci if (adapter->vlans_added) 37868c2ecf20Sopenharmony_ci be_vid_config(adapter); 37878c2ecf20Sopenharmony_ci 37888c2ecf20Sopenharmony_ci __be_set_rx_mode(adapter); 37898c2ecf20Sopenharmony_ci 37908c2ecf20Sopenharmony_ci return 0; 37918c2ecf20Sopenharmony_ci} 37928c2ecf20Sopenharmony_ci 37938c2ecf20Sopenharmony_cistatic int be_open(struct net_device *netdev) 37948c2ecf20Sopenharmony_ci{ 37958c2ecf20Sopenharmony_ci struct be_adapter *adapter = netdev_priv(netdev); 37968c2ecf20Sopenharmony_ci struct be_eq_obj *eqo; 37978c2ecf20Sopenharmony_ci struct be_rx_obj *rxo; 37988c2ecf20Sopenharmony_ci struct be_tx_obj *txo; 37998c2ecf20Sopenharmony_ci u8 link_status; 38008c2ecf20Sopenharmony_ci int status, i; 38018c2ecf20Sopenharmony_ci 38028c2ecf20Sopenharmony_ci status = be_rx_qs_create(adapter); 38038c2ecf20Sopenharmony_ci if (status) 38048c2ecf20Sopenharmony_ci goto err; 38058c2ecf20Sopenharmony_ci 38068c2ecf20Sopenharmony_ci status = be_enable_if_filters(adapter); 38078c2ecf20Sopenharmony_ci if (status) 38088c2ecf20Sopenharmony_ci goto err; 38098c2ecf20Sopenharmony_ci 38108c2ecf20Sopenharmony_ci status = be_irq_register(adapter); 38118c2ecf20Sopenharmony_ci if (status) 38128c2ecf20Sopenharmony_ci goto err; 38138c2ecf20Sopenharmony_ci 38148c2ecf20Sopenharmony_ci for_all_rx_queues(adapter, rxo, i) 38158c2ecf20Sopenharmony_ci be_cq_notify(adapter, rxo->cq.id, true, 0); 38168c2ecf20Sopenharmony_ci 38178c2ecf20Sopenharmony_ci for_all_tx_queues(adapter, txo, i) 38188c2ecf20Sopenharmony_ci be_cq_notify(adapter, txo->cq.id, true, 0); 38198c2ecf20Sopenharmony_ci 38208c2ecf20Sopenharmony_ci be_async_mcc_enable(adapter); 38218c2ecf20Sopenharmony_ci 38228c2ecf20Sopenharmony_ci for_all_evt_queues(adapter, eqo, i) { 38238c2ecf20Sopenharmony_ci napi_enable(&eqo->napi); 38248c2ecf20Sopenharmony_ci be_eq_notify(adapter, eqo->q.id, true, true, 0, 0); 38258c2ecf20Sopenharmony_ci } 38268c2ecf20Sopenharmony_ci adapter->flags |= BE_FLAGS_NAPI_ENABLED; 38278c2ecf20Sopenharmony_ci 38288c2ecf20Sopenharmony_ci status = be_cmd_link_status_query(adapter, NULL, &link_status, 0); 38298c2ecf20Sopenharmony_ci if (!status) 38308c2ecf20Sopenharmony_ci be_link_status_update(adapter, link_status); 38318c2ecf20Sopenharmony_ci 38328c2ecf20Sopenharmony_ci netif_tx_start_all_queues(netdev); 38338c2ecf20Sopenharmony_ci 38348c2ecf20Sopenharmony_ci udp_tunnel_nic_reset_ntf(netdev); 38358c2ecf20Sopenharmony_ci 38368c2ecf20Sopenharmony_ci return 0; 38378c2ecf20Sopenharmony_cierr: 38388c2ecf20Sopenharmony_ci be_close(adapter->netdev); 38398c2ecf20Sopenharmony_ci return -EIO; 38408c2ecf20Sopenharmony_ci} 38418c2ecf20Sopenharmony_ci 38428c2ecf20Sopenharmony_cistatic void be_vf_eth_addr_generate(struct be_adapter *adapter, u8 *mac) 38438c2ecf20Sopenharmony_ci{ 38448c2ecf20Sopenharmony_ci u32 addr; 38458c2ecf20Sopenharmony_ci 38468c2ecf20Sopenharmony_ci addr = jhash(adapter->netdev->dev_addr, ETH_ALEN, 0); 38478c2ecf20Sopenharmony_ci 38488c2ecf20Sopenharmony_ci mac[5] = (u8)(addr & 0xFF); 38498c2ecf20Sopenharmony_ci mac[4] = (u8)((addr >> 8) & 0xFF); 38508c2ecf20Sopenharmony_ci mac[3] = (u8)((addr >> 16) & 0xFF); 38518c2ecf20Sopenharmony_ci /* Use the OUI from the current MAC address */ 38528c2ecf20Sopenharmony_ci memcpy(mac, adapter->netdev->dev_addr, 3); 38538c2ecf20Sopenharmony_ci} 38548c2ecf20Sopenharmony_ci 38558c2ecf20Sopenharmony_ci/* 38568c2ecf20Sopenharmony_ci * Generate a seed MAC address from the PF MAC Address using jhash. 38578c2ecf20Sopenharmony_ci * MAC Address for VFs are assigned incrementally starting from the seed. 38588c2ecf20Sopenharmony_ci * These addresses are programmed in the ASIC by the PF and the VF driver 38598c2ecf20Sopenharmony_ci * queries for the MAC address during its probe. 38608c2ecf20Sopenharmony_ci */ 38618c2ecf20Sopenharmony_cistatic int be_vf_eth_addr_config(struct be_adapter *adapter) 38628c2ecf20Sopenharmony_ci{ 38638c2ecf20Sopenharmony_ci u32 vf; 38648c2ecf20Sopenharmony_ci int status = 0; 38658c2ecf20Sopenharmony_ci u8 mac[ETH_ALEN]; 38668c2ecf20Sopenharmony_ci struct be_vf_cfg *vf_cfg; 38678c2ecf20Sopenharmony_ci 38688c2ecf20Sopenharmony_ci be_vf_eth_addr_generate(adapter, mac); 38698c2ecf20Sopenharmony_ci 38708c2ecf20Sopenharmony_ci for_all_vfs(adapter, vf_cfg, vf) { 38718c2ecf20Sopenharmony_ci if (BEx_chip(adapter)) 38728c2ecf20Sopenharmony_ci status = be_cmd_pmac_add(adapter, mac, 38738c2ecf20Sopenharmony_ci vf_cfg->if_handle, 38748c2ecf20Sopenharmony_ci &vf_cfg->pmac_id, vf + 1); 38758c2ecf20Sopenharmony_ci else 38768c2ecf20Sopenharmony_ci status = be_cmd_set_mac(adapter, mac, vf_cfg->if_handle, 38778c2ecf20Sopenharmony_ci vf + 1); 38788c2ecf20Sopenharmony_ci 38798c2ecf20Sopenharmony_ci if (status) 38808c2ecf20Sopenharmony_ci dev_err(&adapter->pdev->dev, 38818c2ecf20Sopenharmony_ci "Mac address assignment failed for VF %d\n", 38828c2ecf20Sopenharmony_ci vf); 38838c2ecf20Sopenharmony_ci else 38848c2ecf20Sopenharmony_ci memcpy(vf_cfg->mac_addr, mac, ETH_ALEN); 38858c2ecf20Sopenharmony_ci 38868c2ecf20Sopenharmony_ci mac[5] += 1; 38878c2ecf20Sopenharmony_ci } 38888c2ecf20Sopenharmony_ci return status; 38898c2ecf20Sopenharmony_ci} 38908c2ecf20Sopenharmony_ci 38918c2ecf20Sopenharmony_cistatic int be_vfs_mac_query(struct be_adapter *adapter) 38928c2ecf20Sopenharmony_ci{ 38938c2ecf20Sopenharmony_ci int status, vf; 38948c2ecf20Sopenharmony_ci u8 mac[ETH_ALEN]; 38958c2ecf20Sopenharmony_ci struct be_vf_cfg *vf_cfg; 38968c2ecf20Sopenharmony_ci 38978c2ecf20Sopenharmony_ci for_all_vfs(adapter, vf_cfg, vf) { 38988c2ecf20Sopenharmony_ci status = be_cmd_get_active_mac(adapter, vf_cfg->pmac_id, 38998c2ecf20Sopenharmony_ci mac, vf_cfg->if_handle, 39008c2ecf20Sopenharmony_ci false, vf+1); 39018c2ecf20Sopenharmony_ci if (status) 39028c2ecf20Sopenharmony_ci return status; 39038c2ecf20Sopenharmony_ci memcpy(vf_cfg->mac_addr, mac, ETH_ALEN); 39048c2ecf20Sopenharmony_ci } 39058c2ecf20Sopenharmony_ci return 0; 39068c2ecf20Sopenharmony_ci} 39078c2ecf20Sopenharmony_ci 39088c2ecf20Sopenharmony_cistatic void be_vf_clear(struct be_adapter *adapter) 39098c2ecf20Sopenharmony_ci{ 39108c2ecf20Sopenharmony_ci struct be_vf_cfg *vf_cfg; 39118c2ecf20Sopenharmony_ci u32 vf; 39128c2ecf20Sopenharmony_ci 39138c2ecf20Sopenharmony_ci if (pci_vfs_assigned(adapter->pdev)) { 39148c2ecf20Sopenharmony_ci dev_warn(&adapter->pdev->dev, 39158c2ecf20Sopenharmony_ci "VFs are assigned to VMs: not disabling VFs\n"); 39168c2ecf20Sopenharmony_ci goto done; 39178c2ecf20Sopenharmony_ci } 39188c2ecf20Sopenharmony_ci 39198c2ecf20Sopenharmony_ci pci_disable_sriov(adapter->pdev); 39208c2ecf20Sopenharmony_ci 39218c2ecf20Sopenharmony_ci for_all_vfs(adapter, vf_cfg, vf) { 39228c2ecf20Sopenharmony_ci if (BEx_chip(adapter)) 39238c2ecf20Sopenharmony_ci be_cmd_pmac_del(adapter, vf_cfg->if_handle, 39248c2ecf20Sopenharmony_ci vf_cfg->pmac_id, vf + 1); 39258c2ecf20Sopenharmony_ci else 39268c2ecf20Sopenharmony_ci be_cmd_set_mac(adapter, NULL, vf_cfg->if_handle, 39278c2ecf20Sopenharmony_ci vf + 1); 39288c2ecf20Sopenharmony_ci 39298c2ecf20Sopenharmony_ci be_cmd_if_destroy(adapter, vf_cfg->if_handle, vf + 1); 39308c2ecf20Sopenharmony_ci } 39318c2ecf20Sopenharmony_ci 39328c2ecf20Sopenharmony_ci if (BE3_chip(adapter)) 39338c2ecf20Sopenharmony_ci be_cmd_set_hsw_config(adapter, 0, 0, 39348c2ecf20Sopenharmony_ci adapter->if_handle, 39358c2ecf20Sopenharmony_ci PORT_FWD_TYPE_PASSTHRU, 0); 39368c2ecf20Sopenharmony_cidone: 39378c2ecf20Sopenharmony_ci kfree(adapter->vf_cfg); 39388c2ecf20Sopenharmony_ci adapter->num_vfs = 0; 39398c2ecf20Sopenharmony_ci adapter->flags &= ~BE_FLAGS_SRIOV_ENABLED; 39408c2ecf20Sopenharmony_ci} 39418c2ecf20Sopenharmony_ci 39428c2ecf20Sopenharmony_cistatic void be_clear_queues(struct be_adapter *adapter) 39438c2ecf20Sopenharmony_ci{ 39448c2ecf20Sopenharmony_ci be_mcc_queues_destroy(adapter); 39458c2ecf20Sopenharmony_ci be_rx_cqs_destroy(adapter); 39468c2ecf20Sopenharmony_ci be_tx_queues_destroy(adapter); 39478c2ecf20Sopenharmony_ci be_evt_queues_destroy(adapter); 39488c2ecf20Sopenharmony_ci} 39498c2ecf20Sopenharmony_ci 39508c2ecf20Sopenharmony_cistatic void be_cancel_worker(struct be_adapter *adapter) 39518c2ecf20Sopenharmony_ci{ 39528c2ecf20Sopenharmony_ci if (adapter->flags & BE_FLAGS_WORKER_SCHEDULED) { 39538c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&adapter->work); 39548c2ecf20Sopenharmony_ci adapter->flags &= ~BE_FLAGS_WORKER_SCHEDULED; 39558c2ecf20Sopenharmony_ci } 39568c2ecf20Sopenharmony_ci} 39578c2ecf20Sopenharmony_ci 39588c2ecf20Sopenharmony_cistatic void be_cancel_err_detection(struct be_adapter *adapter) 39598c2ecf20Sopenharmony_ci{ 39608c2ecf20Sopenharmony_ci struct be_error_recovery *err_rec = &adapter->error_recovery; 39618c2ecf20Sopenharmony_ci 39628c2ecf20Sopenharmony_ci if (!be_err_recovery_workq) 39638c2ecf20Sopenharmony_ci return; 39648c2ecf20Sopenharmony_ci 39658c2ecf20Sopenharmony_ci if (adapter->flags & BE_FLAGS_ERR_DETECTION_SCHEDULED) { 39668c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&err_rec->err_detection_work); 39678c2ecf20Sopenharmony_ci adapter->flags &= ~BE_FLAGS_ERR_DETECTION_SCHEDULED; 39688c2ecf20Sopenharmony_ci } 39698c2ecf20Sopenharmony_ci} 39708c2ecf20Sopenharmony_ci 39718c2ecf20Sopenharmony_ci/* VxLAN offload Notes: 39728c2ecf20Sopenharmony_ci * 39738c2ecf20Sopenharmony_ci * The stack defines tunnel offload flags (hw_enc_features) for IP and doesn't 39748c2ecf20Sopenharmony_ci * distinguish various types of transports (VxLAN, GRE, NVGRE ..). So, offload 39758c2ecf20Sopenharmony_ci * is expected to work across all types of IP tunnels once exported. Skyhawk 39768c2ecf20Sopenharmony_ci * supports offloads for either VxLAN or NVGRE, exclusively. So we export VxLAN 39778c2ecf20Sopenharmony_ci * offloads in hw_enc_features only when a VxLAN port is added. If other (non 39788c2ecf20Sopenharmony_ci * VxLAN) tunnels are configured while VxLAN offloads are enabled, offloads for 39798c2ecf20Sopenharmony_ci * those other tunnels are unexported on the fly through ndo_features_check(). 39808c2ecf20Sopenharmony_ci */ 39818c2ecf20Sopenharmony_cistatic int be_vxlan_set_port(struct net_device *netdev, unsigned int table, 39828c2ecf20Sopenharmony_ci unsigned int entry, struct udp_tunnel_info *ti) 39838c2ecf20Sopenharmony_ci{ 39848c2ecf20Sopenharmony_ci struct be_adapter *adapter = netdev_priv(netdev); 39858c2ecf20Sopenharmony_ci struct device *dev = &adapter->pdev->dev; 39868c2ecf20Sopenharmony_ci int status; 39878c2ecf20Sopenharmony_ci 39888c2ecf20Sopenharmony_ci status = be_cmd_manage_iface(adapter, adapter->if_handle, 39898c2ecf20Sopenharmony_ci OP_CONVERT_NORMAL_TO_TUNNEL); 39908c2ecf20Sopenharmony_ci if (status) { 39918c2ecf20Sopenharmony_ci dev_warn(dev, "Failed to convert normal interface to tunnel\n"); 39928c2ecf20Sopenharmony_ci return status; 39938c2ecf20Sopenharmony_ci } 39948c2ecf20Sopenharmony_ci adapter->flags |= BE_FLAGS_VXLAN_OFFLOADS; 39958c2ecf20Sopenharmony_ci 39968c2ecf20Sopenharmony_ci status = be_cmd_set_vxlan_port(adapter, ti->port); 39978c2ecf20Sopenharmony_ci if (status) { 39988c2ecf20Sopenharmony_ci dev_warn(dev, "Failed to add VxLAN port\n"); 39998c2ecf20Sopenharmony_ci return status; 40008c2ecf20Sopenharmony_ci } 40018c2ecf20Sopenharmony_ci adapter->vxlan_port = ti->port; 40028c2ecf20Sopenharmony_ci 40038c2ecf20Sopenharmony_ci netdev->hw_enc_features |= NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | 40048c2ecf20Sopenharmony_ci NETIF_F_TSO | NETIF_F_TSO6 | 40058c2ecf20Sopenharmony_ci NETIF_F_GSO_UDP_TUNNEL; 40068c2ecf20Sopenharmony_ci 40078c2ecf20Sopenharmony_ci dev_info(dev, "Enabled VxLAN offloads for UDP port %d\n", 40088c2ecf20Sopenharmony_ci be16_to_cpu(ti->port)); 40098c2ecf20Sopenharmony_ci return 0; 40108c2ecf20Sopenharmony_ci} 40118c2ecf20Sopenharmony_ci 40128c2ecf20Sopenharmony_cistatic int be_vxlan_unset_port(struct net_device *netdev, unsigned int table, 40138c2ecf20Sopenharmony_ci unsigned int entry, struct udp_tunnel_info *ti) 40148c2ecf20Sopenharmony_ci{ 40158c2ecf20Sopenharmony_ci struct be_adapter *adapter = netdev_priv(netdev); 40168c2ecf20Sopenharmony_ci 40178c2ecf20Sopenharmony_ci if (adapter->flags & BE_FLAGS_VXLAN_OFFLOADS) 40188c2ecf20Sopenharmony_ci be_cmd_manage_iface(adapter, adapter->if_handle, 40198c2ecf20Sopenharmony_ci OP_CONVERT_TUNNEL_TO_NORMAL); 40208c2ecf20Sopenharmony_ci 40218c2ecf20Sopenharmony_ci if (adapter->vxlan_port) 40228c2ecf20Sopenharmony_ci be_cmd_set_vxlan_port(adapter, 0); 40238c2ecf20Sopenharmony_ci 40248c2ecf20Sopenharmony_ci adapter->flags &= ~BE_FLAGS_VXLAN_OFFLOADS; 40258c2ecf20Sopenharmony_ci adapter->vxlan_port = 0; 40268c2ecf20Sopenharmony_ci 40278c2ecf20Sopenharmony_ci netdev->hw_enc_features = 0; 40288c2ecf20Sopenharmony_ci return 0; 40298c2ecf20Sopenharmony_ci} 40308c2ecf20Sopenharmony_ci 40318c2ecf20Sopenharmony_cistatic const struct udp_tunnel_nic_info be_udp_tunnels = { 40328c2ecf20Sopenharmony_ci .set_port = be_vxlan_set_port, 40338c2ecf20Sopenharmony_ci .unset_port = be_vxlan_unset_port, 40348c2ecf20Sopenharmony_ci .flags = UDP_TUNNEL_NIC_INFO_MAY_SLEEP | 40358c2ecf20Sopenharmony_ci UDP_TUNNEL_NIC_INFO_OPEN_ONLY, 40368c2ecf20Sopenharmony_ci .tables = { 40378c2ecf20Sopenharmony_ci { .n_entries = 1, .tunnel_types = UDP_TUNNEL_TYPE_VXLAN, }, 40388c2ecf20Sopenharmony_ci }, 40398c2ecf20Sopenharmony_ci}; 40408c2ecf20Sopenharmony_ci 40418c2ecf20Sopenharmony_cistatic void be_calculate_vf_res(struct be_adapter *adapter, u16 num_vfs, 40428c2ecf20Sopenharmony_ci struct be_resources *vft_res) 40438c2ecf20Sopenharmony_ci{ 40448c2ecf20Sopenharmony_ci struct be_resources res = adapter->pool_res; 40458c2ecf20Sopenharmony_ci u32 vf_if_cap_flags = res.vf_if_cap_flags; 40468c2ecf20Sopenharmony_ci struct be_resources res_mod = {0}; 40478c2ecf20Sopenharmony_ci u16 num_vf_qs = 1; 40488c2ecf20Sopenharmony_ci 40498c2ecf20Sopenharmony_ci /* Distribute the queue resources among the PF and it's VFs */ 40508c2ecf20Sopenharmony_ci if (num_vfs) { 40518c2ecf20Sopenharmony_ci /* Divide the rx queues evenly among the VFs and the PF, capped 40528c2ecf20Sopenharmony_ci * at VF-EQ-count. Any remainder queues belong to the PF. 40538c2ecf20Sopenharmony_ci */ 40548c2ecf20Sopenharmony_ci num_vf_qs = min(SH_VF_MAX_NIC_EQS, 40558c2ecf20Sopenharmony_ci res.max_rss_qs / (num_vfs + 1)); 40568c2ecf20Sopenharmony_ci 40578c2ecf20Sopenharmony_ci /* Skyhawk-R chip supports only MAX_PORT_RSS_TABLES 40588c2ecf20Sopenharmony_ci * RSS Tables per port. Provide RSS on VFs, only if number of 40598c2ecf20Sopenharmony_ci * VFs requested is less than it's PF Pool's RSS Tables limit. 40608c2ecf20Sopenharmony_ci */ 40618c2ecf20Sopenharmony_ci if (num_vfs >= be_max_pf_pool_rss_tables(adapter)) 40628c2ecf20Sopenharmony_ci num_vf_qs = 1; 40638c2ecf20Sopenharmony_ci } 40648c2ecf20Sopenharmony_ci 40658c2ecf20Sopenharmony_ci /* Resource with fields set to all '1's by GET_PROFILE_CONFIG cmd, 40668c2ecf20Sopenharmony_ci * which are modifiable using SET_PROFILE_CONFIG cmd. 40678c2ecf20Sopenharmony_ci */ 40688c2ecf20Sopenharmony_ci be_cmd_get_profile_config(adapter, &res_mod, NULL, ACTIVE_PROFILE_TYPE, 40698c2ecf20Sopenharmony_ci RESOURCE_MODIFIABLE, 0); 40708c2ecf20Sopenharmony_ci 40718c2ecf20Sopenharmony_ci /* If RSS IFACE capability flags are modifiable for a VF, set the 40728c2ecf20Sopenharmony_ci * capability flag as valid and set RSS and DEFQ_RSS IFACE flags if 40738c2ecf20Sopenharmony_ci * more than 1 RSSQ is available for a VF. 40748c2ecf20Sopenharmony_ci * Otherwise, provision only 1 queue pair for VF. 40758c2ecf20Sopenharmony_ci */ 40768c2ecf20Sopenharmony_ci if (res_mod.vf_if_cap_flags & BE_IF_FLAGS_RSS) { 40778c2ecf20Sopenharmony_ci vft_res->flags |= BIT(IF_CAPS_FLAGS_VALID_SHIFT); 40788c2ecf20Sopenharmony_ci if (num_vf_qs > 1) { 40798c2ecf20Sopenharmony_ci vf_if_cap_flags |= BE_IF_FLAGS_RSS; 40808c2ecf20Sopenharmony_ci if (res.if_cap_flags & BE_IF_FLAGS_DEFQ_RSS) 40818c2ecf20Sopenharmony_ci vf_if_cap_flags |= BE_IF_FLAGS_DEFQ_RSS; 40828c2ecf20Sopenharmony_ci } else { 40838c2ecf20Sopenharmony_ci vf_if_cap_flags &= ~(BE_IF_FLAGS_RSS | 40848c2ecf20Sopenharmony_ci BE_IF_FLAGS_DEFQ_RSS); 40858c2ecf20Sopenharmony_ci } 40868c2ecf20Sopenharmony_ci } else { 40878c2ecf20Sopenharmony_ci num_vf_qs = 1; 40888c2ecf20Sopenharmony_ci } 40898c2ecf20Sopenharmony_ci 40908c2ecf20Sopenharmony_ci if (res_mod.vf_if_cap_flags & BE_IF_FLAGS_VLAN_PROMISCUOUS) { 40918c2ecf20Sopenharmony_ci vft_res->flags |= BIT(IF_CAPS_FLAGS_VALID_SHIFT); 40928c2ecf20Sopenharmony_ci vf_if_cap_flags &= ~BE_IF_FLAGS_VLAN_PROMISCUOUS; 40938c2ecf20Sopenharmony_ci } 40948c2ecf20Sopenharmony_ci 40958c2ecf20Sopenharmony_ci vft_res->vf_if_cap_flags = vf_if_cap_flags; 40968c2ecf20Sopenharmony_ci vft_res->max_rx_qs = num_vf_qs; 40978c2ecf20Sopenharmony_ci vft_res->max_rss_qs = num_vf_qs; 40988c2ecf20Sopenharmony_ci vft_res->max_tx_qs = res.max_tx_qs / (num_vfs + 1); 40998c2ecf20Sopenharmony_ci vft_res->max_cq_count = res.max_cq_count / (num_vfs + 1); 41008c2ecf20Sopenharmony_ci 41018c2ecf20Sopenharmony_ci /* Distribute unicast MACs, VLANs, IFACE count and MCCQ count equally 41028c2ecf20Sopenharmony_ci * among the PF and it's VFs, if the fields are changeable 41038c2ecf20Sopenharmony_ci */ 41048c2ecf20Sopenharmony_ci if (res_mod.max_uc_mac == FIELD_MODIFIABLE) 41058c2ecf20Sopenharmony_ci vft_res->max_uc_mac = res.max_uc_mac / (num_vfs + 1); 41068c2ecf20Sopenharmony_ci 41078c2ecf20Sopenharmony_ci if (res_mod.max_vlans == FIELD_MODIFIABLE) 41088c2ecf20Sopenharmony_ci vft_res->max_vlans = res.max_vlans / (num_vfs + 1); 41098c2ecf20Sopenharmony_ci 41108c2ecf20Sopenharmony_ci if (res_mod.max_iface_count == FIELD_MODIFIABLE) 41118c2ecf20Sopenharmony_ci vft_res->max_iface_count = res.max_iface_count / (num_vfs + 1); 41128c2ecf20Sopenharmony_ci 41138c2ecf20Sopenharmony_ci if (res_mod.max_mcc_count == FIELD_MODIFIABLE) 41148c2ecf20Sopenharmony_ci vft_res->max_mcc_count = res.max_mcc_count / (num_vfs + 1); 41158c2ecf20Sopenharmony_ci} 41168c2ecf20Sopenharmony_ci 41178c2ecf20Sopenharmony_cistatic void be_if_destroy(struct be_adapter *adapter) 41188c2ecf20Sopenharmony_ci{ 41198c2ecf20Sopenharmony_ci be_cmd_if_destroy(adapter, adapter->if_handle, 0); 41208c2ecf20Sopenharmony_ci 41218c2ecf20Sopenharmony_ci kfree(adapter->pmac_id); 41228c2ecf20Sopenharmony_ci adapter->pmac_id = NULL; 41238c2ecf20Sopenharmony_ci 41248c2ecf20Sopenharmony_ci kfree(adapter->mc_list); 41258c2ecf20Sopenharmony_ci adapter->mc_list = NULL; 41268c2ecf20Sopenharmony_ci 41278c2ecf20Sopenharmony_ci kfree(adapter->uc_list); 41288c2ecf20Sopenharmony_ci adapter->uc_list = NULL; 41298c2ecf20Sopenharmony_ci} 41308c2ecf20Sopenharmony_ci 41318c2ecf20Sopenharmony_cistatic int be_clear(struct be_adapter *adapter) 41328c2ecf20Sopenharmony_ci{ 41338c2ecf20Sopenharmony_ci struct pci_dev *pdev = adapter->pdev; 41348c2ecf20Sopenharmony_ci struct be_resources vft_res = {0}; 41358c2ecf20Sopenharmony_ci 41368c2ecf20Sopenharmony_ci be_cancel_worker(adapter); 41378c2ecf20Sopenharmony_ci 41388c2ecf20Sopenharmony_ci flush_workqueue(be_wq); 41398c2ecf20Sopenharmony_ci 41408c2ecf20Sopenharmony_ci if (sriov_enabled(adapter)) 41418c2ecf20Sopenharmony_ci be_vf_clear(adapter); 41428c2ecf20Sopenharmony_ci 41438c2ecf20Sopenharmony_ci /* Re-configure FW to distribute resources evenly across max-supported 41448c2ecf20Sopenharmony_ci * number of VFs, only when VFs are not already enabled. 41458c2ecf20Sopenharmony_ci */ 41468c2ecf20Sopenharmony_ci if (skyhawk_chip(adapter) && be_physfn(adapter) && 41478c2ecf20Sopenharmony_ci !pci_vfs_assigned(pdev)) { 41488c2ecf20Sopenharmony_ci be_calculate_vf_res(adapter, 41498c2ecf20Sopenharmony_ci pci_sriov_get_totalvfs(pdev), 41508c2ecf20Sopenharmony_ci &vft_res); 41518c2ecf20Sopenharmony_ci be_cmd_set_sriov_config(adapter, adapter->pool_res, 41528c2ecf20Sopenharmony_ci pci_sriov_get_totalvfs(pdev), 41538c2ecf20Sopenharmony_ci &vft_res); 41548c2ecf20Sopenharmony_ci } 41558c2ecf20Sopenharmony_ci 41568c2ecf20Sopenharmony_ci be_vxlan_unset_port(adapter->netdev, 0, 0, NULL); 41578c2ecf20Sopenharmony_ci 41588c2ecf20Sopenharmony_ci be_if_destroy(adapter); 41598c2ecf20Sopenharmony_ci 41608c2ecf20Sopenharmony_ci be_clear_queues(adapter); 41618c2ecf20Sopenharmony_ci 41628c2ecf20Sopenharmony_ci be_msix_disable(adapter); 41638c2ecf20Sopenharmony_ci adapter->flags &= ~BE_FLAGS_SETUP_DONE; 41648c2ecf20Sopenharmony_ci return 0; 41658c2ecf20Sopenharmony_ci} 41668c2ecf20Sopenharmony_ci 41678c2ecf20Sopenharmony_cistatic int be_vfs_if_create(struct be_adapter *adapter) 41688c2ecf20Sopenharmony_ci{ 41698c2ecf20Sopenharmony_ci struct be_resources res = {0}; 41708c2ecf20Sopenharmony_ci u32 cap_flags, en_flags, vf; 41718c2ecf20Sopenharmony_ci struct be_vf_cfg *vf_cfg; 41728c2ecf20Sopenharmony_ci int status; 41738c2ecf20Sopenharmony_ci 41748c2ecf20Sopenharmony_ci /* If a FW profile exists, then cap_flags are updated */ 41758c2ecf20Sopenharmony_ci cap_flags = BE_VF_IF_EN_FLAGS; 41768c2ecf20Sopenharmony_ci 41778c2ecf20Sopenharmony_ci for_all_vfs(adapter, vf_cfg, vf) { 41788c2ecf20Sopenharmony_ci if (!BE3_chip(adapter)) { 41798c2ecf20Sopenharmony_ci status = be_cmd_get_profile_config(adapter, &res, NULL, 41808c2ecf20Sopenharmony_ci ACTIVE_PROFILE_TYPE, 41818c2ecf20Sopenharmony_ci RESOURCE_LIMITS, 41828c2ecf20Sopenharmony_ci vf + 1); 41838c2ecf20Sopenharmony_ci if (!status) { 41848c2ecf20Sopenharmony_ci cap_flags = res.if_cap_flags; 41858c2ecf20Sopenharmony_ci /* Prevent VFs from enabling VLAN promiscuous 41868c2ecf20Sopenharmony_ci * mode 41878c2ecf20Sopenharmony_ci */ 41888c2ecf20Sopenharmony_ci cap_flags &= ~BE_IF_FLAGS_VLAN_PROMISCUOUS; 41898c2ecf20Sopenharmony_ci } 41908c2ecf20Sopenharmony_ci } 41918c2ecf20Sopenharmony_ci 41928c2ecf20Sopenharmony_ci /* PF should enable IF flags during proxy if_create call */ 41938c2ecf20Sopenharmony_ci en_flags = cap_flags & BE_VF_IF_EN_FLAGS; 41948c2ecf20Sopenharmony_ci status = be_cmd_if_create(adapter, cap_flags, en_flags, 41958c2ecf20Sopenharmony_ci &vf_cfg->if_handle, vf + 1); 41968c2ecf20Sopenharmony_ci if (status) 41978c2ecf20Sopenharmony_ci return status; 41988c2ecf20Sopenharmony_ci } 41998c2ecf20Sopenharmony_ci 42008c2ecf20Sopenharmony_ci return 0; 42018c2ecf20Sopenharmony_ci} 42028c2ecf20Sopenharmony_ci 42038c2ecf20Sopenharmony_cistatic int be_vf_setup_init(struct be_adapter *adapter) 42048c2ecf20Sopenharmony_ci{ 42058c2ecf20Sopenharmony_ci struct be_vf_cfg *vf_cfg; 42068c2ecf20Sopenharmony_ci int vf; 42078c2ecf20Sopenharmony_ci 42088c2ecf20Sopenharmony_ci adapter->vf_cfg = kcalloc(adapter->num_vfs, sizeof(*vf_cfg), 42098c2ecf20Sopenharmony_ci GFP_KERNEL); 42108c2ecf20Sopenharmony_ci if (!adapter->vf_cfg) 42118c2ecf20Sopenharmony_ci return -ENOMEM; 42128c2ecf20Sopenharmony_ci 42138c2ecf20Sopenharmony_ci for_all_vfs(adapter, vf_cfg, vf) { 42148c2ecf20Sopenharmony_ci vf_cfg->if_handle = -1; 42158c2ecf20Sopenharmony_ci vf_cfg->pmac_id = -1; 42168c2ecf20Sopenharmony_ci } 42178c2ecf20Sopenharmony_ci return 0; 42188c2ecf20Sopenharmony_ci} 42198c2ecf20Sopenharmony_ci 42208c2ecf20Sopenharmony_cistatic int be_vf_setup(struct be_adapter *adapter) 42218c2ecf20Sopenharmony_ci{ 42228c2ecf20Sopenharmony_ci struct device *dev = &adapter->pdev->dev; 42238c2ecf20Sopenharmony_ci struct be_vf_cfg *vf_cfg; 42248c2ecf20Sopenharmony_ci int status, old_vfs, vf; 42258c2ecf20Sopenharmony_ci bool spoofchk; 42268c2ecf20Sopenharmony_ci 42278c2ecf20Sopenharmony_ci old_vfs = pci_num_vf(adapter->pdev); 42288c2ecf20Sopenharmony_ci 42298c2ecf20Sopenharmony_ci status = be_vf_setup_init(adapter); 42308c2ecf20Sopenharmony_ci if (status) 42318c2ecf20Sopenharmony_ci goto err; 42328c2ecf20Sopenharmony_ci 42338c2ecf20Sopenharmony_ci if (old_vfs) { 42348c2ecf20Sopenharmony_ci for_all_vfs(adapter, vf_cfg, vf) { 42358c2ecf20Sopenharmony_ci status = be_cmd_get_if_id(adapter, vf_cfg, vf); 42368c2ecf20Sopenharmony_ci if (status) 42378c2ecf20Sopenharmony_ci goto err; 42388c2ecf20Sopenharmony_ci } 42398c2ecf20Sopenharmony_ci 42408c2ecf20Sopenharmony_ci status = be_vfs_mac_query(adapter); 42418c2ecf20Sopenharmony_ci if (status) 42428c2ecf20Sopenharmony_ci goto err; 42438c2ecf20Sopenharmony_ci } else { 42448c2ecf20Sopenharmony_ci status = be_vfs_if_create(adapter); 42458c2ecf20Sopenharmony_ci if (status) 42468c2ecf20Sopenharmony_ci goto err; 42478c2ecf20Sopenharmony_ci 42488c2ecf20Sopenharmony_ci status = be_vf_eth_addr_config(adapter); 42498c2ecf20Sopenharmony_ci if (status) 42508c2ecf20Sopenharmony_ci goto err; 42518c2ecf20Sopenharmony_ci } 42528c2ecf20Sopenharmony_ci 42538c2ecf20Sopenharmony_ci for_all_vfs(adapter, vf_cfg, vf) { 42548c2ecf20Sopenharmony_ci /* Allow VFs to programs MAC/VLAN filters */ 42558c2ecf20Sopenharmony_ci status = be_cmd_get_fn_privileges(adapter, &vf_cfg->privileges, 42568c2ecf20Sopenharmony_ci vf + 1); 42578c2ecf20Sopenharmony_ci if (!status && !(vf_cfg->privileges & BE_PRIV_FILTMGMT)) { 42588c2ecf20Sopenharmony_ci status = be_cmd_set_fn_privileges(adapter, 42598c2ecf20Sopenharmony_ci vf_cfg->privileges | 42608c2ecf20Sopenharmony_ci BE_PRIV_FILTMGMT, 42618c2ecf20Sopenharmony_ci vf + 1); 42628c2ecf20Sopenharmony_ci if (!status) { 42638c2ecf20Sopenharmony_ci vf_cfg->privileges |= BE_PRIV_FILTMGMT; 42648c2ecf20Sopenharmony_ci dev_info(dev, "VF%d has FILTMGMT privilege\n", 42658c2ecf20Sopenharmony_ci vf); 42668c2ecf20Sopenharmony_ci } 42678c2ecf20Sopenharmony_ci } 42688c2ecf20Sopenharmony_ci 42698c2ecf20Sopenharmony_ci /* Allow full available bandwidth */ 42708c2ecf20Sopenharmony_ci if (!old_vfs) 42718c2ecf20Sopenharmony_ci be_cmd_config_qos(adapter, 0, 0, vf + 1); 42728c2ecf20Sopenharmony_ci 42738c2ecf20Sopenharmony_ci status = be_cmd_get_hsw_config(adapter, NULL, vf + 1, 42748c2ecf20Sopenharmony_ci vf_cfg->if_handle, NULL, 42758c2ecf20Sopenharmony_ci &spoofchk); 42768c2ecf20Sopenharmony_ci if (!status) 42778c2ecf20Sopenharmony_ci vf_cfg->spoofchk = spoofchk; 42788c2ecf20Sopenharmony_ci 42798c2ecf20Sopenharmony_ci if (!old_vfs) { 42808c2ecf20Sopenharmony_ci be_cmd_enable_vf(adapter, vf + 1); 42818c2ecf20Sopenharmony_ci be_cmd_set_logical_link_config(adapter, 42828c2ecf20Sopenharmony_ci IFLA_VF_LINK_STATE_AUTO, 42838c2ecf20Sopenharmony_ci vf+1); 42848c2ecf20Sopenharmony_ci } 42858c2ecf20Sopenharmony_ci } 42868c2ecf20Sopenharmony_ci 42878c2ecf20Sopenharmony_ci if (!old_vfs) { 42888c2ecf20Sopenharmony_ci status = pci_enable_sriov(adapter->pdev, adapter->num_vfs); 42898c2ecf20Sopenharmony_ci if (status) { 42908c2ecf20Sopenharmony_ci dev_err(dev, "SRIOV enable failed\n"); 42918c2ecf20Sopenharmony_ci adapter->num_vfs = 0; 42928c2ecf20Sopenharmony_ci goto err; 42938c2ecf20Sopenharmony_ci } 42948c2ecf20Sopenharmony_ci } 42958c2ecf20Sopenharmony_ci 42968c2ecf20Sopenharmony_ci if (BE3_chip(adapter)) { 42978c2ecf20Sopenharmony_ci /* On BE3, enable VEB only when SRIOV is enabled */ 42988c2ecf20Sopenharmony_ci status = be_cmd_set_hsw_config(adapter, 0, 0, 42998c2ecf20Sopenharmony_ci adapter->if_handle, 43008c2ecf20Sopenharmony_ci PORT_FWD_TYPE_VEB, 0); 43018c2ecf20Sopenharmony_ci if (status) 43028c2ecf20Sopenharmony_ci goto err; 43038c2ecf20Sopenharmony_ci } 43048c2ecf20Sopenharmony_ci 43058c2ecf20Sopenharmony_ci adapter->flags |= BE_FLAGS_SRIOV_ENABLED; 43068c2ecf20Sopenharmony_ci return 0; 43078c2ecf20Sopenharmony_cierr: 43088c2ecf20Sopenharmony_ci dev_err(dev, "VF setup failed\n"); 43098c2ecf20Sopenharmony_ci be_vf_clear(adapter); 43108c2ecf20Sopenharmony_ci return status; 43118c2ecf20Sopenharmony_ci} 43128c2ecf20Sopenharmony_ci 43138c2ecf20Sopenharmony_ci/* Converting function_mode bits on BE3 to SH mc_type enums */ 43148c2ecf20Sopenharmony_ci 43158c2ecf20Sopenharmony_cistatic u8 be_convert_mc_type(u32 function_mode) 43168c2ecf20Sopenharmony_ci{ 43178c2ecf20Sopenharmony_ci if (function_mode & VNIC_MODE && function_mode & QNQ_MODE) 43188c2ecf20Sopenharmony_ci return vNIC1; 43198c2ecf20Sopenharmony_ci else if (function_mode & QNQ_MODE) 43208c2ecf20Sopenharmony_ci return FLEX10; 43218c2ecf20Sopenharmony_ci else if (function_mode & VNIC_MODE) 43228c2ecf20Sopenharmony_ci return vNIC2; 43238c2ecf20Sopenharmony_ci else if (function_mode & UMC_ENABLED) 43248c2ecf20Sopenharmony_ci return UMC; 43258c2ecf20Sopenharmony_ci else 43268c2ecf20Sopenharmony_ci return MC_NONE; 43278c2ecf20Sopenharmony_ci} 43288c2ecf20Sopenharmony_ci 43298c2ecf20Sopenharmony_ci/* On BE2/BE3 FW does not suggest the supported limits */ 43308c2ecf20Sopenharmony_cistatic void BEx_get_resources(struct be_adapter *adapter, 43318c2ecf20Sopenharmony_ci struct be_resources *res) 43328c2ecf20Sopenharmony_ci{ 43338c2ecf20Sopenharmony_ci bool use_sriov = adapter->num_vfs ? 1 : 0; 43348c2ecf20Sopenharmony_ci 43358c2ecf20Sopenharmony_ci if (be_physfn(adapter)) 43368c2ecf20Sopenharmony_ci res->max_uc_mac = BE_UC_PMAC_COUNT; 43378c2ecf20Sopenharmony_ci else 43388c2ecf20Sopenharmony_ci res->max_uc_mac = BE_VF_UC_PMAC_COUNT; 43398c2ecf20Sopenharmony_ci 43408c2ecf20Sopenharmony_ci adapter->mc_type = be_convert_mc_type(adapter->function_mode); 43418c2ecf20Sopenharmony_ci 43428c2ecf20Sopenharmony_ci if (be_is_mc(adapter)) { 43438c2ecf20Sopenharmony_ci /* Assuming that there are 4 channels per port, 43448c2ecf20Sopenharmony_ci * when multi-channel is enabled 43458c2ecf20Sopenharmony_ci */ 43468c2ecf20Sopenharmony_ci if (be_is_qnq_mode(adapter)) 43478c2ecf20Sopenharmony_ci res->max_vlans = BE_NUM_VLANS_SUPPORTED/8; 43488c2ecf20Sopenharmony_ci else 43498c2ecf20Sopenharmony_ci /* In a non-qnq multichannel mode, the pvid 43508c2ecf20Sopenharmony_ci * takes up one vlan entry 43518c2ecf20Sopenharmony_ci */ 43528c2ecf20Sopenharmony_ci res->max_vlans = (BE_NUM_VLANS_SUPPORTED / 4) - 1; 43538c2ecf20Sopenharmony_ci } else { 43548c2ecf20Sopenharmony_ci res->max_vlans = BE_NUM_VLANS_SUPPORTED; 43558c2ecf20Sopenharmony_ci } 43568c2ecf20Sopenharmony_ci 43578c2ecf20Sopenharmony_ci res->max_mcast_mac = BE_MAX_MC; 43588c2ecf20Sopenharmony_ci 43598c2ecf20Sopenharmony_ci /* 1) For BE3 1Gb ports, FW does not support multiple TXQs 43608c2ecf20Sopenharmony_ci * 2) Create multiple TX rings on a BE3-R multi-channel interface 43618c2ecf20Sopenharmony_ci * *only* if it is RSS-capable. 43628c2ecf20Sopenharmony_ci */ 43638c2ecf20Sopenharmony_ci if (BE2_chip(adapter) || use_sriov || (adapter->port_num > 1) || 43648c2ecf20Sopenharmony_ci be_virtfn(adapter) || 43658c2ecf20Sopenharmony_ci (be_is_mc(adapter) && 43668c2ecf20Sopenharmony_ci !(adapter->function_caps & BE_FUNCTION_CAPS_RSS))) { 43678c2ecf20Sopenharmony_ci res->max_tx_qs = 1; 43688c2ecf20Sopenharmony_ci } else if (adapter->function_caps & BE_FUNCTION_CAPS_SUPER_NIC) { 43698c2ecf20Sopenharmony_ci struct be_resources super_nic_res = {0}; 43708c2ecf20Sopenharmony_ci 43718c2ecf20Sopenharmony_ci /* On a SuperNIC profile, the driver needs to use the 43728c2ecf20Sopenharmony_ci * GET_PROFILE_CONFIG cmd to query the per-function TXQ limits 43738c2ecf20Sopenharmony_ci */ 43748c2ecf20Sopenharmony_ci be_cmd_get_profile_config(adapter, &super_nic_res, NULL, 43758c2ecf20Sopenharmony_ci ACTIVE_PROFILE_TYPE, RESOURCE_LIMITS, 43768c2ecf20Sopenharmony_ci 0); 43778c2ecf20Sopenharmony_ci /* Some old versions of BE3 FW don't report max_tx_qs value */ 43788c2ecf20Sopenharmony_ci res->max_tx_qs = super_nic_res.max_tx_qs ? : BE3_MAX_TX_QS; 43798c2ecf20Sopenharmony_ci } else { 43808c2ecf20Sopenharmony_ci res->max_tx_qs = BE3_MAX_TX_QS; 43818c2ecf20Sopenharmony_ci } 43828c2ecf20Sopenharmony_ci 43838c2ecf20Sopenharmony_ci if ((adapter->function_caps & BE_FUNCTION_CAPS_RSS) && 43848c2ecf20Sopenharmony_ci !use_sriov && be_physfn(adapter)) 43858c2ecf20Sopenharmony_ci res->max_rss_qs = (adapter->be3_native) ? 43868c2ecf20Sopenharmony_ci BE3_MAX_RSS_QS : BE2_MAX_RSS_QS; 43878c2ecf20Sopenharmony_ci res->max_rx_qs = res->max_rss_qs + 1; 43888c2ecf20Sopenharmony_ci 43898c2ecf20Sopenharmony_ci if (be_physfn(adapter)) 43908c2ecf20Sopenharmony_ci res->max_evt_qs = (be_max_vfs(adapter) > 0) ? 43918c2ecf20Sopenharmony_ci BE3_SRIOV_MAX_EVT_QS : BE3_MAX_EVT_QS; 43928c2ecf20Sopenharmony_ci else 43938c2ecf20Sopenharmony_ci res->max_evt_qs = 1; 43948c2ecf20Sopenharmony_ci 43958c2ecf20Sopenharmony_ci res->if_cap_flags = BE_IF_CAP_FLAGS_WANT; 43968c2ecf20Sopenharmony_ci res->if_cap_flags &= ~BE_IF_FLAGS_DEFQ_RSS; 43978c2ecf20Sopenharmony_ci if (!(adapter->function_caps & BE_FUNCTION_CAPS_RSS)) 43988c2ecf20Sopenharmony_ci res->if_cap_flags &= ~BE_IF_FLAGS_RSS; 43998c2ecf20Sopenharmony_ci} 44008c2ecf20Sopenharmony_ci 44018c2ecf20Sopenharmony_cistatic void be_setup_init(struct be_adapter *adapter) 44028c2ecf20Sopenharmony_ci{ 44038c2ecf20Sopenharmony_ci adapter->vlan_prio_bmap = 0xff; 44048c2ecf20Sopenharmony_ci adapter->phy.link_speed = -1; 44058c2ecf20Sopenharmony_ci adapter->if_handle = -1; 44068c2ecf20Sopenharmony_ci adapter->be3_native = false; 44078c2ecf20Sopenharmony_ci adapter->if_flags = 0; 44088c2ecf20Sopenharmony_ci adapter->phy_state = BE_UNKNOWN_PHY_STATE; 44098c2ecf20Sopenharmony_ci if (be_physfn(adapter)) 44108c2ecf20Sopenharmony_ci adapter->cmd_privileges = MAX_PRIVILEGES; 44118c2ecf20Sopenharmony_ci else 44128c2ecf20Sopenharmony_ci adapter->cmd_privileges = MIN_PRIVILEGES; 44138c2ecf20Sopenharmony_ci} 44148c2ecf20Sopenharmony_ci 44158c2ecf20Sopenharmony_ci/* HW supports only MAX_PORT_RSS_TABLES RSS Policy Tables per port. 44168c2ecf20Sopenharmony_ci * However, this HW limitation is not exposed to the host via any SLI cmd. 44178c2ecf20Sopenharmony_ci * As a result, in the case of SRIOV and in particular multi-partition configs 44188c2ecf20Sopenharmony_ci * the driver needs to calcuate a proportional share of RSS Tables per PF-pool 44198c2ecf20Sopenharmony_ci * for distribution between the VFs. This self-imposed limit will determine the 44208c2ecf20Sopenharmony_ci * no: of VFs for which RSS can be enabled. 44218c2ecf20Sopenharmony_ci */ 44228c2ecf20Sopenharmony_cistatic void be_calculate_pf_pool_rss_tables(struct be_adapter *adapter) 44238c2ecf20Sopenharmony_ci{ 44248c2ecf20Sopenharmony_ci struct be_port_resources port_res = {0}; 44258c2ecf20Sopenharmony_ci u8 rss_tables_on_port; 44268c2ecf20Sopenharmony_ci u16 max_vfs = be_max_vfs(adapter); 44278c2ecf20Sopenharmony_ci 44288c2ecf20Sopenharmony_ci be_cmd_get_profile_config(adapter, NULL, &port_res, SAVED_PROFILE_TYPE, 44298c2ecf20Sopenharmony_ci RESOURCE_LIMITS, 0); 44308c2ecf20Sopenharmony_ci 44318c2ecf20Sopenharmony_ci rss_tables_on_port = MAX_PORT_RSS_TABLES - port_res.nic_pfs; 44328c2ecf20Sopenharmony_ci 44338c2ecf20Sopenharmony_ci /* Each PF Pool's RSS Tables limit = 44348c2ecf20Sopenharmony_ci * PF's Max VFs / Total_Max_VFs on Port * RSS Tables on Port 44358c2ecf20Sopenharmony_ci */ 44368c2ecf20Sopenharmony_ci adapter->pool_res.max_rss_tables = 44378c2ecf20Sopenharmony_ci max_vfs * rss_tables_on_port / port_res.max_vfs; 44388c2ecf20Sopenharmony_ci} 44398c2ecf20Sopenharmony_ci 44408c2ecf20Sopenharmony_cistatic int be_get_sriov_config(struct be_adapter *adapter) 44418c2ecf20Sopenharmony_ci{ 44428c2ecf20Sopenharmony_ci struct be_resources res = {0}; 44438c2ecf20Sopenharmony_ci int max_vfs, old_vfs; 44448c2ecf20Sopenharmony_ci 44458c2ecf20Sopenharmony_ci be_cmd_get_profile_config(adapter, &res, NULL, ACTIVE_PROFILE_TYPE, 44468c2ecf20Sopenharmony_ci RESOURCE_LIMITS, 0); 44478c2ecf20Sopenharmony_ci 44488c2ecf20Sopenharmony_ci /* Some old versions of BE3 FW don't report max_vfs value */ 44498c2ecf20Sopenharmony_ci if (BE3_chip(adapter) && !res.max_vfs) { 44508c2ecf20Sopenharmony_ci max_vfs = pci_sriov_get_totalvfs(adapter->pdev); 44518c2ecf20Sopenharmony_ci res.max_vfs = max_vfs > 0 ? min(MAX_VFS, max_vfs) : 0; 44528c2ecf20Sopenharmony_ci } 44538c2ecf20Sopenharmony_ci 44548c2ecf20Sopenharmony_ci adapter->pool_res = res; 44558c2ecf20Sopenharmony_ci 44568c2ecf20Sopenharmony_ci /* If during previous unload of the driver, the VFs were not disabled, 44578c2ecf20Sopenharmony_ci * then we cannot rely on the PF POOL limits for the TotalVFs value. 44588c2ecf20Sopenharmony_ci * Instead use the TotalVFs value stored in the pci-dev struct. 44598c2ecf20Sopenharmony_ci */ 44608c2ecf20Sopenharmony_ci old_vfs = pci_num_vf(adapter->pdev); 44618c2ecf20Sopenharmony_ci if (old_vfs) { 44628c2ecf20Sopenharmony_ci dev_info(&adapter->pdev->dev, "%d VFs are already enabled\n", 44638c2ecf20Sopenharmony_ci old_vfs); 44648c2ecf20Sopenharmony_ci 44658c2ecf20Sopenharmony_ci adapter->pool_res.max_vfs = 44668c2ecf20Sopenharmony_ci pci_sriov_get_totalvfs(adapter->pdev); 44678c2ecf20Sopenharmony_ci adapter->num_vfs = old_vfs; 44688c2ecf20Sopenharmony_ci } 44698c2ecf20Sopenharmony_ci 44708c2ecf20Sopenharmony_ci if (skyhawk_chip(adapter) && be_max_vfs(adapter) && !old_vfs) { 44718c2ecf20Sopenharmony_ci be_calculate_pf_pool_rss_tables(adapter); 44728c2ecf20Sopenharmony_ci dev_info(&adapter->pdev->dev, 44738c2ecf20Sopenharmony_ci "RSS can be enabled for all VFs if num_vfs <= %d\n", 44748c2ecf20Sopenharmony_ci be_max_pf_pool_rss_tables(adapter)); 44758c2ecf20Sopenharmony_ci } 44768c2ecf20Sopenharmony_ci return 0; 44778c2ecf20Sopenharmony_ci} 44788c2ecf20Sopenharmony_ci 44798c2ecf20Sopenharmony_cistatic void be_alloc_sriov_res(struct be_adapter *adapter) 44808c2ecf20Sopenharmony_ci{ 44818c2ecf20Sopenharmony_ci int old_vfs = pci_num_vf(adapter->pdev); 44828c2ecf20Sopenharmony_ci struct be_resources vft_res = {0}; 44838c2ecf20Sopenharmony_ci int status; 44848c2ecf20Sopenharmony_ci 44858c2ecf20Sopenharmony_ci be_get_sriov_config(adapter); 44868c2ecf20Sopenharmony_ci 44878c2ecf20Sopenharmony_ci if (!old_vfs) 44888c2ecf20Sopenharmony_ci pci_sriov_set_totalvfs(adapter->pdev, be_max_vfs(adapter)); 44898c2ecf20Sopenharmony_ci 44908c2ecf20Sopenharmony_ci /* When the HW is in SRIOV capable configuration, the PF-pool 44918c2ecf20Sopenharmony_ci * resources are given to PF during driver load, if there are no 44928c2ecf20Sopenharmony_ci * old VFs. This facility is not available in BE3 FW. 44938c2ecf20Sopenharmony_ci * Also, this is done by FW in Lancer chip. 44948c2ecf20Sopenharmony_ci */ 44958c2ecf20Sopenharmony_ci if (skyhawk_chip(adapter) && be_max_vfs(adapter) && !old_vfs) { 44968c2ecf20Sopenharmony_ci be_calculate_vf_res(adapter, 0, &vft_res); 44978c2ecf20Sopenharmony_ci status = be_cmd_set_sriov_config(adapter, adapter->pool_res, 0, 44988c2ecf20Sopenharmony_ci &vft_res); 44998c2ecf20Sopenharmony_ci if (status) 45008c2ecf20Sopenharmony_ci dev_err(&adapter->pdev->dev, 45018c2ecf20Sopenharmony_ci "Failed to optimize SRIOV resources\n"); 45028c2ecf20Sopenharmony_ci } 45038c2ecf20Sopenharmony_ci} 45048c2ecf20Sopenharmony_ci 45058c2ecf20Sopenharmony_cistatic int be_get_resources(struct be_adapter *adapter) 45068c2ecf20Sopenharmony_ci{ 45078c2ecf20Sopenharmony_ci struct device *dev = &adapter->pdev->dev; 45088c2ecf20Sopenharmony_ci struct be_resources res = {0}; 45098c2ecf20Sopenharmony_ci int status; 45108c2ecf20Sopenharmony_ci 45118c2ecf20Sopenharmony_ci /* For Lancer, SH etc read per-function resource limits from FW. 45128c2ecf20Sopenharmony_ci * GET_FUNC_CONFIG returns per function guaranteed limits. 45138c2ecf20Sopenharmony_ci * GET_PROFILE_CONFIG returns PCI-E related limits PF-pool limits 45148c2ecf20Sopenharmony_ci */ 45158c2ecf20Sopenharmony_ci if (BEx_chip(adapter)) { 45168c2ecf20Sopenharmony_ci BEx_get_resources(adapter, &res); 45178c2ecf20Sopenharmony_ci } else { 45188c2ecf20Sopenharmony_ci status = be_cmd_get_func_config(adapter, &res); 45198c2ecf20Sopenharmony_ci if (status) 45208c2ecf20Sopenharmony_ci return status; 45218c2ecf20Sopenharmony_ci 45228c2ecf20Sopenharmony_ci /* If a deafault RXQ must be created, we'll use up one RSSQ*/ 45238c2ecf20Sopenharmony_ci if (res.max_rss_qs && res.max_rss_qs == res.max_rx_qs && 45248c2ecf20Sopenharmony_ci !(res.if_cap_flags & BE_IF_FLAGS_DEFQ_RSS)) 45258c2ecf20Sopenharmony_ci res.max_rss_qs -= 1; 45268c2ecf20Sopenharmony_ci } 45278c2ecf20Sopenharmony_ci 45288c2ecf20Sopenharmony_ci /* If RoCE is supported stash away half the EQs for RoCE */ 45298c2ecf20Sopenharmony_ci res.max_nic_evt_qs = be_roce_supported(adapter) ? 45308c2ecf20Sopenharmony_ci res.max_evt_qs / 2 : res.max_evt_qs; 45318c2ecf20Sopenharmony_ci adapter->res = res; 45328c2ecf20Sopenharmony_ci 45338c2ecf20Sopenharmony_ci /* If FW supports RSS default queue, then skip creating non-RSS 45348c2ecf20Sopenharmony_ci * queue for non-IP traffic. 45358c2ecf20Sopenharmony_ci */ 45368c2ecf20Sopenharmony_ci adapter->need_def_rxq = (be_if_cap_flags(adapter) & 45378c2ecf20Sopenharmony_ci BE_IF_FLAGS_DEFQ_RSS) ? 0 : 1; 45388c2ecf20Sopenharmony_ci 45398c2ecf20Sopenharmony_ci dev_info(dev, "Max: txqs %d, rxqs %d, rss %d, eqs %d, vfs %d\n", 45408c2ecf20Sopenharmony_ci be_max_txqs(adapter), be_max_rxqs(adapter), 45418c2ecf20Sopenharmony_ci be_max_rss(adapter), be_max_nic_eqs(adapter), 45428c2ecf20Sopenharmony_ci be_max_vfs(adapter)); 45438c2ecf20Sopenharmony_ci dev_info(dev, "Max: uc-macs %d, mc-macs %d, vlans %d\n", 45448c2ecf20Sopenharmony_ci be_max_uc(adapter), be_max_mc(adapter), 45458c2ecf20Sopenharmony_ci be_max_vlans(adapter)); 45468c2ecf20Sopenharmony_ci 45478c2ecf20Sopenharmony_ci /* Ensure RX and TX queues are created in pairs at init time */ 45488c2ecf20Sopenharmony_ci adapter->cfg_num_rx_irqs = 45498c2ecf20Sopenharmony_ci min_t(u16, netif_get_num_default_rss_queues(), 45508c2ecf20Sopenharmony_ci be_max_qp_irqs(adapter)); 45518c2ecf20Sopenharmony_ci adapter->cfg_num_tx_irqs = adapter->cfg_num_rx_irqs; 45528c2ecf20Sopenharmony_ci return 0; 45538c2ecf20Sopenharmony_ci} 45548c2ecf20Sopenharmony_ci 45558c2ecf20Sopenharmony_cistatic int be_get_config(struct be_adapter *adapter) 45568c2ecf20Sopenharmony_ci{ 45578c2ecf20Sopenharmony_ci int status, level; 45588c2ecf20Sopenharmony_ci u16 profile_id; 45598c2ecf20Sopenharmony_ci 45608c2ecf20Sopenharmony_ci status = be_cmd_get_cntl_attributes(adapter); 45618c2ecf20Sopenharmony_ci if (status) 45628c2ecf20Sopenharmony_ci return status; 45638c2ecf20Sopenharmony_ci 45648c2ecf20Sopenharmony_ci status = be_cmd_query_fw_cfg(adapter); 45658c2ecf20Sopenharmony_ci if (status) 45668c2ecf20Sopenharmony_ci return status; 45678c2ecf20Sopenharmony_ci 45688c2ecf20Sopenharmony_ci if (!lancer_chip(adapter) && be_physfn(adapter)) 45698c2ecf20Sopenharmony_ci be_cmd_get_fat_dump_len(adapter, &adapter->fat_dump_len); 45708c2ecf20Sopenharmony_ci 45718c2ecf20Sopenharmony_ci if (BEx_chip(adapter)) { 45728c2ecf20Sopenharmony_ci level = be_cmd_get_fw_log_level(adapter); 45738c2ecf20Sopenharmony_ci adapter->msg_enable = 45748c2ecf20Sopenharmony_ci level <= FW_LOG_LEVEL_DEFAULT ? NETIF_MSG_HW : 0; 45758c2ecf20Sopenharmony_ci } 45768c2ecf20Sopenharmony_ci 45778c2ecf20Sopenharmony_ci be_cmd_get_acpi_wol_cap(adapter); 45788c2ecf20Sopenharmony_ci pci_enable_wake(adapter->pdev, PCI_D3hot, adapter->wol_en); 45798c2ecf20Sopenharmony_ci pci_enable_wake(adapter->pdev, PCI_D3cold, adapter->wol_en); 45808c2ecf20Sopenharmony_ci 45818c2ecf20Sopenharmony_ci be_cmd_query_port_name(adapter); 45828c2ecf20Sopenharmony_ci 45838c2ecf20Sopenharmony_ci if (be_physfn(adapter)) { 45848c2ecf20Sopenharmony_ci status = be_cmd_get_active_profile(adapter, &profile_id); 45858c2ecf20Sopenharmony_ci if (!status) 45868c2ecf20Sopenharmony_ci dev_info(&adapter->pdev->dev, 45878c2ecf20Sopenharmony_ci "Using profile 0x%x\n", profile_id); 45888c2ecf20Sopenharmony_ci } 45898c2ecf20Sopenharmony_ci 45908c2ecf20Sopenharmony_ci return 0; 45918c2ecf20Sopenharmony_ci} 45928c2ecf20Sopenharmony_ci 45938c2ecf20Sopenharmony_cistatic int be_mac_setup(struct be_adapter *adapter) 45948c2ecf20Sopenharmony_ci{ 45958c2ecf20Sopenharmony_ci u8 mac[ETH_ALEN]; 45968c2ecf20Sopenharmony_ci int status; 45978c2ecf20Sopenharmony_ci 45988c2ecf20Sopenharmony_ci if (is_zero_ether_addr(adapter->netdev->dev_addr)) { 45998c2ecf20Sopenharmony_ci status = be_cmd_get_perm_mac(adapter, mac); 46008c2ecf20Sopenharmony_ci if (status) 46018c2ecf20Sopenharmony_ci return status; 46028c2ecf20Sopenharmony_ci 46038c2ecf20Sopenharmony_ci memcpy(adapter->netdev->dev_addr, mac, ETH_ALEN); 46048c2ecf20Sopenharmony_ci memcpy(adapter->netdev->perm_addr, mac, ETH_ALEN); 46058c2ecf20Sopenharmony_ci 46068c2ecf20Sopenharmony_ci /* Initial MAC for BE3 VFs is already programmed by PF */ 46078c2ecf20Sopenharmony_ci if (BEx_chip(adapter) && be_virtfn(adapter)) 46088c2ecf20Sopenharmony_ci memcpy(adapter->dev_mac, mac, ETH_ALEN); 46098c2ecf20Sopenharmony_ci } 46108c2ecf20Sopenharmony_ci 46118c2ecf20Sopenharmony_ci return 0; 46128c2ecf20Sopenharmony_ci} 46138c2ecf20Sopenharmony_ci 46148c2ecf20Sopenharmony_cistatic void be_schedule_worker(struct be_adapter *adapter) 46158c2ecf20Sopenharmony_ci{ 46168c2ecf20Sopenharmony_ci queue_delayed_work(be_wq, &adapter->work, msecs_to_jiffies(1000)); 46178c2ecf20Sopenharmony_ci adapter->flags |= BE_FLAGS_WORKER_SCHEDULED; 46188c2ecf20Sopenharmony_ci} 46198c2ecf20Sopenharmony_ci 46208c2ecf20Sopenharmony_cistatic void be_destroy_err_recovery_workq(void) 46218c2ecf20Sopenharmony_ci{ 46228c2ecf20Sopenharmony_ci if (!be_err_recovery_workq) 46238c2ecf20Sopenharmony_ci return; 46248c2ecf20Sopenharmony_ci 46258c2ecf20Sopenharmony_ci flush_workqueue(be_err_recovery_workq); 46268c2ecf20Sopenharmony_ci destroy_workqueue(be_err_recovery_workq); 46278c2ecf20Sopenharmony_ci be_err_recovery_workq = NULL; 46288c2ecf20Sopenharmony_ci} 46298c2ecf20Sopenharmony_ci 46308c2ecf20Sopenharmony_cistatic void be_schedule_err_detection(struct be_adapter *adapter, u32 delay) 46318c2ecf20Sopenharmony_ci{ 46328c2ecf20Sopenharmony_ci struct be_error_recovery *err_rec = &adapter->error_recovery; 46338c2ecf20Sopenharmony_ci 46348c2ecf20Sopenharmony_ci if (!be_err_recovery_workq) 46358c2ecf20Sopenharmony_ci return; 46368c2ecf20Sopenharmony_ci 46378c2ecf20Sopenharmony_ci queue_delayed_work(be_err_recovery_workq, &err_rec->err_detection_work, 46388c2ecf20Sopenharmony_ci msecs_to_jiffies(delay)); 46398c2ecf20Sopenharmony_ci adapter->flags |= BE_FLAGS_ERR_DETECTION_SCHEDULED; 46408c2ecf20Sopenharmony_ci} 46418c2ecf20Sopenharmony_ci 46428c2ecf20Sopenharmony_cistatic int be_setup_queues(struct be_adapter *adapter) 46438c2ecf20Sopenharmony_ci{ 46448c2ecf20Sopenharmony_ci struct net_device *netdev = adapter->netdev; 46458c2ecf20Sopenharmony_ci int status; 46468c2ecf20Sopenharmony_ci 46478c2ecf20Sopenharmony_ci status = be_evt_queues_create(adapter); 46488c2ecf20Sopenharmony_ci if (status) 46498c2ecf20Sopenharmony_ci goto err; 46508c2ecf20Sopenharmony_ci 46518c2ecf20Sopenharmony_ci status = be_tx_qs_create(adapter); 46528c2ecf20Sopenharmony_ci if (status) 46538c2ecf20Sopenharmony_ci goto err; 46548c2ecf20Sopenharmony_ci 46558c2ecf20Sopenharmony_ci status = be_rx_cqs_create(adapter); 46568c2ecf20Sopenharmony_ci if (status) 46578c2ecf20Sopenharmony_ci goto err; 46588c2ecf20Sopenharmony_ci 46598c2ecf20Sopenharmony_ci status = be_mcc_queues_create(adapter); 46608c2ecf20Sopenharmony_ci if (status) 46618c2ecf20Sopenharmony_ci goto err; 46628c2ecf20Sopenharmony_ci 46638c2ecf20Sopenharmony_ci status = netif_set_real_num_rx_queues(netdev, adapter->num_rx_qs); 46648c2ecf20Sopenharmony_ci if (status) 46658c2ecf20Sopenharmony_ci goto err; 46668c2ecf20Sopenharmony_ci 46678c2ecf20Sopenharmony_ci status = netif_set_real_num_tx_queues(netdev, adapter->num_tx_qs); 46688c2ecf20Sopenharmony_ci if (status) 46698c2ecf20Sopenharmony_ci goto err; 46708c2ecf20Sopenharmony_ci 46718c2ecf20Sopenharmony_ci return 0; 46728c2ecf20Sopenharmony_cierr: 46738c2ecf20Sopenharmony_ci dev_err(&adapter->pdev->dev, "queue_setup failed\n"); 46748c2ecf20Sopenharmony_ci return status; 46758c2ecf20Sopenharmony_ci} 46768c2ecf20Sopenharmony_ci 46778c2ecf20Sopenharmony_cistatic int be_if_create(struct be_adapter *adapter) 46788c2ecf20Sopenharmony_ci{ 46798c2ecf20Sopenharmony_ci u32 en_flags = BE_IF_FLAGS_RSS | BE_IF_FLAGS_DEFQ_RSS; 46808c2ecf20Sopenharmony_ci u32 cap_flags = be_if_cap_flags(adapter); 46818c2ecf20Sopenharmony_ci int status; 46828c2ecf20Sopenharmony_ci 46838c2ecf20Sopenharmony_ci /* alloc required memory for other filtering fields */ 46848c2ecf20Sopenharmony_ci adapter->pmac_id = kcalloc(be_max_uc(adapter), 46858c2ecf20Sopenharmony_ci sizeof(*adapter->pmac_id), GFP_KERNEL); 46868c2ecf20Sopenharmony_ci if (!adapter->pmac_id) 46878c2ecf20Sopenharmony_ci return -ENOMEM; 46888c2ecf20Sopenharmony_ci 46898c2ecf20Sopenharmony_ci adapter->mc_list = kcalloc(be_max_mc(adapter), 46908c2ecf20Sopenharmony_ci sizeof(*adapter->mc_list), GFP_KERNEL); 46918c2ecf20Sopenharmony_ci if (!adapter->mc_list) 46928c2ecf20Sopenharmony_ci return -ENOMEM; 46938c2ecf20Sopenharmony_ci 46948c2ecf20Sopenharmony_ci adapter->uc_list = kcalloc(be_max_uc(adapter), 46958c2ecf20Sopenharmony_ci sizeof(*adapter->uc_list), GFP_KERNEL); 46968c2ecf20Sopenharmony_ci if (!adapter->uc_list) 46978c2ecf20Sopenharmony_ci return -ENOMEM; 46988c2ecf20Sopenharmony_ci 46998c2ecf20Sopenharmony_ci if (adapter->cfg_num_rx_irqs == 1) 47008c2ecf20Sopenharmony_ci cap_flags &= ~(BE_IF_FLAGS_DEFQ_RSS | BE_IF_FLAGS_RSS); 47018c2ecf20Sopenharmony_ci 47028c2ecf20Sopenharmony_ci en_flags &= cap_flags; 47038c2ecf20Sopenharmony_ci /* will enable all the needed filter flags in be_open() */ 47048c2ecf20Sopenharmony_ci status = be_cmd_if_create(adapter, be_if_cap_flags(adapter), en_flags, 47058c2ecf20Sopenharmony_ci &adapter->if_handle, 0); 47068c2ecf20Sopenharmony_ci 47078c2ecf20Sopenharmony_ci if (status) 47088c2ecf20Sopenharmony_ci return status; 47098c2ecf20Sopenharmony_ci 47108c2ecf20Sopenharmony_ci return 0; 47118c2ecf20Sopenharmony_ci} 47128c2ecf20Sopenharmony_ci 47138c2ecf20Sopenharmony_ciint be_update_queues(struct be_adapter *adapter) 47148c2ecf20Sopenharmony_ci{ 47158c2ecf20Sopenharmony_ci struct net_device *netdev = adapter->netdev; 47168c2ecf20Sopenharmony_ci int status; 47178c2ecf20Sopenharmony_ci 47188c2ecf20Sopenharmony_ci if (netif_running(netdev)) { 47198c2ecf20Sopenharmony_ci /* be_tx_timeout() must not run concurrently with this 47208c2ecf20Sopenharmony_ci * function, synchronize with an already-running dev_watchdog 47218c2ecf20Sopenharmony_ci */ 47228c2ecf20Sopenharmony_ci netif_tx_lock_bh(netdev); 47238c2ecf20Sopenharmony_ci /* device cannot transmit now, avoid dev_watchdog timeouts */ 47248c2ecf20Sopenharmony_ci netif_carrier_off(netdev); 47258c2ecf20Sopenharmony_ci netif_tx_unlock_bh(netdev); 47268c2ecf20Sopenharmony_ci 47278c2ecf20Sopenharmony_ci be_close(netdev); 47288c2ecf20Sopenharmony_ci } 47298c2ecf20Sopenharmony_ci 47308c2ecf20Sopenharmony_ci be_cancel_worker(adapter); 47318c2ecf20Sopenharmony_ci 47328c2ecf20Sopenharmony_ci /* If any vectors have been shared with RoCE we cannot re-program 47338c2ecf20Sopenharmony_ci * the MSIx table. 47348c2ecf20Sopenharmony_ci */ 47358c2ecf20Sopenharmony_ci if (!adapter->num_msix_roce_vec) 47368c2ecf20Sopenharmony_ci be_msix_disable(adapter); 47378c2ecf20Sopenharmony_ci 47388c2ecf20Sopenharmony_ci be_clear_queues(adapter); 47398c2ecf20Sopenharmony_ci status = be_cmd_if_destroy(adapter, adapter->if_handle, 0); 47408c2ecf20Sopenharmony_ci if (status) 47418c2ecf20Sopenharmony_ci return status; 47428c2ecf20Sopenharmony_ci 47438c2ecf20Sopenharmony_ci if (!msix_enabled(adapter)) { 47448c2ecf20Sopenharmony_ci status = be_msix_enable(adapter); 47458c2ecf20Sopenharmony_ci if (status) 47468c2ecf20Sopenharmony_ci return status; 47478c2ecf20Sopenharmony_ci } 47488c2ecf20Sopenharmony_ci 47498c2ecf20Sopenharmony_ci status = be_if_create(adapter); 47508c2ecf20Sopenharmony_ci if (status) 47518c2ecf20Sopenharmony_ci return status; 47528c2ecf20Sopenharmony_ci 47538c2ecf20Sopenharmony_ci status = be_setup_queues(adapter); 47548c2ecf20Sopenharmony_ci if (status) 47558c2ecf20Sopenharmony_ci return status; 47568c2ecf20Sopenharmony_ci 47578c2ecf20Sopenharmony_ci be_schedule_worker(adapter); 47588c2ecf20Sopenharmony_ci 47598c2ecf20Sopenharmony_ci /* The IF was destroyed and re-created. We need to clear 47608c2ecf20Sopenharmony_ci * all promiscuous flags valid for the destroyed IF. 47618c2ecf20Sopenharmony_ci * Without this promisc mode is not restored during 47628c2ecf20Sopenharmony_ci * be_open() because the driver thinks that it is 47638c2ecf20Sopenharmony_ci * already enabled in HW. 47648c2ecf20Sopenharmony_ci */ 47658c2ecf20Sopenharmony_ci adapter->if_flags &= ~BE_IF_FLAGS_ALL_PROMISCUOUS; 47668c2ecf20Sopenharmony_ci 47678c2ecf20Sopenharmony_ci if (netif_running(netdev)) 47688c2ecf20Sopenharmony_ci status = be_open(netdev); 47698c2ecf20Sopenharmony_ci 47708c2ecf20Sopenharmony_ci return status; 47718c2ecf20Sopenharmony_ci} 47728c2ecf20Sopenharmony_ci 47738c2ecf20Sopenharmony_cistatic inline int fw_major_num(const char *fw_ver) 47748c2ecf20Sopenharmony_ci{ 47758c2ecf20Sopenharmony_ci int fw_major = 0, i; 47768c2ecf20Sopenharmony_ci 47778c2ecf20Sopenharmony_ci i = sscanf(fw_ver, "%d.", &fw_major); 47788c2ecf20Sopenharmony_ci if (i != 1) 47798c2ecf20Sopenharmony_ci return 0; 47808c2ecf20Sopenharmony_ci 47818c2ecf20Sopenharmony_ci return fw_major; 47828c2ecf20Sopenharmony_ci} 47838c2ecf20Sopenharmony_ci 47848c2ecf20Sopenharmony_ci/* If it is error recovery, FLR the PF 47858c2ecf20Sopenharmony_ci * Else if any VFs are already enabled don't FLR the PF 47868c2ecf20Sopenharmony_ci */ 47878c2ecf20Sopenharmony_cistatic bool be_reset_required(struct be_adapter *adapter) 47888c2ecf20Sopenharmony_ci{ 47898c2ecf20Sopenharmony_ci if (be_error_recovering(adapter)) 47908c2ecf20Sopenharmony_ci return true; 47918c2ecf20Sopenharmony_ci else 47928c2ecf20Sopenharmony_ci return pci_num_vf(adapter->pdev) == 0; 47938c2ecf20Sopenharmony_ci} 47948c2ecf20Sopenharmony_ci 47958c2ecf20Sopenharmony_ci/* Wait for the FW to be ready and perform the required initialization */ 47968c2ecf20Sopenharmony_cistatic int be_func_init(struct be_adapter *adapter) 47978c2ecf20Sopenharmony_ci{ 47988c2ecf20Sopenharmony_ci int status; 47998c2ecf20Sopenharmony_ci 48008c2ecf20Sopenharmony_ci status = be_fw_wait_ready(adapter); 48018c2ecf20Sopenharmony_ci if (status) 48028c2ecf20Sopenharmony_ci return status; 48038c2ecf20Sopenharmony_ci 48048c2ecf20Sopenharmony_ci /* FW is now ready; clear errors to allow cmds/doorbell */ 48058c2ecf20Sopenharmony_ci be_clear_error(adapter, BE_CLEAR_ALL); 48068c2ecf20Sopenharmony_ci 48078c2ecf20Sopenharmony_ci if (be_reset_required(adapter)) { 48088c2ecf20Sopenharmony_ci status = be_cmd_reset_function(adapter); 48098c2ecf20Sopenharmony_ci if (status) 48108c2ecf20Sopenharmony_ci return status; 48118c2ecf20Sopenharmony_ci 48128c2ecf20Sopenharmony_ci /* Wait for interrupts to quiesce after an FLR */ 48138c2ecf20Sopenharmony_ci msleep(100); 48148c2ecf20Sopenharmony_ci } 48158c2ecf20Sopenharmony_ci 48168c2ecf20Sopenharmony_ci /* Tell FW we're ready to fire cmds */ 48178c2ecf20Sopenharmony_ci status = be_cmd_fw_init(adapter); 48188c2ecf20Sopenharmony_ci if (status) 48198c2ecf20Sopenharmony_ci return status; 48208c2ecf20Sopenharmony_ci 48218c2ecf20Sopenharmony_ci /* Allow interrupts for other ULPs running on NIC function */ 48228c2ecf20Sopenharmony_ci be_intr_set(adapter, true); 48238c2ecf20Sopenharmony_ci 48248c2ecf20Sopenharmony_ci return 0; 48258c2ecf20Sopenharmony_ci} 48268c2ecf20Sopenharmony_ci 48278c2ecf20Sopenharmony_cistatic int be_setup(struct be_adapter *adapter) 48288c2ecf20Sopenharmony_ci{ 48298c2ecf20Sopenharmony_ci struct device *dev = &adapter->pdev->dev; 48308c2ecf20Sopenharmony_ci int status; 48318c2ecf20Sopenharmony_ci 48328c2ecf20Sopenharmony_ci status = be_func_init(adapter); 48338c2ecf20Sopenharmony_ci if (status) 48348c2ecf20Sopenharmony_ci return status; 48358c2ecf20Sopenharmony_ci 48368c2ecf20Sopenharmony_ci be_setup_init(adapter); 48378c2ecf20Sopenharmony_ci 48388c2ecf20Sopenharmony_ci if (!lancer_chip(adapter)) 48398c2ecf20Sopenharmony_ci be_cmd_req_native_mode(adapter); 48408c2ecf20Sopenharmony_ci 48418c2ecf20Sopenharmony_ci /* invoke this cmd first to get pf_num and vf_num which are needed 48428c2ecf20Sopenharmony_ci * for issuing profile related cmds 48438c2ecf20Sopenharmony_ci */ 48448c2ecf20Sopenharmony_ci if (!BEx_chip(adapter)) { 48458c2ecf20Sopenharmony_ci status = be_cmd_get_func_config(adapter, NULL); 48468c2ecf20Sopenharmony_ci if (status) 48478c2ecf20Sopenharmony_ci return status; 48488c2ecf20Sopenharmony_ci } 48498c2ecf20Sopenharmony_ci 48508c2ecf20Sopenharmony_ci status = be_get_config(adapter); 48518c2ecf20Sopenharmony_ci if (status) 48528c2ecf20Sopenharmony_ci goto err; 48538c2ecf20Sopenharmony_ci 48548c2ecf20Sopenharmony_ci if (!BE2_chip(adapter) && be_physfn(adapter)) 48558c2ecf20Sopenharmony_ci be_alloc_sriov_res(adapter); 48568c2ecf20Sopenharmony_ci 48578c2ecf20Sopenharmony_ci status = be_get_resources(adapter); 48588c2ecf20Sopenharmony_ci if (status) 48598c2ecf20Sopenharmony_ci goto err; 48608c2ecf20Sopenharmony_ci 48618c2ecf20Sopenharmony_ci status = be_msix_enable(adapter); 48628c2ecf20Sopenharmony_ci if (status) 48638c2ecf20Sopenharmony_ci goto err; 48648c2ecf20Sopenharmony_ci 48658c2ecf20Sopenharmony_ci /* will enable all the needed filter flags in be_open() */ 48668c2ecf20Sopenharmony_ci status = be_if_create(adapter); 48678c2ecf20Sopenharmony_ci if (status) 48688c2ecf20Sopenharmony_ci goto err; 48698c2ecf20Sopenharmony_ci 48708c2ecf20Sopenharmony_ci /* Updating real_num_tx/rx_queues() requires rtnl_lock() */ 48718c2ecf20Sopenharmony_ci rtnl_lock(); 48728c2ecf20Sopenharmony_ci status = be_setup_queues(adapter); 48738c2ecf20Sopenharmony_ci rtnl_unlock(); 48748c2ecf20Sopenharmony_ci if (status) 48758c2ecf20Sopenharmony_ci goto err; 48768c2ecf20Sopenharmony_ci 48778c2ecf20Sopenharmony_ci be_cmd_get_fn_privileges(adapter, &adapter->cmd_privileges, 0); 48788c2ecf20Sopenharmony_ci 48798c2ecf20Sopenharmony_ci status = be_mac_setup(adapter); 48808c2ecf20Sopenharmony_ci if (status) 48818c2ecf20Sopenharmony_ci goto err; 48828c2ecf20Sopenharmony_ci 48838c2ecf20Sopenharmony_ci be_cmd_get_fw_ver(adapter); 48848c2ecf20Sopenharmony_ci dev_info(dev, "FW version is %s\n", adapter->fw_ver); 48858c2ecf20Sopenharmony_ci 48868c2ecf20Sopenharmony_ci if (BE2_chip(adapter) && fw_major_num(adapter->fw_ver) < 4) { 48878c2ecf20Sopenharmony_ci dev_err(dev, "Firmware on card is old(%s), IRQs may not work", 48888c2ecf20Sopenharmony_ci adapter->fw_ver); 48898c2ecf20Sopenharmony_ci dev_err(dev, "Please upgrade firmware to version >= 4.0\n"); 48908c2ecf20Sopenharmony_ci } 48918c2ecf20Sopenharmony_ci 48928c2ecf20Sopenharmony_ci status = be_cmd_set_flow_control(adapter, adapter->tx_fc, 48938c2ecf20Sopenharmony_ci adapter->rx_fc); 48948c2ecf20Sopenharmony_ci if (status) 48958c2ecf20Sopenharmony_ci be_cmd_get_flow_control(adapter, &adapter->tx_fc, 48968c2ecf20Sopenharmony_ci &adapter->rx_fc); 48978c2ecf20Sopenharmony_ci 48988c2ecf20Sopenharmony_ci dev_info(&adapter->pdev->dev, "HW Flow control - TX:%d RX:%d\n", 48998c2ecf20Sopenharmony_ci adapter->tx_fc, adapter->rx_fc); 49008c2ecf20Sopenharmony_ci 49018c2ecf20Sopenharmony_ci if (be_physfn(adapter)) 49028c2ecf20Sopenharmony_ci be_cmd_set_logical_link_config(adapter, 49038c2ecf20Sopenharmony_ci IFLA_VF_LINK_STATE_AUTO, 0); 49048c2ecf20Sopenharmony_ci 49058c2ecf20Sopenharmony_ci /* BE3 EVB echoes broadcast/multicast packets back to PF's vport 49068c2ecf20Sopenharmony_ci * confusing a linux bridge or OVS that it might be connected to. 49078c2ecf20Sopenharmony_ci * Set the EVB to PASSTHRU mode which effectively disables the EVB 49088c2ecf20Sopenharmony_ci * when SRIOV is not enabled. 49098c2ecf20Sopenharmony_ci */ 49108c2ecf20Sopenharmony_ci if (BE3_chip(adapter)) 49118c2ecf20Sopenharmony_ci be_cmd_set_hsw_config(adapter, 0, 0, adapter->if_handle, 49128c2ecf20Sopenharmony_ci PORT_FWD_TYPE_PASSTHRU, 0); 49138c2ecf20Sopenharmony_ci 49148c2ecf20Sopenharmony_ci if (adapter->num_vfs) 49158c2ecf20Sopenharmony_ci be_vf_setup(adapter); 49168c2ecf20Sopenharmony_ci 49178c2ecf20Sopenharmony_ci status = be_cmd_get_phy_info(adapter); 49188c2ecf20Sopenharmony_ci if (!status && be_pause_supported(adapter)) 49198c2ecf20Sopenharmony_ci adapter->phy.fc_autoneg = 1; 49208c2ecf20Sopenharmony_ci 49218c2ecf20Sopenharmony_ci if (be_physfn(adapter) && !lancer_chip(adapter)) 49228c2ecf20Sopenharmony_ci be_cmd_set_features(adapter); 49238c2ecf20Sopenharmony_ci 49248c2ecf20Sopenharmony_ci be_schedule_worker(adapter); 49258c2ecf20Sopenharmony_ci adapter->flags |= BE_FLAGS_SETUP_DONE; 49268c2ecf20Sopenharmony_ci return 0; 49278c2ecf20Sopenharmony_cierr: 49288c2ecf20Sopenharmony_ci be_clear(adapter); 49298c2ecf20Sopenharmony_ci return status; 49308c2ecf20Sopenharmony_ci} 49318c2ecf20Sopenharmony_ci 49328c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER 49338c2ecf20Sopenharmony_cistatic void be_netpoll(struct net_device *netdev) 49348c2ecf20Sopenharmony_ci{ 49358c2ecf20Sopenharmony_ci struct be_adapter *adapter = netdev_priv(netdev); 49368c2ecf20Sopenharmony_ci struct be_eq_obj *eqo; 49378c2ecf20Sopenharmony_ci int i; 49388c2ecf20Sopenharmony_ci 49398c2ecf20Sopenharmony_ci for_all_evt_queues(adapter, eqo, i) { 49408c2ecf20Sopenharmony_ci be_eq_notify(eqo->adapter, eqo->q.id, false, true, 0, 0); 49418c2ecf20Sopenharmony_ci napi_schedule(&eqo->napi); 49428c2ecf20Sopenharmony_ci } 49438c2ecf20Sopenharmony_ci} 49448c2ecf20Sopenharmony_ci#endif 49458c2ecf20Sopenharmony_ci 49468c2ecf20Sopenharmony_ciint be_load_fw(struct be_adapter *adapter, u8 *fw_file) 49478c2ecf20Sopenharmony_ci{ 49488c2ecf20Sopenharmony_ci const struct firmware *fw; 49498c2ecf20Sopenharmony_ci int status; 49508c2ecf20Sopenharmony_ci 49518c2ecf20Sopenharmony_ci if (!netif_running(adapter->netdev)) { 49528c2ecf20Sopenharmony_ci dev_err(&adapter->pdev->dev, 49538c2ecf20Sopenharmony_ci "Firmware load not allowed (interface is down)\n"); 49548c2ecf20Sopenharmony_ci return -ENETDOWN; 49558c2ecf20Sopenharmony_ci } 49568c2ecf20Sopenharmony_ci 49578c2ecf20Sopenharmony_ci status = request_firmware(&fw, fw_file, &adapter->pdev->dev); 49588c2ecf20Sopenharmony_ci if (status) 49598c2ecf20Sopenharmony_ci goto fw_exit; 49608c2ecf20Sopenharmony_ci 49618c2ecf20Sopenharmony_ci dev_info(&adapter->pdev->dev, "Flashing firmware file %s\n", fw_file); 49628c2ecf20Sopenharmony_ci 49638c2ecf20Sopenharmony_ci if (lancer_chip(adapter)) 49648c2ecf20Sopenharmony_ci status = lancer_fw_download(adapter, fw); 49658c2ecf20Sopenharmony_ci else 49668c2ecf20Sopenharmony_ci status = be_fw_download(adapter, fw); 49678c2ecf20Sopenharmony_ci 49688c2ecf20Sopenharmony_ci if (!status) 49698c2ecf20Sopenharmony_ci be_cmd_get_fw_ver(adapter); 49708c2ecf20Sopenharmony_ci 49718c2ecf20Sopenharmony_cifw_exit: 49728c2ecf20Sopenharmony_ci release_firmware(fw); 49738c2ecf20Sopenharmony_ci return status; 49748c2ecf20Sopenharmony_ci} 49758c2ecf20Sopenharmony_ci 49768c2ecf20Sopenharmony_cistatic int be_ndo_bridge_setlink(struct net_device *dev, struct nlmsghdr *nlh, 49778c2ecf20Sopenharmony_ci u16 flags, struct netlink_ext_ack *extack) 49788c2ecf20Sopenharmony_ci{ 49798c2ecf20Sopenharmony_ci struct be_adapter *adapter = netdev_priv(dev); 49808c2ecf20Sopenharmony_ci struct nlattr *attr, *br_spec; 49818c2ecf20Sopenharmony_ci int rem; 49828c2ecf20Sopenharmony_ci int status = 0; 49838c2ecf20Sopenharmony_ci u16 mode = 0; 49848c2ecf20Sopenharmony_ci 49858c2ecf20Sopenharmony_ci if (!sriov_enabled(adapter)) 49868c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 49878c2ecf20Sopenharmony_ci 49888c2ecf20Sopenharmony_ci br_spec = nlmsg_find_attr(nlh, sizeof(struct ifinfomsg), IFLA_AF_SPEC); 49898c2ecf20Sopenharmony_ci if (!br_spec) 49908c2ecf20Sopenharmony_ci return -EINVAL; 49918c2ecf20Sopenharmony_ci 49928c2ecf20Sopenharmony_ci nla_for_each_nested(attr, br_spec, rem) { 49938c2ecf20Sopenharmony_ci if (nla_type(attr) != IFLA_BRIDGE_MODE) 49948c2ecf20Sopenharmony_ci continue; 49958c2ecf20Sopenharmony_ci 49968c2ecf20Sopenharmony_ci if (nla_len(attr) < sizeof(mode)) 49978c2ecf20Sopenharmony_ci return -EINVAL; 49988c2ecf20Sopenharmony_ci 49998c2ecf20Sopenharmony_ci mode = nla_get_u16(attr); 50008c2ecf20Sopenharmony_ci if (BE3_chip(adapter) && mode == BRIDGE_MODE_VEPA) 50018c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 50028c2ecf20Sopenharmony_ci 50038c2ecf20Sopenharmony_ci if (mode != BRIDGE_MODE_VEPA && mode != BRIDGE_MODE_VEB) 50048c2ecf20Sopenharmony_ci return -EINVAL; 50058c2ecf20Sopenharmony_ci 50068c2ecf20Sopenharmony_ci status = be_cmd_set_hsw_config(adapter, 0, 0, 50078c2ecf20Sopenharmony_ci adapter->if_handle, 50088c2ecf20Sopenharmony_ci mode == BRIDGE_MODE_VEPA ? 50098c2ecf20Sopenharmony_ci PORT_FWD_TYPE_VEPA : 50108c2ecf20Sopenharmony_ci PORT_FWD_TYPE_VEB, 0); 50118c2ecf20Sopenharmony_ci if (status) 50128c2ecf20Sopenharmony_ci goto err; 50138c2ecf20Sopenharmony_ci 50148c2ecf20Sopenharmony_ci dev_info(&adapter->pdev->dev, "enabled switch mode: %s\n", 50158c2ecf20Sopenharmony_ci mode == BRIDGE_MODE_VEPA ? "VEPA" : "VEB"); 50168c2ecf20Sopenharmony_ci 50178c2ecf20Sopenharmony_ci return status; 50188c2ecf20Sopenharmony_ci } 50198c2ecf20Sopenharmony_cierr: 50208c2ecf20Sopenharmony_ci dev_err(&adapter->pdev->dev, "Failed to set switch mode %s\n", 50218c2ecf20Sopenharmony_ci mode == BRIDGE_MODE_VEPA ? "VEPA" : "VEB"); 50228c2ecf20Sopenharmony_ci 50238c2ecf20Sopenharmony_ci return status; 50248c2ecf20Sopenharmony_ci} 50258c2ecf20Sopenharmony_ci 50268c2ecf20Sopenharmony_cistatic int be_ndo_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq, 50278c2ecf20Sopenharmony_ci struct net_device *dev, u32 filter_mask, 50288c2ecf20Sopenharmony_ci int nlflags) 50298c2ecf20Sopenharmony_ci{ 50308c2ecf20Sopenharmony_ci struct be_adapter *adapter = netdev_priv(dev); 50318c2ecf20Sopenharmony_ci int status = 0; 50328c2ecf20Sopenharmony_ci u8 hsw_mode; 50338c2ecf20Sopenharmony_ci 50348c2ecf20Sopenharmony_ci /* BE and Lancer chips support VEB mode only */ 50358c2ecf20Sopenharmony_ci if (BEx_chip(adapter) || lancer_chip(adapter)) { 50368c2ecf20Sopenharmony_ci /* VEB is disabled in non-SR-IOV profiles on BE3/Lancer */ 50378c2ecf20Sopenharmony_ci if (!pci_sriov_get_totalvfs(adapter->pdev)) 50388c2ecf20Sopenharmony_ci return 0; 50398c2ecf20Sopenharmony_ci hsw_mode = PORT_FWD_TYPE_VEB; 50408c2ecf20Sopenharmony_ci } else { 50418c2ecf20Sopenharmony_ci status = be_cmd_get_hsw_config(adapter, NULL, 0, 50428c2ecf20Sopenharmony_ci adapter->if_handle, &hsw_mode, 50438c2ecf20Sopenharmony_ci NULL); 50448c2ecf20Sopenharmony_ci if (status) 50458c2ecf20Sopenharmony_ci return 0; 50468c2ecf20Sopenharmony_ci 50478c2ecf20Sopenharmony_ci if (hsw_mode == PORT_FWD_TYPE_PASSTHRU) 50488c2ecf20Sopenharmony_ci return 0; 50498c2ecf20Sopenharmony_ci } 50508c2ecf20Sopenharmony_ci 50518c2ecf20Sopenharmony_ci return ndo_dflt_bridge_getlink(skb, pid, seq, dev, 50528c2ecf20Sopenharmony_ci hsw_mode == PORT_FWD_TYPE_VEPA ? 50538c2ecf20Sopenharmony_ci BRIDGE_MODE_VEPA : BRIDGE_MODE_VEB, 50548c2ecf20Sopenharmony_ci 0, 0, nlflags, filter_mask, NULL); 50558c2ecf20Sopenharmony_ci} 50568c2ecf20Sopenharmony_ci 50578c2ecf20Sopenharmony_cistatic struct be_cmd_work *be_alloc_work(struct be_adapter *adapter, 50588c2ecf20Sopenharmony_ci void (*func)(struct work_struct *)) 50598c2ecf20Sopenharmony_ci{ 50608c2ecf20Sopenharmony_ci struct be_cmd_work *work; 50618c2ecf20Sopenharmony_ci 50628c2ecf20Sopenharmony_ci work = kzalloc(sizeof(*work), GFP_ATOMIC); 50638c2ecf20Sopenharmony_ci if (!work) { 50648c2ecf20Sopenharmony_ci dev_err(&adapter->pdev->dev, 50658c2ecf20Sopenharmony_ci "be_work memory allocation failed\n"); 50668c2ecf20Sopenharmony_ci return NULL; 50678c2ecf20Sopenharmony_ci } 50688c2ecf20Sopenharmony_ci 50698c2ecf20Sopenharmony_ci INIT_WORK(&work->work, func); 50708c2ecf20Sopenharmony_ci work->adapter = adapter; 50718c2ecf20Sopenharmony_ci return work; 50728c2ecf20Sopenharmony_ci} 50738c2ecf20Sopenharmony_ci 50748c2ecf20Sopenharmony_cistatic netdev_features_t be_features_check(struct sk_buff *skb, 50758c2ecf20Sopenharmony_ci struct net_device *dev, 50768c2ecf20Sopenharmony_ci netdev_features_t features) 50778c2ecf20Sopenharmony_ci{ 50788c2ecf20Sopenharmony_ci struct be_adapter *adapter = netdev_priv(dev); 50798c2ecf20Sopenharmony_ci u8 l4_hdr = 0; 50808c2ecf20Sopenharmony_ci 50818c2ecf20Sopenharmony_ci if (skb_is_gso(skb)) { 50828c2ecf20Sopenharmony_ci /* IPv6 TSO requests with extension hdrs are a problem 50838c2ecf20Sopenharmony_ci * to Lancer and BE3 HW. Disable TSO6 feature. 50848c2ecf20Sopenharmony_ci */ 50858c2ecf20Sopenharmony_ci if (!skyhawk_chip(adapter) && is_ipv6_ext_hdr(skb)) 50868c2ecf20Sopenharmony_ci features &= ~NETIF_F_TSO6; 50878c2ecf20Sopenharmony_ci 50888c2ecf20Sopenharmony_ci /* Lancer cannot handle the packet with MSS less than 256. 50898c2ecf20Sopenharmony_ci * Also it can't handle a TSO packet with a single segment 50908c2ecf20Sopenharmony_ci * Disable the GSO support in such cases 50918c2ecf20Sopenharmony_ci */ 50928c2ecf20Sopenharmony_ci if (lancer_chip(adapter) && 50938c2ecf20Sopenharmony_ci (skb_shinfo(skb)->gso_size < 256 || 50948c2ecf20Sopenharmony_ci skb_shinfo(skb)->gso_segs == 1)) 50958c2ecf20Sopenharmony_ci features &= ~NETIF_F_GSO_MASK; 50968c2ecf20Sopenharmony_ci } 50978c2ecf20Sopenharmony_ci 50988c2ecf20Sopenharmony_ci /* The code below restricts offload features for some tunneled and 50998c2ecf20Sopenharmony_ci * Q-in-Q packets. 51008c2ecf20Sopenharmony_ci * Offload features for normal (non tunnel) packets are unchanged. 51018c2ecf20Sopenharmony_ci */ 51028c2ecf20Sopenharmony_ci features = vlan_features_check(skb, features); 51038c2ecf20Sopenharmony_ci if (!skb->encapsulation || 51048c2ecf20Sopenharmony_ci !(adapter->flags & BE_FLAGS_VXLAN_OFFLOADS)) 51058c2ecf20Sopenharmony_ci return features; 51068c2ecf20Sopenharmony_ci 51078c2ecf20Sopenharmony_ci /* It's an encapsulated packet and VxLAN offloads are enabled. We 51088c2ecf20Sopenharmony_ci * should disable tunnel offload features if it's not a VxLAN packet, 51098c2ecf20Sopenharmony_ci * as tunnel offloads have been enabled only for VxLAN. This is done to 51108c2ecf20Sopenharmony_ci * allow other tunneled traffic like GRE work fine while VxLAN 51118c2ecf20Sopenharmony_ci * offloads are configured in Skyhawk-R. 51128c2ecf20Sopenharmony_ci */ 51138c2ecf20Sopenharmony_ci switch (vlan_get_protocol(skb)) { 51148c2ecf20Sopenharmony_ci case htons(ETH_P_IP): 51158c2ecf20Sopenharmony_ci l4_hdr = ip_hdr(skb)->protocol; 51168c2ecf20Sopenharmony_ci break; 51178c2ecf20Sopenharmony_ci case htons(ETH_P_IPV6): 51188c2ecf20Sopenharmony_ci l4_hdr = ipv6_hdr(skb)->nexthdr; 51198c2ecf20Sopenharmony_ci break; 51208c2ecf20Sopenharmony_ci default: 51218c2ecf20Sopenharmony_ci return features; 51228c2ecf20Sopenharmony_ci } 51238c2ecf20Sopenharmony_ci 51248c2ecf20Sopenharmony_ci if (l4_hdr != IPPROTO_UDP || 51258c2ecf20Sopenharmony_ci skb->inner_protocol_type != ENCAP_TYPE_ETHER || 51268c2ecf20Sopenharmony_ci skb->inner_protocol != htons(ETH_P_TEB) || 51278c2ecf20Sopenharmony_ci skb_inner_mac_header(skb) - skb_transport_header(skb) != 51288c2ecf20Sopenharmony_ci sizeof(struct udphdr) + sizeof(struct vxlanhdr) || 51298c2ecf20Sopenharmony_ci !adapter->vxlan_port || 51308c2ecf20Sopenharmony_ci udp_hdr(skb)->dest != adapter->vxlan_port) 51318c2ecf20Sopenharmony_ci return features & ~(NETIF_F_CSUM_MASK | NETIF_F_GSO_MASK); 51328c2ecf20Sopenharmony_ci 51338c2ecf20Sopenharmony_ci return features; 51348c2ecf20Sopenharmony_ci} 51358c2ecf20Sopenharmony_ci 51368c2ecf20Sopenharmony_cistatic int be_get_phys_port_id(struct net_device *dev, 51378c2ecf20Sopenharmony_ci struct netdev_phys_item_id *ppid) 51388c2ecf20Sopenharmony_ci{ 51398c2ecf20Sopenharmony_ci int i, id_len = CNTL_SERIAL_NUM_WORDS * CNTL_SERIAL_NUM_WORD_SZ + 1; 51408c2ecf20Sopenharmony_ci struct be_adapter *adapter = netdev_priv(dev); 51418c2ecf20Sopenharmony_ci u8 *id; 51428c2ecf20Sopenharmony_ci 51438c2ecf20Sopenharmony_ci if (MAX_PHYS_ITEM_ID_LEN < id_len) 51448c2ecf20Sopenharmony_ci return -ENOSPC; 51458c2ecf20Sopenharmony_ci 51468c2ecf20Sopenharmony_ci ppid->id[0] = adapter->hba_port_num + 1; 51478c2ecf20Sopenharmony_ci id = &ppid->id[1]; 51488c2ecf20Sopenharmony_ci for (i = CNTL_SERIAL_NUM_WORDS - 1; i >= 0; 51498c2ecf20Sopenharmony_ci i--, id += CNTL_SERIAL_NUM_WORD_SZ) 51508c2ecf20Sopenharmony_ci memcpy(id, &adapter->serial_num[i], CNTL_SERIAL_NUM_WORD_SZ); 51518c2ecf20Sopenharmony_ci 51528c2ecf20Sopenharmony_ci ppid->id_len = id_len; 51538c2ecf20Sopenharmony_ci 51548c2ecf20Sopenharmony_ci return 0; 51558c2ecf20Sopenharmony_ci} 51568c2ecf20Sopenharmony_ci 51578c2ecf20Sopenharmony_cistatic void be_set_rx_mode(struct net_device *dev) 51588c2ecf20Sopenharmony_ci{ 51598c2ecf20Sopenharmony_ci struct be_adapter *adapter = netdev_priv(dev); 51608c2ecf20Sopenharmony_ci struct be_cmd_work *work; 51618c2ecf20Sopenharmony_ci 51628c2ecf20Sopenharmony_ci work = be_alloc_work(adapter, be_work_set_rx_mode); 51638c2ecf20Sopenharmony_ci if (work) 51648c2ecf20Sopenharmony_ci queue_work(be_wq, &work->work); 51658c2ecf20Sopenharmony_ci} 51668c2ecf20Sopenharmony_ci 51678c2ecf20Sopenharmony_cistatic const struct net_device_ops be_netdev_ops = { 51688c2ecf20Sopenharmony_ci .ndo_open = be_open, 51698c2ecf20Sopenharmony_ci .ndo_stop = be_close, 51708c2ecf20Sopenharmony_ci .ndo_start_xmit = be_xmit, 51718c2ecf20Sopenharmony_ci .ndo_set_rx_mode = be_set_rx_mode, 51728c2ecf20Sopenharmony_ci .ndo_set_mac_address = be_mac_addr_set, 51738c2ecf20Sopenharmony_ci .ndo_get_stats64 = be_get_stats64, 51748c2ecf20Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 51758c2ecf20Sopenharmony_ci .ndo_vlan_rx_add_vid = be_vlan_add_vid, 51768c2ecf20Sopenharmony_ci .ndo_vlan_rx_kill_vid = be_vlan_rem_vid, 51778c2ecf20Sopenharmony_ci .ndo_set_vf_mac = be_set_vf_mac, 51788c2ecf20Sopenharmony_ci .ndo_set_vf_vlan = be_set_vf_vlan, 51798c2ecf20Sopenharmony_ci .ndo_set_vf_rate = be_set_vf_tx_rate, 51808c2ecf20Sopenharmony_ci .ndo_get_vf_config = be_get_vf_config, 51818c2ecf20Sopenharmony_ci .ndo_set_vf_link_state = be_set_vf_link_state, 51828c2ecf20Sopenharmony_ci .ndo_set_vf_spoofchk = be_set_vf_spoofchk, 51838c2ecf20Sopenharmony_ci .ndo_tx_timeout = be_tx_timeout, 51848c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER 51858c2ecf20Sopenharmony_ci .ndo_poll_controller = be_netpoll, 51868c2ecf20Sopenharmony_ci#endif 51878c2ecf20Sopenharmony_ci .ndo_bridge_setlink = be_ndo_bridge_setlink, 51888c2ecf20Sopenharmony_ci .ndo_bridge_getlink = be_ndo_bridge_getlink, 51898c2ecf20Sopenharmony_ci .ndo_udp_tunnel_add = udp_tunnel_nic_add_port, 51908c2ecf20Sopenharmony_ci .ndo_udp_tunnel_del = udp_tunnel_nic_del_port, 51918c2ecf20Sopenharmony_ci .ndo_features_check = be_features_check, 51928c2ecf20Sopenharmony_ci .ndo_get_phys_port_id = be_get_phys_port_id, 51938c2ecf20Sopenharmony_ci}; 51948c2ecf20Sopenharmony_ci 51958c2ecf20Sopenharmony_cistatic void be_netdev_init(struct net_device *netdev) 51968c2ecf20Sopenharmony_ci{ 51978c2ecf20Sopenharmony_ci struct be_adapter *adapter = netdev_priv(netdev); 51988c2ecf20Sopenharmony_ci 51998c2ecf20Sopenharmony_ci netdev->hw_features |= NETIF_F_SG | NETIF_F_TSO | NETIF_F_TSO6 | 52008c2ecf20Sopenharmony_ci NETIF_F_GSO_UDP_TUNNEL | 52018c2ecf20Sopenharmony_ci NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM | NETIF_F_RXCSUM | 52028c2ecf20Sopenharmony_ci NETIF_F_HW_VLAN_CTAG_TX; 52038c2ecf20Sopenharmony_ci if ((be_if_cap_flags(adapter) & BE_IF_FLAGS_RSS)) 52048c2ecf20Sopenharmony_ci netdev->hw_features |= NETIF_F_RXHASH; 52058c2ecf20Sopenharmony_ci 52068c2ecf20Sopenharmony_ci netdev->features |= netdev->hw_features | 52078c2ecf20Sopenharmony_ci NETIF_F_HW_VLAN_CTAG_RX | NETIF_F_HW_VLAN_CTAG_FILTER; 52088c2ecf20Sopenharmony_ci 52098c2ecf20Sopenharmony_ci netdev->vlan_features |= NETIF_F_SG | NETIF_F_TSO | NETIF_F_TSO6 | 52108c2ecf20Sopenharmony_ci NETIF_F_IP_CSUM | NETIF_F_IPV6_CSUM; 52118c2ecf20Sopenharmony_ci 52128c2ecf20Sopenharmony_ci netdev->priv_flags |= IFF_UNICAST_FLT; 52138c2ecf20Sopenharmony_ci 52148c2ecf20Sopenharmony_ci netdev->flags |= IFF_MULTICAST; 52158c2ecf20Sopenharmony_ci 52168c2ecf20Sopenharmony_ci netif_set_gso_max_size(netdev, BE_MAX_GSO_SIZE - ETH_HLEN); 52178c2ecf20Sopenharmony_ci 52188c2ecf20Sopenharmony_ci netdev->netdev_ops = &be_netdev_ops; 52198c2ecf20Sopenharmony_ci 52208c2ecf20Sopenharmony_ci netdev->ethtool_ops = &be_ethtool_ops; 52218c2ecf20Sopenharmony_ci 52228c2ecf20Sopenharmony_ci if (!lancer_chip(adapter) && !BEx_chip(adapter) && !be_is_mc(adapter)) 52238c2ecf20Sopenharmony_ci netdev->udp_tunnel_nic_info = &be_udp_tunnels; 52248c2ecf20Sopenharmony_ci 52258c2ecf20Sopenharmony_ci /* MTU range: 256 - 9000 */ 52268c2ecf20Sopenharmony_ci netdev->min_mtu = BE_MIN_MTU; 52278c2ecf20Sopenharmony_ci netdev->max_mtu = BE_MAX_MTU; 52288c2ecf20Sopenharmony_ci} 52298c2ecf20Sopenharmony_ci 52308c2ecf20Sopenharmony_cistatic void be_cleanup(struct be_adapter *adapter) 52318c2ecf20Sopenharmony_ci{ 52328c2ecf20Sopenharmony_ci struct net_device *netdev = adapter->netdev; 52338c2ecf20Sopenharmony_ci 52348c2ecf20Sopenharmony_ci rtnl_lock(); 52358c2ecf20Sopenharmony_ci netif_device_detach(netdev); 52368c2ecf20Sopenharmony_ci if (netif_running(netdev)) 52378c2ecf20Sopenharmony_ci be_close(netdev); 52388c2ecf20Sopenharmony_ci rtnl_unlock(); 52398c2ecf20Sopenharmony_ci 52408c2ecf20Sopenharmony_ci be_clear(adapter); 52418c2ecf20Sopenharmony_ci} 52428c2ecf20Sopenharmony_ci 52438c2ecf20Sopenharmony_cistatic int be_resume(struct be_adapter *adapter) 52448c2ecf20Sopenharmony_ci{ 52458c2ecf20Sopenharmony_ci struct net_device *netdev = adapter->netdev; 52468c2ecf20Sopenharmony_ci int status; 52478c2ecf20Sopenharmony_ci 52488c2ecf20Sopenharmony_ci status = be_setup(adapter); 52498c2ecf20Sopenharmony_ci if (status) 52508c2ecf20Sopenharmony_ci return status; 52518c2ecf20Sopenharmony_ci 52528c2ecf20Sopenharmony_ci rtnl_lock(); 52538c2ecf20Sopenharmony_ci if (netif_running(netdev)) 52548c2ecf20Sopenharmony_ci status = be_open(netdev); 52558c2ecf20Sopenharmony_ci rtnl_unlock(); 52568c2ecf20Sopenharmony_ci 52578c2ecf20Sopenharmony_ci if (status) 52588c2ecf20Sopenharmony_ci return status; 52598c2ecf20Sopenharmony_ci 52608c2ecf20Sopenharmony_ci netif_device_attach(netdev); 52618c2ecf20Sopenharmony_ci 52628c2ecf20Sopenharmony_ci return 0; 52638c2ecf20Sopenharmony_ci} 52648c2ecf20Sopenharmony_ci 52658c2ecf20Sopenharmony_cistatic void be_soft_reset(struct be_adapter *adapter) 52668c2ecf20Sopenharmony_ci{ 52678c2ecf20Sopenharmony_ci u32 val; 52688c2ecf20Sopenharmony_ci 52698c2ecf20Sopenharmony_ci dev_info(&adapter->pdev->dev, "Initiating chip soft reset\n"); 52708c2ecf20Sopenharmony_ci val = ioread32(adapter->pcicfg + SLIPORT_SOFTRESET_OFFSET); 52718c2ecf20Sopenharmony_ci val |= SLIPORT_SOFTRESET_SR_MASK; 52728c2ecf20Sopenharmony_ci iowrite32(val, adapter->pcicfg + SLIPORT_SOFTRESET_OFFSET); 52738c2ecf20Sopenharmony_ci} 52748c2ecf20Sopenharmony_ci 52758c2ecf20Sopenharmony_cistatic bool be_err_is_recoverable(struct be_adapter *adapter) 52768c2ecf20Sopenharmony_ci{ 52778c2ecf20Sopenharmony_ci struct be_error_recovery *err_rec = &adapter->error_recovery; 52788c2ecf20Sopenharmony_ci unsigned long initial_idle_time = 52798c2ecf20Sopenharmony_ci msecs_to_jiffies(ERR_RECOVERY_IDLE_TIME); 52808c2ecf20Sopenharmony_ci unsigned long recovery_interval = 52818c2ecf20Sopenharmony_ci msecs_to_jiffies(ERR_RECOVERY_INTERVAL); 52828c2ecf20Sopenharmony_ci u16 ue_err_code; 52838c2ecf20Sopenharmony_ci u32 val; 52848c2ecf20Sopenharmony_ci 52858c2ecf20Sopenharmony_ci val = be_POST_stage_get(adapter); 52868c2ecf20Sopenharmony_ci if ((val & POST_STAGE_RECOVERABLE_ERR) != POST_STAGE_RECOVERABLE_ERR) 52878c2ecf20Sopenharmony_ci return false; 52888c2ecf20Sopenharmony_ci ue_err_code = val & POST_ERR_RECOVERY_CODE_MASK; 52898c2ecf20Sopenharmony_ci if (ue_err_code == 0) 52908c2ecf20Sopenharmony_ci return false; 52918c2ecf20Sopenharmony_ci 52928c2ecf20Sopenharmony_ci dev_err(&adapter->pdev->dev, "Recoverable HW error code: 0x%x\n", 52938c2ecf20Sopenharmony_ci ue_err_code); 52948c2ecf20Sopenharmony_ci 52958c2ecf20Sopenharmony_ci if (time_before_eq(jiffies - err_rec->probe_time, initial_idle_time)) { 52968c2ecf20Sopenharmony_ci dev_err(&adapter->pdev->dev, 52978c2ecf20Sopenharmony_ci "Cannot recover within %lu sec from driver load\n", 52988c2ecf20Sopenharmony_ci jiffies_to_msecs(initial_idle_time) / MSEC_PER_SEC); 52998c2ecf20Sopenharmony_ci return false; 53008c2ecf20Sopenharmony_ci } 53018c2ecf20Sopenharmony_ci 53028c2ecf20Sopenharmony_ci if (err_rec->last_recovery_time && time_before_eq( 53038c2ecf20Sopenharmony_ci jiffies - err_rec->last_recovery_time, recovery_interval)) { 53048c2ecf20Sopenharmony_ci dev_err(&adapter->pdev->dev, 53058c2ecf20Sopenharmony_ci "Cannot recover within %lu sec from last recovery\n", 53068c2ecf20Sopenharmony_ci jiffies_to_msecs(recovery_interval) / MSEC_PER_SEC); 53078c2ecf20Sopenharmony_ci return false; 53088c2ecf20Sopenharmony_ci } 53098c2ecf20Sopenharmony_ci 53108c2ecf20Sopenharmony_ci if (ue_err_code == err_rec->last_err_code) { 53118c2ecf20Sopenharmony_ci dev_err(&adapter->pdev->dev, 53128c2ecf20Sopenharmony_ci "Cannot recover from a consecutive TPE error\n"); 53138c2ecf20Sopenharmony_ci return false; 53148c2ecf20Sopenharmony_ci } 53158c2ecf20Sopenharmony_ci 53168c2ecf20Sopenharmony_ci err_rec->last_recovery_time = jiffies; 53178c2ecf20Sopenharmony_ci err_rec->last_err_code = ue_err_code; 53188c2ecf20Sopenharmony_ci return true; 53198c2ecf20Sopenharmony_ci} 53208c2ecf20Sopenharmony_ci 53218c2ecf20Sopenharmony_cistatic int be_tpe_recover(struct be_adapter *adapter) 53228c2ecf20Sopenharmony_ci{ 53238c2ecf20Sopenharmony_ci struct be_error_recovery *err_rec = &adapter->error_recovery; 53248c2ecf20Sopenharmony_ci int status = -EAGAIN; 53258c2ecf20Sopenharmony_ci u32 val; 53268c2ecf20Sopenharmony_ci 53278c2ecf20Sopenharmony_ci switch (err_rec->recovery_state) { 53288c2ecf20Sopenharmony_ci case ERR_RECOVERY_ST_NONE: 53298c2ecf20Sopenharmony_ci err_rec->recovery_state = ERR_RECOVERY_ST_DETECT; 53308c2ecf20Sopenharmony_ci err_rec->resched_delay = ERR_RECOVERY_UE_DETECT_DURATION; 53318c2ecf20Sopenharmony_ci break; 53328c2ecf20Sopenharmony_ci 53338c2ecf20Sopenharmony_ci case ERR_RECOVERY_ST_DETECT: 53348c2ecf20Sopenharmony_ci val = be_POST_stage_get(adapter); 53358c2ecf20Sopenharmony_ci if ((val & POST_STAGE_RECOVERABLE_ERR) != 53368c2ecf20Sopenharmony_ci POST_STAGE_RECOVERABLE_ERR) { 53378c2ecf20Sopenharmony_ci dev_err(&adapter->pdev->dev, 53388c2ecf20Sopenharmony_ci "Unrecoverable HW error detected: 0x%x\n", val); 53398c2ecf20Sopenharmony_ci status = -EINVAL; 53408c2ecf20Sopenharmony_ci err_rec->resched_delay = 0; 53418c2ecf20Sopenharmony_ci break; 53428c2ecf20Sopenharmony_ci } 53438c2ecf20Sopenharmony_ci 53448c2ecf20Sopenharmony_ci dev_err(&adapter->pdev->dev, "Recoverable HW error detected\n"); 53458c2ecf20Sopenharmony_ci 53468c2ecf20Sopenharmony_ci /* Only PF0 initiates Chip Soft Reset. But PF0 must wait UE2SR 53478c2ecf20Sopenharmony_ci * milliseconds before it checks for final error status in 53488c2ecf20Sopenharmony_ci * SLIPORT_SEMAPHORE to determine if recovery criteria is met. 53498c2ecf20Sopenharmony_ci * If it does, then PF0 initiates a Soft Reset. 53508c2ecf20Sopenharmony_ci */ 53518c2ecf20Sopenharmony_ci if (adapter->pf_num == 0) { 53528c2ecf20Sopenharmony_ci err_rec->recovery_state = ERR_RECOVERY_ST_RESET; 53538c2ecf20Sopenharmony_ci err_rec->resched_delay = err_rec->ue_to_reset_time - 53548c2ecf20Sopenharmony_ci ERR_RECOVERY_UE_DETECT_DURATION; 53558c2ecf20Sopenharmony_ci break; 53568c2ecf20Sopenharmony_ci } 53578c2ecf20Sopenharmony_ci 53588c2ecf20Sopenharmony_ci err_rec->recovery_state = ERR_RECOVERY_ST_PRE_POLL; 53598c2ecf20Sopenharmony_ci err_rec->resched_delay = err_rec->ue_to_poll_time - 53608c2ecf20Sopenharmony_ci ERR_RECOVERY_UE_DETECT_DURATION; 53618c2ecf20Sopenharmony_ci break; 53628c2ecf20Sopenharmony_ci 53638c2ecf20Sopenharmony_ci case ERR_RECOVERY_ST_RESET: 53648c2ecf20Sopenharmony_ci if (!be_err_is_recoverable(adapter)) { 53658c2ecf20Sopenharmony_ci dev_err(&adapter->pdev->dev, 53668c2ecf20Sopenharmony_ci "Failed to meet recovery criteria\n"); 53678c2ecf20Sopenharmony_ci status = -EIO; 53688c2ecf20Sopenharmony_ci err_rec->resched_delay = 0; 53698c2ecf20Sopenharmony_ci break; 53708c2ecf20Sopenharmony_ci } 53718c2ecf20Sopenharmony_ci be_soft_reset(adapter); 53728c2ecf20Sopenharmony_ci err_rec->recovery_state = ERR_RECOVERY_ST_PRE_POLL; 53738c2ecf20Sopenharmony_ci err_rec->resched_delay = err_rec->ue_to_poll_time - 53748c2ecf20Sopenharmony_ci err_rec->ue_to_reset_time; 53758c2ecf20Sopenharmony_ci break; 53768c2ecf20Sopenharmony_ci 53778c2ecf20Sopenharmony_ci case ERR_RECOVERY_ST_PRE_POLL: 53788c2ecf20Sopenharmony_ci err_rec->recovery_state = ERR_RECOVERY_ST_REINIT; 53798c2ecf20Sopenharmony_ci err_rec->resched_delay = 0; 53808c2ecf20Sopenharmony_ci status = 0; /* done */ 53818c2ecf20Sopenharmony_ci break; 53828c2ecf20Sopenharmony_ci 53838c2ecf20Sopenharmony_ci default: 53848c2ecf20Sopenharmony_ci status = -EINVAL; 53858c2ecf20Sopenharmony_ci err_rec->resched_delay = 0; 53868c2ecf20Sopenharmony_ci break; 53878c2ecf20Sopenharmony_ci } 53888c2ecf20Sopenharmony_ci 53898c2ecf20Sopenharmony_ci return status; 53908c2ecf20Sopenharmony_ci} 53918c2ecf20Sopenharmony_ci 53928c2ecf20Sopenharmony_cistatic int be_err_recover(struct be_adapter *adapter) 53938c2ecf20Sopenharmony_ci{ 53948c2ecf20Sopenharmony_ci int status; 53958c2ecf20Sopenharmony_ci 53968c2ecf20Sopenharmony_ci if (!lancer_chip(adapter)) { 53978c2ecf20Sopenharmony_ci if (!adapter->error_recovery.recovery_supported || 53988c2ecf20Sopenharmony_ci adapter->priv_flags & BE_DISABLE_TPE_RECOVERY) 53998c2ecf20Sopenharmony_ci return -EIO; 54008c2ecf20Sopenharmony_ci status = be_tpe_recover(adapter); 54018c2ecf20Sopenharmony_ci if (status) 54028c2ecf20Sopenharmony_ci goto err; 54038c2ecf20Sopenharmony_ci } 54048c2ecf20Sopenharmony_ci 54058c2ecf20Sopenharmony_ci /* Wait for adapter to reach quiescent state before 54068c2ecf20Sopenharmony_ci * destroying queues 54078c2ecf20Sopenharmony_ci */ 54088c2ecf20Sopenharmony_ci status = be_fw_wait_ready(adapter); 54098c2ecf20Sopenharmony_ci if (status) 54108c2ecf20Sopenharmony_ci goto err; 54118c2ecf20Sopenharmony_ci 54128c2ecf20Sopenharmony_ci adapter->flags |= BE_FLAGS_TRY_RECOVERY; 54138c2ecf20Sopenharmony_ci 54148c2ecf20Sopenharmony_ci be_cleanup(adapter); 54158c2ecf20Sopenharmony_ci 54168c2ecf20Sopenharmony_ci status = be_resume(adapter); 54178c2ecf20Sopenharmony_ci if (status) 54188c2ecf20Sopenharmony_ci goto err; 54198c2ecf20Sopenharmony_ci 54208c2ecf20Sopenharmony_ci adapter->flags &= ~BE_FLAGS_TRY_RECOVERY; 54218c2ecf20Sopenharmony_ci 54228c2ecf20Sopenharmony_cierr: 54238c2ecf20Sopenharmony_ci return status; 54248c2ecf20Sopenharmony_ci} 54258c2ecf20Sopenharmony_ci 54268c2ecf20Sopenharmony_cistatic void be_err_detection_task(struct work_struct *work) 54278c2ecf20Sopenharmony_ci{ 54288c2ecf20Sopenharmony_ci struct be_error_recovery *err_rec = 54298c2ecf20Sopenharmony_ci container_of(work, struct be_error_recovery, 54308c2ecf20Sopenharmony_ci err_detection_work.work); 54318c2ecf20Sopenharmony_ci struct be_adapter *adapter = 54328c2ecf20Sopenharmony_ci container_of(err_rec, struct be_adapter, 54338c2ecf20Sopenharmony_ci error_recovery); 54348c2ecf20Sopenharmony_ci u32 resched_delay = ERR_RECOVERY_DETECTION_DELAY; 54358c2ecf20Sopenharmony_ci struct device *dev = &adapter->pdev->dev; 54368c2ecf20Sopenharmony_ci int recovery_status; 54378c2ecf20Sopenharmony_ci 54388c2ecf20Sopenharmony_ci be_detect_error(adapter); 54398c2ecf20Sopenharmony_ci if (!be_check_error(adapter, BE_ERROR_HW)) 54408c2ecf20Sopenharmony_ci goto reschedule_task; 54418c2ecf20Sopenharmony_ci 54428c2ecf20Sopenharmony_ci recovery_status = be_err_recover(adapter); 54438c2ecf20Sopenharmony_ci if (!recovery_status) { 54448c2ecf20Sopenharmony_ci err_rec->recovery_retries = 0; 54458c2ecf20Sopenharmony_ci err_rec->recovery_state = ERR_RECOVERY_ST_NONE; 54468c2ecf20Sopenharmony_ci dev_info(dev, "Adapter recovery successful\n"); 54478c2ecf20Sopenharmony_ci goto reschedule_task; 54488c2ecf20Sopenharmony_ci } else if (!lancer_chip(adapter) && err_rec->resched_delay) { 54498c2ecf20Sopenharmony_ci /* BEx/SH recovery state machine */ 54508c2ecf20Sopenharmony_ci if (adapter->pf_num == 0 && 54518c2ecf20Sopenharmony_ci err_rec->recovery_state > ERR_RECOVERY_ST_DETECT) 54528c2ecf20Sopenharmony_ci dev_err(&adapter->pdev->dev, 54538c2ecf20Sopenharmony_ci "Adapter recovery in progress\n"); 54548c2ecf20Sopenharmony_ci resched_delay = err_rec->resched_delay; 54558c2ecf20Sopenharmony_ci goto reschedule_task; 54568c2ecf20Sopenharmony_ci } else if (lancer_chip(adapter) && be_virtfn(adapter)) { 54578c2ecf20Sopenharmony_ci /* For VFs, check if PF have allocated resources 54588c2ecf20Sopenharmony_ci * every second. 54598c2ecf20Sopenharmony_ci */ 54608c2ecf20Sopenharmony_ci dev_err(dev, "Re-trying adapter recovery\n"); 54618c2ecf20Sopenharmony_ci goto reschedule_task; 54628c2ecf20Sopenharmony_ci } else if (lancer_chip(adapter) && err_rec->recovery_retries++ < 54638c2ecf20Sopenharmony_ci ERR_RECOVERY_MAX_RETRY_COUNT) { 54648c2ecf20Sopenharmony_ci /* In case of another error during recovery, it takes 30 sec 54658c2ecf20Sopenharmony_ci * for adapter to come out of error. Retry error recovery after 54668c2ecf20Sopenharmony_ci * this time interval. 54678c2ecf20Sopenharmony_ci */ 54688c2ecf20Sopenharmony_ci dev_err(&adapter->pdev->dev, "Re-trying adapter recovery\n"); 54698c2ecf20Sopenharmony_ci resched_delay = ERR_RECOVERY_RETRY_DELAY; 54708c2ecf20Sopenharmony_ci goto reschedule_task; 54718c2ecf20Sopenharmony_ci } else { 54728c2ecf20Sopenharmony_ci dev_err(dev, "Adapter recovery failed\n"); 54738c2ecf20Sopenharmony_ci dev_err(dev, "Please reboot server to recover\n"); 54748c2ecf20Sopenharmony_ci } 54758c2ecf20Sopenharmony_ci 54768c2ecf20Sopenharmony_ci return; 54778c2ecf20Sopenharmony_ci 54788c2ecf20Sopenharmony_cireschedule_task: 54798c2ecf20Sopenharmony_ci be_schedule_err_detection(adapter, resched_delay); 54808c2ecf20Sopenharmony_ci} 54818c2ecf20Sopenharmony_ci 54828c2ecf20Sopenharmony_cistatic void be_log_sfp_info(struct be_adapter *adapter) 54838c2ecf20Sopenharmony_ci{ 54848c2ecf20Sopenharmony_ci int status; 54858c2ecf20Sopenharmony_ci 54868c2ecf20Sopenharmony_ci status = be_cmd_query_sfp_info(adapter); 54878c2ecf20Sopenharmony_ci if (!status) { 54888c2ecf20Sopenharmony_ci dev_err(&adapter->pdev->dev, 54898c2ecf20Sopenharmony_ci "Port %c: %s Vendor: %s part no: %s", 54908c2ecf20Sopenharmony_ci adapter->port_name, 54918c2ecf20Sopenharmony_ci be_misconfig_evt_port_state[adapter->phy_state], 54928c2ecf20Sopenharmony_ci adapter->phy.vendor_name, 54938c2ecf20Sopenharmony_ci adapter->phy.vendor_pn); 54948c2ecf20Sopenharmony_ci } 54958c2ecf20Sopenharmony_ci adapter->flags &= ~BE_FLAGS_PHY_MISCONFIGURED; 54968c2ecf20Sopenharmony_ci} 54978c2ecf20Sopenharmony_ci 54988c2ecf20Sopenharmony_cistatic void be_worker(struct work_struct *work) 54998c2ecf20Sopenharmony_ci{ 55008c2ecf20Sopenharmony_ci struct be_adapter *adapter = 55018c2ecf20Sopenharmony_ci container_of(work, struct be_adapter, work.work); 55028c2ecf20Sopenharmony_ci struct be_rx_obj *rxo; 55038c2ecf20Sopenharmony_ci int i; 55048c2ecf20Sopenharmony_ci 55058c2ecf20Sopenharmony_ci if (be_physfn(adapter) && 55068c2ecf20Sopenharmony_ci MODULO(adapter->work_counter, adapter->be_get_temp_freq) == 0) 55078c2ecf20Sopenharmony_ci be_cmd_get_die_temperature(adapter); 55088c2ecf20Sopenharmony_ci 55098c2ecf20Sopenharmony_ci /* when interrupts are not yet enabled, just reap any pending 55108c2ecf20Sopenharmony_ci * mcc completions 55118c2ecf20Sopenharmony_ci */ 55128c2ecf20Sopenharmony_ci if (!netif_running(adapter->netdev)) { 55138c2ecf20Sopenharmony_ci local_bh_disable(); 55148c2ecf20Sopenharmony_ci be_process_mcc(adapter); 55158c2ecf20Sopenharmony_ci local_bh_enable(); 55168c2ecf20Sopenharmony_ci goto reschedule; 55178c2ecf20Sopenharmony_ci } 55188c2ecf20Sopenharmony_ci 55198c2ecf20Sopenharmony_ci if (!adapter->stats_cmd_sent) { 55208c2ecf20Sopenharmony_ci if (lancer_chip(adapter)) 55218c2ecf20Sopenharmony_ci lancer_cmd_get_pport_stats(adapter, 55228c2ecf20Sopenharmony_ci &adapter->stats_cmd); 55238c2ecf20Sopenharmony_ci else 55248c2ecf20Sopenharmony_ci be_cmd_get_stats(adapter, &adapter->stats_cmd); 55258c2ecf20Sopenharmony_ci } 55268c2ecf20Sopenharmony_ci 55278c2ecf20Sopenharmony_ci for_all_rx_queues(adapter, rxo, i) { 55288c2ecf20Sopenharmony_ci /* Replenish RX-queues starved due to memory 55298c2ecf20Sopenharmony_ci * allocation failures. 55308c2ecf20Sopenharmony_ci */ 55318c2ecf20Sopenharmony_ci if (rxo->rx_post_starved) 55328c2ecf20Sopenharmony_ci be_post_rx_frags(rxo, GFP_KERNEL, MAX_RX_POST); 55338c2ecf20Sopenharmony_ci } 55348c2ecf20Sopenharmony_ci 55358c2ecf20Sopenharmony_ci /* EQ-delay update for Skyhawk is done while notifying EQ */ 55368c2ecf20Sopenharmony_ci if (!skyhawk_chip(adapter)) 55378c2ecf20Sopenharmony_ci be_eqd_update(adapter, false); 55388c2ecf20Sopenharmony_ci 55398c2ecf20Sopenharmony_ci if (adapter->flags & BE_FLAGS_PHY_MISCONFIGURED) 55408c2ecf20Sopenharmony_ci be_log_sfp_info(adapter); 55418c2ecf20Sopenharmony_ci 55428c2ecf20Sopenharmony_cireschedule: 55438c2ecf20Sopenharmony_ci adapter->work_counter++; 55448c2ecf20Sopenharmony_ci queue_delayed_work(be_wq, &adapter->work, msecs_to_jiffies(1000)); 55458c2ecf20Sopenharmony_ci} 55468c2ecf20Sopenharmony_ci 55478c2ecf20Sopenharmony_cistatic void be_unmap_pci_bars(struct be_adapter *adapter) 55488c2ecf20Sopenharmony_ci{ 55498c2ecf20Sopenharmony_ci if (adapter->csr) 55508c2ecf20Sopenharmony_ci pci_iounmap(adapter->pdev, adapter->csr); 55518c2ecf20Sopenharmony_ci if (adapter->db) 55528c2ecf20Sopenharmony_ci pci_iounmap(adapter->pdev, adapter->db); 55538c2ecf20Sopenharmony_ci if (adapter->pcicfg && adapter->pcicfg_mapped) 55548c2ecf20Sopenharmony_ci pci_iounmap(adapter->pdev, adapter->pcicfg); 55558c2ecf20Sopenharmony_ci} 55568c2ecf20Sopenharmony_ci 55578c2ecf20Sopenharmony_cistatic int db_bar(struct be_adapter *adapter) 55588c2ecf20Sopenharmony_ci{ 55598c2ecf20Sopenharmony_ci if (lancer_chip(adapter) || be_virtfn(adapter)) 55608c2ecf20Sopenharmony_ci return 0; 55618c2ecf20Sopenharmony_ci else 55628c2ecf20Sopenharmony_ci return 4; 55638c2ecf20Sopenharmony_ci} 55648c2ecf20Sopenharmony_ci 55658c2ecf20Sopenharmony_cistatic int be_roce_map_pci_bars(struct be_adapter *adapter) 55668c2ecf20Sopenharmony_ci{ 55678c2ecf20Sopenharmony_ci if (skyhawk_chip(adapter)) { 55688c2ecf20Sopenharmony_ci adapter->roce_db.size = 4096; 55698c2ecf20Sopenharmony_ci adapter->roce_db.io_addr = pci_resource_start(adapter->pdev, 55708c2ecf20Sopenharmony_ci db_bar(adapter)); 55718c2ecf20Sopenharmony_ci adapter->roce_db.total_size = pci_resource_len(adapter->pdev, 55728c2ecf20Sopenharmony_ci db_bar(adapter)); 55738c2ecf20Sopenharmony_ci } 55748c2ecf20Sopenharmony_ci return 0; 55758c2ecf20Sopenharmony_ci} 55768c2ecf20Sopenharmony_ci 55778c2ecf20Sopenharmony_cistatic int be_map_pci_bars(struct be_adapter *adapter) 55788c2ecf20Sopenharmony_ci{ 55798c2ecf20Sopenharmony_ci struct pci_dev *pdev = adapter->pdev; 55808c2ecf20Sopenharmony_ci u8 __iomem *addr; 55818c2ecf20Sopenharmony_ci u32 sli_intf; 55828c2ecf20Sopenharmony_ci 55838c2ecf20Sopenharmony_ci pci_read_config_dword(adapter->pdev, SLI_INTF_REG_OFFSET, &sli_intf); 55848c2ecf20Sopenharmony_ci adapter->sli_family = (sli_intf & SLI_INTF_FAMILY_MASK) >> 55858c2ecf20Sopenharmony_ci SLI_INTF_FAMILY_SHIFT; 55868c2ecf20Sopenharmony_ci adapter->virtfn = (sli_intf & SLI_INTF_FT_MASK) ? 1 : 0; 55878c2ecf20Sopenharmony_ci 55888c2ecf20Sopenharmony_ci if (BEx_chip(adapter) && be_physfn(adapter)) { 55898c2ecf20Sopenharmony_ci adapter->csr = pci_iomap(pdev, 2, 0); 55908c2ecf20Sopenharmony_ci if (!adapter->csr) 55918c2ecf20Sopenharmony_ci return -ENOMEM; 55928c2ecf20Sopenharmony_ci } 55938c2ecf20Sopenharmony_ci 55948c2ecf20Sopenharmony_ci addr = pci_iomap(pdev, db_bar(adapter), 0); 55958c2ecf20Sopenharmony_ci if (!addr) 55968c2ecf20Sopenharmony_ci goto pci_map_err; 55978c2ecf20Sopenharmony_ci adapter->db = addr; 55988c2ecf20Sopenharmony_ci 55998c2ecf20Sopenharmony_ci if (skyhawk_chip(adapter) || BEx_chip(adapter)) { 56008c2ecf20Sopenharmony_ci if (be_physfn(adapter)) { 56018c2ecf20Sopenharmony_ci /* PCICFG is the 2nd BAR in BE2 */ 56028c2ecf20Sopenharmony_ci addr = pci_iomap(pdev, BE2_chip(adapter) ? 1 : 0, 0); 56038c2ecf20Sopenharmony_ci if (!addr) 56048c2ecf20Sopenharmony_ci goto pci_map_err; 56058c2ecf20Sopenharmony_ci adapter->pcicfg = addr; 56068c2ecf20Sopenharmony_ci adapter->pcicfg_mapped = true; 56078c2ecf20Sopenharmony_ci } else { 56088c2ecf20Sopenharmony_ci adapter->pcicfg = adapter->db + SRIOV_VF_PCICFG_OFFSET; 56098c2ecf20Sopenharmony_ci adapter->pcicfg_mapped = false; 56108c2ecf20Sopenharmony_ci } 56118c2ecf20Sopenharmony_ci } 56128c2ecf20Sopenharmony_ci 56138c2ecf20Sopenharmony_ci be_roce_map_pci_bars(adapter); 56148c2ecf20Sopenharmony_ci return 0; 56158c2ecf20Sopenharmony_ci 56168c2ecf20Sopenharmony_cipci_map_err: 56178c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Error in mapping PCI BARs\n"); 56188c2ecf20Sopenharmony_ci be_unmap_pci_bars(adapter); 56198c2ecf20Sopenharmony_ci return -ENOMEM; 56208c2ecf20Sopenharmony_ci} 56218c2ecf20Sopenharmony_ci 56228c2ecf20Sopenharmony_cistatic void be_drv_cleanup(struct be_adapter *adapter) 56238c2ecf20Sopenharmony_ci{ 56248c2ecf20Sopenharmony_ci struct be_dma_mem *mem = &adapter->mbox_mem_alloced; 56258c2ecf20Sopenharmony_ci struct device *dev = &adapter->pdev->dev; 56268c2ecf20Sopenharmony_ci 56278c2ecf20Sopenharmony_ci if (mem->va) 56288c2ecf20Sopenharmony_ci dma_free_coherent(dev, mem->size, mem->va, mem->dma); 56298c2ecf20Sopenharmony_ci 56308c2ecf20Sopenharmony_ci mem = &adapter->rx_filter; 56318c2ecf20Sopenharmony_ci if (mem->va) 56328c2ecf20Sopenharmony_ci dma_free_coherent(dev, mem->size, mem->va, mem->dma); 56338c2ecf20Sopenharmony_ci 56348c2ecf20Sopenharmony_ci mem = &adapter->stats_cmd; 56358c2ecf20Sopenharmony_ci if (mem->va) 56368c2ecf20Sopenharmony_ci dma_free_coherent(dev, mem->size, mem->va, mem->dma); 56378c2ecf20Sopenharmony_ci} 56388c2ecf20Sopenharmony_ci 56398c2ecf20Sopenharmony_ci/* Allocate and initialize various fields in be_adapter struct */ 56408c2ecf20Sopenharmony_cistatic int be_drv_init(struct be_adapter *adapter) 56418c2ecf20Sopenharmony_ci{ 56428c2ecf20Sopenharmony_ci struct be_dma_mem *mbox_mem_alloc = &adapter->mbox_mem_alloced; 56438c2ecf20Sopenharmony_ci struct be_dma_mem *mbox_mem_align = &adapter->mbox_mem; 56448c2ecf20Sopenharmony_ci struct be_dma_mem *rx_filter = &adapter->rx_filter; 56458c2ecf20Sopenharmony_ci struct be_dma_mem *stats_cmd = &adapter->stats_cmd; 56468c2ecf20Sopenharmony_ci struct device *dev = &adapter->pdev->dev; 56478c2ecf20Sopenharmony_ci int status = 0; 56488c2ecf20Sopenharmony_ci 56498c2ecf20Sopenharmony_ci mbox_mem_alloc->size = sizeof(struct be_mcc_mailbox) + 16; 56508c2ecf20Sopenharmony_ci mbox_mem_alloc->va = dma_alloc_coherent(dev, mbox_mem_alloc->size, 56518c2ecf20Sopenharmony_ci &mbox_mem_alloc->dma, 56528c2ecf20Sopenharmony_ci GFP_KERNEL); 56538c2ecf20Sopenharmony_ci if (!mbox_mem_alloc->va) 56548c2ecf20Sopenharmony_ci return -ENOMEM; 56558c2ecf20Sopenharmony_ci 56568c2ecf20Sopenharmony_ci mbox_mem_align->size = sizeof(struct be_mcc_mailbox); 56578c2ecf20Sopenharmony_ci mbox_mem_align->va = PTR_ALIGN(mbox_mem_alloc->va, 16); 56588c2ecf20Sopenharmony_ci mbox_mem_align->dma = PTR_ALIGN(mbox_mem_alloc->dma, 16); 56598c2ecf20Sopenharmony_ci 56608c2ecf20Sopenharmony_ci rx_filter->size = sizeof(struct be_cmd_req_rx_filter); 56618c2ecf20Sopenharmony_ci rx_filter->va = dma_alloc_coherent(dev, rx_filter->size, 56628c2ecf20Sopenharmony_ci &rx_filter->dma, GFP_KERNEL); 56638c2ecf20Sopenharmony_ci if (!rx_filter->va) { 56648c2ecf20Sopenharmony_ci status = -ENOMEM; 56658c2ecf20Sopenharmony_ci goto free_mbox; 56668c2ecf20Sopenharmony_ci } 56678c2ecf20Sopenharmony_ci 56688c2ecf20Sopenharmony_ci if (lancer_chip(adapter)) 56698c2ecf20Sopenharmony_ci stats_cmd->size = sizeof(struct lancer_cmd_req_pport_stats); 56708c2ecf20Sopenharmony_ci else if (BE2_chip(adapter)) 56718c2ecf20Sopenharmony_ci stats_cmd->size = sizeof(struct be_cmd_req_get_stats_v0); 56728c2ecf20Sopenharmony_ci else if (BE3_chip(adapter)) 56738c2ecf20Sopenharmony_ci stats_cmd->size = sizeof(struct be_cmd_req_get_stats_v1); 56748c2ecf20Sopenharmony_ci else 56758c2ecf20Sopenharmony_ci stats_cmd->size = sizeof(struct be_cmd_req_get_stats_v2); 56768c2ecf20Sopenharmony_ci stats_cmd->va = dma_alloc_coherent(dev, stats_cmd->size, 56778c2ecf20Sopenharmony_ci &stats_cmd->dma, GFP_KERNEL); 56788c2ecf20Sopenharmony_ci if (!stats_cmd->va) { 56798c2ecf20Sopenharmony_ci status = -ENOMEM; 56808c2ecf20Sopenharmony_ci goto free_rx_filter; 56818c2ecf20Sopenharmony_ci } 56828c2ecf20Sopenharmony_ci 56838c2ecf20Sopenharmony_ci mutex_init(&adapter->mbox_lock); 56848c2ecf20Sopenharmony_ci mutex_init(&adapter->mcc_lock); 56858c2ecf20Sopenharmony_ci mutex_init(&adapter->rx_filter_lock); 56868c2ecf20Sopenharmony_ci spin_lock_init(&adapter->mcc_cq_lock); 56878c2ecf20Sopenharmony_ci init_completion(&adapter->et_cmd_compl); 56888c2ecf20Sopenharmony_ci 56898c2ecf20Sopenharmony_ci pci_save_state(adapter->pdev); 56908c2ecf20Sopenharmony_ci 56918c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&adapter->work, be_worker); 56928c2ecf20Sopenharmony_ci 56938c2ecf20Sopenharmony_ci adapter->error_recovery.recovery_state = ERR_RECOVERY_ST_NONE; 56948c2ecf20Sopenharmony_ci adapter->error_recovery.resched_delay = 0; 56958c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&adapter->error_recovery.err_detection_work, 56968c2ecf20Sopenharmony_ci be_err_detection_task); 56978c2ecf20Sopenharmony_ci 56988c2ecf20Sopenharmony_ci adapter->rx_fc = true; 56998c2ecf20Sopenharmony_ci adapter->tx_fc = true; 57008c2ecf20Sopenharmony_ci 57018c2ecf20Sopenharmony_ci /* Must be a power of 2 or else MODULO will BUG_ON */ 57028c2ecf20Sopenharmony_ci adapter->be_get_temp_freq = 64; 57038c2ecf20Sopenharmony_ci 57048c2ecf20Sopenharmony_ci return 0; 57058c2ecf20Sopenharmony_ci 57068c2ecf20Sopenharmony_cifree_rx_filter: 57078c2ecf20Sopenharmony_ci dma_free_coherent(dev, rx_filter->size, rx_filter->va, rx_filter->dma); 57088c2ecf20Sopenharmony_cifree_mbox: 57098c2ecf20Sopenharmony_ci dma_free_coherent(dev, mbox_mem_alloc->size, mbox_mem_alloc->va, 57108c2ecf20Sopenharmony_ci mbox_mem_alloc->dma); 57118c2ecf20Sopenharmony_ci return status; 57128c2ecf20Sopenharmony_ci} 57138c2ecf20Sopenharmony_ci 57148c2ecf20Sopenharmony_cistatic void be_remove(struct pci_dev *pdev) 57158c2ecf20Sopenharmony_ci{ 57168c2ecf20Sopenharmony_ci struct be_adapter *adapter = pci_get_drvdata(pdev); 57178c2ecf20Sopenharmony_ci 57188c2ecf20Sopenharmony_ci if (!adapter) 57198c2ecf20Sopenharmony_ci return; 57208c2ecf20Sopenharmony_ci 57218c2ecf20Sopenharmony_ci be_roce_dev_remove(adapter); 57228c2ecf20Sopenharmony_ci be_intr_set(adapter, false); 57238c2ecf20Sopenharmony_ci 57248c2ecf20Sopenharmony_ci be_cancel_err_detection(adapter); 57258c2ecf20Sopenharmony_ci 57268c2ecf20Sopenharmony_ci unregister_netdev(adapter->netdev); 57278c2ecf20Sopenharmony_ci 57288c2ecf20Sopenharmony_ci be_clear(adapter); 57298c2ecf20Sopenharmony_ci 57308c2ecf20Sopenharmony_ci if (!pci_vfs_assigned(adapter->pdev)) 57318c2ecf20Sopenharmony_ci be_cmd_reset_function(adapter); 57328c2ecf20Sopenharmony_ci 57338c2ecf20Sopenharmony_ci /* tell fw we're done with firing cmds */ 57348c2ecf20Sopenharmony_ci be_cmd_fw_clean(adapter); 57358c2ecf20Sopenharmony_ci 57368c2ecf20Sopenharmony_ci be_unmap_pci_bars(adapter); 57378c2ecf20Sopenharmony_ci be_drv_cleanup(adapter); 57388c2ecf20Sopenharmony_ci 57398c2ecf20Sopenharmony_ci pci_disable_pcie_error_reporting(pdev); 57408c2ecf20Sopenharmony_ci 57418c2ecf20Sopenharmony_ci pci_release_regions(pdev); 57428c2ecf20Sopenharmony_ci pci_disable_device(pdev); 57438c2ecf20Sopenharmony_ci 57448c2ecf20Sopenharmony_ci free_netdev(adapter->netdev); 57458c2ecf20Sopenharmony_ci} 57468c2ecf20Sopenharmony_ci 57478c2ecf20Sopenharmony_cistatic ssize_t be_hwmon_show_temp(struct device *dev, 57488c2ecf20Sopenharmony_ci struct device_attribute *dev_attr, 57498c2ecf20Sopenharmony_ci char *buf) 57508c2ecf20Sopenharmony_ci{ 57518c2ecf20Sopenharmony_ci struct be_adapter *adapter = dev_get_drvdata(dev); 57528c2ecf20Sopenharmony_ci 57538c2ecf20Sopenharmony_ci /* Unit: millidegree Celsius */ 57548c2ecf20Sopenharmony_ci if (adapter->hwmon_info.be_on_die_temp == BE_INVALID_DIE_TEMP) 57558c2ecf20Sopenharmony_ci return -EIO; 57568c2ecf20Sopenharmony_ci else 57578c2ecf20Sopenharmony_ci return sprintf(buf, "%u\n", 57588c2ecf20Sopenharmony_ci adapter->hwmon_info.be_on_die_temp * 1000); 57598c2ecf20Sopenharmony_ci} 57608c2ecf20Sopenharmony_ci 57618c2ecf20Sopenharmony_cistatic SENSOR_DEVICE_ATTR(temp1_input, 0444, 57628c2ecf20Sopenharmony_ci be_hwmon_show_temp, NULL, 1); 57638c2ecf20Sopenharmony_ci 57648c2ecf20Sopenharmony_cistatic struct attribute *be_hwmon_attrs[] = { 57658c2ecf20Sopenharmony_ci &sensor_dev_attr_temp1_input.dev_attr.attr, 57668c2ecf20Sopenharmony_ci NULL 57678c2ecf20Sopenharmony_ci}; 57688c2ecf20Sopenharmony_ci 57698c2ecf20Sopenharmony_ciATTRIBUTE_GROUPS(be_hwmon); 57708c2ecf20Sopenharmony_ci 57718c2ecf20Sopenharmony_cistatic char *mc_name(struct be_adapter *adapter) 57728c2ecf20Sopenharmony_ci{ 57738c2ecf20Sopenharmony_ci char *str = ""; /* default */ 57748c2ecf20Sopenharmony_ci 57758c2ecf20Sopenharmony_ci switch (adapter->mc_type) { 57768c2ecf20Sopenharmony_ci case UMC: 57778c2ecf20Sopenharmony_ci str = "UMC"; 57788c2ecf20Sopenharmony_ci break; 57798c2ecf20Sopenharmony_ci case FLEX10: 57808c2ecf20Sopenharmony_ci str = "FLEX10"; 57818c2ecf20Sopenharmony_ci break; 57828c2ecf20Sopenharmony_ci case vNIC1: 57838c2ecf20Sopenharmony_ci str = "vNIC-1"; 57848c2ecf20Sopenharmony_ci break; 57858c2ecf20Sopenharmony_ci case nPAR: 57868c2ecf20Sopenharmony_ci str = "nPAR"; 57878c2ecf20Sopenharmony_ci break; 57888c2ecf20Sopenharmony_ci case UFP: 57898c2ecf20Sopenharmony_ci str = "UFP"; 57908c2ecf20Sopenharmony_ci break; 57918c2ecf20Sopenharmony_ci case vNIC2: 57928c2ecf20Sopenharmony_ci str = "vNIC-2"; 57938c2ecf20Sopenharmony_ci break; 57948c2ecf20Sopenharmony_ci default: 57958c2ecf20Sopenharmony_ci str = ""; 57968c2ecf20Sopenharmony_ci } 57978c2ecf20Sopenharmony_ci 57988c2ecf20Sopenharmony_ci return str; 57998c2ecf20Sopenharmony_ci} 58008c2ecf20Sopenharmony_ci 58018c2ecf20Sopenharmony_cistatic inline char *func_name(struct be_adapter *adapter) 58028c2ecf20Sopenharmony_ci{ 58038c2ecf20Sopenharmony_ci return be_physfn(adapter) ? "PF" : "VF"; 58048c2ecf20Sopenharmony_ci} 58058c2ecf20Sopenharmony_ci 58068c2ecf20Sopenharmony_cistatic inline char *nic_name(struct pci_dev *pdev) 58078c2ecf20Sopenharmony_ci{ 58088c2ecf20Sopenharmony_ci switch (pdev->device) { 58098c2ecf20Sopenharmony_ci case OC_DEVICE_ID1: 58108c2ecf20Sopenharmony_ci return OC_NAME; 58118c2ecf20Sopenharmony_ci case OC_DEVICE_ID2: 58128c2ecf20Sopenharmony_ci return OC_NAME_BE; 58138c2ecf20Sopenharmony_ci case OC_DEVICE_ID3: 58148c2ecf20Sopenharmony_ci case OC_DEVICE_ID4: 58158c2ecf20Sopenharmony_ci return OC_NAME_LANCER; 58168c2ecf20Sopenharmony_ci case BE_DEVICE_ID2: 58178c2ecf20Sopenharmony_ci return BE3_NAME; 58188c2ecf20Sopenharmony_ci case OC_DEVICE_ID5: 58198c2ecf20Sopenharmony_ci case OC_DEVICE_ID6: 58208c2ecf20Sopenharmony_ci return OC_NAME_SH; 58218c2ecf20Sopenharmony_ci default: 58228c2ecf20Sopenharmony_ci return BE_NAME; 58238c2ecf20Sopenharmony_ci } 58248c2ecf20Sopenharmony_ci} 58258c2ecf20Sopenharmony_ci 58268c2ecf20Sopenharmony_cistatic int be_probe(struct pci_dev *pdev, const struct pci_device_id *pdev_id) 58278c2ecf20Sopenharmony_ci{ 58288c2ecf20Sopenharmony_ci struct be_adapter *adapter; 58298c2ecf20Sopenharmony_ci struct net_device *netdev; 58308c2ecf20Sopenharmony_ci int status = 0; 58318c2ecf20Sopenharmony_ci 58328c2ecf20Sopenharmony_ci status = pci_enable_device(pdev); 58338c2ecf20Sopenharmony_ci if (status) 58348c2ecf20Sopenharmony_ci goto do_none; 58358c2ecf20Sopenharmony_ci 58368c2ecf20Sopenharmony_ci status = pci_request_regions(pdev, DRV_NAME); 58378c2ecf20Sopenharmony_ci if (status) 58388c2ecf20Sopenharmony_ci goto disable_dev; 58398c2ecf20Sopenharmony_ci pci_set_master(pdev); 58408c2ecf20Sopenharmony_ci 58418c2ecf20Sopenharmony_ci netdev = alloc_etherdev_mqs(sizeof(*adapter), MAX_TX_QS, MAX_RX_QS); 58428c2ecf20Sopenharmony_ci if (!netdev) { 58438c2ecf20Sopenharmony_ci status = -ENOMEM; 58448c2ecf20Sopenharmony_ci goto rel_reg; 58458c2ecf20Sopenharmony_ci } 58468c2ecf20Sopenharmony_ci adapter = netdev_priv(netdev); 58478c2ecf20Sopenharmony_ci adapter->pdev = pdev; 58488c2ecf20Sopenharmony_ci pci_set_drvdata(pdev, adapter); 58498c2ecf20Sopenharmony_ci adapter->netdev = netdev; 58508c2ecf20Sopenharmony_ci SET_NETDEV_DEV(netdev, &pdev->dev); 58518c2ecf20Sopenharmony_ci 58528c2ecf20Sopenharmony_ci status = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); 58538c2ecf20Sopenharmony_ci if (!status) { 58548c2ecf20Sopenharmony_ci netdev->features |= NETIF_F_HIGHDMA; 58558c2ecf20Sopenharmony_ci } else { 58568c2ecf20Sopenharmony_ci status = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(32)); 58578c2ecf20Sopenharmony_ci if (status) { 58588c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "Could not set PCI DMA Mask\n"); 58598c2ecf20Sopenharmony_ci goto free_netdev; 58608c2ecf20Sopenharmony_ci } 58618c2ecf20Sopenharmony_ci } 58628c2ecf20Sopenharmony_ci 58638c2ecf20Sopenharmony_ci status = pci_enable_pcie_error_reporting(pdev); 58648c2ecf20Sopenharmony_ci if (!status) 58658c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "PCIe error reporting enabled\n"); 58668c2ecf20Sopenharmony_ci 58678c2ecf20Sopenharmony_ci status = be_map_pci_bars(adapter); 58688c2ecf20Sopenharmony_ci if (status) 58698c2ecf20Sopenharmony_ci goto free_netdev; 58708c2ecf20Sopenharmony_ci 58718c2ecf20Sopenharmony_ci status = be_drv_init(adapter); 58728c2ecf20Sopenharmony_ci if (status) 58738c2ecf20Sopenharmony_ci goto unmap_bars; 58748c2ecf20Sopenharmony_ci 58758c2ecf20Sopenharmony_ci status = be_setup(adapter); 58768c2ecf20Sopenharmony_ci if (status) 58778c2ecf20Sopenharmony_ci goto drv_cleanup; 58788c2ecf20Sopenharmony_ci 58798c2ecf20Sopenharmony_ci be_netdev_init(netdev); 58808c2ecf20Sopenharmony_ci status = register_netdev(netdev); 58818c2ecf20Sopenharmony_ci if (status != 0) 58828c2ecf20Sopenharmony_ci goto unsetup; 58838c2ecf20Sopenharmony_ci 58848c2ecf20Sopenharmony_ci be_roce_dev_add(adapter); 58858c2ecf20Sopenharmony_ci 58868c2ecf20Sopenharmony_ci be_schedule_err_detection(adapter, ERR_DETECTION_DELAY); 58878c2ecf20Sopenharmony_ci adapter->error_recovery.probe_time = jiffies; 58888c2ecf20Sopenharmony_ci 58898c2ecf20Sopenharmony_ci /* On Die temperature not supported for VF. */ 58908c2ecf20Sopenharmony_ci if (be_physfn(adapter) && IS_ENABLED(CONFIG_BE2NET_HWMON)) { 58918c2ecf20Sopenharmony_ci adapter->hwmon_info.hwmon_dev = 58928c2ecf20Sopenharmony_ci devm_hwmon_device_register_with_groups(&pdev->dev, 58938c2ecf20Sopenharmony_ci DRV_NAME, 58948c2ecf20Sopenharmony_ci adapter, 58958c2ecf20Sopenharmony_ci be_hwmon_groups); 58968c2ecf20Sopenharmony_ci adapter->hwmon_info.be_on_die_temp = BE_INVALID_DIE_TEMP; 58978c2ecf20Sopenharmony_ci } 58988c2ecf20Sopenharmony_ci 58998c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "%s: %s %s port %c\n", nic_name(pdev), 59008c2ecf20Sopenharmony_ci func_name(adapter), mc_name(adapter), adapter->port_name); 59018c2ecf20Sopenharmony_ci 59028c2ecf20Sopenharmony_ci return 0; 59038c2ecf20Sopenharmony_ci 59048c2ecf20Sopenharmony_ciunsetup: 59058c2ecf20Sopenharmony_ci be_clear(adapter); 59068c2ecf20Sopenharmony_cidrv_cleanup: 59078c2ecf20Sopenharmony_ci be_drv_cleanup(adapter); 59088c2ecf20Sopenharmony_ciunmap_bars: 59098c2ecf20Sopenharmony_ci be_unmap_pci_bars(adapter); 59108c2ecf20Sopenharmony_cifree_netdev: 59118c2ecf20Sopenharmony_ci pci_disable_pcie_error_reporting(pdev); 59128c2ecf20Sopenharmony_ci free_netdev(netdev); 59138c2ecf20Sopenharmony_cirel_reg: 59148c2ecf20Sopenharmony_ci pci_release_regions(pdev); 59158c2ecf20Sopenharmony_cidisable_dev: 59168c2ecf20Sopenharmony_ci pci_disable_device(pdev); 59178c2ecf20Sopenharmony_cido_none: 59188c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "%s initialization failed\n", nic_name(pdev)); 59198c2ecf20Sopenharmony_ci return status; 59208c2ecf20Sopenharmony_ci} 59218c2ecf20Sopenharmony_ci 59228c2ecf20Sopenharmony_cistatic int __maybe_unused be_suspend(struct device *dev_d) 59238c2ecf20Sopenharmony_ci{ 59248c2ecf20Sopenharmony_ci struct be_adapter *adapter = dev_get_drvdata(dev_d); 59258c2ecf20Sopenharmony_ci 59268c2ecf20Sopenharmony_ci be_intr_set(adapter, false); 59278c2ecf20Sopenharmony_ci be_cancel_err_detection(adapter); 59288c2ecf20Sopenharmony_ci 59298c2ecf20Sopenharmony_ci be_cleanup(adapter); 59308c2ecf20Sopenharmony_ci 59318c2ecf20Sopenharmony_ci return 0; 59328c2ecf20Sopenharmony_ci} 59338c2ecf20Sopenharmony_ci 59348c2ecf20Sopenharmony_cistatic int __maybe_unused be_pci_resume(struct device *dev_d) 59358c2ecf20Sopenharmony_ci{ 59368c2ecf20Sopenharmony_ci struct be_adapter *adapter = dev_get_drvdata(dev_d); 59378c2ecf20Sopenharmony_ci int status = 0; 59388c2ecf20Sopenharmony_ci 59398c2ecf20Sopenharmony_ci status = be_resume(adapter); 59408c2ecf20Sopenharmony_ci if (status) 59418c2ecf20Sopenharmony_ci return status; 59428c2ecf20Sopenharmony_ci 59438c2ecf20Sopenharmony_ci be_schedule_err_detection(adapter, ERR_DETECTION_DELAY); 59448c2ecf20Sopenharmony_ci 59458c2ecf20Sopenharmony_ci return 0; 59468c2ecf20Sopenharmony_ci} 59478c2ecf20Sopenharmony_ci 59488c2ecf20Sopenharmony_ci/* 59498c2ecf20Sopenharmony_ci * An FLR will stop BE from DMAing any data. 59508c2ecf20Sopenharmony_ci */ 59518c2ecf20Sopenharmony_cistatic void be_shutdown(struct pci_dev *pdev) 59528c2ecf20Sopenharmony_ci{ 59538c2ecf20Sopenharmony_ci struct be_adapter *adapter = pci_get_drvdata(pdev); 59548c2ecf20Sopenharmony_ci 59558c2ecf20Sopenharmony_ci if (!adapter) 59568c2ecf20Sopenharmony_ci return; 59578c2ecf20Sopenharmony_ci 59588c2ecf20Sopenharmony_ci be_roce_dev_shutdown(adapter); 59598c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&adapter->work); 59608c2ecf20Sopenharmony_ci be_cancel_err_detection(adapter); 59618c2ecf20Sopenharmony_ci 59628c2ecf20Sopenharmony_ci netif_device_detach(adapter->netdev); 59638c2ecf20Sopenharmony_ci 59648c2ecf20Sopenharmony_ci be_cmd_reset_function(adapter); 59658c2ecf20Sopenharmony_ci 59668c2ecf20Sopenharmony_ci pci_disable_device(pdev); 59678c2ecf20Sopenharmony_ci} 59688c2ecf20Sopenharmony_ci 59698c2ecf20Sopenharmony_cistatic pci_ers_result_t be_eeh_err_detected(struct pci_dev *pdev, 59708c2ecf20Sopenharmony_ci pci_channel_state_t state) 59718c2ecf20Sopenharmony_ci{ 59728c2ecf20Sopenharmony_ci struct be_adapter *adapter = pci_get_drvdata(pdev); 59738c2ecf20Sopenharmony_ci 59748c2ecf20Sopenharmony_ci dev_err(&adapter->pdev->dev, "EEH error detected\n"); 59758c2ecf20Sopenharmony_ci 59768c2ecf20Sopenharmony_ci be_roce_dev_remove(adapter); 59778c2ecf20Sopenharmony_ci 59788c2ecf20Sopenharmony_ci if (!be_check_error(adapter, BE_ERROR_EEH)) { 59798c2ecf20Sopenharmony_ci be_set_error(adapter, BE_ERROR_EEH); 59808c2ecf20Sopenharmony_ci 59818c2ecf20Sopenharmony_ci be_cancel_err_detection(adapter); 59828c2ecf20Sopenharmony_ci 59838c2ecf20Sopenharmony_ci be_cleanup(adapter); 59848c2ecf20Sopenharmony_ci } 59858c2ecf20Sopenharmony_ci 59868c2ecf20Sopenharmony_ci if (state == pci_channel_io_perm_failure) 59878c2ecf20Sopenharmony_ci return PCI_ERS_RESULT_DISCONNECT; 59888c2ecf20Sopenharmony_ci 59898c2ecf20Sopenharmony_ci pci_disable_device(pdev); 59908c2ecf20Sopenharmony_ci 59918c2ecf20Sopenharmony_ci /* The error could cause the FW to trigger a flash debug dump. 59928c2ecf20Sopenharmony_ci * Resetting the card while flash dump is in progress 59938c2ecf20Sopenharmony_ci * can cause it not to recover; wait for it to finish. 59948c2ecf20Sopenharmony_ci * Wait only for first function as it is needed only once per 59958c2ecf20Sopenharmony_ci * adapter. 59968c2ecf20Sopenharmony_ci */ 59978c2ecf20Sopenharmony_ci if (pdev->devfn == 0) 59988c2ecf20Sopenharmony_ci ssleep(30); 59998c2ecf20Sopenharmony_ci 60008c2ecf20Sopenharmony_ci return PCI_ERS_RESULT_NEED_RESET; 60018c2ecf20Sopenharmony_ci} 60028c2ecf20Sopenharmony_ci 60038c2ecf20Sopenharmony_cistatic pci_ers_result_t be_eeh_reset(struct pci_dev *pdev) 60048c2ecf20Sopenharmony_ci{ 60058c2ecf20Sopenharmony_ci struct be_adapter *adapter = pci_get_drvdata(pdev); 60068c2ecf20Sopenharmony_ci int status; 60078c2ecf20Sopenharmony_ci 60088c2ecf20Sopenharmony_ci dev_info(&adapter->pdev->dev, "EEH reset\n"); 60098c2ecf20Sopenharmony_ci 60108c2ecf20Sopenharmony_ci status = pci_enable_device(pdev); 60118c2ecf20Sopenharmony_ci if (status) 60128c2ecf20Sopenharmony_ci return PCI_ERS_RESULT_DISCONNECT; 60138c2ecf20Sopenharmony_ci 60148c2ecf20Sopenharmony_ci pci_set_master(pdev); 60158c2ecf20Sopenharmony_ci pci_restore_state(pdev); 60168c2ecf20Sopenharmony_ci 60178c2ecf20Sopenharmony_ci /* Check if card is ok and fw is ready */ 60188c2ecf20Sopenharmony_ci dev_info(&adapter->pdev->dev, 60198c2ecf20Sopenharmony_ci "Waiting for FW to be ready after EEH reset\n"); 60208c2ecf20Sopenharmony_ci status = be_fw_wait_ready(adapter); 60218c2ecf20Sopenharmony_ci if (status) 60228c2ecf20Sopenharmony_ci return PCI_ERS_RESULT_DISCONNECT; 60238c2ecf20Sopenharmony_ci 60248c2ecf20Sopenharmony_ci be_clear_error(adapter, BE_CLEAR_ALL); 60258c2ecf20Sopenharmony_ci return PCI_ERS_RESULT_RECOVERED; 60268c2ecf20Sopenharmony_ci} 60278c2ecf20Sopenharmony_ci 60288c2ecf20Sopenharmony_cistatic void be_eeh_resume(struct pci_dev *pdev) 60298c2ecf20Sopenharmony_ci{ 60308c2ecf20Sopenharmony_ci int status = 0; 60318c2ecf20Sopenharmony_ci struct be_adapter *adapter = pci_get_drvdata(pdev); 60328c2ecf20Sopenharmony_ci 60338c2ecf20Sopenharmony_ci dev_info(&adapter->pdev->dev, "EEH resume\n"); 60348c2ecf20Sopenharmony_ci 60358c2ecf20Sopenharmony_ci pci_save_state(pdev); 60368c2ecf20Sopenharmony_ci 60378c2ecf20Sopenharmony_ci status = be_resume(adapter); 60388c2ecf20Sopenharmony_ci if (status) 60398c2ecf20Sopenharmony_ci goto err; 60408c2ecf20Sopenharmony_ci 60418c2ecf20Sopenharmony_ci be_roce_dev_add(adapter); 60428c2ecf20Sopenharmony_ci 60438c2ecf20Sopenharmony_ci be_schedule_err_detection(adapter, ERR_DETECTION_DELAY); 60448c2ecf20Sopenharmony_ci return; 60458c2ecf20Sopenharmony_cierr: 60468c2ecf20Sopenharmony_ci dev_err(&adapter->pdev->dev, "EEH resume failed\n"); 60478c2ecf20Sopenharmony_ci} 60488c2ecf20Sopenharmony_ci 60498c2ecf20Sopenharmony_cistatic int be_pci_sriov_configure(struct pci_dev *pdev, int num_vfs) 60508c2ecf20Sopenharmony_ci{ 60518c2ecf20Sopenharmony_ci struct be_adapter *adapter = pci_get_drvdata(pdev); 60528c2ecf20Sopenharmony_ci struct be_resources vft_res = {0}; 60538c2ecf20Sopenharmony_ci int status; 60548c2ecf20Sopenharmony_ci 60558c2ecf20Sopenharmony_ci if (!num_vfs) 60568c2ecf20Sopenharmony_ci be_vf_clear(adapter); 60578c2ecf20Sopenharmony_ci 60588c2ecf20Sopenharmony_ci adapter->num_vfs = num_vfs; 60598c2ecf20Sopenharmony_ci 60608c2ecf20Sopenharmony_ci if (adapter->num_vfs == 0 && pci_vfs_assigned(pdev)) { 60618c2ecf20Sopenharmony_ci dev_warn(&pdev->dev, 60628c2ecf20Sopenharmony_ci "Cannot disable VFs while they are assigned\n"); 60638c2ecf20Sopenharmony_ci return -EBUSY; 60648c2ecf20Sopenharmony_ci } 60658c2ecf20Sopenharmony_ci 60668c2ecf20Sopenharmony_ci /* When the HW is in SRIOV capable configuration, the PF-pool resources 60678c2ecf20Sopenharmony_ci * are equally distributed across the max-number of VFs. The user may 60688c2ecf20Sopenharmony_ci * request only a subset of the max-vfs to be enabled. 60698c2ecf20Sopenharmony_ci * Based on num_vfs, redistribute the resources across num_vfs so that 60708c2ecf20Sopenharmony_ci * each VF will have access to more number of resources. 60718c2ecf20Sopenharmony_ci * This facility is not available in BE3 FW. 60728c2ecf20Sopenharmony_ci * Also, this is done by FW in Lancer chip. 60738c2ecf20Sopenharmony_ci */ 60748c2ecf20Sopenharmony_ci if (skyhawk_chip(adapter) && !pci_num_vf(pdev)) { 60758c2ecf20Sopenharmony_ci be_calculate_vf_res(adapter, adapter->num_vfs, 60768c2ecf20Sopenharmony_ci &vft_res); 60778c2ecf20Sopenharmony_ci status = be_cmd_set_sriov_config(adapter, adapter->pool_res, 60788c2ecf20Sopenharmony_ci adapter->num_vfs, &vft_res); 60798c2ecf20Sopenharmony_ci if (status) 60808c2ecf20Sopenharmony_ci dev_err(&pdev->dev, 60818c2ecf20Sopenharmony_ci "Failed to optimize SR-IOV resources\n"); 60828c2ecf20Sopenharmony_ci } 60838c2ecf20Sopenharmony_ci 60848c2ecf20Sopenharmony_ci status = be_get_resources(adapter); 60858c2ecf20Sopenharmony_ci if (status) 60868c2ecf20Sopenharmony_ci return be_cmd_status(status); 60878c2ecf20Sopenharmony_ci 60888c2ecf20Sopenharmony_ci /* Updating real_num_tx/rx_queues() requires rtnl_lock() */ 60898c2ecf20Sopenharmony_ci rtnl_lock(); 60908c2ecf20Sopenharmony_ci status = be_update_queues(adapter); 60918c2ecf20Sopenharmony_ci rtnl_unlock(); 60928c2ecf20Sopenharmony_ci if (status) 60938c2ecf20Sopenharmony_ci return be_cmd_status(status); 60948c2ecf20Sopenharmony_ci 60958c2ecf20Sopenharmony_ci if (adapter->num_vfs) 60968c2ecf20Sopenharmony_ci status = be_vf_setup(adapter); 60978c2ecf20Sopenharmony_ci 60988c2ecf20Sopenharmony_ci if (!status) 60998c2ecf20Sopenharmony_ci return adapter->num_vfs; 61008c2ecf20Sopenharmony_ci 61018c2ecf20Sopenharmony_ci return 0; 61028c2ecf20Sopenharmony_ci} 61038c2ecf20Sopenharmony_ci 61048c2ecf20Sopenharmony_cistatic const struct pci_error_handlers be_eeh_handlers = { 61058c2ecf20Sopenharmony_ci .error_detected = be_eeh_err_detected, 61068c2ecf20Sopenharmony_ci .slot_reset = be_eeh_reset, 61078c2ecf20Sopenharmony_ci .resume = be_eeh_resume, 61088c2ecf20Sopenharmony_ci}; 61098c2ecf20Sopenharmony_ci 61108c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(be_pci_pm_ops, be_suspend, be_pci_resume); 61118c2ecf20Sopenharmony_ci 61128c2ecf20Sopenharmony_cistatic struct pci_driver be_driver = { 61138c2ecf20Sopenharmony_ci .name = DRV_NAME, 61148c2ecf20Sopenharmony_ci .id_table = be_dev_ids, 61158c2ecf20Sopenharmony_ci .probe = be_probe, 61168c2ecf20Sopenharmony_ci .remove = be_remove, 61178c2ecf20Sopenharmony_ci .driver.pm = &be_pci_pm_ops, 61188c2ecf20Sopenharmony_ci .shutdown = be_shutdown, 61198c2ecf20Sopenharmony_ci .sriov_configure = be_pci_sriov_configure, 61208c2ecf20Sopenharmony_ci .err_handler = &be_eeh_handlers 61218c2ecf20Sopenharmony_ci}; 61228c2ecf20Sopenharmony_ci 61238c2ecf20Sopenharmony_cistatic int __init be_init_module(void) 61248c2ecf20Sopenharmony_ci{ 61258c2ecf20Sopenharmony_ci int status; 61268c2ecf20Sopenharmony_ci 61278c2ecf20Sopenharmony_ci if (rx_frag_size != 8192 && rx_frag_size != 4096 && 61288c2ecf20Sopenharmony_ci rx_frag_size != 2048) { 61298c2ecf20Sopenharmony_ci printk(KERN_WARNING DRV_NAME 61308c2ecf20Sopenharmony_ci " : Module param rx_frag_size must be 2048/4096/8192." 61318c2ecf20Sopenharmony_ci " Using 2048\n"); 61328c2ecf20Sopenharmony_ci rx_frag_size = 2048; 61338c2ecf20Sopenharmony_ci } 61348c2ecf20Sopenharmony_ci 61358c2ecf20Sopenharmony_ci if (num_vfs > 0) { 61368c2ecf20Sopenharmony_ci pr_info(DRV_NAME " : Module param num_vfs is obsolete."); 61378c2ecf20Sopenharmony_ci pr_info(DRV_NAME " : Use sysfs method to enable VFs\n"); 61388c2ecf20Sopenharmony_ci } 61398c2ecf20Sopenharmony_ci 61408c2ecf20Sopenharmony_ci be_wq = create_singlethread_workqueue("be_wq"); 61418c2ecf20Sopenharmony_ci if (!be_wq) { 61428c2ecf20Sopenharmony_ci pr_warn(DRV_NAME "workqueue creation failed\n"); 61438c2ecf20Sopenharmony_ci return -1; 61448c2ecf20Sopenharmony_ci } 61458c2ecf20Sopenharmony_ci 61468c2ecf20Sopenharmony_ci be_err_recovery_workq = 61478c2ecf20Sopenharmony_ci create_singlethread_workqueue("be_err_recover"); 61488c2ecf20Sopenharmony_ci if (!be_err_recovery_workq) 61498c2ecf20Sopenharmony_ci pr_warn(DRV_NAME "Could not create error recovery workqueue\n"); 61508c2ecf20Sopenharmony_ci 61518c2ecf20Sopenharmony_ci status = pci_register_driver(&be_driver); 61528c2ecf20Sopenharmony_ci if (status) { 61538c2ecf20Sopenharmony_ci destroy_workqueue(be_wq); 61548c2ecf20Sopenharmony_ci be_destroy_err_recovery_workq(); 61558c2ecf20Sopenharmony_ci } 61568c2ecf20Sopenharmony_ci return status; 61578c2ecf20Sopenharmony_ci} 61588c2ecf20Sopenharmony_cimodule_init(be_init_module); 61598c2ecf20Sopenharmony_ci 61608c2ecf20Sopenharmony_cistatic void __exit be_exit_module(void) 61618c2ecf20Sopenharmony_ci{ 61628c2ecf20Sopenharmony_ci pci_unregister_driver(&be_driver); 61638c2ecf20Sopenharmony_ci 61648c2ecf20Sopenharmony_ci be_destroy_err_recovery_workq(); 61658c2ecf20Sopenharmony_ci 61668c2ecf20Sopenharmony_ci if (be_wq) 61678c2ecf20Sopenharmony_ci destroy_workqueue(be_wq); 61688c2ecf20Sopenharmony_ci} 61698c2ecf20Sopenharmony_cimodule_exit(be_exit_module); 6170