18c2ecf20Sopenharmony_ci/* 28c2ecf20Sopenharmony_ci * Copyright (c) 2003-2008 Chelsio, Inc. All rights reserved. 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * This software is available to you under a choice of one of two 58c2ecf20Sopenharmony_ci * licenses. You may choose to be licensed under the terms of the GNU 68c2ecf20Sopenharmony_ci * General Public License (GPL) Version 2, available from the file 78c2ecf20Sopenharmony_ci * COPYING in the main directory of this source tree, or the 88c2ecf20Sopenharmony_ci * OpenIB.org BSD license below: 98c2ecf20Sopenharmony_ci * 108c2ecf20Sopenharmony_ci * Redistribution and use in source and binary forms, with or 118c2ecf20Sopenharmony_ci * without modification, are permitted provided that the following 128c2ecf20Sopenharmony_ci * conditions are met: 138c2ecf20Sopenharmony_ci * 148c2ecf20Sopenharmony_ci * - Redistributions of source code must retain the above 158c2ecf20Sopenharmony_ci * copyright notice, this list of conditions and the following 168c2ecf20Sopenharmony_ci * disclaimer. 178c2ecf20Sopenharmony_ci * 188c2ecf20Sopenharmony_ci * - Redistributions in binary form must reproduce the above 198c2ecf20Sopenharmony_ci * copyright notice, this list of conditions and the following 208c2ecf20Sopenharmony_ci * disclaimer in the documentation and/or other materials 218c2ecf20Sopenharmony_ci * provided with the distribution. 228c2ecf20Sopenharmony_ci * 238c2ecf20Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 248c2ecf20Sopenharmony_ci * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 258c2ecf20Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 268c2ecf20Sopenharmony_ci * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 278c2ecf20Sopenharmony_ci * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 288c2ecf20Sopenharmony_ci * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 298c2ecf20Sopenharmony_ci * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 308c2ecf20Sopenharmony_ci * SOFTWARE. 318c2ecf20Sopenharmony_ci */ 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci#include <linux/module.h> 368c2ecf20Sopenharmony_ci#include <linux/init.h> 378c2ecf20Sopenharmony_ci#include <linux/pci.h> 388c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 398c2ecf20Sopenharmony_ci#include <linux/netdevice.h> 408c2ecf20Sopenharmony_ci#include <linux/etherdevice.h> 418c2ecf20Sopenharmony_ci#include <linux/if_vlan.h> 428c2ecf20Sopenharmony_ci#include <linux/mdio.h> 438c2ecf20Sopenharmony_ci#include <linux/sockios.h> 448c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 458c2ecf20Sopenharmony_ci#include <linux/proc_fs.h> 468c2ecf20Sopenharmony_ci#include <linux/rtnetlink.h> 478c2ecf20Sopenharmony_ci#include <linux/firmware.h> 488c2ecf20Sopenharmony_ci#include <linux/log2.h> 498c2ecf20Sopenharmony_ci#include <linux/stringify.h> 508c2ecf20Sopenharmony_ci#include <linux/sched.h> 518c2ecf20Sopenharmony_ci#include <linux/slab.h> 528c2ecf20Sopenharmony_ci#include <linux/uaccess.h> 538c2ecf20Sopenharmony_ci#include <linux/nospec.h> 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci#include "common.h" 568c2ecf20Sopenharmony_ci#include "cxgb3_ioctl.h" 578c2ecf20Sopenharmony_ci#include "regs.h" 588c2ecf20Sopenharmony_ci#include "cxgb3_offload.h" 598c2ecf20Sopenharmony_ci#include "version.h" 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci#include "cxgb3_ctl_defs.h" 628c2ecf20Sopenharmony_ci#include "t3_cpl.h" 638c2ecf20Sopenharmony_ci#include "firmware_exports.h" 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_cienum { 668c2ecf20Sopenharmony_ci MAX_TXQ_ENTRIES = 16384, 678c2ecf20Sopenharmony_ci MAX_CTRL_TXQ_ENTRIES = 1024, 688c2ecf20Sopenharmony_ci MAX_RSPQ_ENTRIES = 16384, 698c2ecf20Sopenharmony_ci MAX_RX_BUFFERS = 16384, 708c2ecf20Sopenharmony_ci MAX_RX_JUMBO_BUFFERS = 16384, 718c2ecf20Sopenharmony_ci MIN_TXQ_ENTRIES = 4, 728c2ecf20Sopenharmony_ci MIN_CTRL_TXQ_ENTRIES = 4, 738c2ecf20Sopenharmony_ci MIN_RSPQ_ENTRIES = 32, 748c2ecf20Sopenharmony_ci MIN_FL_ENTRIES = 32 758c2ecf20Sopenharmony_ci}; 768c2ecf20Sopenharmony_ci 778c2ecf20Sopenharmony_ci#define PORT_MASK ((1 << MAX_NPORTS) - 1) 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci#define DFLT_MSG_ENABLE (NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK | \ 808c2ecf20Sopenharmony_ci NETIF_MSG_TIMER | NETIF_MSG_IFDOWN | NETIF_MSG_IFUP |\ 818c2ecf20Sopenharmony_ci NETIF_MSG_RX_ERR | NETIF_MSG_TX_ERR) 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_ci#define EEPROM_MAGIC 0x38E2F10C 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci#define CH_DEVICE(devid, idx) \ 868c2ecf20Sopenharmony_ci { PCI_VENDOR_ID_CHELSIO, devid, PCI_ANY_ID, PCI_ANY_ID, 0, 0, idx } 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_cistatic const struct pci_device_id cxgb3_pci_tbl[] = { 898c2ecf20Sopenharmony_ci CH_DEVICE(0x20, 0), /* PE9000 */ 908c2ecf20Sopenharmony_ci CH_DEVICE(0x21, 1), /* T302E */ 918c2ecf20Sopenharmony_ci CH_DEVICE(0x22, 2), /* T310E */ 928c2ecf20Sopenharmony_ci CH_DEVICE(0x23, 3), /* T320X */ 938c2ecf20Sopenharmony_ci CH_DEVICE(0x24, 1), /* T302X */ 948c2ecf20Sopenharmony_ci CH_DEVICE(0x25, 3), /* T320E */ 958c2ecf20Sopenharmony_ci CH_DEVICE(0x26, 2), /* T310X */ 968c2ecf20Sopenharmony_ci CH_DEVICE(0x30, 2), /* T3B10 */ 978c2ecf20Sopenharmony_ci CH_DEVICE(0x31, 3), /* T3B20 */ 988c2ecf20Sopenharmony_ci CH_DEVICE(0x32, 1), /* T3B02 */ 998c2ecf20Sopenharmony_ci CH_DEVICE(0x35, 6), /* T3C20-derived T3C10 */ 1008c2ecf20Sopenharmony_ci CH_DEVICE(0x36, 3), /* S320E-CR */ 1018c2ecf20Sopenharmony_ci CH_DEVICE(0x37, 7), /* N320E-G2 */ 1028c2ecf20Sopenharmony_ci {0,} 1038c2ecf20Sopenharmony_ci}; 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ciMODULE_DESCRIPTION(DRV_DESC); 1068c2ecf20Sopenharmony_ciMODULE_AUTHOR("Chelsio Communications"); 1078c2ecf20Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL"); 1088c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(pci, cxgb3_pci_tbl); 1098c2ecf20Sopenharmony_ci 1108c2ecf20Sopenharmony_cistatic int dflt_msg_enable = DFLT_MSG_ENABLE; 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cimodule_param(dflt_msg_enable, int, 0644); 1138c2ecf20Sopenharmony_ciMODULE_PARM_DESC(dflt_msg_enable, "Chelsio T3 default message enable bitmap"); 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci/* 1168c2ecf20Sopenharmony_ci * The driver uses the best interrupt scheme available on a platform in the 1178c2ecf20Sopenharmony_ci * order MSI-X, MSI, legacy pin interrupts. This parameter determines which 1188c2ecf20Sopenharmony_ci * of these schemes the driver may consider as follows: 1198c2ecf20Sopenharmony_ci * 1208c2ecf20Sopenharmony_ci * msi = 2: choose from among all three options 1218c2ecf20Sopenharmony_ci * msi = 1: only consider MSI and pin interrupts 1228c2ecf20Sopenharmony_ci * msi = 0: force pin interrupts 1238c2ecf20Sopenharmony_ci */ 1248c2ecf20Sopenharmony_cistatic int msi = 2; 1258c2ecf20Sopenharmony_ci 1268c2ecf20Sopenharmony_cimodule_param(msi, int, 0644); 1278c2ecf20Sopenharmony_ciMODULE_PARM_DESC(msi, "whether to use MSI or MSI-X"); 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci/* 1308c2ecf20Sopenharmony_ci * The driver enables offload as a default. 1318c2ecf20Sopenharmony_ci * To disable it, use ofld_disable = 1. 1328c2ecf20Sopenharmony_ci */ 1338c2ecf20Sopenharmony_ci 1348c2ecf20Sopenharmony_cistatic int ofld_disable = 0; 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_cimodule_param(ofld_disable, int, 0644); 1378c2ecf20Sopenharmony_ciMODULE_PARM_DESC(ofld_disable, "whether to enable offload at init time or not"); 1388c2ecf20Sopenharmony_ci 1398c2ecf20Sopenharmony_ci/* 1408c2ecf20Sopenharmony_ci * We have work elements that we need to cancel when an interface is taken 1418c2ecf20Sopenharmony_ci * down. Normally the work elements would be executed by keventd but that 1428c2ecf20Sopenharmony_ci * can deadlock because of linkwatch. If our close method takes the rtnl 1438c2ecf20Sopenharmony_ci * lock and linkwatch is ahead of our work elements in keventd, linkwatch 1448c2ecf20Sopenharmony_ci * will block keventd as it needs the rtnl lock, and we'll deadlock waiting 1458c2ecf20Sopenharmony_ci * for our work to complete. Get our own work queue to solve this. 1468c2ecf20Sopenharmony_ci */ 1478c2ecf20Sopenharmony_cistruct workqueue_struct *cxgb3_wq; 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci/** 1508c2ecf20Sopenharmony_ci * link_report - show link status and link speed/duplex 1518c2ecf20Sopenharmony_ci * @dev: the port whose settings are to be reported 1528c2ecf20Sopenharmony_ci * 1538c2ecf20Sopenharmony_ci * Shows the link status, speed, and duplex of a port. 1548c2ecf20Sopenharmony_ci */ 1558c2ecf20Sopenharmony_cistatic void link_report(struct net_device *dev) 1568c2ecf20Sopenharmony_ci{ 1578c2ecf20Sopenharmony_ci if (!netif_carrier_ok(dev)) 1588c2ecf20Sopenharmony_ci netdev_info(dev, "link down\n"); 1598c2ecf20Sopenharmony_ci else { 1608c2ecf20Sopenharmony_ci const char *s = "10Mbps"; 1618c2ecf20Sopenharmony_ci const struct port_info *p = netdev_priv(dev); 1628c2ecf20Sopenharmony_ci 1638c2ecf20Sopenharmony_ci switch (p->link_config.speed) { 1648c2ecf20Sopenharmony_ci case SPEED_10000: 1658c2ecf20Sopenharmony_ci s = "10Gbps"; 1668c2ecf20Sopenharmony_ci break; 1678c2ecf20Sopenharmony_ci case SPEED_1000: 1688c2ecf20Sopenharmony_ci s = "1000Mbps"; 1698c2ecf20Sopenharmony_ci break; 1708c2ecf20Sopenharmony_ci case SPEED_100: 1718c2ecf20Sopenharmony_ci s = "100Mbps"; 1728c2ecf20Sopenharmony_ci break; 1738c2ecf20Sopenharmony_ci } 1748c2ecf20Sopenharmony_ci 1758c2ecf20Sopenharmony_ci netdev_info(dev, "link up, %s, %s-duplex\n", 1768c2ecf20Sopenharmony_ci s, p->link_config.duplex == DUPLEX_FULL 1778c2ecf20Sopenharmony_ci ? "full" : "half"); 1788c2ecf20Sopenharmony_ci } 1798c2ecf20Sopenharmony_ci} 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_cistatic void enable_tx_fifo_drain(struct adapter *adapter, 1828c2ecf20Sopenharmony_ci struct port_info *pi) 1838c2ecf20Sopenharmony_ci{ 1848c2ecf20Sopenharmony_ci t3_set_reg_field(adapter, A_XGM_TXFIFO_CFG + pi->mac.offset, 0, 1858c2ecf20Sopenharmony_ci F_ENDROPPKT); 1868c2ecf20Sopenharmony_ci t3_write_reg(adapter, A_XGM_RX_CTRL + pi->mac.offset, 0); 1878c2ecf20Sopenharmony_ci t3_write_reg(adapter, A_XGM_TX_CTRL + pi->mac.offset, F_TXEN); 1888c2ecf20Sopenharmony_ci t3_write_reg(adapter, A_XGM_RX_CTRL + pi->mac.offset, F_RXEN); 1898c2ecf20Sopenharmony_ci} 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_cistatic void disable_tx_fifo_drain(struct adapter *adapter, 1928c2ecf20Sopenharmony_ci struct port_info *pi) 1938c2ecf20Sopenharmony_ci{ 1948c2ecf20Sopenharmony_ci t3_set_reg_field(adapter, A_XGM_TXFIFO_CFG + pi->mac.offset, 1958c2ecf20Sopenharmony_ci F_ENDROPPKT, 0); 1968c2ecf20Sopenharmony_ci} 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_civoid t3_os_link_fault(struct adapter *adap, int port_id, int state) 1998c2ecf20Sopenharmony_ci{ 2008c2ecf20Sopenharmony_ci struct net_device *dev = adap->port[port_id]; 2018c2ecf20Sopenharmony_ci struct port_info *pi = netdev_priv(dev); 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_ci if (state == netif_carrier_ok(dev)) 2048c2ecf20Sopenharmony_ci return; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci if (state) { 2078c2ecf20Sopenharmony_ci struct cmac *mac = &pi->mac; 2088c2ecf20Sopenharmony_ci 2098c2ecf20Sopenharmony_ci netif_carrier_on(dev); 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci disable_tx_fifo_drain(adap, pi); 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci /* Clear local faults */ 2148c2ecf20Sopenharmony_ci t3_xgm_intr_disable(adap, pi->port_id); 2158c2ecf20Sopenharmony_ci t3_read_reg(adap, A_XGM_INT_STATUS + 2168c2ecf20Sopenharmony_ci pi->mac.offset); 2178c2ecf20Sopenharmony_ci t3_write_reg(adap, 2188c2ecf20Sopenharmony_ci A_XGM_INT_CAUSE + pi->mac.offset, 2198c2ecf20Sopenharmony_ci F_XGM_INT); 2208c2ecf20Sopenharmony_ci 2218c2ecf20Sopenharmony_ci t3_set_reg_field(adap, 2228c2ecf20Sopenharmony_ci A_XGM_INT_ENABLE + 2238c2ecf20Sopenharmony_ci pi->mac.offset, 2248c2ecf20Sopenharmony_ci F_XGM_INT, F_XGM_INT); 2258c2ecf20Sopenharmony_ci t3_xgm_intr_enable(adap, pi->port_id); 2268c2ecf20Sopenharmony_ci 2278c2ecf20Sopenharmony_ci t3_mac_enable(mac, MAC_DIRECTION_TX); 2288c2ecf20Sopenharmony_ci } else { 2298c2ecf20Sopenharmony_ci netif_carrier_off(dev); 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci /* Flush TX FIFO */ 2328c2ecf20Sopenharmony_ci enable_tx_fifo_drain(adap, pi); 2338c2ecf20Sopenharmony_ci } 2348c2ecf20Sopenharmony_ci link_report(dev); 2358c2ecf20Sopenharmony_ci} 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci/** 2388c2ecf20Sopenharmony_ci * t3_os_link_changed - handle link status changes 2398c2ecf20Sopenharmony_ci * @adapter: the adapter associated with the link change 2408c2ecf20Sopenharmony_ci * @port_id: the port index whose limk status has changed 2418c2ecf20Sopenharmony_ci * @link_stat: the new status of the link 2428c2ecf20Sopenharmony_ci * @speed: the new speed setting 2438c2ecf20Sopenharmony_ci * @duplex: the new duplex setting 2448c2ecf20Sopenharmony_ci * @pause: the new flow-control setting 2458c2ecf20Sopenharmony_ci * 2468c2ecf20Sopenharmony_ci * This is the OS-dependent handler for link status changes. The OS 2478c2ecf20Sopenharmony_ci * neutral handler takes care of most of the processing for these events, 2488c2ecf20Sopenharmony_ci * then calls this handler for any OS-specific processing. 2498c2ecf20Sopenharmony_ci */ 2508c2ecf20Sopenharmony_civoid t3_os_link_changed(struct adapter *adapter, int port_id, int link_stat, 2518c2ecf20Sopenharmony_ci int speed, int duplex, int pause) 2528c2ecf20Sopenharmony_ci{ 2538c2ecf20Sopenharmony_ci struct net_device *dev = adapter->port[port_id]; 2548c2ecf20Sopenharmony_ci struct port_info *pi = netdev_priv(dev); 2558c2ecf20Sopenharmony_ci struct cmac *mac = &pi->mac; 2568c2ecf20Sopenharmony_ci 2578c2ecf20Sopenharmony_ci /* Skip changes from disabled ports. */ 2588c2ecf20Sopenharmony_ci if (!netif_running(dev)) 2598c2ecf20Sopenharmony_ci return; 2608c2ecf20Sopenharmony_ci 2618c2ecf20Sopenharmony_ci if (link_stat != netif_carrier_ok(dev)) { 2628c2ecf20Sopenharmony_ci if (link_stat) { 2638c2ecf20Sopenharmony_ci disable_tx_fifo_drain(adapter, pi); 2648c2ecf20Sopenharmony_ci 2658c2ecf20Sopenharmony_ci t3_mac_enable(mac, MAC_DIRECTION_RX); 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci /* Clear local faults */ 2688c2ecf20Sopenharmony_ci t3_xgm_intr_disable(adapter, pi->port_id); 2698c2ecf20Sopenharmony_ci t3_read_reg(adapter, A_XGM_INT_STATUS + 2708c2ecf20Sopenharmony_ci pi->mac.offset); 2718c2ecf20Sopenharmony_ci t3_write_reg(adapter, 2728c2ecf20Sopenharmony_ci A_XGM_INT_CAUSE + pi->mac.offset, 2738c2ecf20Sopenharmony_ci F_XGM_INT); 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci t3_set_reg_field(adapter, 2768c2ecf20Sopenharmony_ci A_XGM_INT_ENABLE + pi->mac.offset, 2778c2ecf20Sopenharmony_ci F_XGM_INT, F_XGM_INT); 2788c2ecf20Sopenharmony_ci t3_xgm_intr_enable(adapter, pi->port_id); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci netif_carrier_on(dev); 2818c2ecf20Sopenharmony_ci } else { 2828c2ecf20Sopenharmony_ci netif_carrier_off(dev); 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci t3_xgm_intr_disable(adapter, pi->port_id); 2858c2ecf20Sopenharmony_ci t3_read_reg(adapter, A_XGM_INT_STATUS + pi->mac.offset); 2868c2ecf20Sopenharmony_ci t3_set_reg_field(adapter, 2878c2ecf20Sopenharmony_ci A_XGM_INT_ENABLE + pi->mac.offset, 2888c2ecf20Sopenharmony_ci F_XGM_INT, 0); 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci if (is_10G(adapter)) 2918c2ecf20Sopenharmony_ci pi->phy.ops->power_down(&pi->phy, 1); 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci t3_read_reg(adapter, A_XGM_INT_STATUS + pi->mac.offset); 2948c2ecf20Sopenharmony_ci t3_mac_disable(mac, MAC_DIRECTION_RX); 2958c2ecf20Sopenharmony_ci t3_link_start(&pi->phy, mac, &pi->link_config); 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci /* Flush TX FIFO */ 2988c2ecf20Sopenharmony_ci enable_tx_fifo_drain(adapter, pi); 2998c2ecf20Sopenharmony_ci } 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci link_report(dev); 3028c2ecf20Sopenharmony_ci } 3038c2ecf20Sopenharmony_ci} 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci/** 3068c2ecf20Sopenharmony_ci * t3_os_phymod_changed - handle PHY module changes 3078c2ecf20Sopenharmony_ci * @adap: the adapter associated with the link change 3088c2ecf20Sopenharmony_ci * @port_id: the port index whose limk status has changed 3098c2ecf20Sopenharmony_ci * 3108c2ecf20Sopenharmony_ci * This is the OS-dependent handler for PHY module changes. It is 3118c2ecf20Sopenharmony_ci * invoked when a PHY module is removed or inserted for any OS-specific 3128c2ecf20Sopenharmony_ci * processing. 3138c2ecf20Sopenharmony_ci */ 3148c2ecf20Sopenharmony_civoid t3_os_phymod_changed(struct adapter *adap, int port_id) 3158c2ecf20Sopenharmony_ci{ 3168c2ecf20Sopenharmony_ci static const char *mod_str[] = { 3178c2ecf20Sopenharmony_ci NULL, "SR", "LR", "LRM", "TWINAX", "TWINAX", "unknown" 3188c2ecf20Sopenharmony_ci }; 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci const struct net_device *dev = adap->port[port_id]; 3218c2ecf20Sopenharmony_ci const struct port_info *pi = netdev_priv(dev); 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci if (pi->phy.modtype == phy_modtype_none) 3248c2ecf20Sopenharmony_ci netdev_info(dev, "PHY module unplugged\n"); 3258c2ecf20Sopenharmony_ci else 3268c2ecf20Sopenharmony_ci netdev_info(dev, "%s PHY module inserted\n", 3278c2ecf20Sopenharmony_ci mod_str[pi->phy.modtype]); 3288c2ecf20Sopenharmony_ci} 3298c2ecf20Sopenharmony_ci 3308c2ecf20Sopenharmony_cistatic void cxgb_set_rxmode(struct net_device *dev) 3318c2ecf20Sopenharmony_ci{ 3328c2ecf20Sopenharmony_ci struct port_info *pi = netdev_priv(dev); 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci t3_mac_set_rx_mode(&pi->mac, dev); 3358c2ecf20Sopenharmony_ci} 3368c2ecf20Sopenharmony_ci 3378c2ecf20Sopenharmony_ci/** 3388c2ecf20Sopenharmony_ci * link_start - enable a port 3398c2ecf20Sopenharmony_ci * @dev: the device to enable 3408c2ecf20Sopenharmony_ci * 3418c2ecf20Sopenharmony_ci * Performs the MAC and PHY actions needed to enable a port. 3428c2ecf20Sopenharmony_ci */ 3438c2ecf20Sopenharmony_cistatic void link_start(struct net_device *dev) 3448c2ecf20Sopenharmony_ci{ 3458c2ecf20Sopenharmony_ci struct port_info *pi = netdev_priv(dev); 3468c2ecf20Sopenharmony_ci struct cmac *mac = &pi->mac; 3478c2ecf20Sopenharmony_ci 3488c2ecf20Sopenharmony_ci t3_mac_reset(mac); 3498c2ecf20Sopenharmony_ci t3_mac_set_num_ucast(mac, MAX_MAC_IDX); 3508c2ecf20Sopenharmony_ci t3_mac_set_mtu(mac, dev->mtu); 3518c2ecf20Sopenharmony_ci t3_mac_set_address(mac, LAN_MAC_IDX, dev->dev_addr); 3528c2ecf20Sopenharmony_ci t3_mac_set_address(mac, SAN_MAC_IDX, pi->iscsic.mac_addr); 3538c2ecf20Sopenharmony_ci t3_mac_set_rx_mode(mac, dev); 3548c2ecf20Sopenharmony_ci t3_link_start(&pi->phy, mac, &pi->link_config); 3558c2ecf20Sopenharmony_ci t3_mac_enable(mac, MAC_DIRECTION_RX | MAC_DIRECTION_TX); 3568c2ecf20Sopenharmony_ci} 3578c2ecf20Sopenharmony_ci 3588c2ecf20Sopenharmony_cistatic inline void cxgb_disable_msi(struct adapter *adapter) 3598c2ecf20Sopenharmony_ci{ 3608c2ecf20Sopenharmony_ci if (adapter->flags & USING_MSIX) { 3618c2ecf20Sopenharmony_ci pci_disable_msix(adapter->pdev); 3628c2ecf20Sopenharmony_ci adapter->flags &= ~USING_MSIX; 3638c2ecf20Sopenharmony_ci } else if (adapter->flags & USING_MSI) { 3648c2ecf20Sopenharmony_ci pci_disable_msi(adapter->pdev); 3658c2ecf20Sopenharmony_ci adapter->flags &= ~USING_MSI; 3668c2ecf20Sopenharmony_ci } 3678c2ecf20Sopenharmony_ci} 3688c2ecf20Sopenharmony_ci 3698c2ecf20Sopenharmony_ci/* 3708c2ecf20Sopenharmony_ci * Interrupt handler for asynchronous events used with MSI-X. 3718c2ecf20Sopenharmony_ci */ 3728c2ecf20Sopenharmony_cistatic irqreturn_t t3_async_intr_handler(int irq, void *cookie) 3738c2ecf20Sopenharmony_ci{ 3748c2ecf20Sopenharmony_ci t3_slow_intr_handler(cookie); 3758c2ecf20Sopenharmony_ci return IRQ_HANDLED; 3768c2ecf20Sopenharmony_ci} 3778c2ecf20Sopenharmony_ci 3788c2ecf20Sopenharmony_ci/* 3798c2ecf20Sopenharmony_ci * Name the MSI-X interrupts. 3808c2ecf20Sopenharmony_ci */ 3818c2ecf20Sopenharmony_cistatic void name_msix_vecs(struct adapter *adap) 3828c2ecf20Sopenharmony_ci{ 3838c2ecf20Sopenharmony_ci int i, j, msi_idx = 1, n = sizeof(adap->msix_info[0].desc) - 1; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci snprintf(adap->msix_info[0].desc, n, "%s", adap->name); 3868c2ecf20Sopenharmony_ci adap->msix_info[0].desc[n] = 0; 3878c2ecf20Sopenharmony_ci 3888c2ecf20Sopenharmony_ci for_each_port(adap, j) { 3898c2ecf20Sopenharmony_ci struct net_device *d = adap->port[j]; 3908c2ecf20Sopenharmony_ci const struct port_info *pi = netdev_priv(d); 3918c2ecf20Sopenharmony_ci 3928c2ecf20Sopenharmony_ci for (i = 0; i < pi->nqsets; i++, msi_idx++) { 3938c2ecf20Sopenharmony_ci snprintf(adap->msix_info[msi_idx].desc, n, 3948c2ecf20Sopenharmony_ci "%s-%d", d->name, pi->first_qset + i); 3958c2ecf20Sopenharmony_ci adap->msix_info[msi_idx].desc[n] = 0; 3968c2ecf20Sopenharmony_ci } 3978c2ecf20Sopenharmony_ci } 3988c2ecf20Sopenharmony_ci} 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_cistatic int request_msix_data_irqs(struct adapter *adap) 4018c2ecf20Sopenharmony_ci{ 4028c2ecf20Sopenharmony_ci int i, j, err, qidx = 0; 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci for_each_port(adap, i) { 4058c2ecf20Sopenharmony_ci int nqsets = adap2pinfo(adap, i)->nqsets; 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ci for (j = 0; j < nqsets; ++j) { 4088c2ecf20Sopenharmony_ci err = request_irq(adap->msix_info[qidx + 1].vec, 4098c2ecf20Sopenharmony_ci t3_intr_handler(adap, 4108c2ecf20Sopenharmony_ci adap->sge.qs[qidx]. 4118c2ecf20Sopenharmony_ci rspq.polling), 0, 4128c2ecf20Sopenharmony_ci adap->msix_info[qidx + 1].desc, 4138c2ecf20Sopenharmony_ci &adap->sge.qs[qidx]); 4148c2ecf20Sopenharmony_ci if (err) { 4158c2ecf20Sopenharmony_ci while (--qidx >= 0) 4168c2ecf20Sopenharmony_ci free_irq(adap->msix_info[qidx + 1].vec, 4178c2ecf20Sopenharmony_ci &adap->sge.qs[qidx]); 4188c2ecf20Sopenharmony_ci return err; 4198c2ecf20Sopenharmony_ci } 4208c2ecf20Sopenharmony_ci qidx++; 4218c2ecf20Sopenharmony_ci } 4228c2ecf20Sopenharmony_ci } 4238c2ecf20Sopenharmony_ci return 0; 4248c2ecf20Sopenharmony_ci} 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_cistatic void free_irq_resources(struct adapter *adapter) 4278c2ecf20Sopenharmony_ci{ 4288c2ecf20Sopenharmony_ci if (adapter->flags & USING_MSIX) { 4298c2ecf20Sopenharmony_ci int i, n = 0; 4308c2ecf20Sopenharmony_ci 4318c2ecf20Sopenharmony_ci free_irq(adapter->msix_info[0].vec, adapter); 4328c2ecf20Sopenharmony_ci for_each_port(adapter, i) 4338c2ecf20Sopenharmony_ci n += adap2pinfo(adapter, i)->nqsets; 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci for (i = 0; i < n; ++i) 4368c2ecf20Sopenharmony_ci free_irq(adapter->msix_info[i + 1].vec, 4378c2ecf20Sopenharmony_ci &adapter->sge.qs[i]); 4388c2ecf20Sopenharmony_ci } else 4398c2ecf20Sopenharmony_ci free_irq(adapter->pdev->irq, adapter); 4408c2ecf20Sopenharmony_ci} 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_cistatic int await_mgmt_replies(struct adapter *adap, unsigned long init_cnt, 4438c2ecf20Sopenharmony_ci unsigned long n) 4448c2ecf20Sopenharmony_ci{ 4458c2ecf20Sopenharmony_ci int attempts = 10; 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci while (adap->sge.qs[0].rspq.offload_pkts < init_cnt + n) { 4488c2ecf20Sopenharmony_ci if (!--attempts) 4498c2ecf20Sopenharmony_ci return -ETIMEDOUT; 4508c2ecf20Sopenharmony_ci msleep(10); 4518c2ecf20Sopenharmony_ci } 4528c2ecf20Sopenharmony_ci return 0; 4538c2ecf20Sopenharmony_ci} 4548c2ecf20Sopenharmony_ci 4558c2ecf20Sopenharmony_cistatic int init_tp_parity(struct adapter *adap) 4568c2ecf20Sopenharmony_ci{ 4578c2ecf20Sopenharmony_ci int i; 4588c2ecf20Sopenharmony_ci struct sk_buff *skb; 4598c2ecf20Sopenharmony_ci struct cpl_set_tcb_field *greq; 4608c2ecf20Sopenharmony_ci unsigned long cnt = adap->sge.qs[0].rspq.offload_pkts; 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci t3_tp_set_offload_mode(adap, 1); 4638c2ecf20Sopenharmony_ci 4648c2ecf20Sopenharmony_ci for (i = 0; i < 16; i++) { 4658c2ecf20Sopenharmony_ci struct cpl_smt_write_req *req; 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci skb = alloc_skb(sizeof(*req), GFP_KERNEL); 4688c2ecf20Sopenharmony_ci if (!skb) 4698c2ecf20Sopenharmony_ci skb = adap->nofail_skb; 4708c2ecf20Sopenharmony_ci if (!skb) 4718c2ecf20Sopenharmony_ci goto alloc_skb_fail; 4728c2ecf20Sopenharmony_ci 4738c2ecf20Sopenharmony_ci req = __skb_put_zero(skb, sizeof(*req)); 4748c2ecf20Sopenharmony_ci req->wr.wr_hi = htonl(V_WR_OP(FW_WROPCODE_FORWARD)); 4758c2ecf20Sopenharmony_ci OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_SMT_WRITE_REQ, i)); 4768c2ecf20Sopenharmony_ci req->mtu_idx = NMTUS - 1; 4778c2ecf20Sopenharmony_ci req->iff = i; 4788c2ecf20Sopenharmony_ci t3_mgmt_tx(adap, skb); 4798c2ecf20Sopenharmony_ci if (skb == adap->nofail_skb) { 4808c2ecf20Sopenharmony_ci await_mgmt_replies(adap, cnt, i + 1); 4818c2ecf20Sopenharmony_ci adap->nofail_skb = alloc_skb(sizeof(*greq), GFP_KERNEL); 4828c2ecf20Sopenharmony_ci if (!adap->nofail_skb) 4838c2ecf20Sopenharmony_ci goto alloc_skb_fail; 4848c2ecf20Sopenharmony_ci } 4858c2ecf20Sopenharmony_ci } 4868c2ecf20Sopenharmony_ci 4878c2ecf20Sopenharmony_ci for (i = 0; i < 2048; i++) { 4888c2ecf20Sopenharmony_ci struct cpl_l2t_write_req *req; 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci skb = alloc_skb(sizeof(*req), GFP_KERNEL); 4918c2ecf20Sopenharmony_ci if (!skb) 4928c2ecf20Sopenharmony_ci skb = adap->nofail_skb; 4938c2ecf20Sopenharmony_ci if (!skb) 4948c2ecf20Sopenharmony_ci goto alloc_skb_fail; 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci req = __skb_put_zero(skb, sizeof(*req)); 4978c2ecf20Sopenharmony_ci req->wr.wr_hi = htonl(V_WR_OP(FW_WROPCODE_FORWARD)); 4988c2ecf20Sopenharmony_ci OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_L2T_WRITE_REQ, i)); 4998c2ecf20Sopenharmony_ci req->params = htonl(V_L2T_W_IDX(i)); 5008c2ecf20Sopenharmony_ci t3_mgmt_tx(adap, skb); 5018c2ecf20Sopenharmony_ci if (skb == adap->nofail_skb) { 5028c2ecf20Sopenharmony_ci await_mgmt_replies(adap, cnt, 16 + i + 1); 5038c2ecf20Sopenharmony_ci adap->nofail_skb = alloc_skb(sizeof(*greq), GFP_KERNEL); 5048c2ecf20Sopenharmony_ci if (!adap->nofail_skb) 5058c2ecf20Sopenharmony_ci goto alloc_skb_fail; 5068c2ecf20Sopenharmony_ci } 5078c2ecf20Sopenharmony_ci } 5088c2ecf20Sopenharmony_ci 5098c2ecf20Sopenharmony_ci for (i = 0; i < 2048; i++) { 5108c2ecf20Sopenharmony_ci struct cpl_rte_write_req *req; 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci skb = alloc_skb(sizeof(*req), GFP_KERNEL); 5138c2ecf20Sopenharmony_ci if (!skb) 5148c2ecf20Sopenharmony_ci skb = adap->nofail_skb; 5158c2ecf20Sopenharmony_ci if (!skb) 5168c2ecf20Sopenharmony_ci goto alloc_skb_fail; 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci req = __skb_put_zero(skb, sizeof(*req)); 5198c2ecf20Sopenharmony_ci req->wr.wr_hi = htonl(V_WR_OP(FW_WROPCODE_FORWARD)); 5208c2ecf20Sopenharmony_ci OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_RTE_WRITE_REQ, i)); 5218c2ecf20Sopenharmony_ci req->l2t_idx = htonl(V_L2T_W_IDX(i)); 5228c2ecf20Sopenharmony_ci t3_mgmt_tx(adap, skb); 5238c2ecf20Sopenharmony_ci if (skb == adap->nofail_skb) { 5248c2ecf20Sopenharmony_ci await_mgmt_replies(adap, cnt, 16 + 2048 + i + 1); 5258c2ecf20Sopenharmony_ci adap->nofail_skb = alloc_skb(sizeof(*greq), GFP_KERNEL); 5268c2ecf20Sopenharmony_ci if (!adap->nofail_skb) 5278c2ecf20Sopenharmony_ci goto alloc_skb_fail; 5288c2ecf20Sopenharmony_ci } 5298c2ecf20Sopenharmony_ci } 5308c2ecf20Sopenharmony_ci 5318c2ecf20Sopenharmony_ci skb = alloc_skb(sizeof(*greq), GFP_KERNEL); 5328c2ecf20Sopenharmony_ci if (!skb) 5338c2ecf20Sopenharmony_ci skb = adap->nofail_skb; 5348c2ecf20Sopenharmony_ci if (!skb) 5358c2ecf20Sopenharmony_ci goto alloc_skb_fail; 5368c2ecf20Sopenharmony_ci 5378c2ecf20Sopenharmony_ci greq = __skb_put_zero(skb, sizeof(*greq)); 5388c2ecf20Sopenharmony_ci greq->wr.wr_hi = htonl(V_WR_OP(FW_WROPCODE_FORWARD)); 5398c2ecf20Sopenharmony_ci OPCODE_TID(greq) = htonl(MK_OPCODE_TID(CPL_SET_TCB_FIELD, 0)); 5408c2ecf20Sopenharmony_ci greq->mask = cpu_to_be64(1); 5418c2ecf20Sopenharmony_ci t3_mgmt_tx(adap, skb); 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_ci i = await_mgmt_replies(adap, cnt, 16 + 2048 + 2048 + 1); 5448c2ecf20Sopenharmony_ci if (skb == adap->nofail_skb) { 5458c2ecf20Sopenharmony_ci i = await_mgmt_replies(adap, cnt, 16 + 2048 + 2048 + 1); 5468c2ecf20Sopenharmony_ci adap->nofail_skb = alloc_skb(sizeof(*greq), GFP_KERNEL); 5478c2ecf20Sopenharmony_ci } 5488c2ecf20Sopenharmony_ci 5498c2ecf20Sopenharmony_ci t3_tp_set_offload_mode(adap, 0); 5508c2ecf20Sopenharmony_ci return i; 5518c2ecf20Sopenharmony_ci 5528c2ecf20Sopenharmony_cialloc_skb_fail: 5538c2ecf20Sopenharmony_ci t3_tp_set_offload_mode(adap, 0); 5548c2ecf20Sopenharmony_ci return -ENOMEM; 5558c2ecf20Sopenharmony_ci} 5568c2ecf20Sopenharmony_ci 5578c2ecf20Sopenharmony_ci/** 5588c2ecf20Sopenharmony_ci * setup_rss - configure RSS 5598c2ecf20Sopenharmony_ci * @adap: the adapter 5608c2ecf20Sopenharmony_ci * 5618c2ecf20Sopenharmony_ci * Sets up RSS to distribute packets to multiple receive queues. We 5628c2ecf20Sopenharmony_ci * configure the RSS CPU lookup table to distribute to the number of HW 5638c2ecf20Sopenharmony_ci * receive queues, and the response queue lookup table to narrow that 5648c2ecf20Sopenharmony_ci * down to the response queues actually configured for each port. 5658c2ecf20Sopenharmony_ci * We always configure the RSS mapping for two ports since the mapping 5668c2ecf20Sopenharmony_ci * table has plenty of entries. 5678c2ecf20Sopenharmony_ci */ 5688c2ecf20Sopenharmony_cistatic void setup_rss(struct adapter *adap) 5698c2ecf20Sopenharmony_ci{ 5708c2ecf20Sopenharmony_ci int i; 5718c2ecf20Sopenharmony_ci unsigned int nq0 = adap2pinfo(adap, 0)->nqsets; 5728c2ecf20Sopenharmony_ci unsigned int nq1 = adap->port[1] ? adap2pinfo(adap, 1)->nqsets : 1; 5738c2ecf20Sopenharmony_ci u8 cpus[SGE_QSETS + 1]; 5748c2ecf20Sopenharmony_ci u16 rspq_map[RSS_TABLE_SIZE + 1]; 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci for (i = 0; i < SGE_QSETS; ++i) 5778c2ecf20Sopenharmony_ci cpus[i] = i; 5788c2ecf20Sopenharmony_ci cpus[SGE_QSETS] = 0xff; /* terminator */ 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci for (i = 0; i < RSS_TABLE_SIZE / 2; ++i) { 5818c2ecf20Sopenharmony_ci rspq_map[i] = i % nq0; 5828c2ecf20Sopenharmony_ci rspq_map[i + RSS_TABLE_SIZE / 2] = (i % nq1) + nq0; 5838c2ecf20Sopenharmony_ci } 5848c2ecf20Sopenharmony_ci rspq_map[RSS_TABLE_SIZE] = 0xffff; /* terminator */ 5858c2ecf20Sopenharmony_ci 5868c2ecf20Sopenharmony_ci t3_config_rss(adap, F_RQFEEDBACKENABLE | F_TNLLKPEN | F_TNLMAPEN | 5878c2ecf20Sopenharmony_ci F_TNLPRTEN | F_TNL2TUPEN | F_TNL4TUPEN | 5888c2ecf20Sopenharmony_ci V_RRCPLCPUSIZE(6) | F_HASHTOEPLITZ, cpus, rspq_map); 5898c2ecf20Sopenharmony_ci} 5908c2ecf20Sopenharmony_ci 5918c2ecf20Sopenharmony_cistatic void ring_dbs(struct adapter *adap) 5928c2ecf20Sopenharmony_ci{ 5938c2ecf20Sopenharmony_ci int i, j; 5948c2ecf20Sopenharmony_ci 5958c2ecf20Sopenharmony_ci for (i = 0; i < SGE_QSETS; i++) { 5968c2ecf20Sopenharmony_ci struct sge_qset *qs = &adap->sge.qs[i]; 5978c2ecf20Sopenharmony_ci 5988c2ecf20Sopenharmony_ci if (qs->adap) 5998c2ecf20Sopenharmony_ci for (j = 0; j < SGE_TXQ_PER_SET; j++) 6008c2ecf20Sopenharmony_ci t3_write_reg(adap, A_SG_KDOORBELL, F_SELEGRCNTX | V_EGRCNTX(qs->txq[j].cntxt_id)); 6018c2ecf20Sopenharmony_ci } 6028c2ecf20Sopenharmony_ci} 6038c2ecf20Sopenharmony_ci 6048c2ecf20Sopenharmony_cistatic void init_napi(struct adapter *adap) 6058c2ecf20Sopenharmony_ci{ 6068c2ecf20Sopenharmony_ci int i; 6078c2ecf20Sopenharmony_ci 6088c2ecf20Sopenharmony_ci for (i = 0; i < SGE_QSETS; i++) { 6098c2ecf20Sopenharmony_ci struct sge_qset *qs = &adap->sge.qs[i]; 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci if (qs->adap) 6128c2ecf20Sopenharmony_ci netif_napi_add(qs->netdev, &qs->napi, qs->napi.poll, 6138c2ecf20Sopenharmony_ci 64); 6148c2ecf20Sopenharmony_ci } 6158c2ecf20Sopenharmony_ci 6168c2ecf20Sopenharmony_ci /* 6178c2ecf20Sopenharmony_ci * netif_napi_add() can be called only once per napi_struct because it 6188c2ecf20Sopenharmony_ci * adds each new napi_struct to a list. Be careful not to call it a 6198c2ecf20Sopenharmony_ci * second time, e.g., during EEH recovery, by making a note of it. 6208c2ecf20Sopenharmony_ci */ 6218c2ecf20Sopenharmony_ci adap->flags |= NAPI_INIT; 6228c2ecf20Sopenharmony_ci} 6238c2ecf20Sopenharmony_ci 6248c2ecf20Sopenharmony_ci/* 6258c2ecf20Sopenharmony_ci * Wait until all NAPI handlers are descheduled. This includes the handlers of 6268c2ecf20Sopenharmony_ci * both netdevices representing interfaces and the dummy ones for the extra 6278c2ecf20Sopenharmony_ci * queues. 6288c2ecf20Sopenharmony_ci */ 6298c2ecf20Sopenharmony_cistatic void quiesce_rx(struct adapter *adap) 6308c2ecf20Sopenharmony_ci{ 6318c2ecf20Sopenharmony_ci int i; 6328c2ecf20Sopenharmony_ci 6338c2ecf20Sopenharmony_ci for (i = 0; i < SGE_QSETS; i++) 6348c2ecf20Sopenharmony_ci if (adap->sge.qs[i].adap) 6358c2ecf20Sopenharmony_ci napi_disable(&adap->sge.qs[i].napi); 6368c2ecf20Sopenharmony_ci} 6378c2ecf20Sopenharmony_ci 6388c2ecf20Sopenharmony_cistatic void enable_all_napi(struct adapter *adap) 6398c2ecf20Sopenharmony_ci{ 6408c2ecf20Sopenharmony_ci int i; 6418c2ecf20Sopenharmony_ci for (i = 0; i < SGE_QSETS; i++) 6428c2ecf20Sopenharmony_ci if (adap->sge.qs[i].adap) 6438c2ecf20Sopenharmony_ci napi_enable(&adap->sge.qs[i].napi); 6448c2ecf20Sopenharmony_ci} 6458c2ecf20Sopenharmony_ci 6468c2ecf20Sopenharmony_ci/** 6478c2ecf20Sopenharmony_ci * setup_sge_qsets - configure SGE Tx/Rx/response queues 6488c2ecf20Sopenharmony_ci * @adap: the adapter 6498c2ecf20Sopenharmony_ci * 6508c2ecf20Sopenharmony_ci * Determines how many sets of SGE queues to use and initializes them. 6518c2ecf20Sopenharmony_ci * We support multiple queue sets per port if we have MSI-X, otherwise 6528c2ecf20Sopenharmony_ci * just one queue set per port. 6538c2ecf20Sopenharmony_ci */ 6548c2ecf20Sopenharmony_cistatic int setup_sge_qsets(struct adapter *adap) 6558c2ecf20Sopenharmony_ci{ 6568c2ecf20Sopenharmony_ci int i, j, err, irq_idx = 0, qset_idx = 0; 6578c2ecf20Sopenharmony_ci unsigned int ntxq = SGE_TXQ_PER_SET; 6588c2ecf20Sopenharmony_ci 6598c2ecf20Sopenharmony_ci if (adap->params.rev > 0 && !(adap->flags & USING_MSI)) 6608c2ecf20Sopenharmony_ci irq_idx = -1; 6618c2ecf20Sopenharmony_ci 6628c2ecf20Sopenharmony_ci for_each_port(adap, i) { 6638c2ecf20Sopenharmony_ci struct net_device *dev = adap->port[i]; 6648c2ecf20Sopenharmony_ci struct port_info *pi = netdev_priv(dev); 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci pi->qs = &adap->sge.qs[pi->first_qset]; 6678c2ecf20Sopenharmony_ci for (j = 0; j < pi->nqsets; ++j, ++qset_idx) { 6688c2ecf20Sopenharmony_ci err = t3_sge_alloc_qset(adap, qset_idx, 1, 6698c2ecf20Sopenharmony_ci (adap->flags & USING_MSIX) ? qset_idx + 1 : 6708c2ecf20Sopenharmony_ci irq_idx, 6718c2ecf20Sopenharmony_ci &adap->params.sge.qset[qset_idx], ntxq, dev, 6728c2ecf20Sopenharmony_ci netdev_get_tx_queue(dev, j)); 6738c2ecf20Sopenharmony_ci if (err) { 6748c2ecf20Sopenharmony_ci t3_free_sge_resources(adap); 6758c2ecf20Sopenharmony_ci return err; 6768c2ecf20Sopenharmony_ci } 6778c2ecf20Sopenharmony_ci } 6788c2ecf20Sopenharmony_ci } 6798c2ecf20Sopenharmony_ci 6808c2ecf20Sopenharmony_ci return 0; 6818c2ecf20Sopenharmony_ci} 6828c2ecf20Sopenharmony_ci 6838c2ecf20Sopenharmony_cistatic ssize_t attr_show(struct device *d, char *buf, 6848c2ecf20Sopenharmony_ci ssize_t(*format) (struct net_device *, char *)) 6858c2ecf20Sopenharmony_ci{ 6868c2ecf20Sopenharmony_ci ssize_t len; 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ci /* Synchronize with ioctls that may shut down the device */ 6898c2ecf20Sopenharmony_ci rtnl_lock(); 6908c2ecf20Sopenharmony_ci len = (*format) (to_net_dev(d), buf); 6918c2ecf20Sopenharmony_ci rtnl_unlock(); 6928c2ecf20Sopenharmony_ci return len; 6938c2ecf20Sopenharmony_ci} 6948c2ecf20Sopenharmony_ci 6958c2ecf20Sopenharmony_cistatic ssize_t attr_store(struct device *d, 6968c2ecf20Sopenharmony_ci const char *buf, size_t len, 6978c2ecf20Sopenharmony_ci ssize_t(*set) (struct net_device *, unsigned int), 6988c2ecf20Sopenharmony_ci unsigned int min_val, unsigned int max_val) 6998c2ecf20Sopenharmony_ci{ 7008c2ecf20Sopenharmony_ci ssize_t ret; 7018c2ecf20Sopenharmony_ci unsigned int val; 7028c2ecf20Sopenharmony_ci 7038c2ecf20Sopenharmony_ci if (!capable(CAP_NET_ADMIN)) 7048c2ecf20Sopenharmony_ci return -EPERM; 7058c2ecf20Sopenharmony_ci 7068c2ecf20Sopenharmony_ci ret = kstrtouint(buf, 0, &val); 7078c2ecf20Sopenharmony_ci if (ret) 7088c2ecf20Sopenharmony_ci return ret; 7098c2ecf20Sopenharmony_ci if (val < min_val || val > max_val) 7108c2ecf20Sopenharmony_ci return -EINVAL; 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci rtnl_lock(); 7138c2ecf20Sopenharmony_ci ret = (*set) (to_net_dev(d), val); 7148c2ecf20Sopenharmony_ci if (!ret) 7158c2ecf20Sopenharmony_ci ret = len; 7168c2ecf20Sopenharmony_ci rtnl_unlock(); 7178c2ecf20Sopenharmony_ci return ret; 7188c2ecf20Sopenharmony_ci} 7198c2ecf20Sopenharmony_ci 7208c2ecf20Sopenharmony_ci#define CXGB3_SHOW(name, val_expr) \ 7218c2ecf20Sopenharmony_cistatic ssize_t format_##name(struct net_device *dev, char *buf) \ 7228c2ecf20Sopenharmony_ci{ \ 7238c2ecf20Sopenharmony_ci struct port_info *pi = netdev_priv(dev); \ 7248c2ecf20Sopenharmony_ci struct adapter *adap = pi->adapter; \ 7258c2ecf20Sopenharmony_ci return sprintf(buf, "%u\n", val_expr); \ 7268c2ecf20Sopenharmony_ci} \ 7278c2ecf20Sopenharmony_cistatic ssize_t show_##name(struct device *d, struct device_attribute *attr, \ 7288c2ecf20Sopenharmony_ci char *buf) \ 7298c2ecf20Sopenharmony_ci{ \ 7308c2ecf20Sopenharmony_ci return attr_show(d, buf, format_##name); \ 7318c2ecf20Sopenharmony_ci} 7328c2ecf20Sopenharmony_ci 7338c2ecf20Sopenharmony_cistatic ssize_t set_nfilters(struct net_device *dev, unsigned int val) 7348c2ecf20Sopenharmony_ci{ 7358c2ecf20Sopenharmony_ci struct port_info *pi = netdev_priv(dev); 7368c2ecf20Sopenharmony_ci struct adapter *adap = pi->adapter; 7378c2ecf20Sopenharmony_ci int min_tids = is_offload(adap) ? MC5_MIN_TIDS : 0; 7388c2ecf20Sopenharmony_ci 7398c2ecf20Sopenharmony_ci if (adap->flags & FULL_INIT_DONE) 7408c2ecf20Sopenharmony_ci return -EBUSY; 7418c2ecf20Sopenharmony_ci if (val && adap->params.rev == 0) 7428c2ecf20Sopenharmony_ci return -EINVAL; 7438c2ecf20Sopenharmony_ci if (val > t3_mc5_size(&adap->mc5) - adap->params.mc5.nservers - 7448c2ecf20Sopenharmony_ci min_tids) 7458c2ecf20Sopenharmony_ci return -EINVAL; 7468c2ecf20Sopenharmony_ci adap->params.mc5.nfilters = val; 7478c2ecf20Sopenharmony_ci return 0; 7488c2ecf20Sopenharmony_ci} 7498c2ecf20Sopenharmony_ci 7508c2ecf20Sopenharmony_cistatic ssize_t store_nfilters(struct device *d, struct device_attribute *attr, 7518c2ecf20Sopenharmony_ci const char *buf, size_t len) 7528c2ecf20Sopenharmony_ci{ 7538c2ecf20Sopenharmony_ci return attr_store(d, buf, len, set_nfilters, 0, ~0); 7548c2ecf20Sopenharmony_ci} 7558c2ecf20Sopenharmony_ci 7568c2ecf20Sopenharmony_cistatic ssize_t set_nservers(struct net_device *dev, unsigned int val) 7578c2ecf20Sopenharmony_ci{ 7588c2ecf20Sopenharmony_ci struct port_info *pi = netdev_priv(dev); 7598c2ecf20Sopenharmony_ci struct adapter *adap = pi->adapter; 7608c2ecf20Sopenharmony_ci 7618c2ecf20Sopenharmony_ci if (adap->flags & FULL_INIT_DONE) 7628c2ecf20Sopenharmony_ci return -EBUSY; 7638c2ecf20Sopenharmony_ci if (val > t3_mc5_size(&adap->mc5) - adap->params.mc5.nfilters - 7648c2ecf20Sopenharmony_ci MC5_MIN_TIDS) 7658c2ecf20Sopenharmony_ci return -EINVAL; 7668c2ecf20Sopenharmony_ci adap->params.mc5.nservers = val; 7678c2ecf20Sopenharmony_ci return 0; 7688c2ecf20Sopenharmony_ci} 7698c2ecf20Sopenharmony_ci 7708c2ecf20Sopenharmony_cistatic ssize_t store_nservers(struct device *d, struct device_attribute *attr, 7718c2ecf20Sopenharmony_ci const char *buf, size_t len) 7728c2ecf20Sopenharmony_ci{ 7738c2ecf20Sopenharmony_ci return attr_store(d, buf, len, set_nservers, 0, ~0); 7748c2ecf20Sopenharmony_ci} 7758c2ecf20Sopenharmony_ci 7768c2ecf20Sopenharmony_ci#define CXGB3_ATTR_R(name, val_expr) \ 7778c2ecf20Sopenharmony_ciCXGB3_SHOW(name, val_expr) \ 7788c2ecf20Sopenharmony_cistatic DEVICE_ATTR(name, 0444, show_##name, NULL) 7798c2ecf20Sopenharmony_ci 7808c2ecf20Sopenharmony_ci#define CXGB3_ATTR_RW(name, val_expr, store_method) \ 7818c2ecf20Sopenharmony_ciCXGB3_SHOW(name, val_expr) \ 7828c2ecf20Sopenharmony_cistatic DEVICE_ATTR(name, 0644, show_##name, store_method) 7838c2ecf20Sopenharmony_ci 7848c2ecf20Sopenharmony_ciCXGB3_ATTR_R(cam_size, t3_mc5_size(&adap->mc5)); 7858c2ecf20Sopenharmony_ciCXGB3_ATTR_RW(nfilters, adap->params.mc5.nfilters, store_nfilters); 7868c2ecf20Sopenharmony_ciCXGB3_ATTR_RW(nservers, adap->params.mc5.nservers, store_nservers); 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_cistatic struct attribute *cxgb3_attrs[] = { 7898c2ecf20Sopenharmony_ci &dev_attr_cam_size.attr, 7908c2ecf20Sopenharmony_ci &dev_attr_nfilters.attr, 7918c2ecf20Sopenharmony_ci &dev_attr_nservers.attr, 7928c2ecf20Sopenharmony_ci NULL 7938c2ecf20Sopenharmony_ci}; 7948c2ecf20Sopenharmony_ci 7958c2ecf20Sopenharmony_cistatic const struct attribute_group cxgb3_attr_group = { 7968c2ecf20Sopenharmony_ci .attrs = cxgb3_attrs, 7978c2ecf20Sopenharmony_ci}; 7988c2ecf20Sopenharmony_ci 7998c2ecf20Sopenharmony_cistatic ssize_t tm_attr_show(struct device *d, 8008c2ecf20Sopenharmony_ci char *buf, int sched) 8018c2ecf20Sopenharmony_ci{ 8028c2ecf20Sopenharmony_ci struct port_info *pi = netdev_priv(to_net_dev(d)); 8038c2ecf20Sopenharmony_ci struct adapter *adap = pi->adapter; 8048c2ecf20Sopenharmony_ci unsigned int v, addr, bpt, cpt; 8058c2ecf20Sopenharmony_ci ssize_t len; 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_ci addr = A_TP_TX_MOD_Q1_Q0_RATE_LIMIT - sched / 2; 8088c2ecf20Sopenharmony_ci rtnl_lock(); 8098c2ecf20Sopenharmony_ci t3_write_reg(adap, A_TP_TM_PIO_ADDR, addr); 8108c2ecf20Sopenharmony_ci v = t3_read_reg(adap, A_TP_TM_PIO_DATA); 8118c2ecf20Sopenharmony_ci if (sched & 1) 8128c2ecf20Sopenharmony_ci v >>= 16; 8138c2ecf20Sopenharmony_ci bpt = (v >> 8) & 0xff; 8148c2ecf20Sopenharmony_ci cpt = v & 0xff; 8158c2ecf20Sopenharmony_ci if (!cpt) 8168c2ecf20Sopenharmony_ci len = sprintf(buf, "disabled\n"); 8178c2ecf20Sopenharmony_ci else { 8188c2ecf20Sopenharmony_ci v = (adap->params.vpd.cclk * 1000) / cpt; 8198c2ecf20Sopenharmony_ci len = sprintf(buf, "%u Kbps\n", (v * bpt) / 125); 8208c2ecf20Sopenharmony_ci } 8218c2ecf20Sopenharmony_ci rtnl_unlock(); 8228c2ecf20Sopenharmony_ci return len; 8238c2ecf20Sopenharmony_ci} 8248c2ecf20Sopenharmony_ci 8258c2ecf20Sopenharmony_cistatic ssize_t tm_attr_store(struct device *d, 8268c2ecf20Sopenharmony_ci const char *buf, size_t len, int sched) 8278c2ecf20Sopenharmony_ci{ 8288c2ecf20Sopenharmony_ci struct port_info *pi = netdev_priv(to_net_dev(d)); 8298c2ecf20Sopenharmony_ci struct adapter *adap = pi->adapter; 8308c2ecf20Sopenharmony_ci unsigned int val; 8318c2ecf20Sopenharmony_ci ssize_t ret; 8328c2ecf20Sopenharmony_ci 8338c2ecf20Sopenharmony_ci if (!capable(CAP_NET_ADMIN)) 8348c2ecf20Sopenharmony_ci return -EPERM; 8358c2ecf20Sopenharmony_ci 8368c2ecf20Sopenharmony_ci ret = kstrtouint(buf, 0, &val); 8378c2ecf20Sopenharmony_ci if (ret) 8388c2ecf20Sopenharmony_ci return ret; 8398c2ecf20Sopenharmony_ci if (val > 10000000) 8408c2ecf20Sopenharmony_ci return -EINVAL; 8418c2ecf20Sopenharmony_ci 8428c2ecf20Sopenharmony_ci rtnl_lock(); 8438c2ecf20Sopenharmony_ci ret = t3_config_sched(adap, val, sched); 8448c2ecf20Sopenharmony_ci if (!ret) 8458c2ecf20Sopenharmony_ci ret = len; 8468c2ecf20Sopenharmony_ci rtnl_unlock(); 8478c2ecf20Sopenharmony_ci return ret; 8488c2ecf20Sopenharmony_ci} 8498c2ecf20Sopenharmony_ci 8508c2ecf20Sopenharmony_ci#define TM_ATTR(name, sched) \ 8518c2ecf20Sopenharmony_cistatic ssize_t show_##name(struct device *d, struct device_attribute *attr, \ 8528c2ecf20Sopenharmony_ci char *buf) \ 8538c2ecf20Sopenharmony_ci{ \ 8548c2ecf20Sopenharmony_ci return tm_attr_show(d, buf, sched); \ 8558c2ecf20Sopenharmony_ci} \ 8568c2ecf20Sopenharmony_cistatic ssize_t store_##name(struct device *d, struct device_attribute *attr, \ 8578c2ecf20Sopenharmony_ci const char *buf, size_t len) \ 8588c2ecf20Sopenharmony_ci{ \ 8598c2ecf20Sopenharmony_ci return tm_attr_store(d, buf, len, sched); \ 8608c2ecf20Sopenharmony_ci} \ 8618c2ecf20Sopenharmony_cistatic DEVICE_ATTR(name, 0644, show_##name, store_##name) 8628c2ecf20Sopenharmony_ci 8638c2ecf20Sopenharmony_ciTM_ATTR(sched0, 0); 8648c2ecf20Sopenharmony_ciTM_ATTR(sched1, 1); 8658c2ecf20Sopenharmony_ciTM_ATTR(sched2, 2); 8668c2ecf20Sopenharmony_ciTM_ATTR(sched3, 3); 8678c2ecf20Sopenharmony_ciTM_ATTR(sched4, 4); 8688c2ecf20Sopenharmony_ciTM_ATTR(sched5, 5); 8698c2ecf20Sopenharmony_ciTM_ATTR(sched6, 6); 8708c2ecf20Sopenharmony_ciTM_ATTR(sched7, 7); 8718c2ecf20Sopenharmony_ci 8728c2ecf20Sopenharmony_cistatic struct attribute *offload_attrs[] = { 8738c2ecf20Sopenharmony_ci &dev_attr_sched0.attr, 8748c2ecf20Sopenharmony_ci &dev_attr_sched1.attr, 8758c2ecf20Sopenharmony_ci &dev_attr_sched2.attr, 8768c2ecf20Sopenharmony_ci &dev_attr_sched3.attr, 8778c2ecf20Sopenharmony_ci &dev_attr_sched4.attr, 8788c2ecf20Sopenharmony_ci &dev_attr_sched5.attr, 8798c2ecf20Sopenharmony_ci &dev_attr_sched6.attr, 8808c2ecf20Sopenharmony_ci &dev_attr_sched7.attr, 8818c2ecf20Sopenharmony_ci NULL 8828c2ecf20Sopenharmony_ci}; 8838c2ecf20Sopenharmony_ci 8848c2ecf20Sopenharmony_cistatic const struct attribute_group offload_attr_group = { 8858c2ecf20Sopenharmony_ci .attrs = offload_attrs, 8868c2ecf20Sopenharmony_ci}; 8878c2ecf20Sopenharmony_ci 8888c2ecf20Sopenharmony_ci/* 8898c2ecf20Sopenharmony_ci * Sends an sk_buff to an offload queue driver 8908c2ecf20Sopenharmony_ci * after dealing with any active network taps. 8918c2ecf20Sopenharmony_ci */ 8928c2ecf20Sopenharmony_cistatic inline int offload_tx(struct t3cdev *tdev, struct sk_buff *skb) 8938c2ecf20Sopenharmony_ci{ 8948c2ecf20Sopenharmony_ci int ret; 8958c2ecf20Sopenharmony_ci 8968c2ecf20Sopenharmony_ci local_bh_disable(); 8978c2ecf20Sopenharmony_ci ret = t3_offload_tx(tdev, skb); 8988c2ecf20Sopenharmony_ci local_bh_enable(); 8998c2ecf20Sopenharmony_ci return ret; 9008c2ecf20Sopenharmony_ci} 9018c2ecf20Sopenharmony_ci 9028c2ecf20Sopenharmony_cistatic int write_smt_entry(struct adapter *adapter, int idx) 9038c2ecf20Sopenharmony_ci{ 9048c2ecf20Sopenharmony_ci struct cpl_smt_write_req *req; 9058c2ecf20Sopenharmony_ci struct port_info *pi = netdev_priv(adapter->port[idx]); 9068c2ecf20Sopenharmony_ci struct sk_buff *skb = alloc_skb(sizeof(*req), GFP_KERNEL); 9078c2ecf20Sopenharmony_ci 9088c2ecf20Sopenharmony_ci if (!skb) 9098c2ecf20Sopenharmony_ci return -ENOMEM; 9108c2ecf20Sopenharmony_ci 9118c2ecf20Sopenharmony_ci req = __skb_put(skb, sizeof(*req)); 9128c2ecf20Sopenharmony_ci req->wr.wr_hi = htonl(V_WR_OP(FW_WROPCODE_FORWARD)); 9138c2ecf20Sopenharmony_ci OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_SMT_WRITE_REQ, idx)); 9148c2ecf20Sopenharmony_ci req->mtu_idx = NMTUS - 1; /* should be 0 but there's a T3 bug */ 9158c2ecf20Sopenharmony_ci req->iff = idx; 9168c2ecf20Sopenharmony_ci memcpy(req->src_mac0, adapter->port[idx]->dev_addr, ETH_ALEN); 9178c2ecf20Sopenharmony_ci memcpy(req->src_mac1, pi->iscsic.mac_addr, ETH_ALEN); 9188c2ecf20Sopenharmony_ci skb->priority = 1; 9198c2ecf20Sopenharmony_ci offload_tx(&adapter->tdev, skb); 9208c2ecf20Sopenharmony_ci return 0; 9218c2ecf20Sopenharmony_ci} 9228c2ecf20Sopenharmony_ci 9238c2ecf20Sopenharmony_cistatic int init_smt(struct adapter *adapter) 9248c2ecf20Sopenharmony_ci{ 9258c2ecf20Sopenharmony_ci int i; 9268c2ecf20Sopenharmony_ci 9278c2ecf20Sopenharmony_ci for_each_port(adapter, i) 9288c2ecf20Sopenharmony_ci write_smt_entry(adapter, i); 9298c2ecf20Sopenharmony_ci return 0; 9308c2ecf20Sopenharmony_ci} 9318c2ecf20Sopenharmony_ci 9328c2ecf20Sopenharmony_cistatic void init_port_mtus(struct adapter *adapter) 9338c2ecf20Sopenharmony_ci{ 9348c2ecf20Sopenharmony_ci unsigned int mtus = adapter->port[0]->mtu; 9358c2ecf20Sopenharmony_ci 9368c2ecf20Sopenharmony_ci if (adapter->port[1]) 9378c2ecf20Sopenharmony_ci mtus |= adapter->port[1]->mtu << 16; 9388c2ecf20Sopenharmony_ci t3_write_reg(adapter, A_TP_MTU_PORT_TABLE, mtus); 9398c2ecf20Sopenharmony_ci} 9408c2ecf20Sopenharmony_ci 9418c2ecf20Sopenharmony_cistatic int send_pktsched_cmd(struct adapter *adap, int sched, int qidx, int lo, 9428c2ecf20Sopenharmony_ci int hi, int port) 9438c2ecf20Sopenharmony_ci{ 9448c2ecf20Sopenharmony_ci struct sk_buff *skb; 9458c2ecf20Sopenharmony_ci struct mngt_pktsched_wr *req; 9468c2ecf20Sopenharmony_ci int ret; 9478c2ecf20Sopenharmony_ci 9488c2ecf20Sopenharmony_ci skb = alloc_skb(sizeof(*req), GFP_KERNEL); 9498c2ecf20Sopenharmony_ci if (!skb) 9508c2ecf20Sopenharmony_ci skb = adap->nofail_skb; 9518c2ecf20Sopenharmony_ci if (!skb) 9528c2ecf20Sopenharmony_ci return -ENOMEM; 9538c2ecf20Sopenharmony_ci 9548c2ecf20Sopenharmony_ci req = skb_put(skb, sizeof(*req)); 9558c2ecf20Sopenharmony_ci req->wr_hi = htonl(V_WR_OP(FW_WROPCODE_MNGT)); 9568c2ecf20Sopenharmony_ci req->mngt_opcode = FW_MNGTOPCODE_PKTSCHED_SET; 9578c2ecf20Sopenharmony_ci req->sched = sched; 9588c2ecf20Sopenharmony_ci req->idx = qidx; 9598c2ecf20Sopenharmony_ci req->min = lo; 9608c2ecf20Sopenharmony_ci req->max = hi; 9618c2ecf20Sopenharmony_ci req->binding = port; 9628c2ecf20Sopenharmony_ci ret = t3_mgmt_tx(adap, skb); 9638c2ecf20Sopenharmony_ci if (skb == adap->nofail_skb) { 9648c2ecf20Sopenharmony_ci adap->nofail_skb = alloc_skb(sizeof(struct cpl_set_tcb_field), 9658c2ecf20Sopenharmony_ci GFP_KERNEL); 9668c2ecf20Sopenharmony_ci if (!adap->nofail_skb) 9678c2ecf20Sopenharmony_ci ret = -ENOMEM; 9688c2ecf20Sopenharmony_ci } 9698c2ecf20Sopenharmony_ci 9708c2ecf20Sopenharmony_ci return ret; 9718c2ecf20Sopenharmony_ci} 9728c2ecf20Sopenharmony_ci 9738c2ecf20Sopenharmony_cistatic int bind_qsets(struct adapter *adap) 9748c2ecf20Sopenharmony_ci{ 9758c2ecf20Sopenharmony_ci int i, j, err = 0; 9768c2ecf20Sopenharmony_ci 9778c2ecf20Sopenharmony_ci for_each_port(adap, i) { 9788c2ecf20Sopenharmony_ci const struct port_info *pi = adap2pinfo(adap, i); 9798c2ecf20Sopenharmony_ci 9808c2ecf20Sopenharmony_ci for (j = 0; j < pi->nqsets; ++j) { 9818c2ecf20Sopenharmony_ci int ret = send_pktsched_cmd(adap, 1, 9828c2ecf20Sopenharmony_ci pi->first_qset + j, -1, 9838c2ecf20Sopenharmony_ci -1, i); 9848c2ecf20Sopenharmony_ci if (ret) 9858c2ecf20Sopenharmony_ci err = ret; 9868c2ecf20Sopenharmony_ci } 9878c2ecf20Sopenharmony_ci } 9888c2ecf20Sopenharmony_ci 9898c2ecf20Sopenharmony_ci return err; 9908c2ecf20Sopenharmony_ci} 9918c2ecf20Sopenharmony_ci 9928c2ecf20Sopenharmony_ci#define FW_VERSION __stringify(FW_VERSION_MAJOR) "." \ 9938c2ecf20Sopenharmony_ci __stringify(FW_VERSION_MINOR) "." __stringify(FW_VERSION_MICRO) 9948c2ecf20Sopenharmony_ci#define FW_FNAME "cxgb3/t3fw-" FW_VERSION ".bin" 9958c2ecf20Sopenharmony_ci#define TPSRAM_VERSION __stringify(TP_VERSION_MAJOR) "." \ 9968c2ecf20Sopenharmony_ci __stringify(TP_VERSION_MINOR) "." __stringify(TP_VERSION_MICRO) 9978c2ecf20Sopenharmony_ci#define TPSRAM_NAME "cxgb3/t3%c_psram-" TPSRAM_VERSION ".bin" 9988c2ecf20Sopenharmony_ci#define AEL2005_OPT_EDC_NAME "cxgb3/ael2005_opt_edc.bin" 9998c2ecf20Sopenharmony_ci#define AEL2005_TWX_EDC_NAME "cxgb3/ael2005_twx_edc.bin" 10008c2ecf20Sopenharmony_ci#define AEL2020_TWX_EDC_NAME "cxgb3/ael2020_twx_edc.bin" 10018c2ecf20Sopenharmony_ciMODULE_FIRMWARE(FW_FNAME); 10028c2ecf20Sopenharmony_ciMODULE_FIRMWARE("cxgb3/t3b_psram-" TPSRAM_VERSION ".bin"); 10038c2ecf20Sopenharmony_ciMODULE_FIRMWARE("cxgb3/t3c_psram-" TPSRAM_VERSION ".bin"); 10048c2ecf20Sopenharmony_ciMODULE_FIRMWARE(AEL2005_OPT_EDC_NAME); 10058c2ecf20Sopenharmony_ciMODULE_FIRMWARE(AEL2005_TWX_EDC_NAME); 10068c2ecf20Sopenharmony_ciMODULE_FIRMWARE(AEL2020_TWX_EDC_NAME); 10078c2ecf20Sopenharmony_ci 10088c2ecf20Sopenharmony_cistatic inline const char *get_edc_fw_name(int edc_idx) 10098c2ecf20Sopenharmony_ci{ 10108c2ecf20Sopenharmony_ci const char *fw_name = NULL; 10118c2ecf20Sopenharmony_ci 10128c2ecf20Sopenharmony_ci switch (edc_idx) { 10138c2ecf20Sopenharmony_ci case EDC_OPT_AEL2005: 10148c2ecf20Sopenharmony_ci fw_name = AEL2005_OPT_EDC_NAME; 10158c2ecf20Sopenharmony_ci break; 10168c2ecf20Sopenharmony_ci case EDC_TWX_AEL2005: 10178c2ecf20Sopenharmony_ci fw_name = AEL2005_TWX_EDC_NAME; 10188c2ecf20Sopenharmony_ci break; 10198c2ecf20Sopenharmony_ci case EDC_TWX_AEL2020: 10208c2ecf20Sopenharmony_ci fw_name = AEL2020_TWX_EDC_NAME; 10218c2ecf20Sopenharmony_ci break; 10228c2ecf20Sopenharmony_ci } 10238c2ecf20Sopenharmony_ci return fw_name; 10248c2ecf20Sopenharmony_ci} 10258c2ecf20Sopenharmony_ci 10268c2ecf20Sopenharmony_ciint t3_get_edc_fw(struct cphy *phy, int edc_idx, int size) 10278c2ecf20Sopenharmony_ci{ 10288c2ecf20Sopenharmony_ci struct adapter *adapter = phy->adapter; 10298c2ecf20Sopenharmony_ci const struct firmware *fw; 10308c2ecf20Sopenharmony_ci const char *fw_name; 10318c2ecf20Sopenharmony_ci u32 csum; 10328c2ecf20Sopenharmony_ci const __be32 *p; 10338c2ecf20Sopenharmony_ci u16 *cache = phy->phy_cache; 10348c2ecf20Sopenharmony_ci int i, ret = -EINVAL; 10358c2ecf20Sopenharmony_ci 10368c2ecf20Sopenharmony_ci fw_name = get_edc_fw_name(edc_idx); 10378c2ecf20Sopenharmony_ci if (fw_name) 10388c2ecf20Sopenharmony_ci ret = request_firmware(&fw, fw_name, &adapter->pdev->dev); 10398c2ecf20Sopenharmony_ci if (ret < 0) { 10408c2ecf20Sopenharmony_ci dev_err(&adapter->pdev->dev, 10418c2ecf20Sopenharmony_ci "could not upgrade firmware: unable to load %s\n", 10428c2ecf20Sopenharmony_ci fw_name); 10438c2ecf20Sopenharmony_ci return ret; 10448c2ecf20Sopenharmony_ci } 10458c2ecf20Sopenharmony_ci 10468c2ecf20Sopenharmony_ci /* check size, take checksum in account */ 10478c2ecf20Sopenharmony_ci if (fw->size > size + 4) { 10488c2ecf20Sopenharmony_ci CH_ERR(adapter, "firmware image too large %u, expected %d\n", 10498c2ecf20Sopenharmony_ci (unsigned int)fw->size, size + 4); 10508c2ecf20Sopenharmony_ci ret = -EINVAL; 10518c2ecf20Sopenharmony_ci } 10528c2ecf20Sopenharmony_ci 10538c2ecf20Sopenharmony_ci /* compute checksum */ 10548c2ecf20Sopenharmony_ci p = (const __be32 *)fw->data; 10558c2ecf20Sopenharmony_ci for (csum = 0, i = 0; i < fw->size / sizeof(csum); i++) 10568c2ecf20Sopenharmony_ci csum += ntohl(p[i]); 10578c2ecf20Sopenharmony_ci 10588c2ecf20Sopenharmony_ci if (csum != 0xffffffff) { 10598c2ecf20Sopenharmony_ci CH_ERR(adapter, "corrupted firmware image, checksum %u\n", 10608c2ecf20Sopenharmony_ci csum); 10618c2ecf20Sopenharmony_ci ret = -EINVAL; 10628c2ecf20Sopenharmony_ci } 10638c2ecf20Sopenharmony_ci 10648c2ecf20Sopenharmony_ci for (i = 0; i < size / 4 ; i++) { 10658c2ecf20Sopenharmony_ci *cache++ = (be32_to_cpu(p[i]) & 0xffff0000) >> 16; 10668c2ecf20Sopenharmony_ci *cache++ = be32_to_cpu(p[i]) & 0xffff; 10678c2ecf20Sopenharmony_ci } 10688c2ecf20Sopenharmony_ci 10698c2ecf20Sopenharmony_ci release_firmware(fw); 10708c2ecf20Sopenharmony_ci 10718c2ecf20Sopenharmony_ci return ret; 10728c2ecf20Sopenharmony_ci} 10738c2ecf20Sopenharmony_ci 10748c2ecf20Sopenharmony_cistatic int upgrade_fw(struct adapter *adap) 10758c2ecf20Sopenharmony_ci{ 10768c2ecf20Sopenharmony_ci int ret; 10778c2ecf20Sopenharmony_ci const struct firmware *fw; 10788c2ecf20Sopenharmony_ci struct device *dev = &adap->pdev->dev; 10798c2ecf20Sopenharmony_ci 10808c2ecf20Sopenharmony_ci ret = request_firmware(&fw, FW_FNAME, dev); 10818c2ecf20Sopenharmony_ci if (ret < 0) { 10828c2ecf20Sopenharmony_ci dev_err(dev, "could not upgrade firmware: unable to load %s\n", 10838c2ecf20Sopenharmony_ci FW_FNAME); 10848c2ecf20Sopenharmony_ci return ret; 10858c2ecf20Sopenharmony_ci } 10868c2ecf20Sopenharmony_ci ret = t3_load_fw(adap, fw->data, fw->size); 10878c2ecf20Sopenharmony_ci release_firmware(fw); 10888c2ecf20Sopenharmony_ci 10898c2ecf20Sopenharmony_ci if (ret == 0) 10908c2ecf20Sopenharmony_ci dev_info(dev, "successful upgrade to firmware %d.%d.%d\n", 10918c2ecf20Sopenharmony_ci FW_VERSION_MAJOR, FW_VERSION_MINOR, FW_VERSION_MICRO); 10928c2ecf20Sopenharmony_ci else 10938c2ecf20Sopenharmony_ci dev_err(dev, "failed to upgrade to firmware %d.%d.%d\n", 10948c2ecf20Sopenharmony_ci FW_VERSION_MAJOR, FW_VERSION_MINOR, FW_VERSION_MICRO); 10958c2ecf20Sopenharmony_ci 10968c2ecf20Sopenharmony_ci return ret; 10978c2ecf20Sopenharmony_ci} 10988c2ecf20Sopenharmony_ci 10998c2ecf20Sopenharmony_cistatic inline char t3rev2char(struct adapter *adapter) 11008c2ecf20Sopenharmony_ci{ 11018c2ecf20Sopenharmony_ci char rev = 0; 11028c2ecf20Sopenharmony_ci 11038c2ecf20Sopenharmony_ci switch(adapter->params.rev) { 11048c2ecf20Sopenharmony_ci case T3_REV_B: 11058c2ecf20Sopenharmony_ci case T3_REV_B2: 11068c2ecf20Sopenharmony_ci rev = 'b'; 11078c2ecf20Sopenharmony_ci break; 11088c2ecf20Sopenharmony_ci case T3_REV_C: 11098c2ecf20Sopenharmony_ci rev = 'c'; 11108c2ecf20Sopenharmony_ci break; 11118c2ecf20Sopenharmony_ci } 11128c2ecf20Sopenharmony_ci return rev; 11138c2ecf20Sopenharmony_ci} 11148c2ecf20Sopenharmony_ci 11158c2ecf20Sopenharmony_cistatic int update_tpsram(struct adapter *adap) 11168c2ecf20Sopenharmony_ci{ 11178c2ecf20Sopenharmony_ci const struct firmware *tpsram; 11188c2ecf20Sopenharmony_ci char buf[64]; 11198c2ecf20Sopenharmony_ci struct device *dev = &adap->pdev->dev; 11208c2ecf20Sopenharmony_ci int ret; 11218c2ecf20Sopenharmony_ci char rev; 11228c2ecf20Sopenharmony_ci 11238c2ecf20Sopenharmony_ci rev = t3rev2char(adap); 11248c2ecf20Sopenharmony_ci if (!rev) 11258c2ecf20Sopenharmony_ci return 0; 11268c2ecf20Sopenharmony_ci 11278c2ecf20Sopenharmony_ci snprintf(buf, sizeof(buf), TPSRAM_NAME, rev); 11288c2ecf20Sopenharmony_ci 11298c2ecf20Sopenharmony_ci ret = request_firmware(&tpsram, buf, dev); 11308c2ecf20Sopenharmony_ci if (ret < 0) { 11318c2ecf20Sopenharmony_ci dev_err(dev, "could not load TP SRAM: unable to load %s\n", 11328c2ecf20Sopenharmony_ci buf); 11338c2ecf20Sopenharmony_ci return ret; 11348c2ecf20Sopenharmony_ci } 11358c2ecf20Sopenharmony_ci 11368c2ecf20Sopenharmony_ci ret = t3_check_tpsram(adap, tpsram->data, tpsram->size); 11378c2ecf20Sopenharmony_ci if (ret) 11388c2ecf20Sopenharmony_ci goto release_tpsram; 11398c2ecf20Sopenharmony_ci 11408c2ecf20Sopenharmony_ci ret = t3_set_proto_sram(adap, tpsram->data); 11418c2ecf20Sopenharmony_ci if (ret == 0) 11428c2ecf20Sopenharmony_ci dev_info(dev, 11438c2ecf20Sopenharmony_ci "successful update of protocol engine " 11448c2ecf20Sopenharmony_ci "to %d.%d.%d\n", 11458c2ecf20Sopenharmony_ci TP_VERSION_MAJOR, TP_VERSION_MINOR, TP_VERSION_MICRO); 11468c2ecf20Sopenharmony_ci else 11478c2ecf20Sopenharmony_ci dev_err(dev, "failed to update of protocol engine %d.%d.%d\n", 11488c2ecf20Sopenharmony_ci TP_VERSION_MAJOR, TP_VERSION_MINOR, TP_VERSION_MICRO); 11498c2ecf20Sopenharmony_ci if (ret) 11508c2ecf20Sopenharmony_ci dev_err(dev, "loading protocol SRAM failed\n"); 11518c2ecf20Sopenharmony_ci 11528c2ecf20Sopenharmony_cirelease_tpsram: 11538c2ecf20Sopenharmony_ci release_firmware(tpsram); 11548c2ecf20Sopenharmony_ci 11558c2ecf20Sopenharmony_ci return ret; 11568c2ecf20Sopenharmony_ci} 11578c2ecf20Sopenharmony_ci 11588c2ecf20Sopenharmony_ci/** 11598c2ecf20Sopenharmony_ci * t3_synchronize_rx - wait for current Rx processing on a port to complete 11608c2ecf20Sopenharmony_ci * @adap: the adapter 11618c2ecf20Sopenharmony_ci * @p: the port 11628c2ecf20Sopenharmony_ci * 11638c2ecf20Sopenharmony_ci * Ensures that current Rx processing on any of the queues associated with 11648c2ecf20Sopenharmony_ci * the given port completes before returning. We do this by acquiring and 11658c2ecf20Sopenharmony_ci * releasing the locks of the response queues associated with the port. 11668c2ecf20Sopenharmony_ci */ 11678c2ecf20Sopenharmony_cistatic void t3_synchronize_rx(struct adapter *adap, const struct port_info *p) 11688c2ecf20Sopenharmony_ci{ 11698c2ecf20Sopenharmony_ci int i; 11708c2ecf20Sopenharmony_ci 11718c2ecf20Sopenharmony_ci for (i = p->first_qset; i < p->first_qset + p->nqsets; i++) { 11728c2ecf20Sopenharmony_ci struct sge_rspq *q = &adap->sge.qs[i].rspq; 11738c2ecf20Sopenharmony_ci 11748c2ecf20Sopenharmony_ci spin_lock_irq(&q->lock); 11758c2ecf20Sopenharmony_ci spin_unlock_irq(&q->lock); 11768c2ecf20Sopenharmony_ci } 11778c2ecf20Sopenharmony_ci} 11788c2ecf20Sopenharmony_ci 11798c2ecf20Sopenharmony_cistatic void cxgb_vlan_mode(struct net_device *dev, netdev_features_t features) 11808c2ecf20Sopenharmony_ci{ 11818c2ecf20Sopenharmony_ci struct port_info *pi = netdev_priv(dev); 11828c2ecf20Sopenharmony_ci struct adapter *adapter = pi->adapter; 11838c2ecf20Sopenharmony_ci 11848c2ecf20Sopenharmony_ci if (adapter->params.rev > 0) { 11858c2ecf20Sopenharmony_ci t3_set_vlan_accel(adapter, 1 << pi->port_id, 11868c2ecf20Sopenharmony_ci features & NETIF_F_HW_VLAN_CTAG_RX); 11878c2ecf20Sopenharmony_ci } else { 11888c2ecf20Sopenharmony_ci /* single control for all ports */ 11898c2ecf20Sopenharmony_ci unsigned int i, have_vlans = features & NETIF_F_HW_VLAN_CTAG_RX; 11908c2ecf20Sopenharmony_ci 11918c2ecf20Sopenharmony_ci for_each_port(adapter, i) 11928c2ecf20Sopenharmony_ci have_vlans |= 11938c2ecf20Sopenharmony_ci adapter->port[i]->features & 11948c2ecf20Sopenharmony_ci NETIF_F_HW_VLAN_CTAG_RX; 11958c2ecf20Sopenharmony_ci 11968c2ecf20Sopenharmony_ci t3_set_vlan_accel(adapter, 1, have_vlans); 11978c2ecf20Sopenharmony_ci } 11988c2ecf20Sopenharmony_ci t3_synchronize_rx(adapter, pi); 11998c2ecf20Sopenharmony_ci} 12008c2ecf20Sopenharmony_ci 12018c2ecf20Sopenharmony_ci/** 12028c2ecf20Sopenharmony_ci * cxgb_up - enable the adapter 12038c2ecf20Sopenharmony_ci * @adap: adapter being enabled 12048c2ecf20Sopenharmony_ci * 12058c2ecf20Sopenharmony_ci * Called when the first port is enabled, this function performs the 12068c2ecf20Sopenharmony_ci * actions necessary to make an adapter operational, such as completing 12078c2ecf20Sopenharmony_ci * the initialization of HW modules, and enabling interrupts. 12088c2ecf20Sopenharmony_ci * 12098c2ecf20Sopenharmony_ci * Must be called with the rtnl lock held. 12108c2ecf20Sopenharmony_ci */ 12118c2ecf20Sopenharmony_cistatic int cxgb_up(struct adapter *adap) 12128c2ecf20Sopenharmony_ci{ 12138c2ecf20Sopenharmony_ci int i, err; 12148c2ecf20Sopenharmony_ci 12158c2ecf20Sopenharmony_ci if (!(adap->flags & FULL_INIT_DONE)) { 12168c2ecf20Sopenharmony_ci err = t3_check_fw_version(adap); 12178c2ecf20Sopenharmony_ci if (err == -EINVAL) { 12188c2ecf20Sopenharmony_ci err = upgrade_fw(adap); 12198c2ecf20Sopenharmony_ci CH_WARN(adap, "FW upgrade to %d.%d.%d %s\n", 12208c2ecf20Sopenharmony_ci FW_VERSION_MAJOR, FW_VERSION_MINOR, 12218c2ecf20Sopenharmony_ci FW_VERSION_MICRO, err ? "failed" : "succeeded"); 12228c2ecf20Sopenharmony_ci } 12238c2ecf20Sopenharmony_ci 12248c2ecf20Sopenharmony_ci err = t3_check_tpsram_version(adap); 12258c2ecf20Sopenharmony_ci if (err == -EINVAL) { 12268c2ecf20Sopenharmony_ci err = update_tpsram(adap); 12278c2ecf20Sopenharmony_ci CH_WARN(adap, "TP upgrade to %d.%d.%d %s\n", 12288c2ecf20Sopenharmony_ci TP_VERSION_MAJOR, TP_VERSION_MINOR, 12298c2ecf20Sopenharmony_ci TP_VERSION_MICRO, err ? "failed" : "succeeded"); 12308c2ecf20Sopenharmony_ci } 12318c2ecf20Sopenharmony_ci 12328c2ecf20Sopenharmony_ci /* 12338c2ecf20Sopenharmony_ci * Clear interrupts now to catch errors if t3_init_hw fails. 12348c2ecf20Sopenharmony_ci * We clear them again later as initialization may trigger 12358c2ecf20Sopenharmony_ci * conditions that can interrupt. 12368c2ecf20Sopenharmony_ci */ 12378c2ecf20Sopenharmony_ci t3_intr_clear(adap); 12388c2ecf20Sopenharmony_ci 12398c2ecf20Sopenharmony_ci err = t3_init_hw(adap, 0); 12408c2ecf20Sopenharmony_ci if (err) 12418c2ecf20Sopenharmony_ci goto out; 12428c2ecf20Sopenharmony_ci 12438c2ecf20Sopenharmony_ci t3_set_reg_field(adap, A_TP_PARA_REG5, 0, F_RXDDPOFFINIT); 12448c2ecf20Sopenharmony_ci t3_write_reg(adap, A_ULPRX_TDDP_PSZ, V_HPZ0(PAGE_SHIFT - 12)); 12458c2ecf20Sopenharmony_ci 12468c2ecf20Sopenharmony_ci err = setup_sge_qsets(adap); 12478c2ecf20Sopenharmony_ci if (err) 12488c2ecf20Sopenharmony_ci goto out; 12498c2ecf20Sopenharmony_ci 12508c2ecf20Sopenharmony_ci for_each_port(adap, i) 12518c2ecf20Sopenharmony_ci cxgb_vlan_mode(adap->port[i], adap->port[i]->features); 12528c2ecf20Sopenharmony_ci 12538c2ecf20Sopenharmony_ci setup_rss(adap); 12548c2ecf20Sopenharmony_ci if (!(adap->flags & NAPI_INIT)) 12558c2ecf20Sopenharmony_ci init_napi(adap); 12568c2ecf20Sopenharmony_ci 12578c2ecf20Sopenharmony_ci t3_start_sge_timers(adap); 12588c2ecf20Sopenharmony_ci adap->flags |= FULL_INIT_DONE; 12598c2ecf20Sopenharmony_ci } 12608c2ecf20Sopenharmony_ci 12618c2ecf20Sopenharmony_ci t3_intr_clear(adap); 12628c2ecf20Sopenharmony_ci 12638c2ecf20Sopenharmony_ci if (adap->flags & USING_MSIX) { 12648c2ecf20Sopenharmony_ci name_msix_vecs(adap); 12658c2ecf20Sopenharmony_ci err = request_irq(adap->msix_info[0].vec, 12668c2ecf20Sopenharmony_ci t3_async_intr_handler, 0, 12678c2ecf20Sopenharmony_ci adap->msix_info[0].desc, adap); 12688c2ecf20Sopenharmony_ci if (err) 12698c2ecf20Sopenharmony_ci goto irq_err; 12708c2ecf20Sopenharmony_ci 12718c2ecf20Sopenharmony_ci err = request_msix_data_irqs(adap); 12728c2ecf20Sopenharmony_ci if (err) { 12738c2ecf20Sopenharmony_ci free_irq(adap->msix_info[0].vec, adap); 12748c2ecf20Sopenharmony_ci goto irq_err; 12758c2ecf20Sopenharmony_ci } 12768c2ecf20Sopenharmony_ci } else if ((err = request_irq(adap->pdev->irq, 12778c2ecf20Sopenharmony_ci t3_intr_handler(adap, 12788c2ecf20Sopenharmony_ci adap->sge.qs[0].rspq. 12798c2ecf20Sopenharmony_ci polling), 12808c2ecf20Sopenharmony_ci (adap->flags & USING_MSI) ? 12818c2ecf20Sopenharmony_ci 0 : IRQF_SHARED, 12828c2ecf20Sopenharmony_ci adap->name, adap))) 12838c2ecf20Sopenharmony_ci goto irq_err; 12848c2ecf20Sopenharmony_ci 12858c2ecf20Sopenharmony_ci enable_all_napi(adap); 12868c2ecf20Sopenharmony_ci t3_sge_start(adap); 12878c2ecf20Sopenharmony_ci t3_intr_enable(adap); 12888c2ecf20Sopenharmony_ci 12898c2ecf20Sopenharmony_ci if (adap->params.rev >= T3_REV_C && !(adap->flags & TP_PARITY_INIT) && 12908c2ecf20Sopenharmony_ci is_offload(adap) && init_tp_parity(adap) == 0) 12918c2ecf20Sopenharmony_ci adap->flags |= TP_PARITY_INIT; 12928c2ecf20Sopenharmony_ci 12938c2ecf20Sopenharmony_ci if (adap->flags & TP_PARITY_INIT) { 12948c2ecf20Sopenharmony_ci t3_write_reg(adap, A_TP_INT_CAUSE, 12958c2ecf20Sopenharmony_ci F_CMCACHEPERR | F_ARPLUTPERR); 12968c2ecf20Sopenharmony_ci t3_write_reg(adap, A_TP_INT_ENABLE, 0x7fbfffff); 12978c2ecf20Sopenharmony_ci } 12988c2ecf20Sopenharmony_ci 12998c2ecf20Sopenharmony_ci if (!(adap->flags & QUEUES_BOUND)) { 13008c2ecf20Sopenharmony_ci int ret = bind_qsets(adap); 13018c2ecf20Sopenharmony_ci 13028c2ecf20Sopenharmony_ci if (ret < 0) { 13038c2ecf20Sopenharmony_ci CH_ERR(adap, "failed to bind qsets, err %d\n", ret); 13048c2ecf20Sopenharmony_ci t3_intr_disable(adap); 13058c2ecf20Sopenharmony_ci quiesce_rx(adap); 13068c2ecf20Sopenharmony_ci free_irq_resources(adap); 13078c2ecf20Sopenharmony_ci err = ret; 13088c2ecf20Sopenharmony_ci goto out; 13098c2ecf20Sopenharmony_ci } 13108c2ecf20Sopenharmony_ci adap->flags |= QUEUES_BOUND; 13118c2ecf20Sopenharmony_ci } 13128c2ecf20Sopenharmony_ci 13138c2ecf20Sopenharmony_ciout: 13148c2ecf20Sopenharmony_ci return err; 13158c2ecf20Sopenharmony_ciirq_err: 13168c2ecf20Sopenharmony_ci CH_ERR(adap, "request_irq failed, err %d\n", err); 13178c2ecf20Sopenharmony_ci goto out; 13188c2ecf20Sopenharmony_ci} 13198c2ecf20Sopenharmony_ci 13208c2ecf20Sopenharmony_ci/* 13218c2ecf20Sopenharmony_ci * Release resources when all the ports and offloading have been stopped. 13228c2ecf20Sopenharmony_ci */ 13238c2ecf20Sopenharmony_cistatic void cxgb_down(struct adapter *adapter, int on_wq) 13248c2ecf20Sopenharmony_ci{ 13258c2ecf20Sopenharmony_ci t3_sge_stop(adapter); 13268c2ecf20Sopenharmony_ci spin_lock_irq(&adapter->work_lock); /* sync with PHY intr task */ 13278c2ecf20Sopenharmony_ci t3_intr_disable(adapter); 13288c2ecf20Sopenharmony_ci spin_unlock_irq(&adapter->work_lock); 13298c2ecf20Sopenharmony_ci 13308c2ecf20Sopenharmony_ci free_irq_resources(adapter); 13318c2ecf20Sopenharmony_ci quiesce_rx(adapter); 13328c2ecf20Sopenharmony_ci t3_sge_stop(adapter); 13338c2ecf20Sopenharmony_ci if (!on_wq) 13348c2ecf20Sopenharmony_ci flush_workqueue(cxgb3_wq);/* wait for external IRQ handler */ 13358c2ecf20Sopenharmony_ci} 13368c2ecf20Sopenharmony_ci 13378c2ecf20Sopenharmony_cistatic void schedule_chk_task(struct adapter *adap) 13388c2ecf20Sopenharmony_ci{ 13398c2ecf20Sopenharmony_ci unsigned int timeo; 13408c2ecf20Sopenharmony_ci 13418c2ecf20Sopenharmony_ci timeo = adap->params.linkpoll_period ? 13428c2ecf20Sopenharmony_ci (HZ * adap->params.linkpoll_period) / 10 : 13438c2ecf20Sopenharmony_ci adap->params.stats_update_period * HZ; 13448c2ecf20Sopenharmony_ci if (timeo) 13458c2ecf20Sopenharmony_ci queue_delayed_work(cxgb3_wq, &adap->adap_check_task, timeo); 13468c2ecf20Sopenharmony_ci} 13478c2ecf20Sopenharmony_ci 13488c2ecf20Sopenharmony_cistatic int offload_open(struct net_device *dev) 13498c2ecf20Sopenharmony_ci{ 13508c2ecf20Sopenharmony_ci struct port_info *pi = netdev_priv(dev); 13518c2ecf20Sopenharmony_ci struct adapter *adapter = pi->adapter; 13528c2ecf20Sopenharmony_ci struct t3cdev *tdev = dev2t3cdev(dev); 13538c2ecf20Sopenharmony_ci int adap_up = adapter->open_device_map & PORT_MASK; 13548c2ecf20Sopenharmony_ci int err; 13558c2ecf20Sopenharmony_ci 13568c2ecf20Sopenharmony_ci if (test_and_set_bit(OFFLOAD_DEVMAP_BIT, &adapter->open_device_map)) 13578c2ecf20Sopenharmony_ci return 0; 13588c2ecf20Sopenharmony_ci 13598c2ecf20Sopenharmony_ci if (!adap_up && (err = cxgb_up(adapter)) < 0) 13608c2ecf20Sopenharmony_ci goto out; 13618c2ecf20Sopenharmony_ci 13628c2ecf20Sopenharmony_ci t3_tp_set_offload_mode(adapter, 1); 13638c2ecf20Sopenharmony_ci tdev->lldev = adapter->port[0]; 13648c2ecf20Sopenharmony_ci err = cxgb3_offload_activate(adapter); 13658c2ecf20Sopenharmony_ci if (err) 13668c2ecf20Sopenharmony_ci goto out; 13678c2ecf20Sopenharmony_ci 13688c2ecf20Sopenharmony_ci init_port_mtus(adapter); 13698c2ecf20Sopenharmony_ci t3_load_mtus(adapter, adapter->params.mtus, adapter->params.a_wnd, 13708c2ecf20Sopenharmony_ci adapter->params.b_wnd, 13718c2ecf20Sopenharmony_ci adapter->params.rev == 0 ? 13728c2ecf20Sopenharmony_ci adapter->port[0]->mtu : 0xffff); 13738c2ecf20Sopenharmony_ci init_smt(adapter); 13748c2ecf20Sopenharmony_ci 13758c2ecf20Sopenharmony_ci if (sysfs_create_group(&tdev->lldev->dev.kobj, &offload_attr_group)) 13768c2ecf20Sopenharmony_ci dev_dbg(&dev->dev, "cannot create sysfs group\n"); 13778c2ecf20Sopenharmony_ci 13788c2ecf20Sopenharmony_ci /* Call back all registered clients */ 13798c2ecf20Sopenharmony_ci cxgb3_add_clients(tdev); 13808c2ecf20Sopenharmony_ci 13818c2ecf20Sopenharmony_ciout: 13828c2ecf20Sopenharmony_ci /* restore them in case the offload module has changed them */ 13838c2ecf20Sopenharmony_ci if (err) { 13848c2ecf20Sopenharmony_ci t3_tp_set_offload_mode(adapter, 0); 13858c2ecf20Sopenharmony_ci clear_bit(OFFLOAD_DEVMAP_BIT, &adapter->open_device_map); 13868c2ecf20Sopenharmony_ci cxgb3_set_dummy_ops(tdev); 13878c2ecf20Sopenharmony_ci } 13888c2ecf20Sopenharmony_ci return err; 13898c2ecf20Sopenharmony_ci} 13908c2ecf20Sopenharmony_ci 13918c2ecf20Sopenharmony_cistatic int offload_close(struct t3cdev *tdev) 13928c2ecf20Sopenharmony_ci{ 13938c2ecf20Sopenharmony_ci struct adapter *adapter = tdev2adap(tdev); 13948c2ecf20Sopenharmony_ci struct t3c_data *td = T3C_DATA(tdev); 13958c2ecf20Sopenharmony_ci 13968c2ecf20Sopenharmony_ci if (!test_bit(OFFLOAD_DEVMAP_BIT, &adapter->open_device_map)) 13978c2ecf20Sopenharmony_ci return 0; 13988c2ecf20Sopenharmony_ci 13998c2ecf20Sopenharmony_ci /* Call back all registered clients */ 14008c2ecf20Sopenharmony_ci cxgb3_remove_clients(tdev); 14018c2ecf20Sopenharmony_ci 14028c2ecf20Sopenharmony_ci sysfs_remove_group(&tdev->lldev->dev.kobj, &offload_attr_group); 14038c2ecf20Sopenharmony_ci 14048c2ecf20Sopenharmony_ci /* Flush work scheduled while releasing TIDs */ 14058c2ecf20Sopenharmony_ci flush_work(&td->tid_release_task); 14068c2ecf20Sopenharmony_ci 14078c2ecf20Sopenharmony_ci tdev->lldev = NULL; 14088c2ecf20Sopenharmony_ci cxgb3_set_dummy_ops(tdev); 14098c2ecf20Sopenharmony_ci t3_tp_set_offload_mode(adapter, 0); 14108c2ecf20Sopenharmony_ci clear_bit(OFFLOAD_DEVMAP_BIT, &adapter->open_device_map); 14118c2ecf20Sopenharmony_ci 14128c2ecf20Sopenharmony_ci if (!adapter->open_device_map) 14138c2ecf20Sopenharmony_ci cxgb_down(adapter, 0); 14148c2ecf20Sopenharmony_ci 14158c2ecf20Sopenharmony_ci cxgb3_offload_deactivate(adapter); 14168c2ecf20Sopenharmony_ci return 0; 14178c2ecf20Sopenharmony_ci} 14188c2ecf20Sopenharmony_ci 14198c2ecf20Sopenharmony_cistatic int cxgb_open(struct net_device *dev) 14208c2ecf20Sopenharmony_ci{ 14218c2ecf20Sopenharmony_ci struct port_info *pi = netdev_priv(dev); 14228c2ecf20Sopenharmony_ci struct adapter *adapter = pi->adapter; 14238c2ecf20Sopenharmony_ci int other_ports = adapter->open_device_map & PORT_MASK; 14248c2ecf20Sopenharmony_ci int err; 14258c2ecf20Sopenharmony_ci 14268c2ecf20Sopenharmony_ci if (!adapter->open_device_map && (err = cxgb_up(adapter)) < 0) 14278c2ecf20Sopenharmony_ci return err; 14288c2ecf20Sopenharmony_ci 14298c2ecf20Sopenharmony_ci set_bit(pi->port_id, &adapter->open_device_map); 14308c2ecf20Sopenharmony_ci if (is_offload(adapter) && !ofld_disable) { 14318c2ecf20Sopenharmony_ci err = offload_open(dev); 14328c2ecf20Sopenharmony_ci if (err) 14338c2ecf20Sopenharmony_ci pr_warn("Could not initialize offload capabilities\n"); 14348c2ecf20Sopenharmony_ci } 14358c2ecf20Sopenharmony_ci 14368c2ecf20Sopenharmony_ci netif_set_real_num_tx_queues(dev, pi->nqsets); 14378c2ecf20Sopenharmony_ci err = netif_set_real_num_rx_queues(dev, pi->nqsets); 14388c2ecf20Sopenharmony_ci if (err) 14398c2ecf20Sopenharmony_ci return err; 14408c2ecf20Sopenharmony_ci link_start(dev); 14418c2ecf20Sopenharmony_ci t3_port_intr_enable(adapter, pi->port_id); 14428c2ecf20Sopenharmony_ci netif_tx_start_all_queues(dev); 14438c2ecf20Sopenharmony_ci if (!other_ports) 14448c2ecf20Sopenharmony_ci schedule_chk_task(adapter); 14458c2ecf20Sopenharmony_ci 14468c2ecf20Sopenharmony_ci cxgb3_event_notify(&adapter->tdev, OFFLOAD_PORT_UP, pi->port_id); 14478c2ecf20Sopenharmony_ci return 0; 14488c2ecf20Sopenharmony_ci} 14498c2ecf20Sopenharmony_ci 14508c2ecf20Sopenharmony_cistatic int __cxgb_close(struct net_device *dev, int on_wq) 14518c2ecf20Sopenharmony_ci{ 14528c2ecf20Sopenharmony_ci struct port_info *pi = netdev_priv(dev); 14538c2ecf20Sopenharmony_ci struct adapter *adapter = pi->adapter; 14548c2ecf20Sopenharmony_ci 14558c2ecf20Sopenharmony_ci 14568c2ecf20Sopenharmony_ci if (!adapter->open_device_map) 14578c2ecf20Sopenharmony_ci return 0; 14588c2ecf20Sopenharmony_ci 14598c2ecf20Sopenharmony_ci /* Stop link fault interrupts */ 14608c2ecf20Sopenharmony_ci t3_xgm_intr_disable(adapter, pi->port_id); 14618c2ecf20Sopenharmony_ci t3_read_reg(adapter, A_XGM_INT_STATUS + pi->mac.offset); 14628c2ecf20Sopenharmony_ci 14638c2ecf20Sopenharmony_ci t3_port_intr_disable(adapter, pi->port_id); 14648c2ecf20Sopenharmony_ci netif_tx_stop_all_queues(dev); 14658c2ecf20Sopenharmony_ci pi->phy.ops->power_down(&pi->phy, 1); 14668c2ecf20Sopenharmony_ci netif_carrier_off(dev); 14678c2ecf20Sopenharmony_ci t3_mac_disable(&pi->mac, MAC_DIRECTION_TX | MAC_DIRECTION_RX); 14688c2ecf20Sopenharmony_ci 14698c2ecf20Sopenharmony_ci spin_lock_irq(&adapter->work_lock); /* sync with update task */ 14708c2ecf20Sopenharmony_ci clear_bit(pi->port_id, &adapter->open_device_map); 14718c2ecf20Sopenharmony_ci spin_unlock_irq(&adapter->work_lock); 14728c2ecf20Sopenharmony_ci 14738c2ecf20Sopenharmony_ci if (!(adapter->open_device_map & PORT_MASK)) 14748c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&adapter->adap_check_task); 14758c2ecf20Sopenharmony_ci 14768c2ecf20Sopenharmony_ci if (!adapter->open_device_map) 14778c2ecf20Sopenharmony_ci cxgb_down(adapter, on_wq); 14788c2ecf20Sopenharmony_ci 14798c2ecf20Sopenharmony_ci cxgb3_event_notify(&adapter->tdev, OFFLOAD_PORT_DOWN, pi->port_id); 14808c2ecf20Sopenharmony_ci return 0; 14818c2ecf20Sopenharmony_ci} 14828c2ecf20Sopenharmony_ci 14838c2ecf20Sopenharmony_cistatic int cxgb_close(struct net_device *dev) 14848c2ecf20Sopenharmony_ci{ 14858c2ecf20Sopenharmony_ci return __cxgb_close(dev, 0); 14868c2ecf20Sopenharmony_ci} 14878c2ecf20Sopenharmony_ci 14888c2ecf20Sopenharmony_cistatic struct net_device_stats *cxgb_get_stats(struct net_device *dev) 14898c2ecf20Sopenharmony_ci{ 14908c2ecf20Sopenharmony_ci struct port_info *pi = netdev_priv(dev); 14918c2ecf20Sopenharmony_ci struct adapter *adapter = pi->adapter; 14928c2ecf20Sopenharmony_ci struct net_device_stats *ns = &dev->stats; 14938c2ecf20Sopenharmony_ci const struct mac_stats *pstats; 14948c2ecf20Sopenharmony_ci 14958c2ecf20Sopenharmony_ci spin_lock(&adapter->stats_lock); 14968c2ecf20Sopenharmony_ci pstats = t3_mac_update_stats(&pi->mac); 14978c2ecf20Sopenharmony_ci spin_unlock(&adapter->stats_lock); 14988c2ecf20Sopenharmony_ci 14998c2ecf20Sopenharmony_ci ns->tx_bytes = pstats->tx_octets; 15008c2ecf20Sopenharmony_ci ns->tx_packets = pstats->tx_frames; 15018c2ecf20Sopenharmony_ci ns->rx_bytes = pstats->rx_octets; 15028c2ecf20Sopenharmony_ci ns->rx_packets = pstats->rx_frames; 15038c2ecf20Sopenharmony_ci ns->multicast = pstats->rx_mcast_frames; 15048c2ecf20Sopenharmony_ci 15058c2ecf20Sopenharmony_ci ns->tx_errors = pstats->tx_underrun; 15068c2ecf20Sopenharmony_ci ns->rx_errors = pstats->rx_symbol_errs + pstats->rx_fcs_errs + 15078c2ecf20Sopenharmony_ci pstats->rx_too_long + pstats->rx_jabber + pstats->rx_short + 15088c2ecf20Sopenharmony_ci pstats->rx_fifo_ovfl; 15098c2ecf20Sopenharmony_ci 15108c2ecf20Sopenharmony_ci /* detailed rx_errors */ 15118c2ecf20Sopenharmony_ci ns->rx_length_errors = pstats->rx_jabber + pstats->rx_too_long; 15128c2ecf20Sopenharmony_ci ns->rx_over_errors = 0; 15138c2ecf20Sopenharmony_ci ns->rx_crc_errors = pstats->rx_fcs_errs; 15148c2ecf20Sopenharmony_ci ns->rx_frame_errors = pstats->rx_symbol_errs; 15158c2ecf20Sopenharmony_ci ns->rx_fifo_errors = pstats->rx_fifo_ovfl; 15168c2ecf20Sopenharmony_ci ns->rx_missed_errors = pstats->rx_cong_drops; 15178c2ecf20Sopenharmony_ci 15188c2ecf20Sopenharmony_ci /* detailed tx_errors */ 15198c2ecf20Sopenharmony_ci ns->tx_aborted_errors = 0; 15208c2ecf20Sopenharmony_ci ns->tx_carrier_errors = 0; 15218c2ecf20Sopenharmony_ci ns->tx_fifo_errors = pstats->tx_underrun; 15228c2ecf20Sopenharmony_ci ns->tx_heartbeat_errors = 0; 15238c2ecf20Sopenharmony_ci ns->tx_window_errors = 0; 15248c2ecf20Sopenharmony_ci return ns; 15258c2ecf20Sopenharmony_ci} 15268c2ecf20Sopenharmony_ci 15278c2ecf20Sopenharmony_cistatic u32 get_msglevel(struct net_device *dev) 15288c2ecf20Sopenharmony_ci{ 15298c2ecf20Sopenharmony_ci struct port_info *pi = netdev_priv(dev); 15308c2ecf20Sopenharmony_ci struct adapter *adapter = pi->adapter; 15318c2ecf20Sopenharmony_ci 15328c2ecf20Sopenharmony_ci return adapter->msg_enable; 15338c2ecf20Sopenharmony_ci} 15348c2ecf20Sopenharmony_ci 15358c2ecf20Sopenharmony_cistatic void set_msglevel(struct net_device *dev, u32 val) 15368c2ecf20Sopenharmony_ci{ 15378c2ecf20Sopenharmony_ci struct port_info *pi = netdev_priv(dev); 15388c2ecf20Sopenharmony_ci struct adapter *adapter = pi->adapter; 15398c2ecf20Sopenharmony_ci 15408c2ecf20Sopenharmony_ci adapter->msg_enable = val; 15418c2ecf20Sopenharmony_ci} 15428c2ecf20Sopenharmony_ci 15438c2ecf20Sopenharmony_cistatic const char stats_strings[][ETH_GSTRING_LEN] = { 15448c2ecf20Sopenharmony_ci "TxOctetsOK ", 15458c2ecf20Sopenharmony_ci "TxFramesOK ", 15468c2ecf20Sopenharmony_ci "TxMulticastFramesOK", 15478c2ecf20Sopenharmony_ci "TxBroadcastFramesOK", 15488c2ecf20Sopenharmony_ci "TxPauseFrames ", 15498c2ecf20Sopenharmony_ci "TxUnderrun ", 15508c2ecf20Sopenharmony_ci "TxExtUnderrun ", 15518c2ecf20Sopenharmony_ci 15528c2ecf20Sopenharmony_ci "TxFrames64 ", 15538c2ecf20Sopenharmony_ci "TxFrames65To127 ", 15548c2ecf20Sopenharmony_ci "TxFrames128To255 ", 15558c2ecf20Sopenharmony_ci "TxFrames256To511 ", 15568c2ecf20Sopenharmony_ci "TxFrames512To1023 ", 15578c2ecf20Sopenharmony_ci "TxFrames1024To1518 ", 15588c2ecf20Sopenharmony_ci "TxFrames1519ToMax ", 15598c2ecf20Sopenharmony_ci 15608c2ecf20Sopenharmony_ci "RxOctetsOK ", 15618c2ecf20Sopenharmony_ci "RxFramesOK ", 15628c2ecf20Sopenharmony_ci "RxMulticastFramesOK", 15638c2ecf20Sopenharmony_ci "RxBroadcastFramesOK", 15648c2ecf20Sopenharmony_ci "RxPauseFrames ", 15658c2ecf20Sopenharmony_ci "RxFCSErrors ", 15668c2ecf20Sopenharmony_ci "RxSymbolErrors ", 15678c2ecf20Sopenharmony_ci "RxShortErrors ", 15688c2ecf20Sopenharmony_ci "RxJabberErrors ", 15698c2ecf20Sopenharmony_ci "RxLengthErrors ", 15708c2ecf20Sopenharmony_ci "RxFIFOoverflow ", 15718c2ecf20Sopenharmony_ci 15728c2ecf20Sopenharmony_ci "RxFrames64 ", 15738c2ecf20Sopenharmony_ci "RxFrames65To127 ", 15748c2ecf20Sopenharmony_ci "RxFrames128To255 ", 15758c2ecf20Sopenharmony_ci "RxFrames256To511 ", 15768c2ecf20Sopenharmony_ci "RxFrames512To1023 ", 15778c2ecf20Sopenharmony_ci "RxFrames1024To1518 ", 15788c2ecf20Sopenharmony_ci "RxFrames1519ToMax ", 15798c2ecf20Sopenharmony_ci 15808c2ecf20Sopenharmony_ci "PhyFIFOErrors ", 15818c2ecf20Sopenharmony_ci "TSO ", 15828c2ecf20Sopenharmony_ci "VLANextractions ", 15838c2ecf20Sopenharmony_ci "VLANinsertions ", 15848c2ecf20Sopenharmony_ci "TxCsumOffload ", 15858c2ecf20Sopenharmony_ci "RxCsumGood ", 15868c2ecf20Sopenharmony_ci "LroAggregated ", 15878c2ecf20Sopenharmony_ci "LroFlushed ", 15888c2ecf20Sopenharmony_ci "LroNoDesc ", 15898c2ecf20Sopenharmony_ci "RxDrops ", 15908c2ecf20Sopenharmony_ci 15918c2ecf20Sopenharmony_ci "CheckTXEnToggled ", 15928c2ecf20Sopenharmony_ci "CheckResets ", 15938c2ecf20Sopenharmony_ci 15948c2ecf20Sopenharmony_ci "LinkFaults ", 15958c2ecf20Sopenharmony_ci}; 15968c2ecf20Sopenharmony_ci 15978c2ecf20Sopenharmony_cistatic int get_sset_count(struct net_device *dev, int sset) 15988c2ecf20Sopenharmony_ci{ 15998c2ecf20Sopenharmony_ci switch (sset) { 16008c2ecf20Sopenharmony_ci case ETH_SS_STATS: 16018c2ecf20Sopenharmony_ci return ARRAY_SIZE(stats_strings); 16028c2ecf20Sopenharmony_ci default: 16038c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 16048c2ecf20Sopenharmony_ci } 16058c2ecf20Sopenharmony_ci} 16068c2ecf20Sopenharmony_ci 16078c2ecf20Sopenharmony_ci#define T3_REGMAP_SIZE (3 * 1024) 16088c2ecf20Sopenharmony_ci 16098c2ecf20Sopenharmony_cistatic int get_regs_len(struct net_device *dev) 16108c2ecf20Sopenharmony_ci{ 16118c2ecf20Sopenharmony_ci return T3_REGMAP_SIZE; 16128c2ecf20Sopenharmony_ci} 16138c2ecf20Sopenharmony_ci 16148c2ecf20Sopenharmony_cistatic int get_eeprom_len(struct net_device *dev) 16158c2ecf20Sopenharmony_ci{ 16168c2ecf20Sopenharmony_ci return EEPROMSIZE; 16178c2ecf20Sopenharmony_ci} 16188c2ecf20Sopenharmony_ci 16198c2ecf20Sopenharmony_cistatic void get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) 16208c2ecf20Sopenharmony_ci{ 16218c2ecf20Sopenharmony_ci struct port_info *pi = netdev_priv(dev); 16228c2ecf20Sopenharmony_ci struct adapter *adapter = pi->adapter; 16238c2ecf20Sopenharmony_ci u32 fw_vers = 0; 16248c2ecf20Sopenharmony_ci u32 tp_vers = 0; 16258c2ecf20Sopenharmony_ci 16268c2ecf20Sopenharmony_ci spin_lock(&adapter->stats_lock); 16278c2ecf20Sopenharmony_ci t3_get_fw_version(adapter, &fw_vers); 16288c2ecf20Sopenharmony_ci t3_get_tp_version(adapter, &tp_vers); 16298c2ecf20Sopenharmony_ci spin_unlock(&adapter->stats_lock); 16308c2ecf20Sopenharmony_ci 16318c2ecf20Sopenharmony_ci strlcpy(info->driver, DRV_NAME, sizeof(info->driver)); 16328c2ecf20Sopenharmony_ci strlcpy(info->bus_info, pci_name(adapter->pdev), 16338c2ecf20Sopenharmony_ci sizeof(info->bus_info)); 16348c2ecf20Sopenharmony_ci if (fw_vers) 16358c2ecf20Sopenharmony_ci snprintf(info->fw_version, sizeof(info->fw_version), 16368c2ecf20Sopenharmony_ci "%s %u.%u.%u TP %u.%u.%u", 16378c2ecf20Sopenharmony_ci G_FW_VERSION_TYPE(fw_vers) ? "T" : "N", 16388c2ecf20Sopenharmony_ci G_FW_VERSION_MAJOR(fw_vers), 16398c2ecf20Sopenharmony_ci G_FW_VERSION_MINOR(fw_vers), 16408c2ecf20Sopenharmony_ci G_FW_VERSION_MICRO(fw_vers), 16418c2ecf20Sopenharmony_ci G_TP_VERSION_MAJOR(tp_vers), 16428c2ecf20Sopenharmony_ci G_TP_VERSION_MINOR(tp_vers), 16438c2ecf20Sopenharmony_ci G_TP_VERSION_MICRO(tp_vers)); 16448c2ecf20Sopenharmony_ci} 16458c2ecf20Sopenharmony_ci 16468c2ecf20Sopenharmony_cistatic void get_strings(struct net_device *dev, u32 stringset, u8 * data) 16478c2ecf20Sopenharmony_ci{ 16488c2ecf20Sopenharmony_ci if (stringset == ETH_SS_STATS) 16498c2ecf20Sopenharmony_ci memcpy(data, stats_strings, sizeof(stats_strings)); 16508c2ecf20Sopenharmony_ci} 16518c2ecf20Sopenharmony_ci 16528c2ecf20Sopenharmony_cistatic unsigned long collect_sge_port_stats(struct adapter *adapter, 16538c2ecf20Sopenharmony_ci struct port_info *p, int idx) 16548c2ecf20Sopenharmony_ci{ 16558c2ecf20Sopenharmony_ci int i; 16568c2ecf20Sopenharmony_ci unsigned long tot = 0; 16578c2ecf20Sopenharmony_ci 16588c2ecf20Sopenharmony_ci for (i = p->first_qset; i < p->first_qset + p->nqsets; ++i) 16598c2ecf20Sopenharmony_ci tot += adapter->sge.qs[i].port_stats[idx]; 16608c2ecf20Sopenharmony_ci return tot; 16618c2ecf20Sopenharmony_ci} 16628c2ecf20Sopenharmony_ci 16638c2ecf20Sopenharmony_cistatic void get_stats(struct net_device *dev, struct ethtool_stats *stats, 16648c2ecf20Sopenharmony_ci u64 *data) 16658c2ecf20Sopenharmony_ci{ 16668c2ecf20Sopenharmony_ci struct port_info *pi = netdev_priv(dev); 16678c2ecf20Sopenharmony_ci struct adapter *adapter = pi->adapter; 16688c2ecf20Sopenharmony_ci const struct mac_stats *s; 16698c2ecf20Sopenharmony_ci 16708c2ecf20Sopenharmony_ci spin_lock(&adapter->stats_lock); 16718c2ecf20Sopenharmony_ci s = t3_mac_update_stats(&pi->mac); 16728c2ecf20Sopenharmony_ci spin_unlock(&adapter->stats_lock); 16738c2ecf20Sopenharmony_ci 16748c2ecf20Sopenharmony_ci *data++ = s->tx_octets; 16758c2ecf20Sopenharmony_ci *data++ = s->tx_frames; 16768c2ecf20Sopenharmony_ci *data++ = s->tx_mcast_frames; 16778c2ecf20Sopenharmony_ci *data++ = s->tx_bcast_frames; 16788c2ecf20Sopenharmony_ci *data++ = s->tx_pause; 16798c2ecf20Sopenharmony_ci *data++ = s->tx_underrun; 16808c2ecf20Sopenharmony_ci *data++ = s->tx_fifo_urun; 16818c2ecf20Sopenharmony_ci 16828c2ecf20Sopenharmony_ci *data++ = s->tx_frames_64; 16838c2ecf20Sopenharmony_ci *data++ = s->tx_frames_65_127; 16848c2ecf20Sopenharmony_ci *data++ = s->tx_frames_128_255; 16858c2ecf20Sopenharmony_ci *data++ = s->tx_frames_256_511; 16868c2ecf20Sopenharmony_ci *data++ = s->tx_frames_512_1023; 16878c2ecf20Sopenharmony_ci *data++ = s->tx_frames_1024_1518; 16888c2ecf20Sopenharmony_ci *data++ = s->tx_frames_1519_max; 16898c2ecf20Sopenharmony_ci 16908c2ecf20Sopenharmony_ci *data++ = s->rx_octets; 16918c2ecf20Sopenharmony_ci *data++ = s->rx_frames; 16928c2ecf20Sopenharmony_ci *data++ = s->rx_mcast_frames; 16938c2ecf20Sopenharmony_ci *data++ = s->rx_bcast_frames; 16948c2ecf20Sopenharmony_ci *data++ = s->rx_pause; 16958c2ecf20Sopenharmony_ci *data++ = s->rx_fcs_errs; 16968c2ecf20Sopenharmony_ci *data++ = s->rx_symbol_errs; 16978c2ecf20Sopenharmony_ci *data++ = s->rx_short; 16988c2ecf20Sopenharmony_ci *data++ = s->rx_jabber; 16998c2ecf20Sopenharmony_ci *data++ = s->rx_too_long; 17008c2ecf20Sopenharmony_ci *data++ = s->rx_fifo_ovfl; 17018c2ecf20Sopenharmony_ci 17028c2ecf20Sopenharmony_ci *data++ = s->rx_frames_64; 17038c2ecf20Sopenharmony_ci *data++ = s->rx_frames_65_127; 17048c2ecf20Sopenharmony_ci *data++ = s->rx_frames_128_255; 17058c2ecf20Sopenharmony_ci *data++ = s->rx_frames_256_511; 17068c2ecf20Sopenharmony_ci *data++ = s->rx_frames_512_1023; 17078c2ecf20Sopenharmony_ci *data++ = s->rx_frames_1024_1518; 17088c2ecf20Sopenharmony_ci *data++ = s->rx_frames_1519_max; 17098c2ecf20Sopenharmony_ci 17108c2ecf20Sopenharmony_ci *data++ = pi->phy.fifo_errors; 17118c2ecf20Sopenharmony_ci 17128c2ecf20Sopenharmony_ci *data++ = collect_sge_port_stats(adapter, pi, SGE_PSTAT_TSO); 17138c2ecf20Sopenharmony_ci *data++ = collect_sge_port_stats(adapter, pi, SGE_PSTAT_VLANEX); 17148c2ecf20Sopenharmony_ci *data++ = collect_sge_port_stats(adapter, pi, SGE_PSTAT_VLANINS); 17158c2ecf20Sopenharmony_ci *data++ = collect_sge_port_stats(adapter, pi, SGE_PSTAT_TX_CSUM); 17168c2ecf20Sopenharmony_ci *data++ = collect_sge_port_stats(adapter, pi, SGE_PSTAT_RX_CSUM_GOOD); 17178c2ecf20Sopenharmony_ci *data++ = 0; 17188c2ecf20Sopenharmony_ci *data++ = 0; 17198c2ecf20Sopenharmony_ci *data++ = 0; 17208c2ecf20Sopenharmony_ci *data++ = s->rx_cong_drops; 17218c2ecf20Sopenharmony_ci 17228c2ecf20Sopenharmony_ci *data++ = s->num_toggled; 17238c2ecf20Sopenharmony_ci *data++ = s->num_resets; 17248c2ecf20Sopenharmony_ci 17258c2ecf20Sopenharmony_ci *data++ = s->link_faults; 17268c2ecf20Sopenharmony_ci} 17278c2ecf20Sopenharmony_ci 17288c2ecf20Sopenharmony_cistatic inline void reg_block_dump(struct adapter *ap, void *buf, 17298c2ecf20Sopenharmony_ci unsigned int start, unsigned int end) 17308c2ecf20Sopenharmony_ci{ 17318c2ecf20Sopenharmony_ci u32 *p = buf + start; 17328c2ecf20Sopenharmony_ci 17338c2ecf20Sopenharmony_ci for (; start <= end; start += sizeof(u32)) 17348c2ecf20Sopenharmony_ci *p++ = t3_read_reg(ap, start); 17358c2ecf20Sopenharmony_ci} 17368c2ecf20Sopenharmony_ci 17378c2ecf20Sopenharmony_cistatic void get_regs(struct net_device *dev, struct ethtool_regs *regs, 17388c2ecf20Sopenharmony_ci void *buf) 17398c2ecf20Sopenharmony_ci{ 17408c2ecf20Sopenharmony_ci struct port_info *pi = netdev_priv(dev); 17418c2ecf20Sopenharmony_ci struct adapter *ap = pi->adapter; 17428c2ecf20Sopenharmony_ci 17438c2ecf20Sopenharmony_ci /* 17448c2ecf20Sopenharmony_ci * Version scheme: 17458c2ecf20Sopenharmony_ci * bits 0..9: chip version 17468c2ecf20Sopenharmony_ci * bits 10..15: chip revision 17478c2ecf20Sopenharmony_ci * bit 31: set for PCIe cards 17488c2ecf20Sopenharmony_ci */ 17498c2ecf20Sopenharmony_ci regs->version = 3 | (ap->params.rev << 10) | (is_pcie(ap) << 31); 17508c2ecf20Sopenharmony_ci 17518c2ecf20Sopenharmony_ci /* 17528c2ecf20Sopenharmony_ci * We skip the MAC statistics registers because they are clear-on-read. 17538c2ecf20Sopenharmony_ci * Also reading multi-register stats would need to synchronize with the 17548c2ecf20Sopenharmony_ci * periodic mac stats accumulation. Hard to justify the complexity. 17558c2ecf20Sopenharmony_ci */ 17568c2ecf20Sopenharmony_ci memset(buf, 0, T3_REGMAP_SIZE); 17578c2ecf20Sopenharmony_ci reg_block_dump(ap, buf, 0, A_SG_RSPQ_CREDIT_RETURN); 17588c2ecf20Sopenharmony_ci reg_block_dump(ap, buf, A_SG_HI_DRB_HI_THRSH, A_ULPRX_PBL_ULIMIT); 17598c2ecf20Sopenharmony_ci reg_block_dump(ap, buf, A_ULPTX_CONFIG, A_MPS_INT_CAUSE); 17608c2ecf20Sopenharmony_ci reg_block_dump(ap, buf, A_CPL_SWITCH_CNTRL, A_CPL_MAP_TBL_DATA); 17618c2ecf20Sopenharmony_ci reg_block_dump(ap, buf, A_SMB_GLOBAL_TIME_CFG, A_XGM_SERDES_STAT3); 17628c2ecf20Sopenharmony_ci reg_block_dump(ap, buf, A_XGM_SERDES_STATUS0, 17638c2ecf20Sopenharmony_ci XGM_REG(A_XGM_SERDES_STAT3, 1)); 17648c2ecf20Sopenharmony_ci reg_block_dump(ap, buf, XGM_REG(A_XGM_SERDES_STATUS0, 1), 17658c2ecf20Sopenharmony_ci XGM_REG(A_XGM_RX_SPI4_SOP_EOP_CNT, 1)); 17668c2ecf20Sopenharmony_ci} 17678c2ecf20Sopenharmony_ci 17688c2ecf20Sopenharmony_cistatic int restart_autoneg(struct net_device *dev) 17698c2ecf20Sopenharmony_ci{ 17708c2ecf20Sopenharmony_ci struct port_info *p = netdev_priv(dev); 17718c2ecf20Sopenharmony_ci 17728c2ecf20Sopenharmony_ci if (!netif_running(dev)) 17738c2ecf20Sopenharmony_ci return -EAGAIN; 17748c2ecf20Sopenharmony_ci if (p->link_config.autoneg != AUTONEG_ENABLE) 17758c2ecf20Sopenharmony_ci return -EINVAL; 17768c2ecf20Sopenharmony_ci p->phy.ops->autoneg_restart(&p->phy); 17778c2ecf20Sopenharmony_ci return 0; 17788c2ecf20Sopenharmony_ci} 17798c2ecf20Sopenharmony_ci 17808c2ecf20Sopenharmony_cistatic int set_phys_id(struct net_device *dev, 17818c2ecf20Sopenharmony_ci enum ethtool_phys_id_state state) 17828c2ecf20Sopenharmony_ci{ 17838c2ecf20Sopenharmony_ci struct port_info *pi = netdev_priv(dev); 17848c2ecf20Sopenharmony_ci struct adapter *adapter = pi->adapter; 17858c2ecf20Sopenharmony_ci 17868c2ecf20Sopenharmony_ci switch (state) { 17878c2ecf20Sopenharmony_ci case ETHTOOL_ID_ACTIVE: 17888c2ecf20Sopenharmony_ci return 1; /* cycle on/off once per second */ 17898c2ecf20Sopenharmony_ci 17908c2ecf20Sopenharmony_ci case ETHTOOL_ID_OFF: 17918c2ecf20Sopenharmony_ci t3_set_reg_field(adapter, A_T3DBG_GPIO_EN, F_GPIO0_OUT_VAL, 0); 17928c2ecf20Sopenharmony_ci break; 17938c2ecf20Sopenharmony_ci 17948c2ecf20Sopenharmony_ci case ETHTOOL_ID_ON: 17958c2ecf20Sopenharmony_ci case ETHTOOL_ID_INACTIVE: 17968c2ecf20Sopenharmony_ci t3_set_reg_field(adapter, A_T3DBG_GPIO_EN, F_GPIO0_OUT_VAL, 17978c2ecf20Sopenharmony_ci F_GPIO0_OUT_VAL); 17988c2ecf20Sopenharmony_ci } 17998c2ecf20Sopenharmony_ci 18008c2ecf20Sopenharmony_ci return 0; 18018c2ecf20Sopenharmony_ci} 18028c2ecf20Sopenharmony_ci 18038c2ecf20Sopenharmony_cistatic int get_link_ksettings(struct net_device *dev, 18048c2ecf20Sopenharmony_ci struct ethtool_link_ksettings *cmd) 18058c2ecf20Sopenharmony_ci{ 18068c2ecf20Sopenharmony_ci struct port_info *p = netdev_priv(dev); 18078c2ecf20Sopenharmony_ci u32 supported; 18088c2ecf20Sopenharmony_ci 18098c2ecf20Sopenharmony_ci ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported, 18108c2ecf20Sopenharmony_ci p->link_config.supported); 18118c2ecf20Sopenharmony_ci ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising, 18128c2ecf20Sopenharmony_ci p->link_config.advertising); 18138c2ecf20Sopenharmony_ci 18148c2ecf20Sopenharmony_ci if (netif_carrier_ok(dev)) { 18158c2ecf20Sopenharmony_ci cmd->base.speed = p->link_config.speed; 18168c2ecf20Sopenharmony_ci cmd->base.duplex = p->link_config.duplex; 18178c2ecf20Sopenharmony_ci } else { 18188c2ecf20Sopenharmony_ci cmd->base.speed = SPEED_UNKNOWN; 18198c2ecf20Sopenharmony_ci cmd->base.duplex = DUPLEX_UNKNOWN; 18208c2ecf20Sopenharmony_ci } 18218c2ecf20Sopenharmony_ci 18228c2ecf20Sopenharmony_ci ethtool_convert_link_mode_to_legacy_u32(&supported, 18238c2ecf20Sopenharmony_ci cmd->link_modes.supported); 18248c2ecf20Sopenharmony_ci 18258c2ecf20Sopenharmony_ci cmd->base.port = (supported & SUPPORTED_TP) ? PORT_TP : PORT_FIBRE; 18268c2ecf20Sopenharmony_ci cmd->base.phy_address = p->phy.mdio.prtad; 18278c2ecf20Sopenharmony_ci cmd->base.autoneg = p->link_config.autoneg; 18288c2ecf20Sopenharmony_ci return 0; 18298c2ecf20Sopenharmony_ci} 18308c2ecf20Sopenharmony_ci 18318c2ecf20Sopenharmony_cistatic int speed_duplex_to_caps(int speed, int duplex) 18328c2ecf20Sopenharmony_ci{ 18338c2ecf20Sopenharmony_ci int cap = 0; 18348c2ecf20Sopenharmony_ci 18358c2ecf20Sopenharmony_ci switch (speed) { 18368c2ecf20Sopenharmony_ci case SPEED_10: 18378c2ecf20Sopenharmony_ci if (duplex == DUPLEX_FULL) 18388c2ecf20Sopenharmony_ci cap = SUPPORTED_10baseT_Full; 18398c2ecf20Sopenharmony_ci else 18408c2ecf20Sopenharmony_ci cap = SUPPORTED_10baseT_Half; 18418c2ecf20Sopenharmony_ci break; 18428c2ecf20Sopenharmony_ci case SPEED_100: 18438c2ecf20Sopenharmony_ci if (duplex == DUPLEX_FULL) 18448c2ecf20Sopenharmony_ci cap = SUPPORTED_100baseT_Full; 18458c2ecf20Sopenharmony_ci else 18468c2ecf20Sopenharmony_ci cap = SUPPORTED_100baseT_Half; 18478c2ecf20Sopenharmony_ci break; 18488c2ecf20Sopenharmony_ci case SPEED_1000: 18498c2ecf20Sopenharmony_ci if (duplex == DUPLEX_FULL) 18508c2ecf20Sopenharmony_ci cap = SUPPORTED_1000baseT_Full; 18518c2ecf20Sopenharmony_ci else 18528c2ecf20Sopenharmony_ci cap = SUPPORTED_1000baseT_Half; 18538c2ecf20Sopenharmony_ci break; 18548c2ecf20Sopenharmony_ci case SPEED_10000: 18558c2ecf20Sopenharmony_ci if (duplex == DUPLEX_FULL) 18568c2ecf20Sopenharmony_ci cap = SUPPORTED_10000baseT_Full; 18578c2ecf20Sopenharmony_ci } 18588c2ecf20Sopenharmony_ci return cap; 18598c2ecf20Sopenharmony_ci} 18608c2ecf20Sopenharmony_ci 18618c2ecf20Sopenharmony_ci#define ADVERTISED_MASK (ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full | \ 18628c2ecf20Sopenharmony_ci ADVERTISED_100baseT_Half | ADVERTISED_100baseT_Full | \ 18638c2ecf20Sopenharmony_ci ADVERTISED_1000baseT_Half | ADVERTISED_1000baseT_Full | \ 18648c2ecf20Sopenharmony_ci ADVERTISED_10000baseT_Full) 18658c2ecf20Sopenharmony_ci 18668c2ecf20Sopenharmony_cistatic int set_link_ksettings(struct net_device *dev, 18678c2ecf20Sopenharmony_ci const struct ethtool_link_ksettings *cmd) 18688c2ecf20Sopenharmony_ci{ 18698c2ecf20Sopenharmony_ci struct port_info *p = netdev_priv(dev); 18708c2ecf20Sopenharmony_ci struct link_config *lc = &p->link_config; 18718c2ecf20Sopenharmony_ci u32 advertising; 18728c2ecf20Sopenharmony_ci 18738c2ecf20Sopenharmony_ci ethtool_convert_link_mode_to_legacy_u32(&advertising, 18748c2ecf20Sopenharmony_ci cmd->link_modes.advertising); 18758c2ecf20Sopenharmony_ci 18768c2ecf20Sopenharmony_ci if (!(lc->supported & SUPPORTED_Autoneg)) { 18778c2ecf20Sopenharmony_ci /* 18788c2ecf20Sopenharmony_ci * PHY offers a single speed/duplex. See if that's what's 18798c2ecf20Sopenharmony_ci * being requested. 18808c2ecf20Sopenharmony_ci */ 18818c2ecf20Sopenharmony_ci if (cmd->base.autoneg == AUTONEG_DISABLE) { 18828c2ecf20Sopenharmony_ci u32 speed = cmd->base.speed; 18838c2ecf20Sopenharmony_ci int cap = speed_duplex_to_caps(speed, cmd->base.duplex); 18848c2ecf20Sopenharmony_ci if (lc->supported & cap) 18858c2ecf20Sopenharmony_ci return 0; 18868c2ecf20Sopenharmony_ci } 18878c2ecf20Sopenharmony_ci return -EINVAL; 18888c2ecf20Sopenharmony_ci } 18898c2ecf20Sopenharmony_ci 18908c2ecf20Sopenharmony_ci if (cmd->base.autoneg == AUTONEG_DISABLE) { 18918c2ecf20Sopenharmony_ci u32 speed = cmd->base.speed; 18928c2ecf20Sopenharmony_ci int cap = speed_duplex_to_caps(speed, cmd->base.duplex); 18938c2ecf20Sopenharmony_ci 18948c2ecf20Sopenharmony_ci if (!(lc->supported & cap) || (speed == SPEED_1000)) 18958c2ecf20Sopenharmony_ci return -EINVAL; 18968c2ecf20Sopenharmony_ci lc->requested_speed = speed; 18978c2ecf20Sopenharmony_ci lc->requested_duplex = cmd->base.duplex; 18988c2ecf20Sopenharmony_ci lc->advertising = 0; 18998c2ecf20Sopenharmony_ci } else { 19008c2ecf20Sopenharmony_ci advertising &= ADVERTISED_MASK; 19018c2ecf20Sopenharmony_ci advertising &= lc->supported; 19028c2ecf20Sopenharmony_ci if (!advertising) 19038c2ecf20Sopenharmony_ci return -EINVAL; 19048c2ecf20Sopenharmony_ci lc->requested_speed = SPEED_INVALID; 19058c2ecf20Sopenharmony_ci lc->requested_duplex = DUPLEX_INVALID; 19068c2ecf20Sopenharmony_ci lc->advertising = advertising | ADVERTISED_Autoneg; 19078c2ecf20Sopenharmony_ci } 19088c2ecf20Sopenharmony_ci lc->autoneg = cmd->base.autoneg; 19098c2ecf20Sopenharmony_ci if (netif_running(dev)) 19108c2ecf20Sopenharmony_ci t3_link_start(&p->phy, &p->mac, lc); 19118c2ecf20Sopenharmony_ci return 0; 19128c2ecf20Sopenharmony_ci} 19138c2ecf20Sopenharmony_ci 19148c2ecf20Sopenharmony_cistatic void get_pauseparam(struct net_device *dev, 19158c2ecf20Sopenharmony_ci struct ethtool_pauseparam *epause) 19168c2ecf20Sopenharmony_ci{ 19178c2ecf20Sopenharmony_ci struct port_info *p = netdev_priv(dev); 19188c2ecf20Sopenharmony_ci 19198c2ecf20Sopenharmony_ci epause->autoneg = (p->link_config.requested_fc & PAUSE_AUTONEG) != 0; 19208c2ecf20Sopenharmony_ci epause->rx_pause = (p->link_config.fc & PAUSE_RX) != 0; 19218c2ecf20Sopenharmony_ci epause->tx_pause = (p->link_config.fc & PAUSE_TX) != 0; 19228c2ecf20Sopenharmony_ci} 19238c2ecf20Sopenharmony_ci 19248c2ecf20Sopenharmony_cistatic int set_pauseparam(struct net_device *dev, 19258c2ecf20Sopenharmony_ci struct ethtool_pauseparam *epause) 19268c2ecf20Sopenharmony_ci{ 19278c2ecf20Sopenharmony_ci struct port_info *p = netdev_priv(dev); 19288c2ecf20Sopenharmony_ci struct link_config *lc = &p->link_config; 19298c2ecf20Sopenharmony_ci 19308c2ecf20Sopenharmony_ci if (epause->autoneg == AUTONEG_DISABLE) 19318c2ecf20Sopenharmony_ci lc->requested_fc = 0; 19328c2ecf20Sopenharmony_ci else if (lc->supported & SUPPORTED_Autoneg) 19338c2ecf20Sopenharmony_ci lc->requested_fc = PAUSE_AUTONEG; 19348c2ecf20Sopenharmony_ci else 19358c2ecf20Sopenharmony_ci return -EINVAL; 19368c2ecf20Sopenharmony_ci 19378c2ecf20Sopenharmony_ci if (epause->rx_pause) 19388c2ecf20Sopenharmony_ci lc->requested_fc |= PAUSE_RX; 19398c2ecf20Sopenharmony_ci if (epause->tx_pause) 19408c2ecf20Sopenharmony_ci lc->requested_fc |= PAUSE_TX; 19418c2ecf20Sopenharmony_ci if (lc->autoneg == AUTONEG_ENABLE) { 19428c2ecf20Sopenharmony_ci if (netif_running(dev)) 19438c2ecf20Sopenharmony_ci t3_link_start(&p->phy, &p->mac, lc); 19448c2ecf20Sopenharmony_ci } else { 19458c2ecf20Sopenharmony_ci lc->fc = lc->requested_fc & (PAUSE_RX | PAUSE_TX); 19468c2ecf20Sopenharmony_ci if (netif_running(dev)) 19478c2ecf20Sopenharmony_ci t3_mac_set_speed_duplex_fc(&p->mac, -1, -1, lc->fc); 19488c2ecf20Sopenharmony_ci } 19498c2ecf20Sopenharmony_ci return 0; 19508c2ecf20Sopenharmony_ci} 19518c2ecf20Sopenharmony_ci 19528c2ecf20Sopenharmony_cistatic void get_sge_param(struct net_device *dev, struct ethtool_ringparam *e) 19538c2ecf20Sopenharmony_ci{ 19548c2ecf20Sopenharmony_ci struct port_info *pi = netdev_priv(dev); 19558c2ecf20Sopenharmony_ci struct adapter *adapter = pi->adapter; 19568c2ecf20Sopenharmony_ci const struct qset_params *q = &adapter->params.sge.qset[pi->first_qset]; 19578c2ecf20Sopenharmony_ci 19588c2ecf20Sopenharmony_ci e->rx_max_pending = MAX_RX_BUFFERS; 19598c2ecf20Sopenharmony_ci e->rx_jumbo_max_pending = MAX_RX_JUMBO_BUFFERS; 19608c2ecf20Sopenharmony_ci e->tx_max_pending = MAX_TXQ_ENTRIES; 19618c2ecf20Sopenharmony_ci 19628c2ecf20Sopenharmony_ci e->rx_pending = q->fl_size; 19638c2ecf20Sopenharmony_ci e->rx_mini_pending = q->rspq_size; 19648c2ecf20Sopenharmony_ci e->rx_jumbo_pending = q->jumbo_size; 19658c2ecf20Sopenharmony_ci e->tx_pending = q->txq_size[0]; 19668c2ecf20Sopenharmony_ci} 19678c2ecf20Sopenharmony_ci 19688c2ecf20Sopenharmony_cistatic int set_sge_param(struct net_device *dev, struct ethtool_ringparam *e) 19698c2ecf20Sopenharmony_ci{ 19708c2ecf20Sopenharmony_ci struct port_info *pi = netdev_priv(dev); 19718c2ecf20Sopenharmony_ci struct adapter *adapter = pi->adapter; 19728c2ecf20Sopenharmony_ci struct qset_params *q; 19738c2ecf20Sopenharmony_ci int i; 19748c2ecf20Sopenharmony_ci 19758c2ecf20Sopenharmony_ci if (e->rx_pending > MAX_RX_BUFFERS || 19768c2ecf20Sopenharmony_ci e->rx_jumbo_pending > MAX_RX_JUMBO_BUFFERS || 19778c2ecf20Sopenharmony_ci e->tx_pending > MAX_TXQ_ENTRIES || 19788c2ecf20Sopenharmony_ci e->rx_mini_pending > MAX_RSPQ_ENTRIES || 19798c2ecf20Sopenharmony_ci e->rx_mini_pending < MIN_RSPQ_ENTRIES || 19808c2ecf20Sopenharmony_ci e->rx_pending < MIN_FL_ENTRIES || 19818c2ecf20Sopenharmony_ci e->rx_jumbo_pending < MIN_FL_ENTRIES || 19828c2ecf20Sopenharmony_ci e->tx_pending < adapter->params.nports * MIN_TXQ_ENTRIES) 19838c2ecf20Sopenharmony_ci return -EINVAL; 19848c2ecf20Sopenharmony_ci 19858c2ecf20Sopenharmony_ci if (adapter->flags & FULL_INIT_DONE) 19868c2ecf20Sopenharmony_ci return -EBUSY; 19878c2ecf20Sopenharmony_ci 19888c2ecf20Sopenharmony_ci q = &adapter->params.sge.qset[pi->first_qset]; 19898c2ecf20Sopenharmony_ci for (i = 0; i < pi->nqsets; ++i, ++q) { 19908c2ecf20Sopenharmony_ci q->rspq_size = e->rx_mini_pending; 19918c2ecf20Sopenharmony_ci q->fl_size = e->rx_pending; 19928c2ecf20Sopenharmony_ci q->jumbo_size = e->rx_jumbo_pending; 19938c2ecf20Sopenharmony_ci q->txq_size[0] = e->tx_pending; 19948c2ecf20Sopenharmony_ci q->txq_size[1] = e->tx_pending; 19958c2ecf20Sopenharmony_ci q->txq_size[2] = e->tx_pending; 19968c2ecf20Sopenharmony_ci } 19978c2ecf20Sopenharmony_ci return 0; 19988c2ecf20Sopenharmony_ci} 19998c2ecf20Sopenharmony_ci 20008c2ecf20Sopenharmony_cistatic int set_coalesce(struct net_device *dev, struct ethtool_coalesce *c) 20018c2ecf20Sopenharmony_ci{ 20028c2ecf20Sopenharmony_ci struct port_info *pi = netdev_priv(dev); 20038c2ecf20Sopenharmony_ci struct adapter *adapter = pi->adapter; 20048c2ecf20Sopenharmony_ci struct qset_params *qsp; 20058c2ecf20Sopenharmony_ci struct sge_qset *qs; 20068c2ecf20Sopenharmony_ci int i; 20078c2ecf20Sopenharmony_ci 20088c2ecf20Sopenharmony_ci if (c->rx_coalesce_usecs * 10 > M_NEWTIMER) 20098c2ecf20Sopenharmony_ci return -EINVAL; 20108c2ecf20Sopenharmony_ci 20118c2ecf20Sopenharmony_ci for (i = 0; i < pi->nqsets; i++) { 20128c2ecf20Sopenharmony_ci qsp = &adapter->params.sge.qset[i]; 20138c2ecf20Sopenharmony_ci qs = &adapter->sge.qs[i]; 20148c2ecf20Sopenharmony_ci qsp->coalesce_usecs = c->rx_coalesce_usecs; 20158c2ecf20Sopenharmony_ci t3_update_qset_coalesce(qs, qsp); 20168c2ecf20Sopenharmony_ci } 20178c2ecf20Sopenharmony_ci 20188c2ecf20Sopenharmony_ci return 0; 20198c2ecf20Sopenharmony_ci} 20208c2ecf20Sopenharmony_ci 20218c2ecf20Sopenharmony_cistatic int get_coalesce(struct net_device *dev, struct ethtool_coalesce *c) 20228c2ecf20Sopenharmony_ci{ 20238c2ecf20Sopenharmony_ci struct port_info *pi = netdev_priv(dev); 20248c2ecf20Sopenharmony_ci struct adapter *adapter = pi->adapter; 20258c2ecf20Sopenharmony_ci struct qset_params *q = adapter->params.sge.qset; 20268c2ecf20Sopenharmony_ci 20278c2ecf20Sopenharmony_ci c->rx_coalesce_usecs = q->coalesce_usecs; 20288c2ecf20Sopenharmony_ci return 0; 20298c2ecf20Sopenharmony_ci} 20308c2ecf20Sopenharmony_ci 20318c2ecf20Sopenharmony_cistatic int get_eeprom(struct net_device *dev, struct ethtool_eeprom *e, 20328c2ecf20Sopenharmony_ci u8 * data) 20338c2ecf20Sopenharmony_ci{ 20348c2ecf20Sopenharmony_ci struct port_info *pi = netdev_priv(dev); 20358c2ecf20Sopenharmony_ci struct adapter *adapter = pi->adapter; 20368c2ecf20Sopenharmony_ci int i, err = 0; 20378c2ecf20Sopenharmony_ci 20388c2ecf20Sopenharmony_ci u8 *buf = kmalloc(EEPROMSIZE, GFP_KERNEL); 20398c2ecf20Sopenharmony_ci if (!buf) 20408c2ecf20Sopenharmony_ci return -ENOMEM; 20418c2ecf20Sopenharmony_ci 20428c2ecf20Sopenharmony_ci e->magic = EEPROM_MAGIC; 20438c2ecf20Sopenharmony_ci for (i = e->offset & ~3; !err && i < e->offset + e->len; i += 4) 20448c2ecf20Sopenharmony_ci err = t3_seeprom_read(adapter, i, (__le32 *) & buf[i]); 20458c2ecf20Sopenharmony_ci 20468c2ecf20Sopenharmony_ci if (!err) 20478c2ecf20Sopenharmony_ci memcpy(data, buf + e->offset, e->len); 20488c2ecf20Sopenharmony_ci kfree(buf); 20498c2ecf20Sopenharmony_ci return err; 20508c2ecf20Sopenharmony_ci} 20518c2ecf20Sopenharmony_ci 20528c2ecf20Sopenharmony_cistatic int set_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom, 20538c2ecf20Sopenharmony_ci u8 * data) 20548c2ecf20Sopenharmony_ci{ 20558c2ecf20Sopenharmony_ci struct port_info *pi = netdev_priv(dev); 20568c2ecf20Sopenharmony_ci struct adapter *adapter = pi->adapter; 20578c2ecf20Sopenharmony_ci u32 aligned_offset, aligned_len; 20588c2ecf20Sopenharmony_ci __le32 *p; 20598c2ecf20Sopenharmony_ci u8 *buf; 20608c2ecf20Sopenharmony_ci int err; 20618c2ecf20Sopenharmony_ci 20628c2ecf20Sopenharmony_ci if (eeprom->magic != EEPROM_MAGIC) 20638c2ecf20Sopenharmony_ci return -EINVAL; 20648c2ecf20Sopenharmony_ci 20658c2ecf20Sopenharmony_ci aligned_offset = eeprom->offset & ~3; 20668c2ecf20Sopenharmony_ci aligned_len = (eeprom->len + (eeprom->offset & 3) + 3) & ~3; 20678c2ecf20Sopenharmony_ci 20688c2ecf20Sopenharmony_ci if (aligned_offset != eeprom->offset || aligned_len != eeprom->len) { 20698c2ecf20Sopenharmony_ci buf = kmalloc(aligned_len, GFP_KERNEL); 20708c2ecf20Sopenharmony_ci if (!buf) 20718c2ecf20Sopenharmony_ci return -ENOMEM; 20728c2ecf20Sopenharmony_ci err = t3_seeprom_read(adapter, aligned_offset, (__le32 *) buf); 20738c2ecf20Sopenharmony_ci if (!err && aligned_len > 4) 20748c2ecf20Sopenharmony_ci err = t3_seeprom_read(adapter, 20758c2ecf20Sopenharmony_ci aligned_offset + aligned_len - 4, 20768c2ecf20Sopenharmony_ci (__le32 *) & buf[aligned_len - 4]); 20778c2ecf20Sopenharmony_ci if (err) 20788c2ecf20Sopenharmony_ci goto out; 20798c2ecf20Sopenharmony_ci memcpy(buf + (eeprom->offset & 3), data, eeprom->len); 20808c2ecf20Sopenharmony_ci } else 20818c2ecf20Sopenharmony_ci buf = data; 20828c2ecf20Sopenharmony_ci 20838c2ecf20Sopenharmony_ci err = t3_seeprom_wp(adapter, 0); 20848c2ecf20Sopenharmony_ci if (err) 20858c2ecf20Sopenharmony_ci goto out; 20868c2ecf20Sopenharmony_ci 20878c2ecf20Sopenharmony_ci for (p = (__le32 *) buf; !err && aligned_len; aligned_len -= 4, p++) { 20888c2ecf20Sopenharmony_ci err = t3_seeprom_write(adapter, aligned_offset, *p); 20898c2ecf20Sopenharmony_ci aligned_offset += 4; 20908c2ecf20Sopenharmony_ci } 20918c2ecf20Sopenharmony_ci 20928c2ecf20Sopenharmony_ci if (!err) 20938c2ecf20Sopenharmony_ci err = t3_seeprom_wp(adapter, 1); 20948c2ecf20Sopenharmony_ciout: 20958c2ecf20Sopenharmony_ci if (buf != data) 20968c2ecf20Sopenharmony_ci kfree(buf); 20978c2ecf20Sopenharmony_ci return err; 20988c2ecf20Sopenharmony_ci} 20998c2ecf20Sopenharmony_ci 21008c2ecf20Sopenharmony_cistatic void get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) 21018c2ecf20Sopenharmony_ci{ 21028c2ecf20Sopenharmony_ci wol->supported = 0; 21038c2ecf20Sopenharmony_ci wol->wolopts = 0; 21048c2ecf20Sopenharmony_ci memset(&wol->sopass, 0, sizeof(wol->sopass)); 21058c2ecf20Sopenharmony_ci} 21068c2ecf20Sopenharmony_ci 21078c2ecf20Sopenharmony_cistatic const struct ethtool_ops cxgb_ethtool_ops = { 21088c2ecf20Sopenharmony_ci .supported_coalesce_params = ETHTOOL_COALESCE_RX_USECS, 21098c2ecf20Sopenharmony_ci .get_drvinfo = get_drvinfo, 21108c2ecf20Sopenharmony_ci .get_msglevel = get_msglevel, 21118c2ecf20Sopenharmony_ci .set_msglevel = set_msglevel, 21128c2ecf20Sopenharmony_ci .get_ringparam = get_sge_param, 21138c2ecf20Sopenharmony_ci .set_ringparam = set_sge_param, 21148c2ecf20Sopenharmony_ci .get_coalesce = get_coalesce, 21158c2ecf20Sopenharmony_ci .set_coalesce = set_coalesce, 21168c2ecf20Sopenharmony_ci .get_eeprom_len = get_eeprom_len, 21178c2ecf20Sopenharmony_ci .get_eeprom = get_eeprom, 21188c2ecf20Sopenharmony_ci .set_eeprom = set_eeprom, 21198c2ecf20Sopenharmony_ci .get_pauseparam = get_pauseparam, 21208c2ecf20Sopenharmony_ci .set_pauseparam = set_pauseparam, 21218c2ecf20Sopenharmony_ci .get_link = ethtool_op_get_link, 21228c2ecf20Sopenharmony_ci .get_strings = get_strings, 21238c2ecf20Sopenharmony_ci .set_phys_id = set_phys_id, 21248c2ecf20Sopenharmony_ci .nway_reset = restart_autoneg, 21258c2ecf20Sopenharmony_ci .get_sset_count = get_sset_count, 21268c2ecf20Sopenharmony_ci .get_ethtool_stats = get_stats, 21278c2ecf20Sopenharmony_ci .get_regs_len = get_regs_len, 21288c2ecf20Sopenharmony_ci .get_regs = get_regs, 21298c2ecf20Sopenharmony_ci .get_wol = get_wol, 21308c2ecf20Sopenharmony_ci .get_link_ksettings = get_link_ksettings, 21318c2ecf20Sopenharmony_ci .set_link_ksettings = set_link_ksettings, 21328c2ecf20Sopenharmony_ci}; 21338c2ecf20Sopenharmony_ci 21348c2ecf20Sopenharmony_cistatic int in_range(int val, int lo, int hi) 21358c2ecf20Sopenharmony_ci{ 21368c2ecf20Sopenharmony_ci return val < 0 || (val <= hi && val >= lo); 21378c2ecf20Sopenharmony_ci} 21388c2ecf20Sopenharmony_ci 21398c2ecf20Sopenharmony_cistatic int cxgb_extension_ioctl(struct net_device *dev, void __user *useraddr) 21408c2ecf20Sopenharmony_ci{ 21418c2ecf20Sopenharmony_ci struct port_info *pi = netdev_priv(dev); 21428c2ecf20Sopenharmony_ci struct adapter *adapter = pi->adapter; 21438c2ecf20Sopenharmony_ci u32 cmd; 21448c2ecf20Sopenharmony_ci int ret; 21458c2ecf20Sopenharmony_ci 21468c2ecf20Sopenharmony_ci if (copy_from_user(&cmd, useraddr, sizeof(cmd))) 21478c2ecf20Sopenharmony_ci return -EFAULT; 21488c2ecf20Sopenharmony_ci 21498c2ecf20Sopenharmony_ci switch (cmd) { 21508c2ecf20Sopenharmony_ci case CHELSIO_SET_QSET_PARAMS:{ 21518c2ecf20Sopenharmony_ci int i; 21528c2ecf20Sopenharmony_ci struct qset_params *q; 21538c2ecf20Sopenharmony_ci struct ch_qset_params t; 21548c2ecf20Sopenharmony_ci int q1 = pi->first_qset; 21558c2ecf20Sopenharmony_ci int nqsets = pi->nqsets; 21568c2ecf20Sopenharmony_ci 21578c2ecf20Sopenharmony_ci if (!capable(CAP_NET_ADMIN)) 21588c2ecf20Sopenharmony_ci return -EPERM; 21598c2ecf20Sopenharmony_ci if (copy_from_user(&t, useraddr, sizeof(t))) 21608c2ecf20Sopenharmony_ci return -EFAULT; 21618c2ecf20Sopenharmony_ci if (t.cmd != CHELSIO_SET_QSET_PARAMS) 21628c2ecf20Sopenharmony_ci return -EINVAL; 21638c2ecf20Sopenharmony_ci if (t.qset_idx >= SGE_QSETS) 21648c2ecf20Sopenharmony_ci return -EINVAL; 21658c2ecf20Sopenharmony_ci if (!in_range(t.intr_lat, 0, M_NEWTIMER) || 21668c2ecf20Sopenharmony_ci !in_range(t.cong_thres, 0, 255) || 21678c2ecf20Sopenharmony_ci !in_range(t.txq_size[0], MIN_TXQ_ENTRIES, 21688c2ecf20Sopenharmony_ci MAX_TXQ_ENTRIES) || 21698c2ecf20Sopenharmony_ci !in_range(t.txq_size[1], MIN_TXQ_ENTRIES, 21708c2ecf20Sopenharmony_ci MAX_TXQ_ENTRIES) || 21718c2ecf20Sopenharmony_ci !in_range(t.txq_size[2], MIN_CTRL_TXQ_ENTRIES, 21728c2ecf20Sopenharmony_ci MAX_CTRL_TXQ_ENTRIES) || 21738c2ecf20Sopenharmony_ci !in_range(t.fl_size[0], MIN_FL_ENTRIES, 21748c2ecf20Sopenharmony_ci MAX_RX_BUFFERS) || 21758c2ecf20Sopenharmony_ci !in_range(t.fl_size[1], MIN_FL_ENTRIES, 21768c2ecf20Sopenharmony_ci MAX_RX_JUMBO_BUFFERS) || 21778c2ecf20Sopenharmony_ci !in_range(t.rspq_size, MIN_RSPQ_ENTRIES, 21788c2ecf20Sopenharmony_ci MAX_RSPQ_ENTRIES)) 21798c2ecf20Sopenharmony_ci return -EINVAL; 21808c2ecf20Sopenharmony_ci 21818c2ecf20Sopenharmony_ci if ((adapter->flags & FULL_INIT_DONE) && 21828c2ecf20Sopenharmony_ci (t.rspq_size >= 0 || t.fl_size[0] >= 0 || 21838c2ecf20Sopenharmony_ci t.fl_size[1] >= 0 || t.txq_size[0] >= 0 || 21848c2ecf20Sopenharmony_ci t.txq_size[1] >= 0 || t.txq_size[2] >= 0 || 21858c2ecf20Sopenharmony_ci t.polling >= 0 || t.cong_thres >= 0)) 21868c2ecf20Sopenharmony_ci return -EBUSY; 21878c2ecf20Sopenharmony_ci 21888c2ecf20Sopenharmony_ci /* Allow setting of any available qset when offload enabled */ 21898c2ecf20Sopenharmony_ci if (test_bit(OFFLOAD_DEVMAP_BIT, &adapter->open_device_map)) { 21908c2ecf20Sopenharmony_ci q1 = 0; 21918c2ecf20Sopenharmony_ci for_each_port(adapter, i) { 21928c2ecf20Sopenharmony_ci pi = adap2pinfo(adapter, i); 21938c2ecf20Sopenharmony_ci nqsets += pi->first_qset + pi->nqsets; 21948c2ecf20Sopenharmony_ci } 21958c2ecf20Sopenharmony_ci } 21968c2ecf20Sopenharmony_ci 21978c2ecf20Sopenharmony_ci if (t.qset_idx < q1) 21988c2ecf20Sopenharmony_ci return -EINVAL; 21998c2ecf20Sopenharmony_ci if (t.qset_idx > q1 + nqsets - 1) 22008c2ecf20Sopenharmony_ci return -EINVAL; 22018c2ecf20Sopenharmony_ci 22028c2ecf20Sopenharmony_ci q = &adapter->params.sge.qset[t.qset_idx]; 22038c2ecf20Sopenharmony_ci 22048c2ecf20Sopenharmony_ci if (t.rspq_size >= 0) 22058c2ecf20Sopenharmony_ci q->rspq_size = t.rspq_size; 22068c2ecf20Sopenharmony_ci if (t.fl_size[0] >= 0) 22078c2ecf20Sopenharmony_ci q->fl_size = t.fl_size[0]; 22088c2ecf20Sopenharmony_ci if (t.fl_size[1] >= 0) 22098c2ecf20Sopenharmony_ci q->jumbo_size = t.fl_size[1]; 22108c2ecf20Sopenharmony_ci if (t.txq_size[0] >= 0) 22118c2ecf20Sopenharmony_ci q->txq_size[0] = t.txq_size[0]; 22128c2ecf20Sopenharmony_ci if (t.txq_size[1] >= 0) 22138c2ecf20Sopenharmony_ci q->txq_size[1] = t.txq_size[1]; 22148c2ecf20Sopenharmony_ci if (t.txq_size[2] >= 0) 22158c2ecf20Sopenharmony_ci q->txq_size[2] = t.txq_size[2]; 22168c2ecf20Sopenharmony_ci if (t.cong_thres >= 0) 22178c2ecf20Sopenharmony_ci q->cong_thres = t.cong_thres; 22188c2ecf20Sopenharmony_ci if (t.intr_lat >= 0) { 22198c2ecf20Sopenharmony_ci struct sge_qset *qs = 22208c2ecf20Sopenharmony_ci &adapter->sge.qs[t.qset_idx]; 22218c2ecf20Sopenharmony_ci 22228c2ecf20Sopenharmony_ci q->coalesce_usecs = t.intr_lat; 22238c2ecf20Sopenharmony_ci t3_update_qset_coalesce(qs, q); 22248c2ecf20Sopenharmony_ci } 22258c2ecf20Sopenharmony_ci if (t.polling >= 0) { 22268c2ecf20Sopenharmony_ci if (adapter->flags & USING_MSIX) 22278c2ecf20Sopenharmony_ci q->polling = t.polling; 22288c2ecf20Sopenharmony_ci else { 22298c2ecf20Sopenharmony_ci /* No polling with INTx for T3A */ 22308c2ecf20Sopenharmony_ci if (adapter->params.rev == 0 && 22318c2ecf20Sopenharmony_ci !(adapter->flags & USING_MSI)) 22328c2ecf20Sopenharmony_ci t.polling = 0; 22338c2ecf20Sopenharmony_ci 22348c2ecf20Sopenharmony_ci for (i = 0; i < SGE_QSETS; i++) { 22358c2ecf20Sopenharmony_ci q = &adapter->params.sge. 22368c2ecf20Sopenharmony_ci qset[i]; 22378c2ecf20Sopenharmony_ci q->polling = t.polling; 22388c2ecf20Sopenharmony_ci } 22398c2ecf20Sopenharmony_ci } 22408c2ecf20Sopenharmony_ci } 22418c2ecf20Sopenharmony_ci 22428c2ecf20Sopenharmony_ci if (t.lro >= 0) { 22438c2ecf20Sopenharmony_ci if (t.lro) 22448c2ecf20Sopenharmony_ci dev->wanted_features |= NETIF_F_GRO; 22458c2ecf20Sopenharmony_ci else 22468c2ecf20Sopenharmony_ci dev->wanted_features &= ~NETIF_F_GRO; 22478c2ecf20Sopenharmony_ci netdev_update_features(dev); 22488c2ecf20Sopenharmony_ci } 22498c2ecf20Sopenharmony_ci 22508c2ecf20Sopenharmony_ci break; 22518c2ecf20Sopenharmony_ci } 22528c2ecf20Sopenharmony_ci case CHELSIO_GET_QSET_PARAMS:{ 22538c2ecf20Sopenharmony_ci struct qset_params *q; 22548c2ecf20Sopenharmony_ci struct ch_qset_params t; 22558c2ecf20Sopenharmony_ci int q1 = pi->first_qset; 22568c2ecf20Sopenharmony_ci int nqsets = pi->nqsets; 22578c2ecf20Sopenharmony_ci int i; 22588c2ecf20Sopenharmony_ci 22598c2ecf20Sopenharmony_ci if (copy_from_user(&t, useraddr, sizeof(t))) 22608c2ecf20Sopenharmony_ci return -EFAULT; 22618c2ecf20Sopenharmony_ci 22628c2ecf20Sopenharmony_ci if (t.cmd != CHELSIO_GET_QSET_PARAMS) 22638c2ecf20Sopenharmony_ci return -EINVAL; 22648c2ecf20Sopenharmony_ci 22658c2ecf20Sopenharmony_ci /* Display qsets for all ports when offload enabled */ 22668c2ecf20Sopenharmony_ci if (test_bit(OFFLOAD_DEVMAP_BIT, &adapter->open_device_map)) { 22678c2ecf20Sopenharmony_ci q1 = 0; 22688c2ecf20Sopenharmony_ci for_each_port(adapter, i) { 22698c2ecf20Sopenharmony_ci pi = adap2pinfo(adapter, i); 22708c2ecf20Sopenharmony_ci nqsets = pi->first_qset + pi->nqsets; 22718c2ecf20Sopenharmony_ci } 22728c2ecf20Sopenharmony_ci } 22738c2ecf20Sopenharmony_ci 22748c2ecf20Sopenharmony_ci if (t.qset_idx >= nqsets) 22758c2ecf20Sopenharmony_ci return -EINVAL; 22768c2ecf20Sopenharmony_ci t.qset_idx = array_index_nospec(t.qset_idx, nqsets); 22778c2ecf20Sopenharmony_ci 22788c2ecf20Sopenharmony_ci q = &adapter->params.sge.qset[q1 + t.qset_idx]; 22798c2ecf20Sopenharmony_ci t.rspq_size = q->rspq_size; 22808c2ecf20Sopenharmony_ci t.txq_size[0] = q->txq_size[0]; 22818c2ecf20Sopenharmony_ci t.txq_size[1] = q->txq_size[1]; 22828c2ecf20Sopenharmony_ci t.txq_size[2] = q->txq_size[2]; 22838c2ecf20Sopenharmony_ci t.fl_size[0] = q->fl_size; 22848c2ecf20Sopenharmony_ci t.fl_size[1] = q->jumbo_size; 22858c2ecf20Sopenharmony_ci t.polling = q->polling; 22868c2ecf20Sopenharmony_ci t.lro = !!(dev->features & NETIF_F_GRO); 22878c2ecf20Sopenharmony_ci t.intr_lat = q->coalesce_usecs; 22888c2ecf20Sopenharmony_ci t.cong_thres = q->cong_thres; 22898c2ecf20Sopenharmony_ci t.qnum = q1; 22908c2ecf20Sopenharmony_ci 22918c2ecf20Sopenharmony_ci if (adapter->flags & USING_MSIX) 22928c2ecf20Sopenharmony_ci t.vector = adapter->msix_info[q1 + t.qset_idx + 1].vec; 22938c2ecf20Sopenharmony_ci else 22948c2ecf20Sopenharmony_ci t.vector = adapter->pdev->irq; 22958c2ecf20Sopenharmony_ci 22968c2ecf20Sopenharmony_ci if (copy_to_user(useraddr, &t, sizeof(t))) 22978c2ecf20Sopenharmony_ci return -EFAULT; 22988c2ecf20Sopenharmony_ci break; 22998c2ecf20Sopenharmony_ci } 23008c2ecf20Sopenharmony_ci case CHELSIO_SET_QSET_NUM:{ 23018c2ecf20Sopenharmony_ci struct ch_reg edata; 23028c2ecf20Sopenharmony_ci unsigned int i, first_qset = 0, other_qsets = 0; 23038c2ecf20Sopenharmony_ci 23048c2ecf20Sopenharmony_ci if (!capable(CAP_NET_ADMIN)) 23058c2ecf20Sopenharmony_ci return -EPERM; 23068c2ecf20Sopenharmony_ci if (adapter->flags & FULL_INIT_DONE) 23078c2ecf20Sopenharmony_ci return -EBUSY; 23088c2ecf20Sopenharmony_ci if (copy_from_user(&edata, useraddr, sizeof(edata))) 23098c2ecf20Sopenharmony_ci return -EFAULT; 23108c2ecf20Sopenharmony_ci if (edata.cmd != CHELSIO_SET_QSET_NUM) 23118c2ecf20Sopenharmony_ci return -EINVAL; 23128c2ecf20Sopenharmony_ci if (edata.val < 1 || 23138c2ecf20Sopenharmony_ci (edata.val > 1 && !(adapter->flags & USING_MSIX))) 23148c2ecf20Sopenharmony_ci return -EINVAL; 23158c2ecf20Sopenharmony_ci 23168c2ecf20Sopenharmony_ci for_each_port(adapter, i) 23178c2ecf20Sopenharmony_ci if (adapter->port[i] && adapter->port[i] != dev) 23188c2ecf20Sopenharmony_ci other_qsets += adap2pinfo(adapter, i)->nqsets; 23198c2ecf20Sopenharmony_ci 23208c2ecf20Sopenharmony_ci if (edata.val + other_qsets > SGE_QSETS) 23218c2ecf20Sopenharmony_ci return -EINVAL; 23228c2ecf20Sopenharmony_ci 23238c2ecf20Sopenharmony_ci pi->nqsets = edata.val; 23248c2ecf20Sopenharmony_ci 23258c2ecf20Sopenharmony_ci for_each_port(adapter, i) 23268c2ecf20Sopenharmony_ci if (adapter->port[i]) { 23278c2ecf20Sopenharmony_ci pi = adap2pinfo(adapter, i); 23288c2ecf20Sopenharmony_ci pi->first_qset = first_qset; 23298c2ecf20Sopenharmony_ci first_qset += pi->nqsets; 23308c2ecf20Sopenharmony_ci } 23318c2ecf20Sopenharmony_ci break; 23328c2ecf20Sopenharmony_ci } 23338c2ecf20Sopenharmony_ci case CHELSIO_GET_QSET_NUM:{ 23348c2ecf20Sopenharmony_ci struct ch_reg edata; 23358c2ecf20Sopenharmony_ci 23368c2ecf20Sopenharmony_ci memset(&edata, 0, sizeof(struct ch_reg)); 23378c2ecf20Sopenharmony_ci 23388c2ecf20Sopenharmony_ci edata.cmd = CHELSIO_GET_QSET_NUM; 23398c2ecf20Sopenharmony_ci edata.val = pi->nqsets; 23408c2ecf20Sopenharmony_ci if (copy_to_user(useraddr, &edata, sizeof(edata))) 23418c2ecf20Sopenharmony_ci return -EFAULT; 23428c2ecf20Sopenharmony_ci break; 23438c2ecf20Sopenharmony_ci } 23448c2ecf20Sopenharmony_ci case CHELSIO_LOAD_FW:{ 23458c2ecf20Sopenharmony_ci u8 *fw_data; 23468c2ecf20Sopenharmony_ci struct ch_mem_range t; 23478c2ecf20Sopenharmony_ci 23488c2ecf20Sopenharmony_ci if (!capable(CAP_SYS_RAWIO)) 23498c2ecf20Sopenharmony_ci return -EPERM; 23508c2ecf20Sopenharmony_ci if (copy_from_user(&t, useraddr, sizeof(t))) 23518c2ecf20Sopenharmony_ci return -EFAULT; 23528c2ecf20Sopenharmony_ci if (t.cmd != CHELSIO_LOAD_FW) 23538c2ecf20Sopenharmony_ci return -EINVAL; 23548c2ecf20Sopenharmony_ci /* Check t.len sanity ? */ 23558c2ecf20Sopenharmony_ci fw_data = memdup_user(useraddr + sizeof(t), t.len); 23568c2ecf20Sopenharmony_ci if (IS_ERR(fw_data)) 23578c2ecf20Sopenharmony_ci return PTR_ERR(fw_data); 23588c2ecf20Sopenharmony_ci 23598c2ecf20Sopenharmony_ci ret = t3_load_fw(adapter, fw_data, t.len); 23608c2ecf20Sopenharmony_ci kfree(fw_data); 23618c2ecf20Sopenharmony_ci if (ret) 23628c2ecf20Sopenharmony_ci return ret; 23638c2ecf20Sopenharmony_ci break; 23648c2ecf20Sopenharmony_ci } 23658c2ecf20Sopenharmony_ci case CHELSIO_SETMTUTAB:{ 23668c2ecf20Sopenharmony_ci struct ch_mtus m; 23678c2ecf20Sopenharmony_ci int i; 23688c2ecf20Sopenharmony_ci 23698c2ecf20Sopenharmony_ci if (!is_offload(adapter)) 23708c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 23718c2ecf20Sopenharmony_ci if (!capable(CAP_NET_ADMIN)) 23728c2ecf20Sopenharmony_ci return -EPERM; 23738c2ecf20Sopenharmony_ci if (offload_running(adapter)) 23748c2ecf20Sopenharmony_ci return -EBUSY; 23758c2ecf20Sopenharmony_ci if (copy_from_user(&m, useraddr, sizeof(m))) 23768c2ecf20Sopenharmony_ci return -EFAULT; 23778c2ecf20Sopenharmony_ci if (m.cmd != CHELSIO_SETMTUTAB) 23788c2ecf20Sopenharmony_ci return -EINVAL; 23798c2ecf20Sopenharmony_ci if (m.nmtus != NMTUS) 23808c2ecf20Sopenharmony_ci return -EINVAL; 23818c2ecf20Sopenharmony_ci if (m.mtus[0] < 81) /* accommodate SACK */ 23828c2ecf20Sopenharmony_ci return -EINVAL; 23838c2ecf20Sopenharmony_ci 23848c2ecf20Sopenharmony_ci /* MTUs must be in ascending order */ 23858c2ecf20Sopenharmony_ci for (i = 1; i < NMTUS; ++i) 23868c2ecf20Sopenharmony_ci if (m.mtus[i] < m.mtus[i - 1]) 23878c2ecf20Sopenharmony_ci return -EINVAL; 23888c2ecf20Sopenharmony_ci 23898c2ecf20Sopenharmony_ci memcpy(adapter->params.mtus, m.mtus, 23908c2ecf20Sopenharmony_ci sizeof(adapter->params.mtus)); 23918c2ecf20Sopenharmony_ci break; 23928c2ecf20Sopenharmony_ci } 23938c2ecf20Sopenharmony_ci case CHELSIO_GET_PM:{ 23948c2ecf20Sopenharmony_ci struct tp_params *p = &adapter->params.tp; 23958c2ecf20Sopenharmony_ci struct ch_pm m = {.cmd = CHELSIO_GET_PM }; 23968c2ecf20Sopenharmony_ci 23978c2ecf20Sopenharmony_ci if (!is_offload(adapter)) 23988c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 23998c2ecf20Sopenharmony_ci m.tx_pg_sz = p->tx_pg_size; 24008c2ecf20Sopenharmony_ci m.tx_num_pg = p->tx_num_pgs; 24018c2ecf20Sopenharmony_ci m.rx_pg_sz = p->rx_pg_size; 24028c2ecf20Sopenharmony_ci m.rx_num_pg = p->rx_num_pgs; 24038c2ecf20Sopenharmony_ci m.pm_total = p->pmtx_size + p->chan_rx_size * p->nchan; 24048c2ecf20Sopenharmony_ci if (copy_to_user(useraddr, &m, sizeof(m))) 24058c2ecf20Sopenharmony_ci return -EFAULT; 24068c2ecf20Sopenharmony_ci break; 24078c2ecf20Sopenharmony_ci } 24088c2ecf20Sopenharmony_ci case CHELSIO_SET_PM:{ 24098c2ecf20Sopenharmony_ci struct ch_pm m; 24108c2ecf20Sopenharmony_ci struct tp_params *p = &adapter->params.tp; 24118c2ecf20Sopenharmony_ci 24128c2ecf20Sopenharmony_ci if (!is_offload(adapter)) 24138c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 24148c2ecf20Sopenharmony_ci if (!capable(CAP_NET_ADMIN)) 24158c2ecf20Sopenharmony_ci return -EPERM; 24168c2ecf20Sopenharmony_ci if (adapter->flags & FULL_INIT_DONE) 24178c2ecf20Sopenharmony_ci return -EBUSY; 24188c2ecf20Sopenharmony_ci if (copy_from_user(&m, useraddr, sizeof(m))) 24198c2ecf20Sopenharmony_ci return -EFAULT; 24208c2ecf20Sopenharmony_ci if (m.cmd != CHELSIO_SET_PM) 24218c2ecf20Sopenharmony_ci return -EINVAL; 24228c2ecf20Sopenharmony_ci if (!is_power_of_2(m.rx_pg_sz) || 24238c2ecf20Sopenharmony_ci !is_power_of_2(m.tx_pg_sz)) 24248c2ecf20Sopenharmony_ci return -EINVAL; /* not power of 2 */ 24258c2ecf20Sopenharmony_ci if (!(m.rx_pg_sz & 0x14000)) 24268c2ecf20Sopenharmony_ci return -EINVAL; /* not 16KB or 64KB */ 24278c2ecf20Sopenharmony_ci if (!(m.tx_pg_sz & 0x1554000)) 24288c2ecf20Sopenharmony_ci return -EINVAL; 24298c2ecf20Sopenharmony_ci if (m.tx_num_pg == -1) 24308c2ecf20Sopenharmony_ci m.tx_num_pg = p->tx_num_pgs; 24318c2ecf20Sopenharmony_ci if (m.rx_num_pg == -1) 24328c2ecf20Sopenharmony_ci m.rx_num_pg = p->rx_num_pgs; 24338c2ecf20Sopenharmony_ci if (m.tx_num_pg % 24 || m.rx_num_pg % 24) 24348c2ecf20Sopenharmony_ci return -EINVAL; 24358c2ecf20Sopenharmony_ci if (m.rx_num_pg * m.rx_pg_sz > p->chan_rx_size || 24368c2ecf20Sopenharmony_ci m.tx_num_pg * m.tx_pg_sz > p->chan_tx_size) 24378c2ecf20Sopenharmony_ci return -EINVAL; 24388c2ecf20Sopenharmony_ci p->rx_pg_size = m.rx_pg_sz; 24398c2ecf20Sopenharmony_ci p->tx_pg_size = m.tx_pg_sz; 24408c2ecf20Sopenharmony_ci p->rx_num_pgs = m.rx_num_pg; 24418c2ecf20Sopenharmony_ci p->tx_num_pgs = m.tx_num_pg; 24428c2ecf20Sopenharmony_ci break; 24438c2ecf20Sopenharmony_ci } 24448c2ecf20Sopenharmony_ci case CHELSIO_GET_MEM:{ 24458c2ecf20Sopenharmony_ci struct ch_mem_range t; 24468c2ecf20Sopenharmony_ci struct mc7 *mem; 24478c2ecf20Sopenharmony_ci u64 buf[32]; 24488c2ecf20Sopenharmony_ci 24498c2ecf20Sopenharmony_ci if (!is_offload(adapter)) 24508c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 24518c2ecf20Sopenharmony_ci if (!capable(CAP_NET_ADMIN)) 24528c2ecf20Sopenharmony_ci return -EPERM; 24538c2ecf20Sopenharmony_ci if (!(adapter->flags & FULL_INIT_DONE)) 24548c2ecf20Sopenharmony_ci return -EIO; /* need the memory controllers */ 24558c2ecf20Sopenharmony_ci if (copy_from_user(&t, useraddr, sizeof(t))) 24568c2ecf20Sopenharmony_ci return -EFAULT; 24578c2ecf20Sopenharmony_ci if (t.cmd != CHELSIO_GET_MEM) 24588c2ecf20Sopenharmony_ci return -EINVAL; 24598c2ecf20Sopenharmony_ci if ((t.addr & 7) || (t.len & 7)) 24608c2ecf20Sopenharmony_ci return -EINVAL; 24618c2ecf20Sopenharmony_ci if (t.mem_id == MEM_CM) 24628c2ecf20Sopenharmony_ci mem = &adapter->cm; 24638c2ecf20Sopenharmony_ci else if (t.mem_id == MEM_PMRX) 24648c2ecf20Sopenharmony_ci mem = &adapter->pmrx; 24658c2ecf20Sopenharmony_ci else if (t.mem_id == MEM_PMTX) 24668c2ecf20Sopenharmony_ci mem = &adapter->pmtx; 24678c2ecf20Sopenharmony_ci else 24688c2ecf20Sopenharmony_ci return -EINVAL; 24698c2ecf20Sopenharmony_ci 24708c2ecf20Sopenharmony_ci /* 24718c2ecf20Sopenharmony_ci * Version scheme: 24728c2ecf20Sopenharmony_ci * bits 0..9: chip version 24738c2ecf20Sopenharmony_ci * bits 10..15: chip revision 24748c2ecf20Sopenharmony_ci */ 24758c2ecf20Sopenharmony_ci t.version = 3 | (adapter->params.rev << 10); 24768c2ecf20Sopenharmony_ci if (copy_to_user(useraddr, &t, sizeof(t))) 24778c2ecf20Sopenharmony_ci return -EFAULT; 24788c2ecf20Sopenharmony_ci 24798c2ecf20Sopenharmony_ci /* 24808c2ecf20Sopenharmony_ci * Read 256 bytes at a time as len can be large and we don't 24818c2ecf20Sopenharmony_ci * want to use huge intermediate buffers. 24828c2ecf20Sopenharmony_ci */ 24838c2ecf20Sopenharmony_ci useraddr += sizeof(t); /* advance to start of buffer */ 24848c2ecf20Sopenharmony_ci while (t.len) { 24858c2ecf20Sopenharmony_ci unsigned int chunk = 24868c2ecf20Sopenharmony_ci min_t(unsigned int, t.len, sizeof(buf)); 24878c2ecf20Sopenharmony_ci 24888c2ecf20Sopenharmony_ci ret = 24898c2ecf20Sopenharmony_ci t3_mc7_bd_read(mem, t.addr / 8, chunk / 8, 24908c2ecf20Sopenharmony_ci buf); 24918c2ecf20Sopenharmony_ci if (ret) 24928c2ecf20Sopenharmony_ci return ret; 24938c2ecf20Sopenharmony_ci if (copy_to_user(useraddr, buf, chunk)) 24948c2ecf20Sopenharmony_ci return -EFAULT; 24958c2ecf20Sopenharmony_ci useraddr += chunk; 24968c2ecf20Sopenharmony_ci t.addr += chunk; 24978c2ecf20Sopenharmony_ci t.len -= chunk; 24988c2ecf20Sopenharmony_ci } 24998c2ecf20Sopenharmony_ci break; 25008c2ecf20Sopenharmony_ci } 25018c2ecf20Sopenharmony_ci case CHELSIO_SET_TRACE_FILTER:{ 25028c2ecf20Sopenharmony_ci struct ch_trace t; 25038c2ecf20Sopenharmony_ci const struct trace_params *tp; 25048c2ecf20Sopenharmony_ci 25058c2ecf20Sopenharmony_ci if (!capable(CAP_NET_ADMIN)) 25068c2ecf20Sopenharmony_ci return -EPERM; 25078c2ecf20Sopenharmony_ci if (!offload_running(adapter)) 25088c2ecf20Sopenharmony_ci return -EAGAIN; 25098c2ecf20Sopenharmony_ci if (copy_from_user(&t, useraddr, sizeof(t))) 25108c2ecf20Sopenharmony_ci return -EFAULT; 25118c2ecf20Sopenharmony_ci if (t.cmd != CHELSIO_SET_TRACE_FILTER) 25128c2ecf20Sopenharmony_ci return -EINVAL; 25138c2ecf20Sopenharmony_ci 25148c2ecf20Sopenharmony_ci tp = (const struct trace_params *)&t.sip; 25158c2ecf20Sopenharmony_ci if (t.config_tx) 25168c2ecf20Sopenharmony_ci t3_config_trace_filter(adapter, tp, 0, 25178c2ecf20Sopenharmony_ci t.invert_match, 25188c2ecf20Sopenharmony_ci t.trace_tx); 25198c2ecf20Sopenharmony_ci if (t.config_rx) 25208c2ecf20Sopenharmony_ci t3_config_trace_filter(adapter, tp, 1, 25218c2ecf20Sopenharmony_ci t.invert_match, 25228c2ecf20Sopenharmony_ci t.trace_rx); 25238c2ecf20Sopenharmony_ci break; 25248c2ecf20Sopenharmony_ci } 25258c2ecf20Sopenharmony_ci default: 25268c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 25278c2ecf20Sopenharmony_ci } 25288c2ecf20Sopenharmony_ci return 0; 25298c2ecf20Sopenharmony_ci} 25308c2ecf20Sopenharmony_ci 25318c2ecf20Sopenharmony_cistatic int cxgb_ioctl(struct net_device *dev, struct ifreq *req, int cmd) 25328c2ecf20Sopenharmony_ci{ 25338c2ecf20Sopenharmony_ci struct mii_ioctl_data *data = if_mii(req); 25348c2ecf20Sopenharmony_ci struct port_info *pi = netdev_priv(dev); 25358c2ecf20Sopenharmony_ci struct adapter *adapter = pi->adapter; 25368c2ecf20Sopenharmony_ci 25378c2ecf20Sopenharmony_ci switch (cmd) { 25388c2ecf20Sopenharmony_ci case SIOCGMIIREG: 25398c2ecf20Sopenharmony_ci case SIOCSMIIREG: 25408c2ecf20Sopenharmony_ci /* Convert phy_id from older PRTAD/DEVAD format */ 25418c2ecf20Sopenharmony_ci if (is_10G(adapter) && 25428c2ecf20Sopenharmony_ci !mdio_phy_id_is_c45(data->phy_id) && 25438c2ecf20Sopenharmony_ci (data->phy_id & 0x1f00) && 25448c2ecf20Sopenharmony_ci !(data->phy_id & 0xe0e0)) 25458c2ecf20Sopenharmony_ci data->phy_id = mdio_phy_id_c45(data->phy_id >> 8, 25468c2ecf20Sopenharmony_ci data->phy_id & 0x1f); 25478c2ecf20Sopenharmony_ci fallthrough; 25488c2ecf20Sopenharmony_ci case SIOCGMIIPHY: 25498c2ecf20Sopenharmony_ci return mdio_mii_ioctl(&pi->phy.mdio, data, cmd); 25508c2ecf20Sopenharmony_ci case SIOCCHIOCTL: 25518c2ecf20Sopenharmony_ci return cxgb_extension_ioctl(dev, req->ifr_data); 25528c2ecf20Sopenharmony_ci default: 25538c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 25548c2ecf20Sopenharmony_ci } 25558c2ecf20Sopenharmony_ci} 25568c2ecf20Sopenharmony_ci 25578c2ecf20Sopenharmony_cistatic int cxgb_change_mtu(struct net_device *dev, int new_mtu) 25588c2ecf20Sopenharmony_ci{ 25598c2ecf20Sopenharmony_ci struct port_info *pi = netdev_priv(dev); 25608c2ecf20Sopenharmony_ci struct adapter *adapter = pi->adapter; 25618c2ecf20Sopenharmony_ci int ret; 25628c2ecf20Sopenharmony_ci 25638c2ecf20Sopenharmony_ci if ((ret = t3_mac_set_mtu(&pi->mac, new_mtu))) 25648c2ecf20Sopenharmony_ci return ret; 25658c2ecf20Sopenharmony_ci dev->mtu = new_mtu; 25668c2ecf20Sopenharmony_ci init_port_mtus(adapter); 25678c2ecf20Sopenharmony_ci if (adapter->params.rev == 0 && offload_running(adapter)) 25688c2ecf20Sopenharmony_ci t3_load_mtus(adapter, adapter->params.mtus, 25698c2ecf20Sopenharmony_ci adapter->params.a_wnd, adapter->params.b_wnd, 25708c2ecf20Sopenharmony_ci adapter->port[0]->mtu); 25718c2ecf20Sopenharmony_ci return 0; 25728c2ecf20Sopenharmony_ci} 25738c2ecf20Sopenharmony_ci 25748c2ecf20Sopenharmony_cistatic int cxgb_set_mac_addr(struct net_device *dev, void *p) 25758c2ecf20Sopenharmony_ci{ 25768c2ecf20Sopenharmony_ci struct port_info *pi = netdev_priv(dev); 25778c2ecf20Sopenharmony_ci struct adapter *adapter = pi->adapter; 25788c2ecf20Sopenharmony_ci struct sockaddr *addr = p; 25798c2ecf20Sopenharmony_ci 25808c2ecf20Sopenharmony_ci if (!is_valid_ether_addr(addr->sa_data)) 25818c2ecf20Sopenharmony_ci return -EADDRNOTAVAIL; 25828c2ecf20Sopenharmony_ci 25838c2ecf20Sopenharmony_ci memcpy(dev->dev_addr, addr->sa_data, dev->addr_len); 25848c2ecf20Sopenharmony_ci t3_mac_set_address(&pi->mac, LAN_MAC_IDX, dev->dev_addr); 25858c2ecf20Sopenharmony_ci if (offload_running(adapter)) 25868c2ecf20Sopenharmony_ci write_smt_entry(adapter, pi->port_id); 25878c2ecf20Sopenharmony_ci return 0; 25888c2ecf20Sopenharmony_ci} 25898c2ecf20Sopenharmony_ci 25908c2ecf20Sopenharmony_cistatic netdev_features_t cxgb_fix_features(struct net_device *dev, 25918c2ecf20Sopenharmony_ci netdev_features_t features) 25928c2ecf20Sopenharmony_ci{ 25938c2ecf20Sopenharmony_ci /* 25948c2ecf20Sopenharmony_ci * Since there is no support for separate rx/tx vlan accel 25958c2ecf20Sopenharmony_ci * enable/disable make sure tx flag is always in same state as rx. 25968c2ecf20Sopenharmony_ci */ 25978c2ecf20Sopenharmony_ci if (features & NETIF_F_HW_VLAN_CTAG_RX) 25988c2ecf20Sopenharmony_ci features |= NETIF_F_HW_VLAN_CTAG_TX; 25998c2ecf20Sopenharmony_ci else 26008c2ecf20Sopenharmony_ci features &= ~NETIF_F_HW_VLAN_CTAG_TX; 26018c2ecf20Sopenharmony_ci 26028c2ecf20Sopenharmony_ci return features; 26038c2ecf20Sopenharmony_ci} 26048c2ecf20Sopenharmony_ci 26058c2ecf20Sopenharmony_cistatic int cxgb_set_features(struct net_device *dev, netdev_features_t features) 26068c2ecf20Sopenharmony_ci{ 26078c2ecf20Sopenharmony_ci netdev_features_t changed = dev->features ^ features; 26088c2ecf20Sopenharmony_ci 26098c2ecf20Sopenharmony_ci if (changed & NETIF_F_HW_VLAN_CTAG_RX) 26108c2ecf20Sopenharmony_ci cxgb_vlan_mode(dev, features); 26118c2ecf20Sopenharmony_ci 26128c2ecf20Sopenharmony_ci return 0; 26138c2ecf20Sopenharmony_ci} 26148c2ecf20Sopenharmony_ci 26158c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER 26168c2ecf20Sopenharmony_cistatic void cxgb_netpoll(struct net_device *dev) 26178c2ecf20Sopenharmony_ci{ 26188c2ecf20Sopenharmony_ci struct port_info *pi = netdev_priv(dev); 26198c2ecf20Sopenharmony_ci struct adapter *adapter = pi->adapter; 26208c2ecf20Sopenharmony_ci int qidx; 26218c2ecf20Sopenharmony_ci 26228c2ecf20Sopenharmony_ci for (qidx = pi->first_qset; qidx < pi->first_qset + pi->nqsets; qidx++) { 26238c2ecf20Sopenharmony_ci struct sge_qset *qs = &adapter->sge.qs[qidx]; 26248c2ecf20Sopenharmony_ci void *source; 26258c2ecf20Sopenharmony_ci 26268c2ecf20Sopenharmony_ci if (adapter->flags & USING_MSIX) 26278c2ecf20Sopenharmony_ci source = qs; 26288c2ecf20Sopenharmony_ci else 26298c2ecf20Sopenharmony_ci source = adapter; 26308c2ecf20Sopenharmony_ci 26318c2ecf20Sopenharmony_ci t3_intr_handler(adapter, qs->rspq.polling) (0, source); 26328c2ecf20Sopenharmony_ci } 26338c2ecf20Sopenharmony_ci} 26348c2ecf20Sopenharmony_ci#endif 26358c2ecf20Sopenharmony_ci 26368c2ecf20Sopenharmony_ci/* 26378c2ecf20Sopenharmony_ci * Periodic accumulation of MAC statistics. 26388c2ecf20Sopenharmony_ci */ 26398c2ecf20Sopenharmony_cistatic void mac_stats_update(struct adapter *adapter) 26408c2ecf20Sopenharmony_ci{ 26418c2ecf20Sopenharmony_ci int i; 26428c2ecf20Sopenharmony_ci 26438c2ecf20Sopenharmony_ci for_each_port(adapter, i) { 26448c2ecf20Sopenharmony_ci struct net_device *dev = adapter->port[i]; 26458c2ecf20Sopenharmony_ci struct port_info *p = netdev_priv(dev); 26468c2ecf20Sopenharmony_ci 26478c2ecf20Sopenharmony_ci if (netif_running(dev)) { 26488c2ecf20Sopenharmony_ci spin_lock(&adapter->stats_lock); 26498c2ecf20Sopenharmony_ci t3_mac_update_stats(&p->mac); 26508c2ecf20Sopenharmony_ci spin_unlock(&adapter->stats_lock); 26518c2ecf20Sopenharmony_ci } 26528c2ecf20Sopenharmony_ci } 26538c2ecf20Sopenharmony_ci} 26548c2ecf20Sopenharmony_ci 26558c2ecf20Sopenharmony_cistatic void check_link_status(struct adapter *adapter) 26568c2ecf20Sopenharmony_ci{ 26578c2ecf20Sopenharmony_ci int i; 26588c2ecf20Sopenharmony_ci 26598c2ecf20Sopenharmony_ci for_each_port(adapter, i) { 26608c2ecf20Sopenharmony_ci struct net_device *dev = adapter->port[i]; 26618c2ecf20Sopenharmony_ci struct port_info *p = netdev_priv(dev); 26628c2ecf20Sopenharmony_ci int link_fault; 26638c2ecf20Sopenharmony_ci 26648c2ecf20Sopenharmony_ci spin_lock_irq(&adapter->work_lock); 26658c2ecf20Sopenharmony_ci link_fault = p->link_fault; 26668c2ecf20Sopenharmony_ci spin_unlock_irq(&adapter->work_lock); 26678c2ecf20Sopenharmony_ci 26688c2ecf20Sopenharmony_ci if (link_fault) { 26698c2ecf20Sopenharmony_ci t3_link_fault(adapter, i); 26708c2ecf20Sopenharmony_ci continue; 26718c2ecf20Sopenharmony_ci } 26728c2ecf20Sopenharmony_ci 26738c2ecf20Sopenharmony_ci if (!(p->phy.caps & SUPPORTED_IRQ) && netif_running(dev)) { 26748c2ecf20Sopenharmony_ci t3_xgm_intr_disable(adapter, i); 26758c2ecf20Sopenharmony_ci t3_read_reg(adapter, A_XGM_INT_STATUS + p->mac.offset); 26768c2ecf20Sopenharmony_ci 26778c2ecf20Sopenharmony_ci t3_link_changed(adapter, i); 26788c2ecf20Sopenharmony_ci t3_xgm_intr_enable(adapter, i); 26798c2ecf20Sopenharmony_ci } 26808c2ecf20Sopenharmony_ci } 26818c2ecf20Sopenharmony_ci} 26828c2ecf20Sopenharmony_ci 26838c2ecf20Sopenharmony_cistatic void check_t3b2_mac(struct adapter *adapter) 26848c2ecf20Sopenharmony_ci{ 26858c2ecf20Sopenharmony_ci int i; 26868c2ecf20Sopenharmony_ci 26878c2ecf20Sopenharmony_ci if (!rtnl_trylock()) /* synchronize with ifdown */ 26888c2ecf20Sopenharmony_ci return; 26898c2ecf20Sopenharmony_ci 26908c2ecf20Sopenharmony_ci for_each_port(adapter, i) { 26918c2ecf20Sopenharmony_ci struct net_device *dev = adapter->port[i]; 26928c2ecf20Sopenharmony_ci struct port_info *p = netdev_priv(dev); 26938c2ecf20Sopenharmony_ci int status; 26948c2ecf20Sopenharmony_ci 26958c2ecf20Sopenharmony_ci if (!netif_running(dev)) 26968c2ecf20Sopenharmony_ci continue; 26978c2ecf20Sopenharmony_ci 26988c2ecf20Sopenharmony_ci status = 0; 26998c2ecf20Sopenharmony_ci if (netif_running(dev) && netif_carrier_ok(dev)) 27008c2ecf20Sopenharmony_ci status = t3b2_mac_watchdog_task(&p->mac); 27018c2ecf20Sopenharmony_ci if (status == 1) 27028c2ecf20Sopenharmony_ci p->mac.stats.num_toggled++; 27038c2ecf20Sopenharmony_ci else if (status == 2) { 27048c2ecf20Sopenharmony_ci struct cmac *mac = &p->mac; 27058c2ecf20Sopenharmony_ci 27068c2ecf20Sopenharmony_ci t3_mac_set_mtu(mac, dev->mtu); 27078c2ecf20Sopenharmony_ci t3_mac_set_address(mac, LAN_MAC_IDX, dev->dev_addr); 27088c2ecf20Sopenharmony_ci cxgb_set_rxmode(dev); 27098c2ecf20Sopenharmony_ci t3_link_start(&p->phy, mac, &p->link_config); 27108c2ecf20Sopenharmony_ci t3_mac_enable(mac, MAC_DIRECTION_RX | MAC_DIRECTION_TX); 27118c2ecf20Sopenharmony_ci t3_port_intr_enable(adapter, p->port_id); 27128c2ecf20Sopenharmony_ci p->mac.stats.num_resets++; 27138c2ecf20Sopenharmony_ci } 27148c2ecf20Sopenharmony_ci } 27158c2ecf20Sopenharmony_ci rtnl_unlock(); 27168c2ecf20Sopenharmony_ci} 27178c2ecf20Sopenharmony_ci 27188c2ecf20Sopenharmony_ci 27198c2ecf20Sopenharmony_cistatic void t3_adap_check_task(struct work_struct *work) 27208c2ecf20Sopenharmony_ci{ 27218c2ecf20Sopenharmony_ci struct adapter *adapter = container_of(work, struct adapter, 27228c2ecf20Sopenharmony_ci adap_check_task.work); 27238c2ecf20Sopenharmony_ci const struct adapter_params *p = &adapter->params; 27248c2ecf20Sopenharmony_ci int port; 27258c2ecf20Sopenharmony_ci unsigned int v, status, reset; 27268c2ecf20Sopenharmony_ci 27278c2ecf20Sopenharmony_ci adapter->check_task_cnt++; 27288c2ecf20Sopenharmony_ci 27298c2ecf20Sopenharmony_ci check_link_status(adapter); 27308c2ecf20Sopenharmony_ci 27318c2ecf20Sopenharmony_ci /* Accumulate MAC stats if needed */ 27328c2ecf20Sopenharmony_ci if (!p->linkpoll_period || 27338c2ecf20Sopenharmony_ci (adapter->check_task_cnt * p->linkpoll_period) / 10 >= 27348c2ecf20Sopenharmony_ci p->stats_update_period) { 27358c2ecf20Sopenharmony_ci mac_stats_update(adapter); 27368c2ecf20Sopenharmony_ci adapter->check_task_cnt = 0; 27378c2ecf20Sopenharmony_ci } 27388c2ecf20Sopenharmony_ci 27398c2ecf20Sopenharmony_ci if (p->rev == T3_REV_B2) 27408c2ecf20Sopenharmony_ci check_t3b2_mac(adapter); 27418c2ecf20Sopenharmony_ci 27428c2ecf20Sopenharmony_ci /* 27438c2ecf20Sopenharmony_ci * Scan the XGMAC's to check for various conditions which we want to 27448c2ecf20Sopenharmony_ci * monitor in a periodic polling manner rather than via an interrupt 27458c2ecf20Sopenharmony_ci * condition. This is used for conditions which would otherwise flood 27468c2ecf20Sopenharmony_ci * the system with interrupts and we only really need to know that the 27478c2ecf20Sopenharmony_ci * conditions are "happening" ... For each condition we count the 27488c2ecf20Sopenharmony_ci * detection of the condition and reset it for the next polling loop. 27498c2ecf20Sopenharmony_ci */ 27508c2ecf20Sopenharmony_ci for_each_port(adapter, port) { 27518c2ecf20Sopenharmony_ci struct cmac *mac = &adap2pinfo(adapter, port)->mac; 27528c2ecf20Sopenharmony_ci u32 cause; 27538c2ecf20Sopenharmony_ci 27548c2ecf20Sopenharmony_ci cause = t3_read_reg(adapter, A_XGM_INT_CAUSE + mac->offset); 27558c2ecf20Sopenharmony_ci reset = 0; 27568c2ecf20Sopenharmony_ci if (cause & F_RXFIFO_OVERFLOW) { 27578c2ecf20Sopenharmony_ci mac->stats.rx_fifo_ovfl++; 27588c2ecf20Sopenharmony_ci reset |= F_RXFIFO_OVERFLOW; 27598c2ecf20Sopenharmony_ci } 27608c2ecf20Sopenharmony_ci 27618c2ecf20Sopenharmony_ci t3_write_reg(adapter, A_XGM_INT_CAUSE + mac->offset, reset); 27628c2ecf20Sopenharmony_ci } 27638c2ecf20Sopenharmony_ci 27648c2ecf20Sopenharmony_ci /* 27658c2ecf20Sopenharmony_ci * We do the same as above for FL_EMPTY interrupts. 27668c2ecf20Sopenharmony_ci */ 27678c2ecf20Sopenharmony_ci status = t3_read_reg(adapter, A_SG_INT_CAUSE); 27688c2ecf20Sopenharmony_ci reset = 0; 27698c2ecf20Sopenharmony_ci 27708c2ecf20Sopenharmony_ci if (status & F_FLEMPTY) { 27718c2ecf20Sopenharmony_ci struct sge_qset *qs = &adapter->sge.qs[0]; 27728c2ecf20Sopenharmony_ci int i = 0; 27738c2ecf20Sopenharmony_ci 27748c2ecf20Sopenharmony_ci reset |= F_FLEMPTY; 27758c2ecf20Sopenharmony_ci 27768c2ecf20Sopenharmony_ci v = (t3_read_reg(adapter, A_SG_RSPQ_FL_STATUS) >> S_FL0EMPTY) & 27778c2ecf20Sopenharmony_ci 0xffff; 27788c2ecf20Sopenharmony_ci 27798c2ecf20Sopenharmony_ci while (v) { 27808c2ecf20Sopenharmony_ci qs->fl[i].empty += (v & 1); 27818c2ecf20Sopenharmony_ci if (i) 27828c2ecf20Sopenharmony_ci qs++; 27838c2ecf20Sopenharmony_ci i ^= 1; 27848c2ecf20Sopenharmony_ci v >>= 1; 27858c2ecf20Sopenharmony_ci } 27868c2ecf20Sopenharmony_ci } 27878c2ecf20Sopenharmony_ci 27888c2ecf20Sopenharmony_ci t3_write_reg(adapter, A_SG_INT_CAUSE, reset); 27898c2ecf20Sopenharmony_ci 27908c2ecf20Sopenharmony_ci /* Schedule the next check update if any port is active. */ 27918c2ecf20Sopenharmony_ci spin_lock_irq(&adapter->work_lock); 27928c2ecf20Sopenharmony_ci if (adapter->open_device_map & PORT_MASK) 27938c2ecf20Sopenharmony_ci schedule_chk_task(adapter); 27948c2ecf20Sopenharmony_ci spin_unlock_irq(&adapter->work_lock); 27958c2ecf20Sopenharmony_ci} 27968c2ecf20Sopenharmony_ci 27978c2ecf20Sopenharmony_cistatic void db_full_task(struct work_struct *work) 27988c2ecf20Sopenharmony_ci{ 27998c2ecf20Sopenharmony_ci struct adapter *adapter = container_of(work, struct adapter, 28008c2ecf20Sopenharmony_ci db_full_task); 28018c2ecf20Sopenharmony_ci 28028c2ecf20Sopenharmony_ci cxgb3_event_notify(&adapter->tdev, OFFLOAD_DB_FULL, 0); 28038c2ecf20Sopenharmony_ci} 28048c2ecf20Sopenharmony_ci 28058c2ecf20Sopenharmony_cistatic void db_empty_task(struct work_struct *work) 28068c2ecf20Sopenharmony_ci{ 28078c2ecf20Sopenharmony_ci struct adapter *adapter = container_of(work, struct adapter, 28088c2ecf20Sopenharmony_ci db_empty_task); 28098c2ecf20Sopenharmony_ci 28108c2ecf20Sopenharmony_ci cxgb3_event_notify(&adapter->tdev, OFFLOAD_DB_EMPTY, 0); 28118c2ecf20Sopenharmony_ci} 28128c2ecf20Sopenharmony_ci 28138c2ecf20Sopenharmony_cistatic void db_drop_task(struct work_struct *work) 28148c2ecf20Sopenharmony_ci{ 28158c2ecf20Sopenharmony_ci struct adapter *adapter = container_of(work, struct adapter, 28168c2ecf20Sopenharmony_ci db_drop_task); 28178c2ecf20Sopenharmony_ci unsigned long delay = 1000; 28188c2ecf20Sopenharmony_ci unsigned short r; 28198c2ecf20Sopenharmony_ci 28208c2ecf20Sopenharmony_ci cxgb3_event_notify(&adapter->tdev, OFFLOAD_DB_DROP, 0); 28218c2ecf20Sopenharmony_ci 28228c2ecf20Sopenharmony_ci /* 28238c2ecf20Sopenharmony_ci * Sleep a while before ringing the driver qset dbs. 28248c2ecf20Sopenharmony_ci * The delay is between 1000-2023 usecs. 28258c2ecf20Sopenharmony_ci */ 28268c2ecf20Sopenharmony_ci get_random_bytes(&r, 2); 28278c2ecf20Sopenharmony_ci delay += r & 1023; 28288c2ecf20Sopenharmony_ci set_current_state(TASK_UNINTERRUPTIBLE); 28298c2ecf20Sopenharmony_ci schedule_timeout(usecs_to_jiffies(delay)); 28308c2ecf20Sopenharmony_ci ring_dbs(adapter); 28318c2ecf20Sopenharmony_ci} 28328c2ecf20Sopenharmony_ci 28338c2ecf20Sopenharmony_ci/* 28348c2ecf20Sopenharmony_ci * Processes external (PHY) interrupts in process context. 28358c2ecf20Sopenharmony_ci */ 28368c2ecf20Sopenharmony_cistatic void ext_intr_task(struct work_struct *work) 28378c2ecf20Sopenharmony_ci{ 28388c2ecf20Sopenharmony_ci struct adapter *adapter = container_of(work, struct adapter, 28398c2ecf20Sopenharmony_ci ext_intr_handler_task); 28408c2ecf20Sopenharmony_ci int i; 28418c2ecf20Sopenharmony_ci 28428c2ecf20Sopenharmony_ci /* Disable link fault interrupts */ 28438c2ecf20Sopenharmony_ci for_each_port(adapter, i) { 28448c2ecf20Sopenharmony_ci struct net_device *dev = adapter->port[i]; 28458c2ecf20Sopenharmony_ci struct port_info *p = netdev_priv(dev); 28468c2ecf20Sopenharmony_ci 28478c2ecf20Sopenharmony_ci t3_xgm_intr_disable(adapter, i); 28488c2ecf20Sopenharmony_ci t3_read_reg(adapter, A_XGM_INT_STATUS + p->mac.offset); 28498c2ecf20Sopenharmony_ci } 28508c2ecf20Sopenharmony_ci 28518c2ecf20Sopenharmony_ci /* Re-enable link fault interrupts */ 28528c2ecf20Sopenharmony_ci t3_phy_intr_handler(adapter); 28538c2ecf20Sopenharmony_ci 28548c2ecf20Sopenharmony_ci for_each_port(adapter, i) 28558c2ecf20Sopenharmony_ci t3_xgm_intr_enable(adapter, i); 28568c2ecf20Sopenharmony_ci 28578c2ecf20Sopenharmony_ci /* Now reenable external interrupts */ 28588c2ecf20Sopenharmony_ci spin_lock_irq(&adapter->work_lock); 28598c2ecf20Sopenharmony_ci if (adapter->slow_intr_mask) { 28608c2ecf20Sopenharmony_ci adapter->slow_intr_mask |= F_T3DBG; 28618c2ecf20Sopenharmony_ci t3_write_reg(adapter, A_PL_INT_CAUSE0, F_T3DBG); 28628c2ecf20Sopenharmony_ci t3_write_reg(adapter, A_PL_INT_ENABLE0, 28638c2ecf20Sopenharmony_ci adapter->slow_intr_mask); 28648c2ecf20Sopenharmony_ci } 28658c2ecf20Sopenharmony_ci spin_unlock_irq(&adapter->work_lock); 28668c2ecf20Sopenharmony_ci} 28678c2ecf20Sopenharmony_ci 28688c2ecf20Sopenharmony_ci/* 28698c2ecf20Sopenharmony_ci * Interrupt-context handler for external (PHY) interrupts. 28708c2ecf20Sopenharmony_ci */ 28718c2ecf20Sopenharmony_civoid t3_os_ext_intr_handler(struct adapter *adapter) 28728c2ecf20Sopenharmony_ci{ 28738c2ecf20Sopenharmony_ci /* 28748c2ecf20Sopenharmony_ci * Schedule a task to handle external interrupts as they may be slow 28758c2ecf20Sopenharmony_ci * and we use a mutex to protect MDIO registers. We disable PHY 28768c2ecf20Sopenharmony_ci * interrupts in the meantime and let the task reenable them when 28778c2ecf20Sopenharmony_ci * it's done. 28788c2ecf20Sopenharmony_ci */ 28798c2ecf20Sopenharmony_ci spin_lock(&adapter->work_lock); 28808c2ecf20Sopenharmony_ci if (adapter->slow_intr_mask) { 28818c2ecf20Sopenharmony_ci adapter->slow_intr_mask &= ~F_T3DBG; 28828c2ecf20Sopenharmony_ci t3_write_reg(adapter, A_PL_INT_ENABLE0, 28838c2ecf20Sopenharmony_ci adapter->slow_intr_mask); 28848c2ecf20Sopenharmony_ci queue_work(cxgb3_wq, &adapter->ext_intr_handler_task); 28858c2ecf20Sopenharmony_ci } 28868c2ecf20Sopenharmony_ci spin_unlock(&adapter->work_lock); 28878c2ecf20Sopenharmony_ci} 28888c2ecf20Sopenharmony_ci 28898c2ecf20Sopenharmony_civoid t3_os_link_fault_handler(struct adapter *adapter, int port_id) 28908c2ecf20Sopenharmony_ci{ 28918c2ecf20Sopenharmony_ci struct net_device *netdev = adapter->port[port_id]; 28928c2ecf20Sopenharmony_ci struct port_info *pi = netdev_priv(netdev); 28938c2ecf20Sopenharmony_ci 28948c2ecf20Sopenharmony_ci spin_lock(&adapter->work_lock); 28958c2ecf20Sopenharmony_ci pi->link_fault = 1; 28968c2ecf20Sopenharmony_ci spin_unlock(&adapter->work_lock); 28978c2ecf20Sopenharmony_ci} 28988c2ecf20Sopenharmony_ci 28998c2ecf20Sopenharmony_cistatic int t3_adapter_error(struct adapter *adapter, int reset, int on_wq) 29008c2ecf20Sopenharmony_ci{ 29018c2ecf20Sopenharmony_ci int i, ret = 0; 29028c2ecf20Sopenharmony_ci 29038c2ecf20Sopenharmony_ci if (is_offload(adapter) && 29048c2ecf20Sopenharmony_ci test_bit(OFFLOAD_DEVMAP_BIT, &adapter->open_device_map)) { 29058c2ecf20Sopenharmony_ci cxgb3_event_notify(&adapter->tdev, OFFLOAD_STATUS_DOWN, 0); 29068c2ecf20Sopenharmony_ci offload_close(&adapter->tdev); 29078c2ecf20Sopenharmony_ci } 29088c2ecf20Sopenharmony_ci 29098c2ecf20Sopenharmony_ci /* Stop all ports */ 29108c2ecf20Sopenharmony_ci for_each_port(adapter, i) { 29118c2ecf20Sopenharmony_ci struct net_device *netdev = adapter->port[i]; 29128c2ecf20Sopenharmony_ci 29138c2ecf20Sopenharmony_ci if (netif_running(netdev)) 29148c2ecf20Sopenharmony_ci __cxgb_close(netdev, on_wq); 29158c2ecf20Sopenharmony_ci } 29168c2ecf20Sopenharmony_ci 29178c2ecf20Sopenharmony_ci /* Stop SGE timers */ 29188c2ecf20Sopenharmony_ci t3_stop_sge_timers(adapter); 29198c2ecf20Sopenharmony_ci 29208c2ecf20Sopenharmony_ci adapter->flags &= ~FULL_INIT_DONE; 29218c2ecf20Sopenharmony_ci 29228c2ecf20Sopenharmony_ci if (reset) 29238c2ecf20Sopenharmony_ci ret = t3_reset_adapter(adapter); 29248c2ecf20Sopenharmony_ci 29258c2ecf20Sopenharmony_ci pci_disable_device(adapter->pdev); 29268c2ecf20Sopenharmony_ci 29278c2ecf20Sopenharmony_ci return ret; 29288c2ecf20Sopenharmony_ci} 29298c2ecf20Sopenharmony_ci 29308c2ecf20Sopenharmony_cistatic int t3_reenable_adapter(struct adapter *adapter) 29318c2ecf20Sopenharmony_ci{ 29328c2ecf20Sopenharmony_ci if (pci_enable_device(adapter->pdev)) { 29338c2ecf20Sopenharmony_ci dev_err(&adapter->pdev->dev, 29348c2ecf20Sopenharmony_ci "Cannot re-enable PCI device after reset.\n"); 29358c2ecf20Sopenharmony_ci goto err; 29368c2ecf20Sopenharmony_ci } 29378c2ecf20Sopenharmony_ci pci_set_master(adapter->pdev); 29388c2ecf20Sopenharmony_ci pci_restore_state(adapter->pdev); 29398c2ecf20Sopenharmony_ci pci_save_state(adapter->pdev); 29408c2ecf20Sopenharmony_ci 29418c2ecf20Sopenharmony_ci /* Free sge resources */ 29428c2ecf20Sopenharmony_ci t3_free_sge_resources(adapter); 29438c2ecf20Sopenharmony_ci 29448c2ecf20Sopenharmony_ci if (t3_replay_prep_adapter(adapter)) 29458c2ecf20Sopenharmony_ci goto err; 29468c2ecf20Sopenharmony_ci 29478c2ecf20Sopenharmony_ci return 0; 29488c2ecf20Sopenharmony_cierr: 29498c2ecf20Sopenharmony_ci return -1; 29508c2ecf20Sopenharmony_ci} 29518c2ecf20Sopenharmony_ci 29528c2ecf20Sopenharmony_cistatic void t3_resume_ports(struct adapter *adapter) 29538c2ecf20Sopenharmony_ci{ 29548c2ecf20Sopenharmony_ci int i; 29558c2ecf20Sopenharmony_ci 29568c2ecf20Sopenharmony_ci /* Restart the ports */ 29578c2ecf20Sopenharmony_ci for_each_port(adapter, i) { 29588c2ecf20Sopenharmony_ci struct net_device *netdev = adapter->port[i]; 29598c2ecf20Sopenharmony_ci 29608c2ecf20Sopenharmony_ci if (netif_running(netdev)) { 29618c2ecf20Sopenharmony_ci if (cxgb_open(netdev)) { 29628c2ecf20Sopenharmony_ci dev_err(&adapter->pdev->dev, 29638c2ecf20Sopenharmony_ci "can't bring device back up" 29648c2ecf20Sopenharmony_ci " after reset\n"); 29658c2ecf20Sopenharmony_ci continue; 29668c2ecf20Sopenharmony_ci } 29678c2ecf20Sopenharmony_ci } 29688c2ecf20Sopenharmony_ci } 29698c2ecf20Sopenharmony_ci 29708c2ecf20Sopenharmony_ci if (is_offload(adapter) && !ofld_disable) 29718c2ecf20Sopenharmony_ci cxgb3_event_notify(&adapter->tdev, OFFLOAD_STATUS_UP, 0); 29728c2ecf20Sopenharmony_ci} 29738c2ecf20Sopenharmony_ci 29748c2ecf20Sopenharmony_ci/* 29758c2ecf20Sopenharmony_ci * processes a fatal error. 29768c2ecf20Sopenharmony_ci * Bring the ports down, reset the chip, bring the ports back up. 29778c2ecf20Sopenharmony_ci */ 29788c2ecf20Sopenharmony_cistatic void fatal_error_task(struct work_struct *work) 29798c2ecf20Sopenharmony_ci{ 29808c2ecf20Sopenharmony_ci struct adapter *adapter = container_of(work, struct adapter, 29818c2ecf20Sopenharmony_ci fatal_error_handler_task); 29828c2ecf20Sopenharmony_ci int err = 0; 29838c2ecf20Sopenharmony_ci 29848c2ecf20Sopenharmony_ci rtnl_lock(); 29858c2ecf20Sopenharmony_ci err = t3_adapter_error(adapter, 1, 1); 29868c2ecf20Sopenharmony_ci if (!err) 29878c2ecf20Sopenharmony_ci err = t3_reenable_adapter(adapter); 29888c2ecf20Sopenharmony_ci if (!err) 29898c2ecf20Sopenharmony_ci t3_resume_ports(adapter); 29908c2ecf20Sopenharmony_ci 29918c2ecf20Sopenharmony_ci CH_ALERT(adapter, "adapter reset %s\n", err ? "failed" : "succeeded"); 29928c2ecf20Sopenharmony_ci rtnl_unlock(); 29938c2ecf20Sopenharmony_ci} 29948c2ecf20Sopenharmony_ci 29958c2ecf20Sopenharmony_civoid t3_fatal_err(struct adapter *adapter) 29968c2ecf20Sopenharmony_ci{ 29978c2ecf20Sopenharmony_ci unsigned int fw_status[4]; 29988c2ecf20Sopenharmony_ci 29998c2ecf20Sopenharmony_ci if (adapter->flags & FULL_INIT_DONE) { 30008c2ecf20Sopenharmony_ci t3_sge_stop_dma(adapter); 30018c2ecf20Sopenharmony_ci t3_write_reg(adapter, A_XGM_TX_CTRL, 0); 30028c2ecf20Sopenharmony_ci t3_write_reg(adapter, A_XGM_RX_CTRL, 0); 30038c2ecf20Sopenharmony_ci t3_write_reg(adapter, XGM_REG(A_XGM_TX_CTRL, 1), 0); 30048c2ecf20Sopenharmony_ci t3_write_reg(adapter, XGM_REG(A_XGM_RX_CTRL, 1), 0); 30058c2ecf20Sopenharmony_ci 30068c2ecf20Sopenharmony_ci spin_lock(&adapter->work_lock); 30078c2ecf20Sopenharmony_ci t3_intr_disable(adapter); 30088c2ecf20Sopenharmony_ci queue_work(cxgb3_wq, &adapter->fatal_error_handler_task); 30098c2ecf20Sopenharmony_ci spin_unlock(&adapter->work_lock); 30108c2ecf20Sopenharmony_ci } 30118c2ecf20Sopenharmony_ci CH_ALERT(adapter, "encountered fatal error, operation suspended\n"); 30128c2ecf20Sopenharmony_ci if (!t3_cim_ctl_blk_read(adapter, 0xa0, 4, fw_status)) 30138c2ecf20Sopenharmony_ci CH_ALERT(adapter, "FW status: 0x%x, 0x%x, 0x%x, 0x%x\n", 30148c2ecf20Sopenharmony_ci fw_status[0], fw_status[1], 30158c2ecf20Sopenharmony_ci fw_status[2], fw_status[3]); 30168c2ecf20Sopenharmony_ci} 30178c2ecf20Sopenharmony_ci 30188c2ecf20Sopenharmony_ci/** 30198c2ecf20Sopenharmony_ci * t3_io_error_detected - called when PCI error is detected 30208c2ecf20Sopenharmony_ci * @pdev: Pointer to PCI device 30218c2ecf20Sopenharmony_ci * @state: The current pci connection state 30228c2ecf20Sopenharmony_ci * 30238c2ecf20Sopenharmony_ci * This function is called after a PCI bus error affecting 30248c2ecf20Sopenharmony_ci * this device has been detected. 30258c2ecf20Sopenharmony_ci */ 30268c2ecf20Sopenharmony_cistatic pci_ers_result_t t3_io_error_detected(struct pci_dev *pdev, 30278c2ecf20Sopenharmony_ci pci_channel_state_t state) 30288c2ecf20Sopenharmony_ci{ 30298c2ecf20Sopenharmony_ci struct adapter *adapter = pci_get_drvdata(pdev); 30308c2ecf20Sopenharmony_ci 30318c2ecf20Sopenharmony_ci if (state == pci_channel_io_perm_failure) 30328c2ecf20Sopenharmony_ci return PCI_ERS_RESULT_DISCONNECT; 30338c2ecf20Sopenharmony_ci 30348c2ecf20Sopenharmony_ci t3_adapter_error(adapter, 0, 0); 30358c2ecf20Sopenharmony_ci 30368c2ecf20Sopenharmony_ci /* Request a slot reset. */ 30378c2ecf20Sopenharmony_ci return PCI_ERS_RESULT_NEED_RESET; 30388c2ecf20Sopenharmony_ci} 30398c2ecf20Sopenharmony_ci 30408c2ecf20Sopenharmony_ci/** 30418c2ecf20Sopenharmony_ci * t3_io_slot_reset - called after the pci bus has been reset. 30428c2ecf20Sopenharmony_ci * @pdev: Pointer to PCI device 30438c2ecf20Sopenharmony_ci * 30448c2ecf20Sopenharmony_ci * Restart the card from scratch, as if from a cold-boot. 30458c2ecf20Sopenharmony_ci */ 30468c2ecf20Sopenharmony_cistatic pci_ers_result_t t3_io_slot_reset(struct pci_dev *pdev) 30478c2ecf20Sopenharmony_ci{ 30488c2ecf20Sopenharmony_ci struct adapter *adapter = pci_get_drvdata(pdev); 30498c2ecf20Sopenharmony_ci 30508c2ecf20Sopenharmony_ci if (!t3_reenable_adapter(adapter)) 30518c2ecf20Sopenharmony_ci return PCI_ERS_RESULT_RECOVERED; 30528c2ecf20Sopenharmony_ci 30538c2ecf20Sopenharmony_ci return PCI_ERS_RESULT_DISCONNECT; 30548c2ecf20Sopenharmony_ci} 30558c2ecf20Sopenharmony_ci 30568c2ecf20Sopenharmony_ci/** 30578c2ecf20Sopenharmony_ci * t3_io_resume - called when traffic can start flowing again. 30588c2ecf20Sopenharmony_ci * @pdev: Pointer to PCI device 30598c2ecf20Sopenharmony_ci * 30608c2ecf20Sopenharmony_ci * This callback is called when the error recovery driver tells us that 30618c2ecf20Sopenharmony_ci * its OK to resume normal operation. 30628c2ecf20Sopenharmony_ci */ 30638c2ecf20Sopenharmony_cistatic void t3_io_resume(struct pci_dev *pdev) 30648c2ecf20Sopenharmony_ci{ 30658c2ecf20Sopenharmony_ci struct adapter *adapter = pci_get_drvdata(pdev); 30668c2ecf20Sopenharmony_ci 30678c2ecf20Sopenharmony_ci CH_ALERT(adapter, "adapter recovering, PEX ERR 0x%x\n", 30688c2ecf20Sopenharmony_ci t3_read_reg(adapter, A_PCIE_PEX_ERR)); 30698c2ecf20Sopenharmony_ci 30708c2ecf20Sopenharmony_ci rtnl_lock(); 30718c2ecf20Sopenharmony_ci t3_resume_ports(adapter); 30728c2ecf20Sopenharmony_ci rtnl_unlock(); 30738c2ecf20Sopenharmony_ci} 30748c2ecf20Sopenharmony_ci 30758c2ecf20Sopenharmony_cistatic const struct pci_error_handlers t3_err_handler = { 30768c2ecf20Sopenharmony_ci .error_detected = t3_io_error_detected, 30778c2ecf20Sopenharmony_ci .slot_reset = t3_io_slot_reset, 30788c2ecf20Sopenharmony_ci .resume = t3_io_resume, 30798c2ecf20Sopenharmony_ci}; 30808c2ecf20Sopenharmony_ci 30818c2ecf20Sopenharmony_ci/* 30828c2ecf20Sopenharmony_ci * Set the number of qsets based on the number of CPUs and the number of ports, 30838c2ecf20Sopenharmony_ci * not to exceed the number of available qsets, assuming there are enough qsets 30848c2ecf20Sopenharmony_ci * per port in HW. 30858c2ecf20Sopenharmony_ci */ 30868c2ecf20Sopenharmony_cistatic void set_nqsets(struct adapter *adap) 30878c2ecf20Sopenharmony_ci{ 30888c2ecf20Sopenharmony_ci int i, j = 0; 30898c2ecf20Sopenharmony_ci int num_cpus = netif_get_num_default_rss_queues(); 30908c2ecf20Sopenharmony_ci int hwports = adap->params.nports; 30918c2ecf20Sopenharmony_ci int nqsets = adap->msix_nvectors - 1; 30928c2ecf20Sopenharmony_ci 30938c2ecf20Sopenharmony_ci if (adap->params.rev > 0 && adap->flags & USING_MSIX) { 30948c2ecf20Sopenharmony_ci if (hwports == 2 && 30958c2ecf20Sopenharmony_ci (hwports * nqsets > SGE_QSETS || 30968c2ecf20Sopenharmony_ci num_cpus >= nqsets / hwports)) 30978c2ecf20Sopenharmony_ci nqsets /= hwports; 30988c2ecf20Sopenharmony_ci if (nqsets > num_cpus) 30998c2ecf20Sopenharmony_ci nqsets = num_cpus; 31008c2ecf20Sopenharmony_ci if (nqsets < 1 || hwports == 4) 31018c2ecf20Sopenharmony_ci nqsets = 1; 31028c2ecf20Sopenharmony_ci } else 31038c2ecf20Sopenharmony_ci nqsets = 1; 31048c2ecf20Sopenharmony_ci 31058c2ecf20Sopenharmony_ci for_each_port(adap, i) { 31068c2ecf20Sopenharmony_ci struct port_info *pi = adap2pinfo(adap, i); 31078c2ecf20Sopenharmony_ci 31088c2ecf20Sopenharmony_ci pi->first_qset = j; 31098c2ecf20Sopenharmony_ci pi->nqsets = nqsets; 31108c2ecf20Sopenharmony_ci j = pi->first_qset + nqsets; 31118c2ecf20Sopenharmony_ci 31128c2ecf20Sopenharmony_ci dev_info(&adap->pdev->dev, 31138c2ecf20Sopenharmony_ci "Port %d using %d queue sets.\n", i, nqsets); 31148c2ecf20Sopenharmony_ci } 31158c2ecf20Sopenharmony_ci} 31168c2ecf20Sopenharmony_ci 31178c2ecf20Sopenharmony_cistatic int cxgb_enable_msix(struct adapter *adap) 31188c2ecf20Sopenharmony_ci{ 31198c2ecf20Sopenharmony_ci struct msix_entry entries[SGE_QSETS + 1]; 31208c2ecf20Sopenharmony_ci int vectors; 31218c2ecf20Sopenharmony_ci int i; 31228c2ecf20Sopenharmony_ci 31238c2ecf20Sopenharmony_ci vectors = ARRAY_SIZE(entries); 31248c2ecf20Sopenharmony_ci for (i = 0; i < vectors; ++i) 31258c2ecf20Sopenharmony_ci entries[i].entry = i; 31268c2ecf20Sopenharmony_ci 31278c2ecf20Sopenharmony_ci vectors = pci_enable_msix_range(adap->pdev, entries, 31288c2ecf20Sopenharmony_ci adap->params.nports + 1, vectors); 31298c2ecf20Sopenharmony_ci if (vectors < 0) 31308c2ecf20Sopenharmony_ci return vectors; 31318c2ecf20Sopenharmony_ci 31328c2ecf20Sopenharmony_ci for (i = 0; i < vectors; ++i) 31338c2ecf20Sopenharmony_ci adap->msix_info[i].vec = entries[i].vector; 31348c2ecf20Sopenharmony_ci adap->msix_nvectors = vectors; 31358c2ecf20Sopenharmony_ci 31368c2ecf20Sopenharmony_ci return 0; 31378c2ecf20Sopenharmony_ci} 31388c2ecf20Sopenharmony_ci 31398c2ecf20Sopenharmony_cistatic void print_port_info(struct adapter *adap, const struct adapter_info *ai) 31408c2ecf20Sopenharmony_ci{ 31418c2ecf20Sopenharmony_ci static const char *pci_variant[] = { 31428c2ecf20Sopenharmony_ci "PCI", "PCI-X", "PCI-X ECC", "PCI-X 266", "PCI Express" 31438c2ecf20Sopenharmony_ci }; 31448c2ecf20Sopenharmony_ci 31458c2ecf20Sopenharmony_ci int i; 31468c2ecf20Sopenharmony_ci char buf[80]; 31478c2ecf20Sopenharmony_ci 31488c2ecf20Sopenharmony_ci if (is_pcie(adap)) 31498c2ecf20Sopenharmony_ci snprintf(buf, sizeof(buf), "%s x%d", 31508c2ecf20Sopenharmony_ci pci_variant[adap->params.pci.variant], 31518c2ecf20Sopenharmony_ci adap->params.pci.width); 31528c2ecf20Sopenharmony_ci else 31538c2ecf20Sopenharmony_ci snprintf(buf, sizeof(buf), "%s %dMHz/%d-bit", 31548c2ecf20Sopenharmony_ci pci_variant[adap->params.pci.variant], 31558c2ecf20Sopenharmony_ci adap->params.pci.speed, adap->params.pci.width); 31568c2ecf20Sopenharmony_ci 31578c2ecf20Sopenharmony_ci for_each_port(adap, i) { 31588c2ecf20Sopenharmony_ci struct net_device *dev = adap->port[i]; 31598c2ecf20Sopenharmony_ci const struct port_info *pi = netdev_priv(dev); 31608c2ecf20Sopenharmony_ci 31618c2ecf20Sopenharmony_ci if (!test_bit(i, &adap->registered_device_map)) 31628c2ecf20Sopenharmony_ci continue; 31638c2ecf20Sopenharmony_ci netdev_info(dev, "%s %s %sNIC (rev %d) %s%s\n", 31648c2ecf20Sopenharmony_ci ai->desc, pi->phy.desc, 31658c2ecf20Sopenharmony_ci is_offload(adap) ? "R" : "", adap->params.rev, buf, 31668c2ecf20Sopenharmony_ci (adap->flags & USING_MSIX) ? " MSI-X" : 31678c2ecf20Sopenharmony_ci (adap->flags & USING_MSI) ? " MSI" : ""); 31688c2ecf20Sopenharmony_ci if (adap->name == dev->name && adap->params.vpd.mclk) 31698c2ecf20Sopenharmony_ci pr_info("%s: %uMB CM, %uMB PMTX, %uMB PMRX, S/N: %s\n", 31708c2ecf20Sopenharmony_ci adap->name, t3_mc7_size(&adap->cm) >> 20, 31718c2ecf20Sopenharmony_ci t3_mc7_size(&adap->pmtx) >> 20, 31728c2ecf20Sopenharmony_ci t3_mc7_size(&adap->pmrx) >> 20, 31738c2ecf20Sopenharmony_ci adap->params.vpd.sn); 31748c2ecf20Sopenharmony_ci } 31758c2ecf20Sopenharmony_ci} 31768c2ecf20Sopenharmony_ci 31778c2ecf20Sopenharmony_cistatic const struct net_device_ops cxgb_netdev_ops = { 31788c2ecf20Sopenharmony_ci .ndo_open = cxgb_open, 31798c2ecf20Sopenharmony_ci .ndo_stop = cxgb_close, 31808c2ecf20Sopenharmony_ci .ndo_start_xmit = t3_eth_xmit, 31818c2ecf20Sopenharmony_ci .ndo_get_stats = cxgb_get_stats, 31828c2ecf20Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 31838c2ecf20Sopenharmony_ci .ndo_set_rx_mode = cxgb_set_rxmode, 31848c2ecf20Sopenharmony_ci .ndo_do_ioctl = cxgb_ioctl, 31858c2ecf20Sopenharmony_ci .ndo_change_mtu = cxgb_change_mtu, 31868c2ecf20Sopenharmony_ci .ndo_set_mac_address = cxgb_set_mac_addr, 31878c2ecf20Sopenharmony_ci .ndo_fix_features = cxgb_fix_features, 31888c2ecf20Sopenharmony_ci .ndo_set_features = cxgb_set_features, 31898c2ecf20Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER 31908c2ecf20Sopenharmony_ci .ndo_poll_controller = cxgb_netpoll, 31918c2ecf20Sopenharmony_ci#endif 31928c2ecf20Sopenharmony_ci}; 31938c2ecf20Sopenharmony_ci 31948c2ecf20Sopenharmony_cistatic void cxgb3_init_iscsi_mac(struct net_device *dev) 31958c2ecf20Sopenharmony_ci{ 31968c2ecf20Sopenharmony_ci struct port_info *pi = netdev_priv(dev); 31978c2ecf20Sopenharmony_ci 31988c2ecf20Sopenharmony_ci memcpy(pi->iscsic.mac_addr, dev->dev_addr, ETH_ALEN); 31998c2ecf20Sopenharmony_ci pi->iscsic.mac_addr[3] |= 0x80; 32008c2ecf20Sopenharmony_ci} 32018c2ecf20Sopenharmony_ci 32028c2ecf20Sopenharmony_ci#define TSO_FLAGS (NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_TSO_ECN) 32038c2ecf20Sopenharmony_ci#define VLAN_FEAT (NETIF_F_SG | NETIF_F_IP_CSUM | TSO_FLAGS | \ 32048c2ecf20Sopenharmony_ci NETIF_F_IPV6_CSUM | NETIF_F_HIGHDMA) 32058c2ecf20Sopenharmony_cistatic int init_one(struct pci_dev *pdev, const struct pci_device_id *ent) 32068c2ecf20Sopenharmony_ci{ 32078c2ecf20Sopenharmony_ci int i, err, pci_using_dac = 0; 32088c2ecf20Sopenharmony_ci resource_size_t mmio_start, mmio_len; 32098c2ecf20Sopenharmony_ci const struct adapter_info *ai; 32108c2ecf20Sopenharmony_ci struct adapter *adapter = NULL; 32118c2ecf20Sopenharmony_ci struct port_info *pi; 32128c2ecf20Sopenharmony_ci 32138c2ecf20Sopenharmony_ci if (!cxgb3_wq) { 32148c2ecf20Sopenharmony_ci cxgb3_wq = create_singlethread_workqueue(DRV_NAME); 32158c2ecf20Sopenharmony_ci if (!cxgb3_wq) { 32168c2ecf20Sopenharmony_ci pr_err("cannot initialize work queue\n"); 32178c2ecf20Sopenharmony_ci return -ENOMEM; 32188c2ecf20Sopenharmony_ci } 32198c2ecf20Sopenharmony_ci } 32208c2ecf20Sopenharmony_ci 32218c2ecf20Sopenharmony_ci err = pci_enable_device(pdev); 32228c2ecf20Sopenharmony_ci if (err) { 32238c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "cannot enable PCI device\n"); 32248c2ecf20Sopenharmony_ci goto out; 32258c2ecf20Sopenharmony_ci } 32268c2ecf20Sopenharmony_ci 32278c2ecf20Sopenharmony_ci err = pci_request_regions(pdev, DRV_NAME); 32288c2ecf20Sopenharmony_ci if (err) { 32298c2ecf20Sopenharmony_ci /* Just info, some other driver may have claimed the device. */ 32308c2ecf20Sopenharmony_ci dev_info(&pdev->dev, "cannot obtain PCI resources\n"); 32318c2ecf20Sopenharmony_ci goto out_disable_device; 32328c2ecf20Sopenharmony_ci } 32338c2ecf20Sopenharmony_ci 32348c2ecf20Sopenharmony_ci if (!pci_set_dma_mask(pdev, DMA_BIT_MASK(64))) { 32358c2ecf20Sopenharmony_ci pci_using_dac = 1; 32368c2ecf20Sopenharmony_ci err = pci_set_consistent_dma_mask(pdev, DMA_BIT_MASK(64)); 32378c2ecf20Sopenharmony_ci if (err) { 32388c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "unable to obtain 64-bit DMA for " 32398c2ecf20Sopenharmony_ci "coherent allocations\n"); 32408c2ecf20Sopenharmony_ci goto out_release_regions; 32418c2ecf20Sopenharmony_ci } 32428c2ecf20Sopenharmony_ci } else if ((err = pci_set_dma_mask(pdev, DMA_BIT_MASK(32))) != 0) { 32438c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "no usable DMA configuration\n"); 32448c2ecf20Sopenharmony_ci goto out_release_regions; 32458c2ecf20Sopenharmony_ci } 32468c2ecf20Sopenharmony_ci 32478c2ecf20Sopenharmony_ci pci_set_master(pdev); 32488c2ecf20Sopenharmony_ci pci_save_state(pdev); 32498c2ecf20Sopenharmony_ci 32508c2ecf20Sopenharmony_ci mmio_start = pci_resource_start(pdev, 0); 32518c2ecf20Sopenharmony_ci mmio_len = pci_resource_len(pdev, 0); 32528c2ecf20Sopenharmony_ci ai = t3_get_adapter_info(ent->driver_data); 32538c2ecf20Sopenharmony_ci 32548c2ecf20Sopenharmony_ci adapter = kzalloc(sizeof(*adapter), GFP_KERNEL); 32558c2ecf20Sopenharmony_ci if (!adapter) { 32568c2ecf20Sopenharmony_ci err = -ENOMEM; 32578c2ecf20Sopenharmony_ci goto out_release_regions; 32588c2ecf20Sopenharmony_ci } 32598c2ecf20Sopenharmony_ci 32608c2ecf20Sopenharmony_ci adapter->nofail_skb = 32618c2ecf20Sopenharmony_ci alloc_skb(sizeof(struct cpl_set_tcb_field), GFP_KERNEL); 32628c2ecf20Sopenharmony_ci if (!adapter->nofail_skb) { 32638c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "cannot allocate nofail buffer\n"); 32648c2ecf20Sopenharmony_ci err = -ENOMEM; 32658c2ecf20Sopenharmony_ci goto out_free_adapter; 32668c2ecf20Sopenharmony_ci } 32678c2ecf20Sopenharmony_ci 32688c2ecf20Sopenharmony_ci adapter->regs = ioremap(mmio_start, mmio_len); 32698c2ecf20Sopenharmony_ci if (!adapter->regs) { 32708c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "cannot map device registers\n"); 32718c2ecf20Sopenharmony_ci err = -ENOMEM; 32728c2ecf20Sopenharmony_ci goto out_free_adapter_nofail; 32738c2ecf20Sopenharmony_ci } 32748c2ecf20Sopenharmony_ci 32758c2ecf20Sopenharmony_ci adapter->pdev = pdev; 32768c2ecf20Sopenharmony_ci adapter->name = pci_name(pdev); 32778c2ecf20Sopenharmony_ci adapter->msg_enable = dflt_msg_enable; 32788c2ecf20Sopenharmony_ci adapter->mmio_len = mmio_len; 32798c2ecf20Sopenharmony_ci 32808c2ecf20Sopenharmony_ci mutex_init(&adapter->mdio_lock); 32818c2ecf20Sopenharmony_ci spin_lock_init(&adapter->work_lock); 32828c2ecf20Sopenharmony_ci spin_lock_init(&adapter->stats_lock); 32838c2ecf20Sopenharmony_ci 32848c2ecf20Sopenharmony_ci INIT_LIST_HEAD(&adapter->adapter_list); 32858c2ecf20Sopenharmony_ci INIT_WORK(&adapter->ext_intr_handler_task, ext_intr_task); 32868c2ecf20Sopenharmony_ci INIT_WORK(&adapter->fatal_error_handler_task, fatal_error_task); 32878c2ecf20Sopenharmony_ci 32888c2ecf20Sopenharmony_ci INIT_WORK(&adapter->db_full_task, db_full_task); 32898c2ecf20Sopenharmony_ci INIT_WORK(&adapter->db_empty_task, db_empty_task); 32908c2ecf20Sopenharmony_ci INIT_WORK(&adapter->db_drop_task, db_drop_task); 32918c2ecf20Sopenharmony_ci 32928c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&adapter->adap_check_task, t3_adap_check_task); 32938c2ecf20Sopenharmony_ci 32948c2ecf20Sopenharmony_ci for (i = 0; i < ai->nports0 + ai->nports1; ++i) { 32958c2ecf20Sopenharmony_ci struct net_device *netdev; 32968c2ecf20Sopenharmony_ci 32978c2ecf20Sopenharmony_ci netdev = alloc_etherdev_mq(sizeof(struct port_info), SGE_QSETS); 32988c2ecf20Sopenharmony_ci if (!netdev) { 32998c2ecf20Sopenharmony_ci err = -ENOMEM; 33008c2ecf20Sopenharmony_ci goto out_free_dev; 33018c2ecf20Sopenharmony_ci } 33028c2ecf20Sopenharmony_ci 33038c2ecf20Sopenharmony_ci SET_NETDEV_DEV(netdev, &pdev->dev); 33048c2ecf20Sopenharmony_ci 33058c2ecf20Sopenharmony_ci adapter->port[i] = netdev; 33068c2ecf20Sopenharmony_ci pi = netdev_priv(netdev); 33078c2ecf20Sopenharmony_ci pi->adapter = adapter; 33088c2ecf20Sopenharmony_ci pi->port_id = i; 33098c2ecf20Sopenharmony_ci netif_carrier_off(netdev); 33108c2ecf20Sopenharmony_ci netdev->irq = pdev->irq; 33118c2ecf20Sopenharmony_ci netdev->mem_start = mmio_start; 33128c2ecf20Sopenharmony_ci netdev->mem_end = mmio_start + mmio_len - 1; 33138c2ecf20Sopenharmony_ci netdev->hw_features = NETIF_F_SG | NETIF_F_IP_CSUM | 33148c2ecf20Sopenharmony_ci NETIF_F_TSO | NETIF_F_RXCSUM | NETIF_F_HW_VLAN_CTAG_RX; 33158c2ecf20Sopenharmony_ci netdev->features |= netdev->hw_features | 33168c2ecf20Sopenharmony_ci NETIF_F_HW_VLAN_CTAG_TX; 33178c2ecf20Sopenharmony_ci netdev->vlan_features |= netdev->features & VLAN_FEAT; 33188c2ecf20Sopenharmony_ci if (pci_using_dac) 33198c2ecf20Sopenharmony_ci netdev->features |= NETIF_F_HIGHDMA; 33208c2ecf20Sopenharmony_ci 33218c2ecf20Sopenharmony_ci netdev->netdev_ops = &cxgb_netdev_ops; 33228c2ecf20Sopenharmony_ci netdev->ethtool_ops = &cxgb_ethtool_ops; 33238c2ecf20Sopenharmony_ci netdev->min_mtu = 81; 33248c2ecf20Sopenharmony_ci netdev->max_mtu = ETH_MAX_MTU; 33258c2ecf20Sopenharmony_ci netdev->dev_port = pi->port_id; 33268c2ecf20Sopenharmony_ci } 33278c2ecf20Sopenharmony_ci 33288c2ecf20Sopenharmony_ci pci_set_drvdata(pdev, adapter); 33298c2ecf20Sopenharmony_ci if (t3_prep_adapter(adapter, ai, 1) < 0) { 33308c2ecf20Sopenharmony_ci err = -ENODEV; 33318c2ecf20Sopenharmony_ci goto out_free_dev; 33328c2ecf20Sopenharmony_ci } 33338c2ecf20Sopenharmony_ci 33348c2ecf20Sopenharmony_ci /* 33358c2ecf20Sopenharmony_ci * The card is now ready to go. If any errors occur during device 33368c2ecf20Sopenharmony_ci * registration we do not fail the whole card but rather proceed only 33378c2ecf20Sopenharmony_ci * with the ports we manage to register successfully. However we must 33388c2ecf20Sopenharmony_ci * register at least one net device. 33398c2ecf20Sopenharmony_ci */ 33408c2ecf20Sopenharmony_ci for_each_port(adapter, i) { 33418c2ecf20Sopenharmony_ci err = register_netdev(adapter->port[i]); 33428c2ecf20Sopenharmony_ci if (err) 33438c2ecf20Sopenharmony_ci dev_warn(&pdev->dev, 33448c2ecf20Sopenharmony_ci "cannot register net device %s, skipping\n", 33458c2ecf20Sopenharmony_ci adapter->port[i]->name); 33468c2ecf20Sopenharmony_ci else { 33478c2ecf20Sopenharmony_ci /* 33488c2ecf20Sopenharmony_ci * Change the name we use for messages to the name of 33498c2ecf20Sopenharmony_ci * the first successfully registered interface. 33508c2ecf20Sopenharmony_ci */ 33518c2ecf20Sopenharmony_ci if (!adapter->registered_device_map) 33528c2ecf20Sopenharmony_ci adapter->name = adapter->port[i]->name; 33538c2ecf20Sopenharmony_ci 33548c2ecf20Sopenharmony_ci __set_bit(i, &adapter->registered_device_map); 33558c2ecf20Sopenharmony_ci } 33568c2ecf20Sopenharmony_ci } 33578c2ecf20Sopenharmony_ci if (!adapter->registered_device_map) { 33588c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "could not register any net devices\n"); 33598c2ecf20Sopenharmony_ci goto out_free_dev; 33608c2ecf20Sopenharmony_ci } 33618c2ecf20Sopenharmony_ci 33628c2ecf20Sopenharmony_ci for_each_port(adapter, i) 33638c2ecf20Sopenharmony_ci cxgb3_init_iscsi_mac(adapter->port[i]); 33648c2ecf20Sopenharmony_ci 33658c2ecf20Sopenharmony_ci /* Driver's ready. Reflect it on LEDs */ 33668c2ecf20Sopenharmony_ci t3_led_ready(adapter); 33678c2ecf20Sopenharmony_ci 33688c2ecf20Sopenharmony_ci if (is_offload(adapter)) { 33698c2ecf20Sopenharmony_ci __set_bit(OFFLOAD_DEVMAP_BIT, &adapter->registered_device_map); 33708c2ecf20Sopenharmony_ci cxgb3_adapter_ofld(adapter); 33718c2ecf20Sopenharmony_ci } 33728c2ecf20Sopenharmony_ci 33738c2ecf20Sopenharmony_ci /* See what interrupts we'll be using */ 33748c2ecf20Sopenharmony_ci if (msi > 1 && cxgb_enable_msix(adapter) == 0) 33758c2ecf20Sopenharmony_ci adapter->flags |= USING_MSIX; 33768c2ecf20Sopenharmony_ci else if (msi > 0 && pci_enable_msi(pdev) == 0) 33778c2ecf20Sopenharmony_ci adapter->flags |= USING_MSI; 33788c2ecf20Sopenharmony_ci 33798c2ecf20Sopenharmony_ci set_nqsets(adapter); 33808c2ecf20Sopenharmony_ci 33818c2ecf20Sopenharmony_ci err = sysfs_create_group(&adapter->port[0]->dev.kobj, 33828c2ecf20Sopenharmony_ci &cxgb3_attr_group); 33838c2ecf20Sopenharmony_ci if (err) { 33848c2ecf20Sopenharmony_ci dev_err(&pdev->dev, "cannot create sysfs group\n"); 33858c2ecf20Sopenharmony_ci goto out_close_led; 33868c2ecf20Sopenharmony_ci } 33878c2ecf20Sopenharmony_ci 33888c2ecf20Sopenharmony_ci print_port_info(adapter, ai); 33898c2ecf20Sopenharmony_ci return 0; 33908c2ecf20Sopenharmony_ci 33918c2ecf20Sopenharmony_ciout_close_led: 33928c2ecf20Sopenharmony_ci t3_set_reg_field(adapter, A_T3DBG_GPIO_EN, F_GPIO0_OUT_VAL, 0); 33938c2ecf20Sopenharmony_ci 33948c2ecf20Sopenharmony_ciout_free_dev: 33958c2ecf20Sopenharmony_ci iounmap(adapter->regs); 33968c2ecf20Sopenharmony_ci for (i = ai->nports0 + ai->nports1 - 1; i >= 0; --i) 33978c2ecf20Sopenharmony_ci if (adapter->port[i]) 33988c2ecf20Sopenharmony_ci free_netdev(adapter->port[i]); 33998c2ecf20Sopenharmony_ci 34008c2ecf20Sopenharmony_ciout_free_adapter_nofail: 34018c2ecf20Sopenharmony_ci kfree_skb(adapter->nofail_skb); 34028c2ecf20Sopenharmony_ci 34038c2ecf20Sopenharmony_ciout_free_adapter: 34048c2ecf20Sopenharmony_ci kfree(adapter); 34058c2ecf20Sopenharmony_ci 34068c2ecf20Sopenharmony_ciout_release_regions: 34078c2ecf20Sopenharmony_ci pci_release_regions(pdev); 34088c2ecf20Sopenharmony_ciout_disable_device: 34098c2ecf20Sopenharmony_ci pci_disable_device(pdev); 34108c2ecf20Sopenharmony_ciout: 34118c2ecf20Sopenharmony_ci return err; 34128c2ecf20Sopenharmony_ci} 34138c2ecf20Sopenharmony_ci 34148c2ecf20Sopenharmony_cistatic void remove_one(struct pci_dev *pdev) 34158c2ecf20Sopenharmony_ci{ 34168c2ecf20Sopenharmony_ci struct adapter *adapter = pci_get_drvdata(pdev); 34178c2ecf20Sopenharmony_ci 34188c2ecf20Sopenharmony_ci if (adapter) { 34198c2ecf20Sopenharmony_ci int i; 34208c2ecf20Sopenharmony_ci 34218c2ecf20Sopenharmony_ci t3_sge_stop(adapter); 34228c2ecf20Sopenharmony_ci sysfs_remove_group(&adapter->port[0]->dev.kobj, 34238c2ecf20Sopenharmony_ci &cxgb3_attr_group); 34248c2ecf20Sopenharmony_ci 34258c2ecf20Sopenharmony_ci if (is_offload(adapter)) { 34268c2ecf20Sopenharmony_ci cxgb3_adapter_unofld(adapter); 34278c2ecf20Sopenharmony_ci if (test_bit(OFFLOAD_DEVMAP_BIT, 34288c2ecf20Sopenharmony_ci &adapter->open_device_map)) 34298c2ecf20Sopenharmony_ci offload_close(&adapter->tdev); 34308c2ecf20Sopenharmony_ci } 34318c2ecf20Sopenharmony_ci 34328c2ecf20Sopenharmony_ci for_each_port(adapter, i) 34338c2ecf20Sopenharmony_ci if (test_bit(i, &adapter->registered_device_map)) 34348c2ecf20Sopenharmony_ci unregister_netdev(adapter->port[i]); 34358c2ecf20Sopenharmony_ci 34368c2ecf20Sopenharmony_ci t3_stop_sge_timers(adapter); 34378c2ecf20Sopenharmony_ci t3_free_sge_resources(adapter); 34388c2ecf20Sopenharmony_ci cxgb_disable_msi(adapter); 34398c2ecf20Sopenharmony_ci 34408c2ecf20Sopenharmony_ci for_each_port(adapter, i) 34418c2ecf20Sopenharmony_ci if (adapter->port[i]) 34428c2ecf20Sopenharmony_ci free_netdev(adapter->port[i]); 34438c2ecf20Sopenharmony_ci 34448c2ecf20Sopenharmony_ci iounmap(adapter->regs); 34458c2ecf20Sopenharmony_ci kfree_skb(adapter->nofail_skb); 34468c2ecf20Sopenharmony_ci kfree(adapter); 34478c2ecf20Sopenharmony_ci pci_release_regions(pdev); 34488c2ecf20Sopenharmony_ci pci_disable_device(pdev); 34498c2ecf20Sopenharmony_ci } 34508c2ecf20Sopenharmony_ci} 34518c2ecf20Sopenharmony_ci 34528c2ecf20Sopenharmony_cistatic struct pci_driver driver = { 34538c2ecf20Sopenharmony_ci .name = DRV_NAME, 34548c2ecf20Sopenharmony_ci .id_table = cxgb3_pci_tbl, 34558c2ecf20Sopenharmony_ci .probe = init_one, 34568c2ecf20Sopenharmony_ci .remove = remove_one, 34578c2ecf20Sopenharmony_ci .err_handler = &t3_err_handler, 34588c2ecf20Sopenharmony_ci}; 34598c2ecf20Sopenharmony_ci 34608c2ecf20Sopenharmony_cistatic int __init cxgb3_init_module(void) 34618c2ecf20Sopenharmony_ci{ 34628c2ecf20Sopenharmony_ci int ret; 34638c2ecf20Sopenharmony_ci 34648c2ecf20Sopenharmony_ci cxgb3_offload_init(); 34658c2ecf20Sopenharmony_ci 34668c2ecf20Sopenharmony_ci ret = pci_register_driver(&driver); 34678c2ecf20Sopenharmony_ci return ret; 34688c2ecf20Sopenharmony_ci} 34698c2ecf20Sopenharmony_ci 34708c2ecf20Sopenharmony_cistatic void __exit cxgb3_cleanup_module(void) 34718c2ecf20Sopenharmony_ci{ 34728c2ecf20Sopenharmony_ci pci_unregister_driver(&driver); 34738c2ecf20Sopenharmony_ci if (cxgb3_wq) 34748c2ecf20Sopenharmony_ci destroy_workqueue(cxgb3_wq); 34758c2ecf20Sopenharmony_ci} 34768c2ecf20Sopenharmony_ci 34778c2ecf20Sopenharmony_cimodule_init(cxgb3_init_module); 34788c2ecf20Sopenharmony_cimodule_exit(cxgb3_cleanup_module); 3479