162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Copyright (c) 2003-2008 Chelsio, Inc. All rights reserved. 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * This software is available to you under a choice of one of two 562306a36Sopenharmony_ci * licenses. You may choose to be licensed under the terms of the GNU 662306a36Sopenharmony_ci * General Public License (GPL) Version 2, available from the file 762306a36Sopenharmony_ci * COPYING in the main directory of this source tree, or the 862306a36Sopenharmony_ci * OpenIB.org BSD license below: 962306a36Sopenharmony_ci * 1062306a36Sopenharmony_ci * Redistribution and use in source and binary forms, with or 1162306a36Sopenharmony_ci * without modification, are permitted provided that the following 1262306a36Sopenharmony_ci * conditions are met: 1362306a36Sopenharmony_ci * 1462306a36Sopenharmony_ci * - Redistributions of source code must retain the above 1562306a36Sopenharmony_ci * copyright notice, this list of conditions and the following 1662306a36Sopenharmony_ci * disclaimer. 1762306a36Sopenharmony_ci * 1862306a36Sopenharmony_ci * - Redistributions in binary form must reproduce the above 1962306a36Sopenharmony_ci * copyright notice, this list of conditions and the following 2062306a36Sopenharmony_ci * disclaimer in the documentation and/or other materials 2162306a36Sopenharmony_ci * provided with the distribution. 2262306a36Sopenharmony_ci * 2362306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 2462306a36Sopenharmony_ci * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 2562306a36Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 2662306a36Sopenharmony_ci * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 2762306a36Sopenharmony_ci * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 2862306a36Sopenharmony_ci * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 2962306a36Sopenharmony_ci * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 3062306a36Sopenharmony_ci * SOFTWARE. 3162306a36Sopenharmony_ci */ 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci#include <linux/module.h> 3662306a36Sopenharmony_ci#include <linux/init.h> 3762306a36Sopenharmony_ci#include <linux/pci.h> 3862306a36Sopenharmony_ci#include <linux/dma-mapping.h> 3962306a36Sopenharmony_ci#include <linux/netdevice.h> 4062306a36Sopenharmony_ci#include <linux/etherdevice.h> 4162306a36Sopenharmony_ci#include <linux/if_vlan.h> 4262306a36Sopenharmony_ci#include <linux/mdio.h> 4362306a36Sopenharmony_ci#include <linux/sockios.h> 4462306a36Sopenharmony_ci#include <linux/workqueue.h> 4562306a36Sopenharmony_ci#include <linux/proc_fs.h> 4662306a36Sopenharmony_ci#include <linux/rtnetlink.h> 4762306a36Sopenharmony_ci#include <linux/firmware.h> 4862306a36Sopenharmony_ci#include <linux/log2.h> 4962306a36Sopenharmony_ci#include <linux/stringify.h> 5062306a36Sopenharmony_ci#include <linux/sched.h> 5162306a36Sopenharmony_ci#include <linux/slab.h> 5262306a36Sopenharmony_ci#include <linux/uaccess.h> 5362306a36Sopenharmony_ci#include <linux/nospec.h> 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ci#include "common.h" 5662306a36Sopenharmony_ci#include "cxgb3_ioctl.h" 5762306a36Sopenharmony_ci#include "regs.h" 5862306a36Sopenharmony_ci#include "cxgb3_offload.h" 5962306a36Sopenharmony_ci#include "version.h" 6062306a36Sopenharmony_ci 6162306a36Sopenharmony_ci#include "cxgb3_ctl_defs.h" 6262306a36Sopenharmony_ci#include "t3_cpl.h" 6362306a36Sopenharmony_ci#include "firmware_exports.h" 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_cienum { 6662306a36Sopenharmony_ci MAX_TXQ_ENTRIES = 16384, 6762306a36Sopenharmony_ci MAX_CTRL_TXQ_ENTRIES = 1024, 6862306a36Sopenharmony_ci MAX_RSPQ_ENTRIES = 16384, 6962306a36Sopenharmony_ci MAX_RX_BUFFERS = 16384, 7062306a36Sopenharmony_ci MAX_RX_JUMBO_BUFFERS = 16384, 7162306a36Sopenharmony_ci MIN_TXQ_ENTRIES = 4, 7262306a36Sopenharmony_ci MIN_CTRL_TXQ_ENTRIES = 4, 7362306a36Sopenharmony_ci MIN_RSPQ_ENTRIES = 32, 7462306a36Sopenharmony_ci MIN_FL_ENTRIES = 32 7562306a36Sopenharmony_ci}; 7662306a36Sopenharmony_ci 7762306a36Sopenharmony_ci#define PORT_MASK ((1 << MAX_NPORTS) - 1) 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci#define DFLT_MSG_ENABLE (NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK | \ 8062306a36Sopenharmony_ci NETIF_MSG_TIMER | NETIF_MSG_IFDOWN | NETIF_MSG_IFUP |\ 8162306a36Sopenharmony_ci NETIF_MSG_RX_ERR | NETIF_MSG_TX_ERR) 8262306a36Sopenharmony_ci 8362306a36Sopenharmony_ci#define EEPROM_MAGIC 0x38E2F10C 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci#define CH_DEVICE(devid, idx) \ 8662306a36Sopenharmony_ci { PCI_VENDOR_ID_CHELSIO, devid, PCI_ANY_ID, PCI_ANY_ID, 0, 0, idx } 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic const struct pci_device_id cxgb3_pci_tbl[] = { 8962306a36Sopenharmony_ci CH_DEVICE(0x20, 0), /* PE9000 */ 9062306a36Sopenharmony_ci CH_DEVICE(0x21, 1), /* T302E */ 9162306a36Sopenharmony_ci CH_DEVICE(0x22, 2), /* T310E */ 9262306a36Sopenharmony_ci CH_DEVICE(0x23, 3), /* T320X */ 9362306a36Sopenharmony_ci CH_DEVICE(0x24, 1), /* T302X */ 9462306a36Sopenharmony_ci CH_DEVICE(0x25, 3), /* T320E */ 9562306a36Sopenharmony_ci CH_DEVICE(0x26, 2), /* T310X */ 9662306a36Sopenharmony_ci CH_DEVICE(0x30, 2), /* T3B10 */ 9762306a36Sopenharmony_ci CH_DEVICE(0x31, 3), /* T3B20 */ 9862306a36Sopenharmony_ci CH_DEVICE(0x32, 1), /* T3B02 */ 9962306a36Sopenharmony_ci CH_DEVICE(0x35, 6), /* T3C20-derived T3C10 */ 10062306a36Sopenharmony_ci CH_DEVICE(0x36, 3), /* S320E-CR */ 10162306a36Sopenharmony_ci CH_DEVICE(0x37, 7), /* N320E-G2 */ 10262306a36Sopenharmony_ci {0,} 10362306a36Sopenharmony_ci}; 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_ciMODULE_DESCRIPTION(DRV_DESC); 10662306a36Sopenharmony_ciMODULE_AUTHOR("Chelsio Communications"); 10762306a36Sopenharmony_ciMODULE_LICENSE("Dual BSD/GPL"); 10862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(pci, cxgb3_pci_tbl); 10962306a36Sopenharmony_ci 11062306a36Sopenharmony_cistatic int dflt_msg_enable = DFLT_MSG_ENABLE; 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_cimodule_param(dflt_msg_enable, int, 0644); 11362306a36Sopenharmony_ciMODULE_PARM_DESC(dflt_msg_enable, "Chelsio T3 default message enable bitmap"); 11462306a36Sopenharmony_ci 11562306a36Sopenharmony_ci/* 11662306a36Sopenharmony_ci * The driver uses the best interrupt scheme available on a platform in the 11762306a36Sopenharmony_ci * order MSI-X, MSI, legacy pin interrupts. This parameter determines which 11862306a36Sopenharmony_ci * of these schemes the driver may consider as follows: 11962306a36Sopenharmony_ci * 12062306a36Sopenharmony_ci * msi = 2: choose from among all three options 12162306a36Sopenharmony_ci * msi = 1: only consider MSI and pin interrupts 12262306a36Sopenharmony_ci * msi = 0: force pin interrupts 12362306a36Sopenharmony_ci */ 12462306a36Sopenharmony_cistatic int msi = 2; 12562306a36Sopenharmony_ci 12662306a36Sopenharmony_cimodule_param(msi, int, 0644); 12762306a36Sopenharmony_ciMODULE_PARM_DESC(msi, "whether to use MSI or MSI-X"); 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_ci/* 13062306a36Sopenharmony_ci * The driver enables offload as a default. 13162306a36Sopenharmony_ci * To disable it, use ofld_disable = 1. 13262306a36Sopenharmony_ci */ 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_cistatic int ofld_disable = 0; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_cimodule_param(ofld_disable, int, 0644); 13762306a36Sopenharmony_ciMODULE_PARM_DESC(ofld_disable, "whether to enable offload at init time or not"); 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ci/* 14062306a36Sopenharmony_ci * We have work elements that we need to cancel when an interface is taken 14162306a36Sopenharmony_ci * down. Normally the work elements would be executed by keventd but that 14262306a36Sopenharmony_ci * can deadlock because of linkwatch. If our close method takes the rtnl 14362306a36Sopenharmony_ci * lock and linkwatch is ahead of our work elements in keventd, linkwatch 14462306a36Sopenharmony_ci * will block keventd as it needs the rtnl lock, and we'll deadlock waiting 14562306a36Sopenharmony_ci * for our work to complete. Get our own work queue to solve this. 14662306a36Sopenharmony_ci */ 14762306a36Sopenharmony_cistruct workqueue_struct *cxgb3_wq; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci/** 15062306a36Sopenharmony_ci * link_report - show link status and link speed/duplex 15162306a36Sopenharmony_ci * @dev: the port whose settings are to be reported 15262306a36Sopenharmony_ci * 15362306a36Sopenharmony_ci * Shows the link status, speed, and duplex of a port. 15462306a36Sopenharmony_ci */ 15562306a36Sopenharmony_cistatic void link_report(struct net_device *dev) 15662306a36Sopenharmony_ci{ 15762306a36Sopenharmony_ci if (!netif_carrier_ok(dev)) 15862306a36Sopenharmony_ci netdev_info(dev, "link down\n"); 15962306a36Sopenharmony_ci else { 16062306a36Sopenharmony_ci const char *s = "10Mbps"; 16162306a36Sopenharmony_ci const struct port_info *p = netdev_priv(dev); 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci switch (p->link_config.speed) { 16462306a36Sopenharmony_ci case SPEED_10000: 16562306a36Sopenharmony_ci s = "10Gbps"; 16662306a36Sopenharmony_ci break; 16762306a36Sopenharmony_ci case SPEED_1000: 16862306a36Sopenharmony_ci s = "1000Mbps"; 16962306a36Sopenharmony_ci break; 17062306a36Sopenharmony_ci case SPEED_100: 17162306a36Sopenharmony_ci s = "100Mbps"; 17262306a36Sopenharmony_ci break; 17362306a36Sopenharmony_ci } 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci netdev_info(dev, "link up, %s, %s-duplex\n", 17662306a36Sopenharmony_ci s, p->link_config.duplex == DUPLEX_FULL 17762306a36Sopenharmony_ci ? "full" : "half"); 17862306a36Sopenharmony_ci } 17962306a36Sopenharmony_ci} 18062306a36Sopenharmony_ci 18162306a36Sopenharmony_cistatic void enable_tx_fifo_drain(struct adapter *adapter, 18262306a36Sopenharmony_ci struct port_info *pi) 18362306a36Sopenharmony_ci{ 18462306a36Sopenharmony_ci t3_set_reg_field(adapter, A_XGM_TXFIFO_CFG + pi->mac.offset, 0, 18562306a36Sopenharmony_ci F_ENDROPPKT); 18662306a36Sopenharmony_ci t3_write_reg(adapter, A_XGM_RX_CTRL + pi->mac.offset, 0); 18762306a36Sopenharmony_ci t3_write_reg(adapter, A_XGM_TX_CTRL + pi->mac.offset, F_TXEN); 18862306a36Sopenharmony_ci t3_write_reg(adapter, A_XGM_RX_CTRL + pi->mac.offset, F_RXEN); 18962306a36Sopenharmony_ci} 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_cistatic void disable_tx_fifo_drain(struct adapter *adapter, 19262306a36Sopenharmony_ci struct port_info *pi) 19362306a36Sopenharmony_ci{ 19462306a36Sopenharmony_ci t3_set_reg_field(adapter, A_XGM_TXFIFO_CFG + pi->mac.offset, 19562306a36Sopenharmony_ci F_ENDROPPKT, 0); 19662306a36Sopenharmony_ci} 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_civoid t3_os_link_fault(struct adapter *adap, int port_id, int state) 19962306a36Sopenharmony_ci{ 20062306a36Sopenharmony_ci struct net_device *dev = adap->port[port_id]; 20162306a36Sopenharmony_ci struct port_info *pi = netdev_priv(dev); 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci if (state == netif_carrier_ok(dev)) 20462306a36Sopenharmony_ci return; 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ci if (state) { 20762306a36Sopenharmony_ci struct cmac *mac = &pi->mac; 20862306a36Sopenharmony_ci 20962306a36Sopenharmony_ci netif_carrier_on(dev); 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci disable_tx_fifo_drain(adap, pi); 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci /* Clear local faults */ 21462306a36Sopenharmony_ci t3_xgm_intr_disable(adap, pi->port_id); 21562306a36Sopenharmony_ci t3_read_reg(adap, A_XGM_INT_STATUS + 21662306a36Sopenharmony_ci pi->mac.offset); 21762306a36Sopenharmony_ci t3_write_reg(adap, 21862306a36Sopenharmony_ci A_XGM_INT_CAUSE + pi->mac.offset, 21962306a36Sopenharmony_ci F_XGM_INT); 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci t3_set_reg_field(adap, 22262306a36Sopenharmony_ci A_XGM_INT_ENABLE + 22362306a36Sopenharmony_ci pi->mac.offset, 22462306a36Sopenharmony_ci F_XGM_INT, F_XGM_INT); 22562306a36Sopenharmony_ci t3_xgm_intr_enable(adap, pi->port_id); 22662306a36Sopenharmony_ci 22762306a36Sopenharmony_ci t3_mac_enable(mac, MAC_DIRECTION_TX); 22862306a36Sopenharmony_ci } else { 22962306a36Sopenharmony_ci netif_carrier_off(dev); 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci /* Flush TX FIFO */ 23262306a36Sopenharmony_ci enable_tx_fifo_drain(adap, pi); 23362306a36Sopenharmony_ci } 23462306a36Sopenharmony_ci link_report(dev); 23562306a36Sopenharmony_ci} 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci/** 23862306a36Sopenharmony_ci * t3_os_link_changed - handle link status changes 23962306a36Sopenharmony_ci * @adapter: the adapter associated with the link change 24062306a36Sopenharmony_ci * @port_id: the port index whose limk status has changed 24162306a36Sopenharmony_ci * @link_stat: the new status of the link 24262306a36Sopenharmony_ci * @speed: the new speed setting 24362306a36Sopenharmony_ci * @duplex: the new duplex setting 24462306a36Sopenharmony_ci * @pause: the new flow-control setting 24562306a36Sopenharmony_ci * 24662306a36Sopenharmony_ci * This is the OS-dependent handler for link status changes. The OS 24762306a36Sopenharmony_ci * neutral handler takes care of most of the processing for these events, 24862306a36Sopenharmony_ci * then calls this handler for any OS-specific processing. 24962306a36Sopenharmony_ci */ 25062306a36Sopenharmony_civoid t3_os_link_changed(struct adapter *adapter, int port_id, int link_stat, 25162306a36Sopenharmony_ci int speed, int duplex, int pause) 25262306a36Sopenharmony_ci{ 25362306a36Sopenharmony_ci struct net_device *dev = adapter->port[port_id]; 25462306a36Sopenharmony_ci struct port_info *pi = netdev_priv(dev); 25562306a36Sopenharmony_ci struct cmac *mac = &pi->mac; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci /* Skip changes from disabled ports. */ 25862306a36Sopenharmony_ci if (!netif_running(dev)) 25962306a36Sopenharmony_ci return; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_ci if (link_stat != netif_carrier_ok(dev)) { 26262306a36Sopenharmony_ci if (link_stat) { 26362306a36Sopenharmony_ci disable_tx_fifo_drain(adapter, pi); 26462306a36Sopenharmony_ci 26562306a36Sopenharmony_ci t3_mac_enable(mac, MAC_DIRECTION_RX); 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci /* Clear local faults */ 26862306a36Sopenharmony_ci t3_xgm_intr_disable(adapter, pi->port_id); 26962306a36Sopenharmony_ci t3_read_reg(adapter, A_XGM_INT_STATUS + 27062306a36Sopenharmony_ci pi->mac.offset); 27162306a36Sopenharmony_ci t3_write_reg(adapter, 27262306a36Sopenharmony_ci A_XGM_INT_CAUSE + pi->mac.offset, 27362306a36Sopenharmony_ci F_XGM_INT); 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci t3_set_reg_field(adapter, 27662306a36Sopenharmony_ci A_XGM_INT_ENABLE + pi->mac.offset, 27762306a36Sopenharmony_ci F_XGM_INT, F_XGM_INT); 27862306a36Sopenharmony_ci t3_xgm_intr_enable(adapter, pi->port_id); 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci netif_carrier_on(dev); 28162306a36Sopenharmony_ci } else { 28262306a36Sopenharmony_ci netif_carrier_off(dev); 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci t3_xgm_intr_disable(adapter, pi->port_id); 28562306a36Sopenharmony_ci t3_read_reg(adapter, A_XGM_INT_STATUS + pi->mac.offset); 28662306a36Sopenharmony_ci t3_set_reg_field(adapter, 28762306a36Sopenharmony_ci A_XGM_INT_ENABLE + pi->mac.offset, 28862306a36Sopenharmony_ci F_XGM_INT, 0); 28962306a36Sopenharmony_ci 29062306a36Sopenharmony_ci if (is_10G(adapter)) 29162306a36Sopenharmony_ci pi->phy.ops->power_down(&pi->phy, 1); 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci t3_read_reg(adapter, A_XGM_INT_STATUS + pi->mac.offset); 29462306a36Sopenharmony_ci t3_mac_disable(mac, MAC_DIRECTION_RX); 29562306a36Sopenharmony_ci t3_link_start(&pi->phy, mac, &pi->link_config); 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci /* Flush TX FIFO */ 29862306a36Sopenharmony_ci enable_tx_fifo_drain(adapter, pi); 29962306a36Sopenharmony_ci } 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci link_report(dev); 30262306a36Sopenharmony_ci } 30362306a36Sopenharmony_ci} 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci/** 30662306a36Sopenharmony_ci * t3_os_phymod_changed - handle PHY module changes 30762306a36Sopenharmony_ci * @adap: the adapter associated with the link change 30862306a36Sopenharmony_ci * @port_id: the port index whose limk status has changed 30962306a36Sopenharmony_ci * 31062306a36Sopenharmony_ci * This is the OS-dependent handler for PHY module changes. It is 31162306a36Sopenharmony_ci * invoked when a PHY module is removed or inserted for any OS-specific 31262306a36Sopenharmony_ci * processing. 31362306a36Sopenharmony_ci */ 31462306a36Sopenharmony_civoid t3_os_phymod_changed(struct adapter *adap, int port_id) 31562306a36Sopenharmony_ci{ 31662306a36Sopenharmony_ci static const char *mod_str[] = { 31762306a36Sopenharmony_ci NULL, "SR", "LR", "LRM", "TWINAX", "TWINAX", "unknown" 31862306a36Sopenharmony_ci }; 31962306a36Sopenharmony_ci 32062306a36Sopenharmony_ci const struct net_device *dev = adap->port[port_id]; 32162306a36Sopenharmony_ci const struct port_info *pi = netdev_priv(dev); 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci if (pi->phy.modtype == phy_modtype_none) 32462306a36Sopenharmony_ci netdev_info(dev, "PHY module unplugged\n"); 32562306a36Sopenharmony_ci else 32662306a36Sopenharmony_ci netdev_info(dev, "%s PHY module inserted\n", 32762306a36Sopenharmony_ci mod_str[pi->phy.modtype]); 32862306a36Sopenharmony_ci} 32962306a36Sopenharmony_ci 33062306a36Sopenharmony_cistatic void cxgb_set_rxmode(struct net_device *dev) 33162306a36Sopenharmony_ci{ 33262306a36Sopenharmony_ci struct port_info *pi = netdev_priv(dev); 33362306a36Sopenharmony_ci 33462306a36Sopenharmony_ci t3_mac_set_rx_mode(&pi->mac, dev); 33562306a36Sopenharmony_ci} 33662306a36Sopenharmony_ci 33762306a36Sopenharmony_ci/** 33862306a36Sopenharmony_ci * link_start - enable a port 33962306a36Sopenharmony_ci * @dev: the device to enable 34062306a36Sopenharmony_ci * 34162306a36Sopenharmony_ci * Performs the MAC and PHY actions needed to enable a port. 34262306a36Sopenharmony_ci */ 34362306a36Sopenharmony_cistatic void link_start(struct net_device *dev) 34462306a36Sopenharmony_ci{ 34562306a36Sopenharmony_ci struct port_info *pi = netdev_priv(dev); 34662306a36Sopenharmony_ci struct cmac *mac = &pi->mac; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci t3_mac_reset(mac); 34962306a36Sopenharmony_ci t3_mac_set_num_ucast(mac, MAX_MAC_IDX); 35062306a36Sopenharmony_ci t3_mac_set_mtu(mac, dev->mtu); 35162306a36Sopenharmony_ci t3_mac_set_address(mac, LAN_MAC_IDX, dev->dev_addr); 35262306a36Sopenharmony_ci t3_mac_set_address(mac, SAN_MAC_IDX, pi->iscsic.mac_addr); 35362306a36Sopenharmony_ci t3_mac_set_rx_mode(mac, dev); 35462306a36Sopenharmony_ci t3_link_start(&pi->phy, mac, &pi->link_config); 35562306a36Sopenharmony_ci t3_mac_enable(mac, MAC_DIRECTION_RX | MAC_DIRECTION_TX); 35662306a36Sopenharmony_ci} 35762306a36Sopenharmony_ci 35862306a36Sopenharmony_cistatic inline void cxgb_disable_msi(struct adapter *adapter) 35962306a36Sopenharmony_ci{ 36062306a36Sopenharmony_ci if (adapter->flags & USING_MSIX) { 36162306a36Sopenharmony_ci pci_disable_msix(adapter->pdev); 36262306a36Sopenharmony_ci adapter->flags &= ~USING_MSIX; 36362306a36Sopenharmony_ci } else if (adapter->flags & USING_MSI) { 36462306a36Sopenharmony_ci pci_disable_msi(adapter->pdev); 36562306a36Sopenharmony_ci adapter->flags &= ~USING_MSI; 36662306a36Sopenharmony_ci } 36762306a36Sopenharmony_ci} 36862306a36Sopenharmony_ci 36962306a36Sopenharmony_ci/* 37062306a36Sopenharmony_ci * Interrupt handler for asynchronous events used with MSI-X. 37162306a36Sopenharmony_ci */ 37262306a36Sopenharmony_cistatic irqreturn_t t3_async_intr_handler(int irq, void *cookie) 37362306a36Sopenharmony_ci{ 37462306a36Sopenharmony_ci t3_slow_intr_handler(cookie); 37562306a36Sopenharmony_ci return IRQ_HANDLED; 37662306a36Sopenharmony_ci} 37762306a36Sopenharmony_ci 37862306a36Sopenharmony_ci/* 37962306a36Sopenharmony_ci * Name the MSI-X interrupts. 38062306a36Sopenharmony_ci */ 38162306a36Sopenharmony_cistatic void name_msix_vecs(struct adapter *adap) 38262306a36Sopenharmony_ci{ 38362306a36Sopenharmony_ci int i, j, msi_idx = 1, n = sizeof(adap->msix_info[0].desc) - 1; 38462306a36Sopenharmony_ci 38562306a36Sopenharmony_ci snprintf(adap->msix_info[0].desc, n, "%s", adap->name); 38662306a36Sopenharmony_ci adap->msix_info[0].desc[n] = 0; 38762306a36Sopenharmony_ci 38862306a36Sopenharmony_ci for_each_port(adap, j) { 38962306a36Sopenharmony_ci struct net_device *d = adap->port[j]; 39062306a36Sopenharmony_ci const struct port_info *pi = netdev_priv(d); 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci for (i = 0; i < pi->nqsets; i++, msi_idx++) { 39362306a36Sopenharmony_ci snprintf(adap->msix_info[msi_idx].desc, n, 39462306a36Sopenharmony_ci "%s-%d", d->name, pi->first_qset + i); 39562306a36Sopenharmony_ci adap->msix_info[msi_idx].desc[n] = 0; 39662306a36Sopenharmony_ci } 39762306a36Sopenharmony_ci } 39862306a36Sopenharmony_ci} 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_cistatic int request_msix_data_irqs(struct adapter *adap) 40162306a36Sopenharmony_ci{ 40262306a36Sopenharmony_ci int i, j, err, qidx = 0; 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci for_each_port(adap, i) { 40562306a36Sopenharmony_ci int nqsets = adap2pinfo(adap, i)->nqsets; 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci for (j = 0; j < nqsets; ++j) { 40862306a36Sopenharmony_ci err = request_irq(adap->msix_info[qidx + 1].vec, 40962306a36Sopenharmony_ci t3_intr_handler(adap, 41062306a36Sopenharmony_ci adap->sge.qs[qidx]. 41162306a36Sopenharmony_ci rspq.polling), 0, 41262306a36Sopenharmony_ci adap->msix_info[qidx + 1].desc, 41362306a36Sopenharmony_ci &adap->sge.qs[qidx]); 41462306a36Sopenharmony_ci if (err) { 41562306a36Sopenharmony_ci while (--qidx >= 0) 41662306a36Sopenharmony_ci free_irq(adap->msix_info[qidx + 1].vec, 41762306a36Sopenharmony_ci &adap->sge.qs[qidx]); 41862306a36Sopenharmony_ci return err; 41962306a36Sopenharmony_ci } 42062306a36Sopenharmony_ci qidx++; 42162306a36Sopenharmony_ci } 42262306a36Sopenharmony_ci } 42362306a36Sopenharmony_ci return 0; 42462306a36Sopenharmony_ci} 42562306a36Sopenharmony_ci 42662306a36Sopenharmony_cistatic void free_irq_resources(struct adapter *adapter) 42762306a36Sopenharmony_ci{ 42862306a36Sopenharmony_ci if (adapter->flags & USING_MSIX) { 42962306a36Sopenharmony_ci int i, n = 0; 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_ci free_irq(adapter->msix_info[0].vec, adapter); 43262306a36Sopenharmony_ci for_each_port(adapter, i) 43362306a36Sopenharmony_ci n += adap2pinfo(adapter, i)->nqsets; 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_ci for (i = 0; i < n; ++i) 43662306a36Sopenharmony_ci free_irq(adapter->msix_info[i + 1].vec, 43762306a36Sopenharmony_ci &adapter->sge.qs[i]); 43862306a36Sopenharmony_ci } else 43962306a36Sopenharmony_ci free_irq(adapter->pdev->irq, adapter); 44062306a36Sopenharmony_ci} 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_cistatic int await_mgmt_replies(struct adapter *adap, unsigned long init_cnt, 44362306a36Sopenharmony_ci unsigned long n) 44462306a36Sopenharmony_ci{ 44562306a36Sopenharmony_ci int attempts = 10; 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci while (adap->sge.qs[0].rspq.offload_pkts < init_cnt + n) { 44862306a36Sopenharmony_ci if (!--attempts) 44962306a36Sopenharmony_ci return -ETIMEDOUT; 45062306a36Sopenharmony_ci msleep(10); 45162306a36Sopenharmony_ci } 45262306a36Sopenharmony_ci return 0; 45362306a36Sopenharmony_ci} 45462306a36Sopenharmony_ci 45562306a36Sopenharmony_cistatic int init_tp_parity(struct adapter *adap) 45662306a36Sopenharmony_ci{ 45762306a36Sopenharmony_ci int i; 45862306a36Sopenharmony_ci struct sk_buff *skb; 45962306a36Sopenharmony_ci struct cpl_set_tcb_field *greq; 46062306a36Sopenharmony_ci unsigned long cnt = adap->sge.qs[0].rspq.offload_pkts; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci t3_tp_set_offload_mode(adap, 1); 46362306a36Sopenharmony_ci 46462306a36Sopenharmony_ci for (i = 0; i < 16; i++) { 46562306a36Sopenharmony_ci struct cpl_smt_write_req *req; 46662306a36Sopenharmony_ci 46762306a36Sopenharmony_ci skb = alloc_skb(sizeof(*req), GFP_KERNEL); 46862306a36Sopenharmony_ci if (!skb) 46962306a36Sopenharmony_ci skb = adap->nofail_skb; 47062306a36Sopenharmony_ci if (!skb) 47162306a36Sopenharmony_ci goto alloc_skb_fail; 47262306a36Sopenharmony_ci 47362306a36Sopenharmony_ci req = __skb_put_zero(skb, sizeof(*req)); 47462306a36Sopenharmony_ci req->wr.wr_hi = htonl(V_WR_OP(FW_WROPCODE_FORWARD)); 47562306a36Sopenharmony_ci OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_SMT_WRITE_REQ, i)); 47662306a36Sopenharmony_ci req->mtu_idx = NMTUS - 1; 47762306a36Sopenharmony_ci req->iff = i; 47862306a36Sopenharmony_ci t3_mgmt_tx(adap, skb); 47962306a36Sopenharmony_ci if (skb == adap->nofail_skb) { 48062306a36Sopenharmony_ci await_mgmt_replies(adap, cnt, i + 1); 48162306a36Sopenharmony_ci adap->nofail_skb = alloc_skb(sizeof(*greq), GFP_KERNEL); 48262306a36Sopenharmony_ci if (!adap->nofail_skb) 48362306a36Sopenharmony_ci goto alloc_skb_fail; 48462306a36Sopenharmony_ci } 48562306a36Sopenharmony_ci } 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci for (i = 0; i < 2048; i++) { 48862306a36Sopenharmony_ci struct cpl_l2t_write_req *req; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci skb = alloc_skb(sizeof(*req), GFP_KERNEL); 49162306a36Sopenharmony_ci if (!skb) 49262306a36Sopenharmony_ci skb = adap->nofail_skb; 49362306a36Sopenharmony_ci if (!skb) 49462306a36Sopenharmony_ci goto alloc_skb_fail; 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci req = __skb_put_zero(skb, sizeof(*req)); 49762306a36Sopenharmony_ci req->wr.wr_hi = htonl(V_WR_OP(FW_WROPCODE_FORWARD)); 49862306a36Sopenharmony_ci OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_L2T_WRITE_REQ, i)); 49962306a36Sopenharmony_ci req->params = htonl(V_L2T_W_IDX(i)); 50062306a36Sopenharmony_ci t3_mgmt_tx(adap, skb); 50162306a36Sopenharmony_ci if (skb == adap->nofail_skb) { 50262306a36Sopenharmony_ci await_mgmt_replies(adap, cnt, 16 + i + 1); 50362306a36Sopenharmony_ci adap->nofail_skb = alloc_skb(sizeof(*greq), GFP_KERNEL); 50462306a36Sopenharmony_ci if (!adap->nofail_skb) 50562306a36Sopenharmony_ci goto alloc_skb_fail; 50662306a36Sopenharmony_ci } 50762306a36Sopenharmony_ci } 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci for (i = 0; i < 2048; i++) { 51062306a36Sopenharmony_ci struct cpl_rte_write_req *req; 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci skb = alloc_skb(sizeof(*req), GFP_KERNEL); 51362306a36Sopenharmony_ci if (!skb) 51462306a36Sopenharmony_ci skb = adap->nofail_skb; 51562306a36Sopenharmony_ci if (!skb) 51662306a36Sopenharmony_ci goto alloc_skb_fail; 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci req = __skb_put_zero(skb, sizeof(*req)); 51962306a36Sopenharmony_ci req->wr.wr_hi = htonl(V_WR_OP(FW_WROPCODE_FORWARD)); 52062306a36Sopenharmony_ci OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_RTE_WRITE_REQ, i)); 52162306a36Sopenharmony_ci req->l2t_idx = htonl(V_L2T_W_IDX(i)); 52262306a36Sopenharmony_ci t3_mgmt_tx(adap, skb); 52362306a36Sopenharmony_ci if (skb == adap->nofail_skb) { 52462306a36Sopenharmony_ci await_mgmt_replies(adap, cnt, 16 + 2048 + i + 1); 52562306a36Sopenharmony_ci adap->nofail_skb = alloc_skb(sizeof(*greq), GFP_KERNEL); 52662306a36Sopenharmony_ci if (!adap->nofail_skb) 52762306a36Sopenharmony_ci goto alloc_skb_fail; 52862306a36Sopenharmony_ci } 52962306a36Sopenharmony_ci } 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci skb = alloc_skb(sizeof(*greq), GFP_KERNEL); 53262306a36Sopenharmony_ci if (!skb) 53362306a36Sopenharmony_ci skb = adap->nofail_skb; 53462306a36Sopenharmony_ci if (!skb) 53562306a36Sopenharmony_ci goto alloc_skb_fail; 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci greq = __skb_put_zero(skb, sizeof(*greq)); 53862306a36Sopenharmony_ci greq->wr.wr_hi = htonl(V_WR_OP(FW_WROPCODE_FORWARD)); 53962306a36Sopenharmony_ci OPCODE_TID(greq) = htonl(MK_OPCODE_TID(CPL_SET_TCB_FIELD, 0)); 54062306a36Sopenharmony_ci greq->mask = cpu_to_be64(1); 54162306a36Sopenharmony_ci t3_mgmt_tx(adap, skb); 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci i = await_mgmt_replies(adap, cnt, 16 + 2048 + 2048 + 1); 54462306a36Sopenharmony_ci if (skb == adap->nofail_skb) { 54562306a36Sopenharmony_ci i = await_mgmt_replies(adap, cnt, 16 + 2048 + 2048 + 1); 54662306a36Sopenharmony_ci adap->nofail_skb = alloc_skb(sizeof(*greq), GFP_KERNEL); 54762306a36Sopenharmony_ci } 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci t3_tp_set_offload_mode(adap, 0); 55062306a36Sopenharmony_ci return i; 55162306a36Sopenharmony_ci 55262306a36Sopenharmony_cialloc_skb_fail: 55362306a36Sopenharmony_ci t3_tp_set_offload_mode(adap, 0); 55462306a36Sopenharmony_ci return -ENOMEM; 55562306a36Sopenharmony_ci} 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci/** 55862306a36Sopenharmony_ci * setup_rss - configure RSS 55962306a36Sopenharmony_ci * @adap: the adapter 56062306a36Sopenharmony_ci * 56162306a36Sopenharmony_ci * Sets up RSS to distribute packets to multiple receive queues. We 56262306a36Sopenharmony_ci * configure the RSS CPU lookup table to distribute to the number of HW 56362306a36Sopenharmony_ci * receive queues, and the response queue lookup table to narrow that 56462306a36Sopenharmony_ci * down to the response queues actually configured for each port. 56562306a36Sopenharmony_ci * We always configure the RSS mapping for two ports since the mapping 56662306a36Sopenharmony_ci * table has plenty of entries. 56762306a36Sopenharmony_ci */ 56862306a36Sopenharmony_cistatic void setup_rss(struct adapter *adap) 56962306a36Sopenharmony_ci{ 57062306a36Sopenharmony_ci int i; 57162306a36Sopenharmony_ci unsigned int nq0 = adap2pinfo(adap, 0)->nqsets; 57262306a36Sopenharmony_ci unsigned int nq1 = adap->port[1] ? adap2pinfo(adap, 1)->nqsets : 1; 57362306a36Sopenharmony_ci u8 cpus[SGE_QSETS + 1]; 57462306a36Sopenharmony_ci u16 rspq_map[RSS_TABLE_SIZE + 1]; 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci for (i = 0; i < SGE_QSETS; ++i) 57762306a36Sopenharmony_ci cpus[i] = i; 57862306a36Sopenharmony_ci cpus[SGE_QSETS] = 0xff; /* terminator */ 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci for (i = 0; i < RSS_TABLE_SIZE / 2; ++i) { 58162306a36Sopenharmony_ci rspq_map[i] = i % nq0; 58262306a36Sopenharmony_ci rspq_map[i + RSS_TABLE_SIZE / 2] = (i % nq1) + nq0; 58362306a36Sopenharmony_ci } 58462306a36Sopenharmony_ci rspq_map[RSS_TABLE_SIZE] = 0xffff; /* terminator */ 58562306a36Sopenharmony_ci 58662306a36Sopenharmony_ci t3_config_rss(adap, F_RQFEEDBACKENABLE | F_TNLLKPEN | F_TNLMAPEN | 58762306a36Sopenharmony_ci F_TNLPRTEN | F_TNL2TUPEN | F_TNL4TUPEN | 58862306a36Sopenharmony_ci V_RRCPLCPUSIZE(6) | F_HASHTOEPLITZ, cpus, rspq_map); 58962306a36Sopenharmony_ci} 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_cistatic void ring_dbs(struct adapter *adap) 59262306a36Sopenharmony_ci{ 59362306a36Sopenharmony_ci int i, j; 59462306a36Sopenharmony_ci 59562306a36Sopenharmony_ci for (i = 0; i < SGE_QSETS; i++) { 59662306a36Sopenharmony_ci struct sge_qset *qs = &adap->sge.qs[i]; 59762306a36Sopenharmony_ci 59862306a36Sopenharmony_ci if (qs->adap) 59962306a36Sopenharmony_ci for (j = 0; j < SGE_TXQ_PER_SET; j++) 60062306a36Sopenharmony_ci t3_write_reg(adap, A_SG_KDOORBELL, F_SELEGRCNTX | V_EGRCNTX(qs->txq[j].cntxt_id)); 60162306a36Sopenharmony_ci } 60262306a36Sopenharmony_ci} 60362306a36Sopenharmony_ci 60462306a36Sopenharmony_cistatic void init_napi(struct adapter *adap) 60562306a36Sopenharmony_ci{ 60662306a36Sopenharmony_ci int i; 60762306a36Sopenharmony_ci 60862306a36Sopenharmony_ci for (i = 0; i < SGE_QSETS; i++) { 60962306a36Sopenharmony_ci struct sge_qset *qs = &adap->sge.qs[i]; 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci if (qs->adap) 61262306a36Sopenharmony_ci netif_napi_add(qs->netdev, &qs->napi, qs->napi.poll); 61362306a36Sopenharmony_ci } 61462306a36Sopenharmony_ci 61562306a36Sopenharmony_ci /* 61662306a36Sopenharmony_ci * netif_napi_add() can be called only once per napi_struct because it 61762306a36Sopenharmony_ci * adds each new napi_struct to a list. Be careful not to call it a 61862306a36Sopenharmony_ci * second time, e.g., during EEH recovery, by making a note of it. 61962306a36Sopenharmony_ci */ 62062306a36Sopenharmony_ci adap->flags |= NAPI_INIT; 62162306a36Sopenharmony_ci} 62262306a36Sopenharmony_ci 62362306a36Sopenharmony_ci/* 62462306a36Sopenharmony_ci * Wait until all NAPI handlers are descheduled. This includes the handlers of 62562306a36Sopenharmony_ci * both netdevices representing interfaces and the dummy ones for the extra 62662306a36Sopenharmony_ci * queues. 62762306a36Sopenharmony_ci */ 62862306a36Sopenharmony_cistatic void quiesce_rx(struct adapter *adap) 62962306a36Sopenharmony_ci{ 63062306a36Sopenharmony_ci int i; 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci for (i = 0; i < SGE_QSETS; i++) 63362306a36Sopenharmony_ci if (adap->sge.qs[i].adap) 63462306a36Sopenharmony_ci napi_disable(&adap->sge.qs[i].napi); 63562306a36Sopenharmony_ci} 63662306a36Sopenharmony_ci 63762306a36Sopenharmony_cistatic void enable_all_napi(struct adapter *adap) 63862306a36Sopenharmony_ci{ 63962306a36Sopenharmony_ci int i; 64062306a36Sopenharmony_ci for (i = 0; i < SGE_QSETS; i++) 64162306a36Sopenharmony_ci if (adap->sge.qs[i].adap) 64262306a36Sopenharmony_ci napi_enable(&adap->sge.qs[i].napi); 64362306a36Sopenharmony_ci} 64462306a36Sopenharmony_ci 64562306a36Sopenharmony_ci/** 64662306a36Sopenharmony_ci * setup_sge_qsets - configure SGE Tx/Rx/response queues 64762306a36Sopenharmony_ci * @adap: the adapter 64862306a36Sopenharmony_ci * 64962306a36Sopenharmony_ci * Determines how many sets of SGE queues to use and initializes them. 65062306a36Sopenharmony_ci * We support multiple queue sets per port if we have MSI-X, otherwise 65162306a36Sopenharmony_ci * just one queue set per port. 65262306a36Sopenharmony_ci */ 65362306a36Sopenharmony_cistatic int setup_sge_qsets(struct adapter *adap) 65462306a36Sopenharmony_ci{ 65562306a36Sopenharmony_ci int i, j, err, irq_idx = 0, qset_idx = 0; 65662306a36Sopenharmony_ci unsigned int ntxq = SGE_TXQ_PER_SET; 65762306a36Sopenharmony_ci 65862306a36Sopenharmony_ci if (adap->params.rev > 0 && !(adap->flags & USING_MSI)) 65962306a36Sopenharmony_ci irq_idx = -1; 66062306a36Sopenharmony_ci 66162306a36Sopenharmony_ci for_each_port(adap, i) { 66262306a36Sopenharmony_ci struct net_device *dev = adap->port[i]; 66362306a36Sopenharmony_ci struct port_info *pi = netdev_priv(dev); 66462306a36Sopenharmony_ci 66562306a36Sopenharmony_ci pi->qs = &adap->sge.qs[pi->first_qset]; 66662306a36Sopenharmony_ci for (j = 0; j < pi->nqsets; ++j, ++qset_idx) { 66762306a36Sopenharmony_ci err = t3_sge_alloc_qset(adap, qset_idx, 1, 66862306a36Sopenharmony_ci (adap->flags & USING_MSIX) ? qset_idx + 1 : 66962306a36Sopenharmony_ci irq_idx, 67062306a36Sopenharmony_ci &adap->params.sge.qset[qset_idx], ntxq, dev, 67162306a36Sopenharmony_ci netdev_get_tx_queue(dev, j)); 67262306a36Sopenharmony_ci if (err) { 67362306a36Sopenharmony_ci t3_free_sge_resources(adap); 67462306a36Sopenharmony_ci return err; 67562306a36Sopenharmony_ci } 67662306a36Sopenharmony_ci } 67762306a36Sopenharmony_ci } 67862306a36Sopenharmony_ci 67962306a36Sopenharmony_ci return 0; 68062306a36Sopenharmony_ci} 68162306a36Sopenharmony_ci 68262306a36Sopenharmony_cistatic ssize_t attr_show(struct device *d, char *buf, 68362306a36Sopenharmony_ci ssize_t(*format) (struct net_device *, char *)) 68462306a36Sopenharmony_ci{ 68562306a36Sopenharmony_ci ssize_t len; 68662306a36Sopenharmony_ci 68762306a36Sopenharmony_ci /* Synchronize with ioctls that may shut down the device */ 68862306a36Sopenharmony_ci rtnl_lock(); 68962306a36Sopenharmony_ci len = (*format) (to_net_dev(d), buf); 69062306a36Sopenharmony_ci rtnl_unlock(); 69162306a36Sopenharmony_ci return len; 69262306a36Sopenharmony_ci} 69362306a36Sopenharmony_ci 69462306a36Sopenharmony_cistatic ssize_t attr_store(struct device *d, 69562306a36Sopenharmony_ci const char *buf, size_t len, 69662306a36Sopenharmony_ci ssize_t(*set) (struct net_device *, unsigned int), 69762306a36Sopenharmony_ci unsigned int min_val, unsigned int max_val) 69862306a36Sopenharmony_ci{ 69962306a36Sopenharmony_ci ssize_t ret; 70062306a36Sopenharmony_ci unsigned int val; 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci if (!capable(CAP_NET_ADMIN)) 70362306a36Sopenharmony_ci return -EPERM; 70462306a36Sopenharmony_ci 70562306a36Sopenharmony_ci ret = kstrtouint(buf, 0, &val); 70662306a36Sopenharmony_ci if (ret) 70762306a36Sopenharmony_ci return ret; 70862306a36Sopenharmony_ci if (val < min_val || val > max_val) 70962306a36Sopenharmony_ci return -EINVAL; 71062306a36Sopenharmony_ci 71162306a36Sopenharmony_ci rtnl_lock(); 71262306a36Sopenharmony_ci ret = (*set) (to_net_dev(d), val); 71362306a36Sopenharmony_ci if (!ret) 71462306a36Sopenharmony_ci ret = len; 71562306a36Sopenharmony_ci rtnl_unlock(); 71662306a36Sopenharmony_ci return ret; 71762306a36Sopenharmony_ci} 71862306a36Sopenharmony_ci 71962306a36Sopenharmony_ci#define CXGB3_SHOW(name, val_expr) \ 72062306a36Sopenharmony_cistatic ssize_t format_##name(struct net_device *dev, char *buf) \ 72162306a36Sopenharmony_ci{ \ 72262306a36Sopenharmony_ci struct port_info *pi = netdev_priv(dev); \ 72362306a36Sopenharmony_ci struct adapter *adap = pi->adapter; \ 72462306a36Sopenharmony_ci return sprintf(buf, "%u\n", val_expr); \ 72562306a36Sopenharmony_ci} \ 72662306a36Sopenharmony_cistatic ssize_t show_##name(struct device *d, struct device_attribute *attr, \ 72762306a36Sopenharmony_ci char *buf) \ 72862306a36Sopenharmony_ci{ \ 72962306a36Sopenharmony_ci return attr_show(d, buf, format_##name); \ 73062306a36Sopenharmony_ci} 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_cistatic ssize_t set_nfilters(struct net_device *dev, unsigned int val) 73362306a36Sopenharmony_ci{ 73462306a36Sopenharmony_ci struct port_info *pi = netdev_priv(dev); 73562306a36Sopenharmony_ci struct adapter *adap = pi->adapter; 73662306a36Sopenharmony_ci int min_tids = is_offload(adap) ? MC5_MIN_TIDS : 0; 73762306a36Sopenharmony_ci 73862306a36Sopenharmony_ci if (adap->flags & FULL_INIT_DONE) 73962306a36Sopenharmony_ci return -EBUSY; 74062306a36Sopenharmony_ci if (val && adap->params.rev == 0) 74162306a36Sopenharmony_ci return -EINVAL; 74262306a36Sopenharmony_ci if (val > t3_mc5_size(&adap->mc5) - adap->params.mc5.nservers - 74362306a36Sopenharmony_ci min_tids) 74462306a36Sopenharmony_ci return -EINVAL; 74562306a36Sopenharmony_ci adap->params.mc5.nfilters = val; 74662306a36Sopenharmony_ci return 0; 74762306a36Sopenharmony_ci} 74862306a36Sopenharmony_ci 74962306a36Sopenharmony_cistatic ssize_t store_nfilters(struct device *d, struct device_attribute *attr, 75062306a36Sopenharmony_ci const char *buf, size_t len) 75162306a36Sopenharmony_ci{ 75262306a36Sopenharmony_ci return attr_store(d, buf, len, set_nfilters, 0, ~0); 75362306a36Sopenharmony_ci} 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_cistatic ssize_t set_nservers(struct net_device *dev, unsigned int val) 75662306a36Sopenharmony_ci{ 75762306a36Sopenharmony_ci struct port_info *pi = netdev_priv(dev); 75862306a36Sopenharmony_ci struct adapter *adap = pi->adapter; 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_ci if (adap->flags & FULL_INIT_DONE) 76162306a36Sopenharmony_ci return -EBUSY; 76262306a36Sopenharmony_ci if (val > t3_mc5_size(&adap->mc5) - adap->params.mc5.nfilters - 76362306a36Sopenharmony_ci MC5_MIN_TIDS) 76462306a36Sopenharmony_ci return -EINVAL; 76562306a36Sopenharmony_ci adap->params.mc5.nservers = val; 76662306a36Sopenharmony_ci return 0; 76762306a36Sopenharmony_ci} 76862306a36Sopenharmony_ci 76962306a36Sopenharmony_cistatic ssize_t store_nservers(struct device *d, struct device_attribute *attr, 77062306a36Sopenharmony_ci const char *buf, size_t len) 77162306a36Sopenharmony_ci{ 77262306a36Sopenharmony_ci return attr_store(d, buf, len, set_nservers, 0, ~0); 77362306a36Sopenharmony_ci} 77462306a36Sopenharmony_ci 77562306a36Sopenharmony_ci#define CXGB3_ATTR_R(name, val_expr) \ 77662306a36Sopenharmony_ciCXGB3_SHOW(name, val_expr) \ 77762306a36Sopenharmony_cistatic DEVICE_ATTR(name, 0444, show_##name, NULL) 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci#define CXGB3_ATTR_RW(name, val_expr, store_method) \ 78062306a36Sopenharmony_ciCXGB3_SHOW(name, val_expr) \ 78162306a36Sopenharmony_cistatic DEVICE_ATTR(name, 0644, show_##name, store_method) 78262306a36Sopenharmony_ci 78362306a36Sopenharmony_ciCXGB3_ATTR_R(cam_size, t3_mc5_size(&adap->mc5)); 78462306a36Sopenharmony_ciCXGB3_ATTR_RW(nfilters, adap->params.mc5.nfilters, store_nfilters); 78562306a36Sopenharmony_ciCXGB3_ATTR_RW(nservers, adap->params.mc5.nservers, store_nservers); 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_cistatic struct attribute *cxgb3_attrs[] = { 78862306a36Sopenharmony_ci &dev_attr_cam_size.attr, 78962306a36Sopenharmony_ci &dev_attr_nfilters.attr, 79062306a36Sopenharmony_ci &dev_attr_nservers.attr, 79162306a36Sopenharmony_ci NULL 79262306a36Sopenharmony_ci}; 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_cistatic const struct attribute_group cxgb3_attr_group = { 79562306a36Sopenharmony_ci .attrs = cxgb3_attrs, 79662306a36Sopenharmony_ci}; 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_cistatic ssize_t tm_attr_show(struct device *d, 79962306a36Sopenharmony_ci char *buf, int sched) 80062306a36Sopenharmony_ci{ 80162306a36Sopenharmony_ci struct port_info *pi = netdev_priv(to_net_dev(d)); 80262306a36Sopenharmony_ci struct adapter *adap = pi->adapter; 80362306a36Sopenharmony_ci unsigned int v, addr, bpt, cpt; 80462306a36Sopenharmony_ci ssize_t len; 80562306a36Sopenharmony_ci 80662306a36Sopenharmony_ci addr = A_TP_TX_MOD_Q1_Q0_RATE_LIMIT - sched / 2; 80762306a36Sopenharmony_ci rtnl_lock(); 80862306a36Sopenharmony_ci t3_write_reg(adap, A_TP_TM_PIO_ADDR, addr); 80962306a36Sopenharmony_ci v = t3_read_reg(adap, A_TP_TM_PIO_DATA); 81062306a36Sopenharmony_ci if (sched & 1) 81162306a36Sopenharmony_ci v >>= 16; 81262306a36Sopenharmony_ci bpt = (v >> 8) & 0xff; 81362306a36Sopenharmony_ci cpt = v & 0xff; 81462306a36Sopenharmony_ci if (!cpt) 81562306a36Sopenharmony_ci len = sprintf(buf, "disabled\n"); 81662306a36Sopenharmony_ci else { 81762306a36Sopenharmony_ci v = (adap->params.vpd.cclk * 1000) / cpt; 81862306a36Sopenharmony_ci len = sprintf(buf, "%u Kbps\n", (v * bpt) / 125); 81962306a36Sopenharmony_ci } 82062306a36Sopenharmony_ci rtnl_unlock(); 82162306a36Sopenharmony_ci return len; 82262306a36Sopenharmony_ci} 82362306a36Sopenharmony_ci 82462306a36Sopenharmony_cistatic ssize_t tm_attr_store(struct device *d, 82562306a36Sopenharmony_ci const char *buf, size_t len, int sched) 82662306a36Sopenharmony_ci{ 82762306a36Sopenharmony_ci struct port_info *pi = netdev_priv(to_net_dev(d)); 82862306a36Sopenharmony_ci struct adapter *adap = pi->adapter; 82962306a36Sopenharmony_ci unsigned int val; 83062306a36Sopenharmony_ci ssize_t ret; 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci if (!capable(CAP_NET_ADMIN)) 83362306a36Sopenharmony_ci return -EPERM; 83462306a36Sopenharmony_ci 83562306a36Sopenharmony_ci ret = kstrtouint(buf, 0, &val); 83662306a36Sopenharmony_ci if (ret) 83762306a36Sopenharmony_ci return ret; 83862306a36Sopenharmony_ci if (val > 10000000) 83962306a36Sopenharmony_ci return -EINVAL; 84062306a36Sopenharmony_ci 84162306a36Sopenharmony_ci rtnl_lock(); 84262306a36Sopenharmony_ci ret = t3_config_sched(adap, val, sched); 84362306a36Sopenharmony_ci if (!ret) 84462306a36Sopenharmony_ci ret = len; 84562306a36Sopenharmony_ci rtnl_unlock(); 84662306a36Sopenharmony_ci return ret; 84762306a36Sopenharmony_ci} 84862306a36Sopenharmony_ci 84962306a36Sopenharmony_ci#define TM_ATTR(name, sched) \ 85062306a36Sopenharmony_cistatic ssize_t show_##name(struct device *d, struct device_attribute *attr, \ 85162306a36Sopenharmony_ci char *buf) \ 85262306a36Sopenharmony_ci{ \ 85362306a36Sopenharmony_ci return tm_attr_show(d, buf, sched); \ 85462306a36Sopenharmony_ci} \ 85562306a36Sopenharmony_cistatic ssize_t store_##name(struct device *d, struct device_attribute *attr, \ 85662306a36Sopenharmony_ci const char *buf, size_t len) \ 85762306a36Sopenharmony_ci{ \ 85862306a36Sopenharmony_ci return tm_attr_store(d, buf, len, sched); \ 85962306a36Sopenharmony_ci} \ 86062306a36Sopenharmony_cistatic DEVICE_ATTR(name, 0644, show_##name, store_##name) 86162306a36Sopenharmony_ci 86262306a36Sopenharmony_ciTM_ATTR(sched0, 0); 86362306a36Sopenharmony_ciTM_ATTR(sched1, 1); 86462306a36Sopenharmony_ciTM_ATTR(sched2, 2); 86562306a36Sopenharmony_ciTM_ATTR(sched3, 3); 86662306a36Sopenharmony_ciTM_ATTR(sched4, 4); 86762306a36Sopenharmony_ciTM_ATTR(sched5, 5); 86862306a36Sopenharmony_ciTM_ATTR(sched6, 6); 86962306a36Sopenharmony_ciTM_ATTR(sched7, 7); 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_cistatic struct attribute *offload_attrs[] = { 87262306a36Sopenharmony_ci &dev_attr_sched0.attr, 87362306a36Sopenharmony_ci &dev_attr_sched1.attr, 87462306a36Sopenharmony_ci &dev_attr_sched2.attr, 87562306a36Sopenharmony_ci &dev_attr_sched3.attr, 87662306a36Sopenharmony_ci &dev_attr_sched4.attr, 87762306a36Sopenharmony_ci &dev_attr_sched5.attr, 87862306a36Sopenharmony_ci &dev_attr_sched6.attr, 87962306a36Sopenharmony_ci &dev_attr_sched7.attr, 88062306a36Sopenharmony_ci NULL 88162306a36Sopenharmony_ci}; 88262306a36Sopenharmony_ci 88362306a36Sopenharmony_cistatic const struct attribute_group offload_attr_group = { 88462306a36Sopenharmony_ci .attrs = offload_attrs, 88562306a36Sopenharmony_ci}; 88662306a36Sopenharmony_ci 88762306a36Sopenharmony_ci/* 88862306a36Sopenharmony_ci * Sends an sk_buff to an offload queue driver 88962306a36Sopenharmony_ci * after dealing with any active network taps. 89062306a36Sopenharmony_ci */ 89162306a36Sopenharmony_cistatic inline int offload_tx(struct t3cdev *tdev, struct sk_buff *skb) 89262306a36Sopenharmony_ci{ 89362306a36Sopenharmony_ci int ret; 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci local_bh_disable(); 89662306a36Sopenharmony_ci ret = t3_offload_tx(tdev, skb); 89762306a36Sopenharmony_ci local_bh_enable(); 89862306a36Sopenharmony_ci return ret; 89962306a36Sopenharmony_ci} 90062306a36Sopenharmony_ci 90162306a36Sopenharmony_cistatic int write_smt_entry(struct adapter *adapter, int idx) 90262306a36Sopenharmony_ci{ 90362306a36Sopenharmony_ci struct cpl_smt_write_req *req; 90462306a36Sopenharmony_ci struct port_info *pi = netdev_priv(adapter->port[idx]); 90562306a36Sopenharmony_ci struct sk_buff *skb = alloc_skb(sizeof(*req), GFP_KERNEL); 90662306a36Sopenharmony_ci 90762306a36Sopenharmony_ci if (!skb) 90862306a36Sopenharmony_ci return -ENOMEM; 90962306a36Sopenharmony_ci 91062306a36Sopenharmony_ci req = __skb_put(skb, sizeof(*req)); 91162306a36Sopenharmony_ci req->wr.wr_hi = htonl(V_WR_OP(FW_WROPCODE_FORWARD)); 91262306a36Sopenharmony_ci OPCODE_TID(req) = htonl(MK_OPCODE_TID(CPL_SMT_WRITE_REQ, idx)); 91362306a36Sopenharmony_ci req->mtu_idx = NMTUS - 1; /* should be 0 but there's a T3 bug */ 91462306a36Sopenharmony_ci req->iff = idx; 91562306a36Sopenharmony_ci memcpy(req->src_mac0, adapter->port[idx]->dev_addr, ETH_ALEN); 91662306a36Sopenharmony_ci memcpy(req->src_mac1, pi->iscsic.mac_addr, ETH_ALEN); 91762306a36Sopenharmony_ci skb->priority = 1; 91862306a36Sopenharmony_ci offload_tx(&adapter->tdev, skb); 91962306a36Sopenharmony_ci return 0; 92062306a36Sopenharmony_ci} 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_cistatic int init_smt(struct adapter *adapter) 92362306a36Sopenharmony_ci{ 92462306a36Sopenharmony_ci int i; 92562306a36Sopenharmony_ci 92662306a36Sopenharmony_ci for_each_port(adapter, i) 92762306a36Sopenharmony_ci write_smt_entry(adapter, i); 92862306a36Sopenharmony_ci return 0; 92962306a36Sopenharmony_ci} 93062306a36Sopenharmony_ci 93162306a36Sopenharmony_cistatic void init_port_mtus(struct adapter *adapter) 93262306a36Sopenharmony_ci{ 93362306a36Sopenharmony_ci unsigned int mtus = adapter->port[0]->mtu; 93462306a36Sopenharmony_ci 93562306a36Sopenharmony_ci if (adapter->port[1]) 93662306a36Sopenharmony_ci mtus |= adapter->port[1]->mtu << 16; 93762306a36Sopenharmony_ci t3_write_reg(adapter, A_TP_MTU_PORT_TABLE, mtus); 93862306a36Sopenharmony_ci} 93962306a36Sopenharmony_ci 94062306a36Sopenharmony_cistatic int send_pktsched_cmd(struct adapter *adap, int sched, int qidx, int lo, 94162306a36Sopenharmony_ci int hi, int port) 94262306a36Sopenharmony_ci{ 94362306a36Sopenharmony_ci struct sk_buff *skb; 94462306a36Sopenharmony_ci struct mngt_pktsched_wr *req; 94562306a36Sopenharmony_ci int ret; 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ci skb = alloc_skb(sizeof(*req), GFP_KERNEL); 94862306a36Sopenharmony_ci if (!skb) 94962306a36Sopenharmony_ci skb = adap->nofail_skb; 95062306a36Sopenharmony_ci if (!skb) 95162306a36Sopenharmony_ci return -ENOMEM; 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_ci req = skb_put(skb, sizeof(*req)); 95462306a36Sopenharmony_ci req->wr_hi = htonl(V_WR_OP(FW_WROPCODE_MNGT)); 95562306a36Sopenharmony_ci req->mngt_opcode = FW_MNGTOPCODE_PKTSCHED_SET; 95662306a36Sopenharmony_ci req->sched = sched; 95762306a36Sopenharmony_ci req->idx = qidx; 95862306a36Sopenharmony_ci req->min = lo; 95962306a36Sopenharmony_ci req->max = hi; 96062306a36Sopenharmony_ci req->binding = port; 96162306a36Sopenharmony_ci ret = t3_mgmt_tx(adap, skb); 96262306a36Sopenharmony_ci if (skb == adap->nofail_skb) { 96362306a36Sopenharmony_ci adap->nofail_skb = alloc_skb(sizeof(struct cpl_set_tcb_field), 96462306a36Sopenharmony_ci GFP_KERNEL); 96562306a36Sopenharmony_ci if (!adap->nofail_skb) 96662306a36Sopenharmony_ci ret = -ENOMEM; 96762306a36Sopenharmony_ci } 96862306a36Sopenharmony_ci 96962306a36Sopenharmony_ci return ret; 97062306a36Sopenharmony_ci} 97162306a36Sopenharmony_ci 97262306a36Sopenharmony_cistatic int bind_qsets(struct adapter *adap) 97362306a36Sopenharmony_ci{ 97462306a36Sopenharmony_ci int i, j, err = 0; 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_ci for_each_port(adap, i) { 97762306a36Sopenharmony_ci const struct port_info *pi = adap2pinfo(adap, i); 97862306a36Sopenharmony_ci 97962306a36Sopenharmony_ci for (j = 0; j < pi->nqsets; ++j) { 98062306a36Sopenharmony_ci int ret = send_pktsched_cmd(adap, 1, 98162306a36Sopenharmony_ci pi->first_qset + j, -1, 98262306a36Sopenharmony_ci -1, i); 98362306a36Sopenharmony_ci if (ret) 98462306a36Sopenharmony_ci err = ret; 98562306a36Sopenharmony_ci } 98662306a36Sopenharmony_ci } 98762306a36Sopenharmony_ci 98862306a36Sopenharmony_ci return err; 98962306a36Sopenharmony_ci} 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci#define FW_VERSION __stringify(FW_VERSION_MAJOR) "." \ 99262306a36Sopenharmony_ci __stringify(FW_VERSION_MINOR) "." __stringify(FW_VERSION_MICRO) 99362306a36Sopenharmony_ci#define FW_FNAME "cxgb3/t3fw-" FW_VERSION ".bin" 99462306a36Sopenharmony_ci#define TPSRAM_VERSION __stringify(TP_VERSION_MAJOR) "." \ 99562306a36Sopenharmony_ci __stringify(TP_VERSION_MINOR) "." __stringify(TP_VERSION_MICRO) 99662306a36Sopenharmony_ci#define TPSRAM_NAME "cxgb3/t3%c_psram-" TPSRAM_VERSION ".bin" 99762306a36Sopenharmony_ci#define AEL2005_OPT_EDC_NAME "cxgb3/ael2005_opt_edc.bin" 99862306a36Sopenharmony_ci#define AEL2005_TWX_EDC_NAME "cxgb3/ael2005_twx_edc.bin" 99962306a36Sopenharmony_ci#define AEL2020_TWX_EDC_NAME "cxgb3/ael2020_twx_edc.bin" 100062306a36Sopenharmony_ciMODULE_FIRMWARE(FW_FNAME); 100162306a36Sopenharmony_ciMODULE_FIRMWARE("cxgb3/t3b_psram-" TPSRAM_VERSION ".bin"); 100262306a36Sopenharmony_ciMODULE_FIRMWARE("cxgb3/t3c_psram-" TPSRAM_VERSION ".bin"); 100362306a36Sopenharmony_ciMODULE_FIRMWARE(AEL2005_OPT_EDC_NAME); 100462306a36Sopenharmony_ciMODULE_FIRMWARE(AEL2005_TWX_EDC_NAME); 100562306a36Sopenharmony_ciMODULE_FIRMWARE(AEL2020_TWX_EDC_NAME); 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_cistatic inline const char *get_edc_fw_name(int edc_idx) 100862306a36Sopenharmony_ci{ 100962306a36Sopenharmony_ci const char *fw_name = NULL; 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ci switch (edc_idx) { 101262306a36Sopenharmony_ci case EDC_OPT_AEL2005: 101362306a36Sopenharmony_ci fw_name = AEL2005_OPT_EDC_NAME; 101462306a36Sopenharmony_ci break; 101562306a36Sopenharmony_ci case EDC_TWX_AEL2005: 101662306a36Sopenharmony_ci fw_name = AEL2005_TWX_EDC_NAME; 101762306a36Sopenharmony_ci break; 101862306a36Sopenharmony_ci case EDC_TWX_AEL2020: 101962306a36Sopenharmony_ci fw_name = AEL2020_TWX_EDC_NAME; 102062306a36Sopenharmony_ci break; 102162306a36Sopenharmony_ci } 102262306a36Sopenharmony_ci return fw_name; 102362306a36Sopenharmony_ci} 102462306a36Sopenharmony_ci 102562306a36Sopenharmony_ciint t3_get_edc_fw(struct cphy *phy, int edc_idx, int size) 102662306a36Sopenharmony_ci{ 102762306a36Sopenharmony_ci struct adapter *adapter = phy->adapter; 102862306a36Sopenharmony_ci const struct firmware *fw; 102962306a36Sopenharmony_ci const char *fw_name; 103062306a36Sopenharmony_ci u32 csum; 103162306a36Sopenharmony_ci const __be32 *p; 103262306a36Sopenharmony_ci u16 *cache = phy->phy_cache; 103362306a36Sopenharmony_ci int i, ret = -EINVAL; 103462306a36Sopenharmony_ci 103562306a36Sopenharmony_ci fw_name = get_edc_fw_name(edc_idx); 103662306a36Sopenharmony_ci if (fw_name) 103762306a36Sopenharmony_ci ret = request_firmware(&fw, fw_name, &adapter->pdev->dev); 103862306a36Sopenharmony_ci if (ret < 0) { 103962306a36Sopenharmony_ci dev_err(&adapter->pdev->dev, 104062306a36Sopenharmony_ci "could not upgrade firmware: unable to load %s\n", 104162306a36Sopenharmony_ci fw_name); 104262306a36Sopenharmony_ci return ret; 104362306a36Sopenharmony_ci } 104462306a36Sopenharmony_ci 104562306a36Sopenharmony_ci /* check size, take checksum in account */ 104662306a36Sopenharmony_ci if (fw->size > size + 4) { 104762306a36Sopenharmony_ci CH_ERR(adapter, "firmware image too large %u, expected %d\n", 104862306a36Sopenharmony_ci (unsigned int)fw->size, size + 4); 104962306a36Sopenharmony_ci ret = -EINVAL; 105062306a36Sopenharmony_ci } 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_ci /* compute checksum */ 105362306a36Sopenharmony_ci p = (const __be32 *)fw->data; 105462306a36Sopenharmony_ci for (csum = 0, i = 0; i < fw->size / sizeof(csum); i++) 105562306a36Sopenharmony_ci csum += ntohl(p[i]); 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_ci if (csum != 0xffffffff) { 105862306a36Sopenharmony_ci CH_ERR(adapter, "corrupted firmware image, checksum %u\n", 105962306a36Sopenharmony_ci csum); 106062306a36Sopenharmony_ci ret = -EINVAL; 106162306a36Sopenharmony_ci } 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_ci for (i = 0; i < size / 4 ; i++) { 106462306a36Sopenharmony_ci *cache++ = (be32_to_cpu(p[i]) & 0xffff0000) >> 16; 106562306a36Sopenharmony_ci *cache++ = be32_to_cpu(p[i]) & 0xffff; 106662306a36Sopenharmony_ci } 106762306a36Sopenharmony_ci 106862306a36Sopenharmony_ci release_firmware(fw); 106962306a36Sopenharmony_ci 107062306a36Sopenharmony_ci return ret; 107162306a36Sopenharmony_ci} 107262306a36Sopenharmony_ci 107362306a36Sopenharmony_cistatic int upgrade_fw(struct adapter *adap) 107462306a36Sopenharmony_ci{ 107562306a36Sopenharmony_ci int ret; 107662306a36Sopenharmony_ci const struct firmware *fw; 107762306a36Sopenharmony_ci struct device *dev = &adap->pdev->dev; 107862306a36Sopenharmony_ci 107962306a36Sopenharmony_ci ret = request_firmware(&fw, FW_FNAME, dev); 108062306a36Sopenharmony_ci if (ret < 0) { 108162306a36Sopenharmony_ci dev_err(dev, "could not upgrade firmware: unable to load %s\n", 108262306a36Sopenharmony_ci FW_FNAME); 108362306a36Sopenharmony_ci return ret; 108462306a36Sopenharmony_ci } 108562306a36Sopenharmony_ci ret = t3_load_fw(adap, fw->data, fw->size); 108662306a36Sopenharmony_ci release_firmware(fw); 108762306a36Sopenharmony_ci 108862306a36Sopenharmony_ci if (ret == 0) 108962306a36Sopenharmony_ci dev_info(dev, "successful upgrade to firmware %d.%d.%d\n", 109062306a36Sopenharmony_ci FW_VERSION_MAJOR, FW_VERSION_MINOR, FW_VERSION_MICRO); 109162306a36Sopenharmony_ci else 109262306a36Sopenharmony_ci dev_err(dev, "failed to upgrade to firmware %d.%d.%d\n", 109362306a36Sopenharmony_ci FW_VERSION_MAJOR, FW_VERSION_MINOR, FW_VERSION_MICRO); 109462306a36Sopenharmony_ci 109562306a36Sopenharmony_ci return ret; 109662306a36Sopenharmony_ci} 109762306a36Sopenharmony_ci 109862306a36Sopenharmony_cistatic inline char t3rev2char(struct adapter *adapter) 109962306a36Sopenharmony_ci{ 110062306a36Sopenharmony_ci char rev = 0; 110162306a36Sopenharmony_ci 110262306a36Sopenharmony_ci switch(adapter->params.rev) { 110362306a36Sopenharmony_ci case T3_REV_B: 110462306a36Sopenharmony_ci case T3_REV_B2: 110562306a36Sopenharmony_ci rev = 'b'; 110662306a36Sopenharmony_ci break; 110762306a36Sopenharmony_ci case T3_REV_C: 110862306a36Sopenharmony_ci rev = 'c'; 110962306a36Sopenharmony_ci break; 111062306a36Sopenharmony_ci } 111162306a36Sopenharmony_ci return rev; 111262306a36Sopenharmony_ci} 111362306a36Sopenharmony_ci 111462306a36Sopenharmony_cistatic int update_tpsram(struct adapter *adap) 111562306a36Sopenharmony_ci{ 111662306a36Sopenharmony_ci const struct firmware *tpsram; 111762306a36Sopenharmony_ci char buf[64]; 111862306a36Sopenharmony_ci struct device *dev = &adap->pdev->dev; 111962306a36Sopenharmony_ci int ret; 112062306a36Sopenharmony_ci char rev; 112162306a36Sopenharmony_ci 112262306a36Sopenharmony_ci rev = t3rev2char(adap); 112362306a36Sopenharmony_ci if (!rev) 112462306a36Sopenharmony_ci return 0; 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_ci snprintf(buf, sizeof(buf), TPSRAM_NAME, rev); 112762306a36Sopenharmony_ci 112862306a36Sopenharmony_ci ret = request_firmware(&tpsram, buf, dev); 112962306a36Sopenharmony_ci if (ret < 0) { 113062306a36Sopenharmony_ci dev_err(dev, "could not load TP SRAM: unable to load %s\n", 113162306a36Sopenharmony_ci buf); 113262306a36Sopenharmony_ci return ret; 113362306a36Sopenharmony_ci } 113462306a36Sopenharmony_ci 113562306a36Sopenharmony_ci ret = t3_check_tpsram(adap, tpsram->data, tpsram->size); 113662306a36Sopenharmony_ci if (ret) 113762306a36Sopenharmony_ci goto release_tpsram; 113862306a36Sopenharmony_ci 113962306a36Sopenharmony_ci ret = t3_set_proto_sram(adap, tpsram->data); 114062306a36Sopenharmony_ci if (ret == 0) 114162306a36Sopenharmony_ci dev_info(dev, 114262306a36Sopenharmony_ci "successful update of protocol engine " 114362306a36Sopenharmony_ci "to %d.%d.%d\n", 114462306a36Sopenharmony_ci TP_VERSION_MAJOR, TP_VERSION_MINOR, TP_VERSION_MICRO); 114562306a36Sopenharmony_ci else 114662306a36Sopenharmony_ci dev_err(dev, "failed to update of protocol engine %d.%d.%d\n", 114762306a36Sopenharmony_ci TP_VERSION_MAJOR, TP_VERSION_MINOR, TP_VERSION_MICRO); 114862306a36Sopenharmony_ci if (ret) 114962306a36Sopenharmony_ci dev_err(dev, "loading protocol SRAM failed\n"); 115062306a36Sopenharmony_ci 115162306a36Sopenharmony_cirelease_tpsram: 115262306a36Sopenharmony_ci release_firmware(tpsram); 115362306a36Sopenharmony_ci 115462306a36Sopenharmony_ci return ret; 115562306a36Sopenharmony_ci} 115662306a36Sopenharmony_ci 115762306a36Sopenharmony_ci/** 115862306a36Sopenharmony_ci * t3_synchronize_rx - wait for current Rx processing on a port to complete 115962306a36Sopenharmony_ci * @adap: the adapter 116062306a36Sopenharmony_ci * @p: the port 116162306a36Sopenharmony_ci * 116262306a36Sopenharmony_ci * Ensures that current Rx processing on any of the queues associated with 116362306a36Sopenharmony_ci * the given port completes before returning. We do this by acquiring and 116462306a36Sopenharmony_ci * releasing the locks of the response queues associated with the port. 116562306a36Sopenharmony_ci */ 116662306a36Sopenharmony_cistatic void t3_synchronize_rx(struct adapter *adap, const struct port_info *p) 116762306a36Sopenharmony_ci{ 116862306a36Sopenharmony_ci int i; 116962306a36Sopenharmony_ci 117062306a36Sopenharmony_ci for (i = p->first_qset; i < p->first_qset + p->nqsets; i++) { 117162306a36Sopenharmony_ci struct sge_rspq *q = &adap->sge.qs[i].rspq; 117262306a36Sopenharmony_ci 117362306a36Sopenharmony_ci spin_lock_irq(&q->lock); 117462306a36Sopenharmony_ci spin_unlock_irq(&q->lock); 117562306a36Sopenharmony_ci } 117662306a36Sopenharmony_ci} 117762306a36Sopenharmony_ci 117862306a36Sopenharmony_cistatic void cxgb_vlan_mode(struct net_device *dev, netdev_features_t features) 117962306a36Sopenharmony_ci{ 118062306a36Sopenharmony_ci struct port_info *pi = netdev_priv(dev); 118162306a36Sopenharmony_ci struct adapter *adapter = pi->adapter; 118262306a36Sopenharmony_ci 118362306a36Sopenharmony_ci if (adapter->params.rev > 0) { 118462306a36Sopenharmony_ci t3_set_vlan_accel(adapter, 1 << pi->port_id, 118562306a36Sopenharmony_ci features & NETIF_F_HW_VLAN_CTAG_RX); 118662306a36Sopenharmony_ci } else { 118762306a36Sopenharmony_ci /* single control for all ports */ 118862306a36Sopenharmony_ci unsigned int i, have_vlans = features & NETIF_F_HW_VLAN_CTAG_RX; 118962306a36Sopenharmony_ci 119062306a36Sopenharmony_ci for_each_port(adapter, i) 119162306a36Sopenharmony_ci have_vlans |= 119262306a36Sopenharmony_ci adapter->port[i]->features & 119362306a36Sopenharmony_ci NETIF_F_HW_VLAN_CTAG_RX; 119462306a36Sopenharmony_ci 119562306a36Sopenharmony_ci t3_set_vlan_accel(adapter, 1, have_vlans); 119662306a36Sopenharmony_ci } 119762306a36Sopenharmony_ci t3_synchronize_rx(adapter, pi); 119862306a36Sopenharmony_ci} 119962306a36Sopenharmony_ci 120062306a36Sopenharmony_ci/** 120162306a36Sopenharmony_ci * cxgb_up - enable the adapter 120262306a36Sopenharmony_ci * @adap: adapter being enabled 120362306a36Sopenharmony_ci * 120462306a36Sopenharmony_ci * Called when the first port is enabled, this function performs the 120562306a36Sopenharmony_ci * actions necessary to make an adapter operational, such as completing 120662306a36Sopenharmony_ci * the initialization of HW modules, and enabling interrupts. 120762306a36Sopenharmony_ci * 120862306a36Sopenharmony_ci * Must be called with the rtnl lock held. 120962306a36Sopenharmony_ci */ 121062306a36Sopenharmony_cistatic int cxgb_up(struct adapter *adap) 121162306a36Sopenharmony_ci{ 121262306a36Sopenharmony_ci int i, err; 121362306a36Sopenharmony_ci 121462306a36Sopenharmony_ci if (!(adap->flags & FULL_INIT_DONE)) { 121562306a36Sopenharmony_ci err = t3_check_fw_version(adap); 121662306a36Sopenharmony_ci if (err == -EINVAL) { 121762306a36Sopenharmony_ci err = upgrade_fw(adap); 121862306a36Sopenharmony_ci CH_WARN(adap, "FW upgrade to %d.%d.%d %s\n", 121962306a36Sopenharmony_ci FW_VERSION_MAJOR, FW_VERSION_MINOR, 122062306a36Sopenharmony_ci FW_VERSION_MICRO, err ? "failed" : "succeeded"); 122162306a36Sopenharmony_ci } 122262306a36Sopenharmony_ci 122362306a36Sopenharmony_ci err = t3_check_tpsram_version(adap); 122462306a36Sopenharmony_ci if (err == -EINVAL) { 122562306a36Sopenharmony_ci err = update_tpsram(adap); 122662306a36Sopenharmony_ci CH_WARN(adap, "TP upgrade to %d.%d.%d %s\n", 122762306a36Sopenharmony_ci TP_VERSION_MAJOR, TP_VERSION_MINOR, 122862306a36Sopenharmony_ci TP_VERSION_MICRO, err ? "failed" : "succeeded"); 122962306a36Sopenharmony_ci } 123062306a36Sopenharmony_ci 123162306a36Sopenharmony_ci /* 123262306a36Sopenharmony_ci * Clear interrupts now to catch errors if t3_init_hw fails. 123362306a36Sopenharmony_ci * We clear them again later as initialization may trigger 123462306a36Sopenharmony_ci * conditions that can interrupt. 123562306a36Sopenharmony_ci */ 123662306a36Sopenharmony_ci t3_intr_clear(adap); 123762306a36Sopenharmony_ci 123862306a36Sopenharmony_ci err = t3_init_hw(adap, 0); 123962306a36Sopenharmony_ci if (err) 124062306a36Sopenharmony_ci goto out; 124162306a36Sopenharmony_ci 124262306a36Sopenharmony_ci t3_set_reg_field(adap, A_TP_PARA_REG5, 0, F_RXDDPOFFINIT); 124362306a36Sopenharmony_ci t3_write_reg(adap, A_ULPRX_TDDP_PSZ, V_HPZ0(PAGE_SHIFT - 12)); 124462306a36Sopenharmony_ci 124562306a36Sopenharmony_ci err = setup_sge_qsets(adap); 124662306a36Sopenharmony_ci if (err) 124762306a36Sopenharmony_ci goto out; 124862306a36Sopenharmony_ci 124962306a36Sopenharmony_ci for_each_port(adap, i) 125062306a36Sopenharmony_ci cxgb_vlan_mode(adap->port[i], adap->port[i]->features); 125162306a36Sopenharmony_ci 125262306a36Sopenharmony_ci setup_rss(adap); 125362306a36Sopenharmony_ci if (!(adap->flags & NAPI_INIT)) 125462306a36Sopenharmony_ci init_napi(adap); 125562306a36Sopenharmony_ci 125662306a36Sopenharmony_ci t3_start_sge_timers(adap); 125762306a36Sopenharmony_ci adap->flags |= FULL_INIT_DONE; 125862306a36Sopenharmony_ci } 125962306a36Sopenharmony_ci 126062306a36Sopenharmony_ci t3_intr_clear(adap); 126162306a36Sopenharmony_ci 126262306a36Sopenharmony_ci if (adap->flags & USING_MSIX) { 126362306a36Sopenharmony_ci name_msix_vecs(adap); 126462306a36Sopenharmony_ci err = request_irq(adap->msix_info[0].vec, 126562306a36Sopenharmony_ci t3_async_intr_handler, 0, 126662306a36Sopenharmony_ci adap->msix_info[0].desc, adap); 126762306a36Sopenharmony_ci if (err) 126862306a36Sopenharmony_ci goto irq_err; 126962306a36Sopenharmony_ci 127062306a36Sopenharmony_ci err = request_msix_data_irqs(adap); 127162306a36Sopenharmony_ci if (err) { 127262306a36Sopenharmony_ci free_irq(adap->msix_info[0].vec, adap); 127362306a36Sopenharmony_ci goto irq_err; 127462306a36Sopenharmony_ci } 127562306a36Sopenharmony_ci } else { 127662306a36Sopenharmony_ci err = request_irq(adap->pdev->irq, 127762306a36Sopenharmony_ci t3_intr_handler(adap, adap->sge.qs[0].rspq.polling), 127862306a36Sopenharmony_ci (adap->flags & USING_MSI) ? 0 : IRQF_SHARED, 127962306a36Sopenharmony_ci adap->name, adap); 128062306a36Sopenharmony_ci if (err) 128162306a36Sopenharmony_ci goto irq_err; 128262306a36Sopenharmony_ci } 128362306a36Sopenharmony_ci 128462306a36Sopenharmony_ci enable_all_napi(adap); 128562306a36Sopenharmony_ci t3_sge_start(adap); 128662306a36Sopenharmony_ci t3_intr_enable(adap); 128762306a36Sopenharmony_ci 128862306a36Sopenharmony_ci if (adap->params.rev >= T3_REV_C && !(adap->flags & TP_PARITY_INIT) && 128962306a36Sopenharmony_ci is_offload(adap) && init_tp_parity(adap) == 0) 129062306a36Sopenharmony_ci adap->flags |= TP_PARITY_INIT; 129162306a36Sopenharmony_ci 129262306a36Sopenharmony_ci if (adap->flags & TP_PARITY_INIT) { 129362306a36Sopenharmony_ci t3_write_reg(adap, A_TP_INT_CAUSE, 129462306a36Sopenharmony_ci F_CMCACHEPERR | F_ARPLUTPERR); 129562306a36Sopenharmony_ci t3_write_reg(adap, A_TP_INT_ENABLE, 0x7fbfffff); 129662306a36Sopenharmony_ci } 129762306a36Sopenharmony_ci 129862306a36Sopenharmony_ci if (!(adap->flags & QUEUES_BOUND)) { 129962306a36Sopenharmony_ci int ret = bind_qsets(adap); 130062306a36Sopenharmony_ci 130162306a36Sopenharmony_ci if (ret < 0) { 130262306a36Sopenharmony_ci CH_ERR(adap, "failed to bind qsets, err %d\n", ret); 130362306a36Sopenharmony_ci t3_intr_disable(adap); 130462306a36Sopenharmony_ci quiesce_rx(adap); 130562306a36Sopenharmony_ci free_irq_resources(adap); 130662306a36Sopenharmony_ci err = ret; 130762306a36Sopenharmony_ci goto out; 130862306a36Sopenharmony_ci } 130962306a36Sopenharmony_ci adap->flags |= QUEUES_BOUND; 131062306a36Sopenharmony_ci } 131162306a36Sopenharmony_ci 131262306a36Sopenharmony_ciout: 131362306a36Sopenharmony_ci return err; 131462306a36Sopenharmony_ciirq_err: 131562306a36Sopenharmony_ci CH_ERR(adap, "request_irq failed, err %d\n", err); 131662306a36Sopenharmony_ci goto out; 131762306a36Sopenharmony_ci} 131862306a36Sopenharmony_ci 131962306a36Sopenharmony_ci/* 132062306a36Sopenharmony_ci * Release resources when all the ports and offloading have been stopped. 132162306a36Sopenharmony_ci */ 132262306a36Sopenharmony_cistatic void cxgb_down(struct adapter *adapter, int on_wq) 132362306a36Sopenharmony_ci{ 132462306a36Sopenharmony_ci t3_sge_stop(adapter); 132562306a36Sopenharmony_ci spin_lock_irq(&adapter->work_lock); /* sync with PHY intr task */ 132662306a36Sopenharmony_ci t3_intr_disable(adapter); 132762306a36Sopenharmony_ci spin_unlock_irq(&adapter->work_lock); 132862306a36Sopenharmony_ci 132962306a36Sopenharmony_ci free_irq_resources(adapter); 133062306a36Sopenharmony_ci quiesce_rx(adapter); 133162306a36Sopenharmony_ci t3_sge_stop(adapter); 133262306a36Sopenharmony_ci if (!on_wq) 133362306a36Sopenharmony_ci flush_workqueue(cxgb3_wq);/* wait for external IRQ handler */ 133462306a36Sopenharmony_ci} 133562306a36Sopenharmony_ci 133662306a36Sopenharmony_cistatic void schedule_chk_task(struct adapter *adap) 133762306a36Sopenharmony_ci{ 133862306a36Sopenharmony_ci unsigned int timeo; 133962306a36Sopenharmony_ci 134062306a36Sopenharmony_ci timeo = adap->params.linkpoll_period ? 134162306a36Sopenharmony_ci (HZ * adap->params.linkpoll_period) / 10 : 134262306a36Sopenharmony_ci adap->params.stats_update_period * HZ; 134362306a36Sopenharmony_ci if (timeo) 134462306a36Sopenharmony_ci queue_delayed_work(cxgb3_wq, &adap->adap_check_task, timeo); 134562306a36Sopenharmony_ci} 134662306a36Sopenharmony_ci 134762306a36Sopenharmony_cistatic int offload_open(struct net_device *dev) 134862306a36Sopenharmony_ci{ 134962306a36Sopenharmony_ci struct port_info *pi = netdev_priv(dev); 135062306a36Sopenharmony_ci struct adapter *adapter = pi->adapter; 135162306a36Sopenharmony_ci struct t3cdev *tdev = dev2t3cdev(dev); 135262306a36Sopenharmony_ci int adap_up = adapter->open_device_map & PORT_MASK; 135362306a36Sopenharmony_ci int err; 135462306a36Sopenharmony_ci 135562306a36Sopenharmony_ci if (test_and_set_bit(OFFLOAD_DEVMAP_BIT, &adapter->open_device_map)) 135662306a36Sopenharmony_ci return 0; 135762306a36Sopenharmony_ci 135862306a36Sopenharmony_ci if (!adap_up && (err = cxgb_up(adapter)) < 0) 135962306a36Sopenharmony_ci goto out; 136062306a36Sopenharmony_ci 136162306a36Sopenharmony_ci t3_tp_set_offload_mode(adapter, 1); 136262306a36Sopenharmony_ci tdev->lldev = adapter->port[0]; 136362306a36Sopenharmony_ci err = cxgb3_offload_activate(adapter); 136462306a36Sopenharmony_ci if (err) 136562306a36Sopenharmony_ci goto out; 136662306a36Sopenharmony_ci 136762306a36Sopenharmony_ci init_port_mtus(adapter); 136862306a36Sopenharmony_ci t3_load_mtus(adapter, adapter->params.mtus, adapter->params.a_wnd, 136962306a36Sopenharmony_ci adapter->params.b_wnd, 137062306a36Sopenharmony_ci adapter->params.rev == 0 ? 137162306a36Sopenharmony_ci adapter->port[0]->mtu : 0xffff); 137262306a36Sopenharmony_ci init_smt(adapter); 137362306a36Sopenharmony_ci 137462306a36Sopenharmony_ci if (sysfs_create_group(&tdev->lldev->dev.kobj, &offload_attr_group)) 137562306a36Sopenharmony_ci dev_dbg(&dev->dev, "cannot create sysfs group\n"); 137662306a36Sopenharmony_ci 137762306a36Sopenharmony_ci /* Call back all registered clients */ 137862306a36Sopenharmony_ci cxgb3_add_clients(tdev); 137962306a36Sopenharmony_ci 138062306a36Sopenharmony_ciout: 138162306a36Sopenharmony_ci /* restore them in case the offload module has changed them */ 138262306a36Sopenharmony_ci if (err) { 138362306a36Sopenharmony_ci t3_tp_set_offload_mode(adapter, 0); 138462306a36Sopenharmony_ci clear_bit(OFFLOAD_DEVMAP_BIT, &adapter->open_device_map); 138562306a36Sopenharmony_ci cxgb3_set_dummy_ops(tdev); 138662306a36Sopenharmony_ci } 138762306a36Sopenharmony_ci return err; 138862306a36Sopenharmony_ci} 138962306a36Sopenharmony_ci 139062306a36Sopenharmony_cistatic int offload_close(struct t3cdev *tdev) 139162306a36Sopenharmony_ci{ 139262306a36Sopenharmony_ci struct adapter *adapter = tdev2adap(tdev); 139362306a36Sopenharmony_ci struct t3c_data *td = T3C_DATA(tdev); 139462306a36Sopenharmony_ci 139562306a36Sopenharmony_ci if (!test_bit(OFFLOAD_DEVMAP_BIT, &adapter->open_device_map)) 139662306a36Sopenharmony_ci return 0; 139762306a36Sopenharmony_ci 139862306a36Sopenharmony_ci /* Call back all registered clients */ 139962306a36Sopenharmony_ci cxgb3_remove_clients(tdev); 140062306a36Sopenharmony_ci 140162306a36Sopenharmony_ci sysfs_remove_group(&tdev->lldev->dev.kobj, &offload_attr_group); 140262306a36Sopenharmony_ci 140362306a36Sopenharmony_ci /* Flush work scheduled while releasing TIDs */ 140462306a36Sopenharmony_ci flush_work(&td->tid_release_task); 140562306a36Sopenharmony_ci 140662306a36Sopenharmony_ci tdev->lldev = NULL; 140762306a36Sopenharmony_ci cxgb3_set_dummy_ops(tdev); 140862306a36Sopenharmony_ci t3_tp_set_offload_mode(adapter, 0); 140962306a36Sopenharmony_ci clear_bit(OFFLOAD_DEVMAP_BIT, &adapter->open_device_map); 141062306a36Sopenharmony_ci 141162306a36Sopenharmony_ci if (!adapter->open_device_map) 141262306a36Sopenharmony_ci cxgb_down(adapter, 0); 141362306a36Sopenharmony_ci 141462306a36Sopenharmony_ci cxgb3_offload_deactivate(adapter); 141562306a36Sopenharmony_ci return 0; 141662306a36Sopenharmony_ci} 141762306a36Sopenharmony_ci 141862306a36Sopenharmony_cistatic int cxgb_open(struct net_device *dev) 141962306a36Sopenharmony_ci{ 142062306a36Sopenharmony_ci struct port_info *pi = netdev_priv(dev); 142162306a36Sopenharmony_ci struct adapter *adapter = pi->adapter; 142262306a36Sopenharmony_ci int other_ports = adapter->open_device_map & PORT_MASK; 142362306a36Sopenharmony_ci int err; 142462306a36Sopenharmony_ci 142562306a36Sopenharmony_ci if (!adapter->open_device_map && (err = cxgb_up(adapter)) < 0) 142662306a36Sopenharmony_ci return err; 142762306a36Sopenharmony_ci 142862306a36Sopenharmony_ci set_bit(pi->port_id, &adapter->open_device_map); 142962306a36Sopenharmony_ci if (is_offload(adapter) && !ofld_disable) { 143062306a36Sopenharmony_ci err = offload_open(dev); 143162306a36Sopenharmony_ci if (err) 143262306a36Sopenharmony_ci pr_warn("Could not initialize offload capabilities\n"); 143362306a36Sopenharmony_ci } 143462306a36Sopenharmony_ci 143562306a36Sopenharmony_ci netif_set_real_num_tx_queues(dev, pi->nqsets); 143662306a36Sopenharmony_ci err = netif_set_real_num_rx_queues(dev, pi->nqsets); 143762306a36Sopenharmony_ci if (err) 143862306a36Sopenharmony_ci return err; 143962306a36Sopenharmony_ci link_start(dev); 144062306a36Sopenharmony_ci t3_port_intr_enable(adapter, pi->port_id); 144162306a36Sopenharmony_ci netif_tx_start_all_queues(dev); 144262306a36Sopenharmony_ci if (!other_ports) 144362306a36Sopenharmony_ci schedule_chk_task(adapter); 144462306a36Sopenharmony_ci 144562306a36Sopenharmony_ci cxgb3_event_notify(&adapter->tdev, OFFLOAD_PORT_UP, pi->port_id); 144662306a36Sopenharmony_ci return 0; 144762306a36Sopenharmony_ci} 144862306a36Sopenharmony_ci 144962306a36Sopenharmony_cistatic int __cxgb_close(struct net_device *dev, int on_wq) 145062306a36Sopenharmony_ci{ 145162306a36Sopenharmony_ci struct port_info *pi = netdev_priv(dev); 145262306a36Sopenharmony_ci struct adapter *adapter = pi->adapter; 145362306a36Sopenharmony_ci 145462306a36Sopenharmony_ci 145562306a36Sopenharmony_ci if (!adapter->open_device_map) 145662306a36Sopenharmony_ci return 0; 145762306a36Sopenharmony_ci 145862306a36Sopenharmony_ci /* Stop link fault interrupts */ 145962306a36Sopenharmony_ci t3_xgm_intr_disable(adapter, pi->port_id); 146062306a36Sopenharmony_ci t3_read_reg(adapter, A_XGM_INT_STATUS + pi->mac.offset); 146162306a36Sopenharmony_ci 146262306a36Sopenharmony_ci t3_port_intr_disable(adapter, pi->port_id); 146362306a36Sopenharmony_ci netif_tx_stop_all_queues(dev); 146462306a36Sopenharmony_ci pi->phy.ops->power_down(&pi->phy, 1); 146562306a36Sopenharmony_ci netif_carrier_off(dev); 146662306a36Sopenharmony_ci t3_mac_disable(&pi->mac, MAC_DIRECTION_TX | MAC_DIRECTION_RX); 146762306a36Sopenharmony_ci 146862306a36Sopenharmony_ci spin_lock_irq(&adapter->work_lock); /* sync with update task */ 146962306a36Sopenharmony_ci clear_bit(pi->port_id, &adapter->open_device_map); 147062306a36Sopenharmony_ci spin_unlock_irq(&adapter->work_lock); 147162306a36Sopenharmony_ci 147262306a36Sopenharmony_ci if (!(adapter->open_device_map & PORT_MASK)) 147362306a36Sopenharmony_ci cancel_delayed_work_sync(&adapter->adap_check_task); 147462306a36Sopenharmony_ci 147562306a36Sopenharmony_ci if (!adapter->open_device_map) 147662306a36Sopenharmony_ci cxgb_down(adapter, on_wq); 147762306a36Sopenharmony_ci 147862306a36Sopenharmony_ci cxgb3_event_notify(&adapter->tdev, OFFLOAD_PORT_DOWN, pi->port_id); 147962306a36Sopenharmony_ci return 0; 148062306a36Sopenharmony_ci} 148162306a36Sopenharmony_ci 148262306a36Sopenharmony_cistatic int cxgb_close(struct net_device *dev) 148362306a36Sopenharmony_ci{ 148462306a36Sopenharmony_ci return __cxgb_close(dev, 0); 148562306a36Sopenharmony_ci} 148662306a36Sopenharmony_ci 148762306a36Sopenharmony_cistatic struct net_device_stats *cxgb_get_stats(struct net_device *dev) 148862306a36Sopenharmony_ci{ 148962306a36Sopenharmony_ci struct port_info *pi = netdev_priv(dev); 149062306a36Sopenharmony_ci struct adapter *adapter = pi->adapter; 149162306a36Sopenharmony_ci struct net_device_stats *ns = &dev->stats; 149262306a36Sopenharmony_ci const struct mac_stats *pstats; 149362306a36Sopenharmony_ci 149462306a36Sopenharmony_ci spin_lock(&adapter->stats_lock); 149562306a36Sopenharmony_ci pstats = t3_mac_update_stats(&pi->mac); 149662306a36Sopenharmony_ci spin_unlock(&adapter->stats_lock); 149762306a36Sopenharmony_ci 149862306a36Sopenharmony_ci ns->tx_bytes = pstats->tx_octets; 149962306a36Sopenharmony_ci ns->tx_packets = pstats->tx_frames; 150062306a36Sopenharmony_ci ns->rx_bytes = pstats->rx_octets; 150162306a36Sopenharmony_ci ns->rx_packets = pstats->rx_frames; 150262306a36Sopenharmony_ci ns->multicast = pstats->rx_mcast_frames; 150362306a36Sopenharmony_ci 150462306a36Sopenharmony_ci ns->tx_errors = pstats->tx_underrun; 150562306a36Sopenharmony_ci ns->rx_errors = pstats->rx_symbol_errs + pstats->rx_fcs_errs + 150662306a36Sopenharmony_ci pstats->rx_too_long + pstats->rx_jabber + pstats->rx_short + 150762306a36Sopenharmony_ci pstats->rx_fifo_ovfl; 150862306a36Sopenharmony_ci 150962306a36Sopenharmony_ci /* detailed rx_errors */ 151062306a36Sopenharmony_ci ns->rx_length_errors = pstats->rx_jabber + pstats->rx_too_long; 151162306a36Sopenharmony_ci ns->rx_over_errors = 0; 151262306a36Sopenharmony_ci ns->rx_crc_errors = pstats->rx_fcs_errs; 151362306a36Sopenharmony_ci ns->rx_frame_errors = pstats->rx_symbol_errs; 151462306a36Sopenharmony_ci ns->rx_fifo_errors = pstats->rx_fifo_ovfl; 151562306a36Sopenharmony_ci ns->rx_missed_errors = pstats->rx_cong_drops; 151662306a36Sopenharmony_ci 151762306a36Sopenharmony_ci /* detailed tx_errors */ 151862306a36Sopenharmony_ci ns->tx_aborted_errors = 0; 151962306a36Sopenharmony_ci ns->tx_carrier_errors = 0; 152062306a36Sopenharmony_ci ns->tx_fifo_errors = pstats->tx_underrun; 152162306a36Sopenharmony_ci ns->tx_heartbeat_errors = 0; 152262306a36Sopenharmony_ci ns->tx_window_errors = 0; 152362306a36Sopenharmony_ci return ns; 152462306a36Sopenharmony_ci} 152562306a36Sopenharmony_ci 152662306a36Sopenharmony_cistatic u32 get_msglevel(struct net_device *dev) 152762306a36Sopenharmony_ci{ 152862306a36Sopenharmony_ci struct port_info *pi = netdev_priv(dev); 152962306a36Sopenharmony_ci struct adapter *adapter = pi->adapter; 153062306a36Sopenharmony_ci 153162306a36Sopenharmony_ci return adapter->msg_enable; 153262306a36Sopenharmony_ci} 153362306a36Sopenharmony_ci 153462306a36Sopenharmony_cistatic void set_msglevel(struct net_device *dev, u32 val) 153562306a36Sopenharmony_ci{ 153662306a36Sopenharmony_ci struct port_info *pi = netdev_priv(dev); 153762306a36Sopenharmony_ci struct adapter *adapter = pi->adapter; 153862306a36Sopenharmony_ci 153962306a36Sopenharmony_ci adapter->msg_enable = val; 154062306a36Sopenharmony_ci} 154162306a36Sopenharmony_ci 154262306a36Sopenharmony_cistatic const char stats_strings[][ETH_GSTRING_LEN] = { 154362306a36Sopenharmony_ci "TxOctetsOK ", 154462306a36Sopenharmony_ci "TxFramesOK ", 154562306a36Sopenharmony_ci "TxMulticastFramesOK", 154662306a36Sopenharmony_ci "TxBroadcastFramesOK", 154762306a36Sopenharmony_ci "TxPauseFrames ", 154862306a36Sopenharmony_ci "TxUnderrun ", 154962306a36Sopenharmony_ci "TxExtUnderrun ", 155062306a36Sopenharmony_ci 155162306a36Sopenharmony_ci "TxFrames64 ", 155262306a36Sopenharmony_ci "TxFrames65To127 ", 155362306a36Sopenharmony_ci "TxFrames128To255 ", 155462306a36Sopenharmony_ci "TxFrames256To511 ", 155562306a36Sopenharmony_ci "TxFrames512To1023 ", 155662306a36Sopenharmony_ci "TxFrames1024To1518 ", 155762306a36Sopenharmony_ci "TxFrames1519ToMax ", 155862306a36Sopenharmony_ci 155962306a36Sopenharmony_ci "RxOctetsOK ", 156062306a36Sopenharmony_ci "RxFramesOK ", 156162306a36Sopenharmony_ci "RxMulticastFramesOK", 156262306a36Sopenharmony_ci "RxBroadcastFramesOK", 156362306a36Sopenharmony_ci "RxPauseFrames ", 156462306a36Sopenharmony_ci "RxFCSErrors ", 156562306a36Sopenharmony_ci "RxSymbolErrors ", 156662306a36Sopenharmony_ci "RxShortErrors ", 156762306a36Sopenharmony_ci "RxJabberErrors ", 156862306a36Sopenharmony_ci "RxLengthErrors ", 156962306a36Sopenharmony_ci "RxFIFOoverflow ", 157062306a36Sopenharmony_ci 157162306a36Sopenharmony_ci "RxFrames64 ", 157262306a36Sopenharmony_ci "RxFrames65To127 ", 157362306a36Sopenharmony_ci "RxFrames128To255 ", 157462306a36Sopenharmony_ci "RxFrames256To511 ", 157562306a36Sopenharmony_ci "RxFrames512To1023 ", 157662306a36Sopenharmony_ci "RxFrames1024To1518 ", 157762306a36Sopenharmony_ci "RxFrames1519ToMax ", 157862306a36Sopenharmony_ci 157962306a36Sopenharmony_ci "PhyFIFOErrors ", 158062306a36Sopenharmony_ci "TSO ", 158162306a36Sopenharmony_ci "VLANextractions ", 158262306a36Sopenharmony_ci "VLANinsertions ", 158362306a36Sopenharmony_ci "TxCsumOffload ", 158462306a36Sopenharmony_ci "RxCsumGood ", 158562306a36Sopenharmony_ci "LroAggregated ", 158662306a36Sopenharmony_ci "LroFlushed ", 158762306a36Sopenharmony_ci "LroNoDesc ", 158862306a36Sopenharmony_ci "RxDrops ", 158962306a36Sopenharmony_ci 159062306a36Sopenharmony_ci "CheckTXEnToggled ", 159162306a36Sopenharmony_ci "CheckResets ", 159262306a36Sopenharmony_ci 159362306a36Sopenharmony_ci "LinkFaults ", 159462306a36Sopenharmony_ci}; 159562306a36Sopenharmony_ci 159662306a36Sopenharmony_cistatic int get_sset_count(struct net_device *dev, int sset) 159762306a36Sopenharmony_ci{ 159862306a36Sopenharmony_ci switch (sset) { 159962306a36Sopenharmony_ci case ETH_SS_STATS: 160062306a36Sopenharmony_ci return ARRAY_SIZE(stats_strings); 160162306a36Sopenharmony_ci default: 160262306a36Sopenharmony_ci return -EOPNOTSUPP; 160362306a36Sopenharmony_ci } 160462306a36Sopenharmony_ci} 160562306a36Sopenharmony_ci 160662306a36Sopenharmony_ci#define T3_REGMAP_SIZE (3 * 1024) 160762306a36Sopenharmony_ci 160862306a36Sopenharmony_cistatic int get_regs_len(struct net_device *dev) 160962306a36Sopenharmony_ci{ 161062306a36Sopenharmony_ci return T3_REGMAP_SIZE; 161162306a36Sopenharmony_ci} 161262306a36Sopenharmony_ci 161362306a36Sopenharmony_cistatic int get_eeprom_len(struct net_device *dev) 161462306a36Sopenharmony_ci{ 161562306a36Sopenharmony_ci return EEPROMSIZE; 161662306a36Sopenharmony_ci} 161762306a36Sopenharmony_ci 161862306a36Sopenharmony_cistatic void get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) 161962306a36Sopenharmony_ci{ 162062306a36Sopenharmony_ci struct port_info *pi = netdev_priv(dev); 162162306a36Sopenharmony_ci struct adapter *adapter = pi->adapter; 162262306a36Sopenharmony_ci u32 fw_vers = 0; 162362306a36Sopenharmony_ci u32 tp_vers = 0; 162462306a36Sopenharmony_ci 162562306a36Sopenharmony_ci spin_lock(&adapter->stats_lock); 162662306a36Sopenharmony_ci t3_get_fw_version(adapter, &fw_vers); 162762306a36Sopenharmony_ci t3_get_tp_version(adapter, &tp_vers); 162862306a36Sopenharmony_ci spin_unlock(&adapter->stats_lock); 162962306a36Sopenharmony_ci 163062306a36Sopenharmony_ci strscpy(info->driver, DRV_NAME, sizeof(info->driver)); 163162306a36Sopenharmony_ci strscpy(info->bus_info, pci_name(adapter->pdev), 163262306a36Sopenharmony_ci sizeof(info->bus_info)); 163362306a36Sopenharmony_ci if (fw_vers) 163462306a36Sopenharmony_ci snprintf(info->fw_version, sizeof(info->fw_version), 163562306a36Sopenharmony_ci "%s %u.%u.%u TP %u.%u.%u", 163662306a36Sopenharmony_ci G_FW_VERSION_TYPE(fw_vers) ? "T" : "N", 163762306a36Sopenharmony_ci G_FW_VERSION_MAJOR(fw_vers), 163862306a36Sopenharmony_ci G_FW_VERSION_MINOR(fw_vers), 163962306a36Sopenharmony_ci G_FW_VERSION_MICRO(fw_vers), 164062306a36Sopenharmony_ci G_TP_VERSION_MAJOR(tp_vers), 164162306a36Sopenharmony_ci G_TP_VERSION_MINOR(tp_vers), 164262306a36Sopenharmony_ci G_TP_VERSION_MICRO(tp_vers)); 164362306a36Sopenharmony_ci} 164462306a36Sopenharmony_ci 164562306a36Sopenharmony_cistatic void get_strings(struct net_device *dev, u32 stringset, u8 * data) 164662306a36Sopenharmony_ci{ 164762306a36Sopenharmony_ci if (stringset == ETH_SS_STATS) 164862306a36Sopenharmony_ci memcpy(data, stats_strings, sizeof(stats_strings)); 164962306a36Sopenharmony_ci} 165062306a36Sopenharmony_ci 165162306a36Sopenharmony_cistatic unsigned long collect_sge_port_stats(struct adapter *adapter, 165262306a36Sopenharmony_ci struct port_info *p, int idx) 165362306a36Sopenharmony_ci{ 165462306a36Sopenharmony_ci int i; 165562306a36Sopenharmony_ci unsigned long tot = 0; 165662306a36Sopenharmony_ci 165762306a36Sopenharmony_ci for (i = p->first_qset; i < p->first_qset + p->nqsets; ++i) 165862306a36Sopenharmony_ci tot += adapter->sge.qs[i].port_stats[idx]; 165962306a36Sopenharmony_ci return tot; 166062306a36Sopenharmony_ci} 166162306a36Sopenharmony_ci 166262306a36Sopenharmony_cistatic void get_stats(struct net_device *dev, struct ethtool_stats *stats, 166362306a36Sopenharmony_ci u64 *data) 166462306a36Sopenharmony_ci{ 166562306a36Sopenharmony_ci struct port_info *pi = netdev_priv(dev); 166662306a36Sopenharmony_ci struct adapter *adapter = pi->adapter; 166762306a36Sopenharmony_ci const struct mac_stats *s; 166862306a36Sopenharmony_ci 166962306a36Sopenharmony_ci spin_lock(&adapter->stats_lock); 167062306a36Sopenharmony_ci s = t3_mac_update_stats(&pi->mac); 167162306a36Sopenharmony_ci spin_unlock(&adapter->stats_lock); 167262306a36Sopenharmony_ci 167362306a36Sopenharmony_ci *data++ = s->tx_octets; 167462306a36Sopenharmony_ci *data++ = s->tx_frames; 167562306a36Sopenharmony_ci *data++ = s->tx_mcast_frames; 167662306a36Sopenharmony_ci *data++ = s->tx_bcast_frames; 167762306a36Sopenharmony_ci *data++ = s->tx_pause; 167862306a36Sopenharmony_ci *data++ = s->tx_underrun; 167962306a36Sopenharmony_ci *data++ = s->tx_fifo_urun; 168062306a36Sopenharmony_ci 168162306a36Sopenharmony_ci *data++ = s->tx_frames_64; 168262306a36Sopenharmony_ci *data++ = s->tx_frames_65_127; 168362306a36Sopenharmony_ci *data++ = s->tx_frames_128_255; 168462306a36Sopenharmony_ci *data++ = s->tx_frames_256_511; 168562306a36Sopenharmony_ci *data++ = s->tx_frames_512_1023; 168662306a36Sopenharmony_ci *data++ = s->tx_frames_1024_1518; 168762306a36Sopenharmony_ci *data++ = s->tx_frames_1519_max; 168862306a36Sopenharmony_ci 168962306a36Sopenharmony_ci *data++ = s->rx_octets; 169062306a36Sopenharmony_ci *data++ = s->rx_frames; 169162306a36Sopenharmony_ci *data++ = s->rx_mcast_frames; 169262306a36Sopenharmony_ci *data++ = s->rx_bcast_frames; 169362306a36Sopenharmony_ci *data++ = s->rx_pause; 169462306a36Sopenharmony_ci *data++ = s->rx_fcs_errs; 169562306a36Sopenharmony_ci *data++ = s->rx_symbol_errs; 169662306a36Sopenharmony_ci *data++ = s->rx_short; 169762306a36Sopenharmony_ci *data++ = s->rx_jabber; 169862306a36Sopenharmony_ci *data++ = s->rx_too_long; 169962306a36Sopenharmony_ci *data++ = s->rx_fifo_ovfl; 170062306a36Sopenharmony_ci 170162306a36Sopenharmony_ci *data++ = s->rx_frames_64; 170262306a36Sopenharmony_ci *data++ = s->rx_frames_65_127; 170362306a36Sopenharmony_ci *data++ = s->rx_frames_128_255; 170462306a36Sopenharmony_ci *data++ = s->rx_frames_256_511; 170562306a36Sopenharmony_ci *data++ = s->rx_frames_512_1023; 170662306a36Sopenharmony_ci *data++ = s->rx_frames_1024_1518; 170762306a36Sopenharmony_ci *data++ = s->rx_frames_1519_max; 170862306a36Sopenharmony_ci 170962306a36Sopenharmony_ci *data++ = pi->phy.fifo_errors; 171062306a36Sopenharmony_ci 171162306a36Sopenharmony_ci *data++ = collect_sge_port_stats(adapter, pi, SGE_PSTAT_TSO); 171262306a36Sopenharmony_ci *data++ = collect_sge_port_stats(adapter, pi, SGE_PSTAT_VLANEX); 171362306a36Sopenharmony_ci *data++ = collect_sge_port_stats(adapter, pi, SGE_PSTAT_VLANINS); 171462306a36Sopenharmony_ci *data++ = collect_sge_port_stats(adapter, pi, SGE_PSTAT_TX_CSUM); 171562306a36Sopenharmony_ci *data++ = collect_sge_port_stats(adapter, pi, SGE_PSTAT_RX_CSUM_GOOD); 171662306a36Sopenharmony_ci *data++ = 0; 171762306a36Sopenharmony_ci *data++ = 0; 171862306a36Sopenharmony_ci *data++ = 0; 171962306a36Sopenharmony_ci *data++ = s->rx_cong_drops; 172062306a36Sopenharmony_ci 172162306a36Sopenharmony_ci *data++ = s->num_toggled; 172262306a36Sopenharmony_ci *data++ = s->num_resets; 172362306a36Sopenharmony_ci 172462306a36Sopenharmony_ci *data++ = s->link_faults; 172562306a36Sopenharmony_ci} 172662306a36Sopenharmony_ci 172762306a36Sopenharmony_cistatic inline void reg_block_dump(struct adapter *ap, void *buf, 172862306a36Sopenharmony_ci unsigned int start, unsigned int end) 172962306a36Sopenharmony_ci{ 173062306a36Sopenharmony_ci u32 *p = buf + start; 173162306a36Sopenharmony_ci 173262306a36Sopenharmony_ci for (; start <= end; start += sizeof(u32)) 173362306a36Sopenharmony_ci *p++ = t3_read_reg(ap, start); 173462306a36Sopenharmony_ci} 173562306a36Sopenharmony_ci 173662306a36Sopenharmony_cistatic void get_regs(struct net_device *dev, struct ethtool_regs *regs, 173762306a36Sopenharmony_ci void *buf) 173862306a36Sopenharmony_ci{ 173962306a36Sopenharmony_ci struct port_info *pi = netdev_priv(dev); 174062306a36Sopenharmony_ci struct adapter *ap = pi->adapter; 174162306a36Sopenharmony_ci 174262306a36Sopenharmony_ci /* 174362306a36Sopenharmony_ci * Version scheme: 174462306a36Sopenharmony_ci * bits 0..9: chip version 174562306a36Sopenharmony_ci * bits 10..15: chip revision 174662306a36Sopenharmony_ci * bit 31: set for PCIe cards 174762306a36Sopenharmony_ci */ 174862306a36Sopenharmony_ci regs->version = 3 | (ap->params.rev << 10) | (is_pcie(ap) << 31); 174962306a36Sopenharmony_ci 175062306a36Sopenharmony_ci /* 175162306a36Sopenharmony_ci * We skip the MAC statistics registers because they are clear-on-read. 175262306a36Sopenharmony_ci * Also reading multi-register stats would need to synchronize with the 175362306a36Sopenharmony_ci * periodic mac stats accumulation. Hard to justify the complexity. 175462306a36Sopenharmony_ci */ 175562306a36Sopenharmony_ci memset(buf, 0, T3_REGMAP_SIZE); 175662306a36Sopenharmony_ci reg_block_dump(ap, buf, 0, A_SG_RSPQ_CREDIT_RETURN); 175762306a36Sopenharmony_ci reg_block_dump(ap, buf, A_SG_HI_DRB_HI_THRSH, A_ULPRX_PBL_ULIMIT); 175862306a36Sopenharmony_ci reg_block_dump(ap, buf, A_ULPTX_CONFIG, A_MPS_INT_CAUSE); 175962306a36Sopenharmony_ci reg_block_dump(ap, buf, A_CPL_SWITCH_CNTRL, A_CPL_MAP_TBL_DATA); 176062306a36Sopenharmony_ci reg_block_dump(ap, buf, A_SMB_GLOBAL_TIME_CFG, A_XGM_SERDES_STAT3); 176162306a36Sopenharmony_ci reg_block_dump(ap, buf, A_XGM_SERDES_STATUS0, 176262306a36Sopenharmony_ci XGM_REG(A_XGM_SERDES_STAT3, 1)); 176362306a36Sopenharmony_ci reg_block_dump(ap, buf, XGM_REG(A_XGM_SERDES_STATUS0, 1), 176462306a36Sopenharmony_ci XGM_REG(A_XGM_RX_SPI4_SOP_EOP_CNT, 1)); 176562306a36Sopenharmony_ci} 176662306a36Sopenharmony_ci 176762306a36Sopenharmony_cistatic int restart_autoneg(struct net_device *dev) 176862306a36Sopenharmony_ci{ 176962306a36Sopenharmony_ci struct port_info *p = netdev_priv(dev); 177062306a36Sopenharmony_ci 177162306a36Sopenharmony_ci if (!netif_running(dev)) 177262306a36Sopenharmony_ci return -EAGAIN; 177362306a36Sopenharmony_ci if (p->link_config.autoneg != AUTONEG_ENABLE) 177462306a36Sopenharmony_ci return -EINVAL; 177562306a36Sopenharmony_ci p->phy.ops->autoneg_restart(&p->phy); 177662306a36Sopenharmony_ci return 0; 177762306a36Sopenharmony_ci} 177862306a36Sopenharmony_ci 177962306a36Sopenharmony_cistatic int set_phys_id(struct net_device *dev, 178062306a36Sopenharmony_ci enum ethtool_phys_id_state state) 178162306a36Sopenharmony_ci{ 178262306a36Sopenharmony_ci struct port_info *pi = netdev_priv(dev); 178362306a36Sopenharmony_ci struct adapter *adapter = pi->adapter; 178462306a36Sopenharmony_ci 178562306a36Sopenharmony_ci switch (state) { 178662306a36Sopenharmony_ci case ETHTOOL_ID_ACTIVE: 178762306a36Sopenharmony_ci return 1; /* cycle on/off once per second */ 178862306a36Sopenharmony_ci 178962306a36Sopenharmony_ci case ETHTOOL_ID_OFF: 179062306a36Sopenharmony_ci t3_set_reg_field(adapter, A_T3DBG_GPIO_EN, F_GPIO0_OUT_VAL, 0); 179162306a36Sopenharmony_ci break; 179262306a36Sopenharmony_ci 179362306a36Sopenharmony_ci case ETHTOOL_ID_ON: 179462306a36Sopenharmony_ci case ETHTOOL_ID_INACTIVE: 179562306a36Sopenharmony_ci t3_set_reg_field(adapter, A_T3DBG_GPIO_EN, F_GPIO0_OUT_VAL, 179662306a36Sopenharmony_ci F_GPIO0_OUT_VAL); 179762306a36Sopenharmony_ci } 179862306a36Sopenharmony_ci 179962306a36Sopenharmony_ci return 0; 180062306a36Sopenharmony_ci} 180162306a36Sopenharmony_ci 180262306a36Sopenharmony_cistatic int get_link_ksettings(struct net_device *dev, 180362306a36Sopenharmony_ci struct ethtool_link_ksettings *cmd) 180462306a36Sopenharmony_ci{ 180562306a36Sopenharmony_ci struct port_info *p = netdev_priv(dev); 180662306a36Sopenharmony_ci u32 supported; 180762306a36Sopenharmony_ci 180862306a36Sopenharmony_ci ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported, 180962306a36Sopenharmony_ci p->link_config.supported); 181062306a36Sopenharmony_ci ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising, 181162306a36Sopenharmony_ci p->link_config.advertising); 181262306a36Sopenharmony_ci 181362306a36Sopenharmony_ci if (netif_carrier_ok(dev)) { 181462306a36Sopenharmony_ci cmd->base.speed = p->link_config.speed; 181562306a36Sopenharmony_ci cmd->base.duplex = p->link_config.duplex; 181662306a36Sopenharmony_ci } else { 181762306a36Sopenharmony_ci cmd->base.speed = SPEED_UNKNOWN; 181862306a36Sopenharmony_ci cmd->base.duplex = DUPLEX_UNKNOWN; 181962306a36Sopenharmony_ci } 182062306a36Sopenharmony_ci 182162306a36Sopenharmony_ci ethtool_convert_link_mode_to_legacy_u32(&supported, 182262306a36Sopenharmony_ci cmd->link_modes.supported); 182362306a36Sopenharmony_ci 182462306a36Sopenharmony_ci cmd->base.port = (supported & SUPPORTED_TP) ? PORT_TP : PORT_FIBRE; 182562306a36Sopenharmony_ci cmd->base.phy_address = p->phy.mdio.prtad; 182662306a36Sopenharmony_ci cmd->base.autoneg = p->link_config.autoneg; 182762306a36Sopenharmony_ci return 0; 182862306a36Sopenharmony_ci} 182962306a36Sopenharmony_ci 183062306a36Sopenharmony_cistatic int speed_duplex_to_caps(int speed, int duplex) 183162306a36Sopenharmony_ci{ 183262306a36Sopenharmony_ci int cap = 0; 183362306a36Sopenharmony_ci 183462306a36Sopenharmony_ci switch (speed) { 183562306a36Sopenharmony_ci case SPEED_10: 183662306a36Sopenharmony_ci if (duplex == DUPLEX_FULL) 183762306a36Sopenharmony_ci cap = SUPPORTED_10baseT_Full; 183862306a36Sopenharmony_ci else 183962306a36Sopenharmony_ci cap = SUPPORTED_10baseT_Half; 184062306a36Sopenharmony_ci break; 184162306a36Sopenharmony_ci case SPEED_100: 184262306a36Sopenharmony_ci if (duplex == DUPLEX_FULL) 184362306a36Sopenharmony_ci cap = SUPPORTED_100baseT_Full; 184462306a36Sopenharmony_ci else 184562306a36Sopenharmony_ci cap = SUPPORTED_100baseT_Half; 184662306a36Sopenharmony_ci break; 184762306a36Sopenharmony_ci case SPEED_1000: 184862306a36Sopenharmony_ci if (duplex == DUPLEX_FULL) 184962306a36Sopenharmony_ci cap = SUPPORTED_1000baseT_Full; 185062306a36Sopenharmony_ci else 185162306a36Sopenharmony_ci cap = SUPPORTED_1000baseT_Half; 185262306a36Sopenharmony_ci break; 185362306a36Sopenharmony_ci case SPEED_10000: 185462306a36Sopenharmony_ci if (duplex == DUPLEX_FULL) 185562306a36Sopenharmony_ci cap = SUPPORTED_10000baseT_Full; 185662306a36Sopenharmony_ci } 185762306a36Sopenharmony_ci return cap; 185862306a36Sopenharmony_ci} 185962306a36Sopenharmony_ci 186062306a36Sopenharmony_ci#define ADVERTISED_MASK (ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full | \ 186162306a36Sopenharmony_ci ADVERTISED_100baseT_Half | ADVERTISED_100baseT_Full | \ 186262306a36Sopenharmony_ci ADVERTISED_1000baseT_Half | ADVERTISED_1000baseT_Full | \ 186362306a36Sopenharmony_ci ADVERTISED_10000baseT_Full) 186462306a36Sopenharmony_ci 186562306a36Sopenharmony_cistatic int set_link_ksettings(struct net_device *dev, 186662306a36Sopenharmony_ci const struct ethtool_link_ksettings *cmd) 186762306a36Sopenharmony_ci{ 186862306a36Sopenharmony_ci struct port_info *p = netdev_priv(dev); 186962306a36Sopenharmony_ci struct link_config *lc = &p->link_config; 187062306a36Sopenharmony_ci u32 advertising; 187162306a36Sopenharmony_ci 187262306a36Sopenharmony_ci ethtool_convert_link_mode_to_legacy_u32(&advertising, 187362306a36Sopenharmony_ci cmd->link_modes.advertising); 187462306a36Sopenharmony_ci 187562306a36Sopenharmony_ci if (!(lc->supported & SUPPORTED_Autoneg)) { 187662306a36Sopenharmony_ci /* 187762306a36Sopenharmony_ci * PHY offers a single speed/duplex. See if that's what's 187862306a36Sopenharmony_ci * being requested. 187962306a36Sopenharmony_ci */ 188062306a36Sopenharmony_ci if (cmd->base.autoneg == AUTONEG_DISABLE) { 188162306a36Sopenharmony_ci u32 speed = cmd->base.speed; 188262306a36Sopenharmony_ci int cap = speed_duplex_to_caps(speed, cmd->base.duplex); 188362306a36Sopenharmony_ci if (lc->supported & cap) 188462306a36Sopenharmony_ci return 0; 188562306a36Sopenharmony_ci } 188662306a36Sopenharmony_ci return -EINVAL; 188762306a36Sopenharmony_ci } 188862306a36Sopenharmony_ci 188962306a36Sopenharmony_ci if (cmd->base.autoneg == AUTONEG_DISABLE) { 189062306a36Sopenharmony_ci u32 speed = cmd->base.speed; 189162306a36Sopenharmony_ci int cap = speed_duplex_to_caps(speed, cmd->base.duplex); 189262306a36Sopenharmony_ci 189362306a36Sopenharmony_ci if (!(lc->supported & cap) || (speed == SPEED_1000)) 189462306a36Sopenharmony_ci return -EINVAL; 189562306a36Sopenharmony_ci lc->requested_speed = speed; 189662306a36Sopenharmony_ci lc->requested_duplex = cmd->base.duplex; 189762306a36Sopenharmony_ci lc->advertising = 0; 189862306a36Sopenharmony_ci } else { 189962306a36Sopenharmony_ci advertising &= ADVERTISED_MASK; 190062306a36Sopenharmony_ci advertising &= lc->supported; 190162306a36Sopenharmony_ci if (!advertising) 190262306a36Sopenharmony_ci return -EINVAL; 190362306a36Sopenharmony_ci lc->requested_speed = SPEED_INVALID; 190462306a36Sopenharmony_ci lc->requested_duplex = DUPLEX_INVALID; 190562306a36Sopenharmony_ci lc->advertising = advertising | ADVERTISED_Autoneg; 190662306a36Sopenharmony_ci } 190762306a36Sopenharmony_ci lc->autoneg = cmd->base.autoneg; 190862306a36Sopenharmony_ci if (netif_running(dev)) 190962306a36Sopenharmony_ci t3_link_start(&p->phy, &p->mac, lc); 191062306a36Sopenharmony_ci return 0; 191162306a36Sopenharmony_ci} 191262306a36Sopenharmony_ci 191362306a36Sopenharmony_cistatic void get_pauseparam(struct net_device *dev, 191462306a36Sopenharmony_ci struct ethtool_pauseparam *epause) 191562306a36Sopenharmony_ci{ 191662306a36Sopenharmony_ci struct port_info *p = netdev_priv(dev); 191762306a36Sopenharmony_ci 191862306a36Sopenharmony_ci epause->autoneg = (p->link_config.requested_fc & PAUSE_AUTONEG) != 0; 191962306a36Sopenharmony_ci epause->rx_pause = (p->link_config.fc & PAUSE_RX) != 0; 192062306a36Sopenharmony_ci epause->tx_pause = (p->link_config.fc & PAUSE_TX) != 0; 192162306a36Sopenharmony_ci} 192262306a36Sopenharmony_ci 192362306a36Sopenharmony_cistatic int set_pauseparam(struct net_device *dev, 192462306a36Sopenharmony_ci struct ethtool_pauseparam *epause) 192562306a36Sopenharmony_ci{ 192662306a36Sopenharmony_ci struct port_info *p = netdev_priv(dev); 192762306a36Sopenharmony_ci struct link_config *lc = &p->link_config; 192862306a36Sopenharmony_ci 192962306a36Sopenharmony_ci if (epause->autoneg == AUTONEG_DISABLE) 193062306a36Sopenharmony_ci lc->requested_fc = 0; 193162306a36Sopenharmony_ci else if (lc->supported & SUPPORTED_Autoneg) 193262306a36Sopenharmony_ci lc->requested_fc = PAUSE_AUTONEG; 193362306a36Sopenharmony_ci else 193462306a36Sopenharmony_ci return -EINVAL; 193562306a36Sopenharmony_ci 193662306a36Sopenharmony_ci if (epause->rx_pause) 193762306a36Sopenharmony_ci lc->requested_fc |= PAUSE_RX; 193862306a36Sopenharmony_ci if (epause->tx_pause) 193962306a36Sopenharmony_ci lc->requested_fc |= PAUSE_TX; 194062306a36Sopenharmony_ci if (lc->autoneg == AUTONEG_ENABLE) { 194162306a36Sopenharmony_ci if (netif_running(dev)) 194262306a36Sopenharmony_ci t3_link_start(&p->phy, &p->mac, lc); 194362306a36Sopenharmony_ci } else { 194462306a36Sopenharmony_ci lc->fc = lc->requested_fc & (PAUSE_RX | PAUSE_TX); 194562306a36Sopenharmony_ci if (netif_running(dev)) 194662306a36Sopenharmony_ci t3_mac_set_speed_duplex_fc(&p->mac, -1, -1, lc->fc); 194762306a36Sopenharmony_ci } 194862306a36Sopenharmony_ci return 0; 194962306a36Sopenharmony_ci} 195062306a36Sopenharmony_ci 195162306a36Sopenharmony_cistatic void get_sge_param(struct net_device *dev, struct ethtool_ringparam *e, 195262306a36Sopenharmony_ci struct kernel_ethtool_ringparam *kernel_e, 195362306a36Sopenharmony_ci struct netlink_ext_ack *extack) 195462306a36Sopenharmony_ci{ 195562306a36Sopenharmony_ci struct port_info *pi = netdev_priv(dev); 195662306a36Sopenharmony_ci struct adapter *adapter = pi->adapter; 195762306a36Sopenharmony_ci const struct qset_params *q = &adapter->params.sge.qset[pi->first_qset]; 195862306a36Sopenharmony_ci 195962306a36Sopenharmony_ci e->rx_max_pending = MAX_RX_BUFFERS; 196062306a36Sopenharmony_ci e->rx_jumbo_max_pending = MAX_RX_JUMBO_BUFFERS; 196162306a36Sopenharmony_ci e->tx_max_pending = MAX_TXQ_ENTRIES; 196262306a36Sopenharmony_ci 196362306a36Sopenharmony_ci e->rx_pending = q->fl_size; 196462306a36Sopenharmony_ci e->rx_mini_pending = q->rspq_size; 196562306a36Sopenharmony_ci e->rx_jumbo_pending = q->jumbo_size; 196662306a36Sopenharmony_ci e->tx_pending = q->txq_size[0]; 196762306a36Sopenharmony_ci} 196862306a36Sopenharmony_ci 196962306a36Sopenharmony_cistatic int set_sge_param(struct net_device *dev, struct ethtool_ringparam *e, 197062306a36Sopenharmony_ci struct kernel_ethtool_ringparam *kernel_e, 197162306a36Sopenharmony_ci struct netlink_ext_ack *extack) 197262306a36Sopenharmony_ci{ 197362306a36Sopenharmony_ci struct port_info *pi = netdev_priv(dev); 197462306a36Sopenharmony_ci struct adapter *adapter = pi->adapter; 197562306a36Sopenharmony_ci struct qset_params *q; 197662306a36Sopenharmony_ci int i; 197762306a36Sopenharmony_ci 197862306a36Sopenharmony_ci if (e->rx_pending > MAX_RX_BUFFERS || 197962306a36Sopenharmony_ci e->rx_jumbo_pending > MAX_RX_JUMBO_BUFFERS || 198062306a36Sopenharmony_ci e->tx_pending > MAX_TXQ_ENTRIES || 198162306a36Sopenharmony_ci e->rx_mini_pending > MAX_RSPQ_ENTRIES || 198262306a36Sopenharmony_ci e->rx_mini_pending < MIN_RSPQ_ENTRIES || 198362306a36Sopenharmony_ci e->rx_pending < MIN_FL_ENTRIES || 198462306a36Sopenharmony_ci e->rx_jumbo_pending < MIN_FL_ENTRIES || 198562306a36Sopenharmony_ci e->tx_pending < adapter->params.nports * MIN_TXQ_ENTRIES) 198662306a36Sopenharmony_ci return -EINVAL; 198762306a36Sopenharmony_ci 198862306a36Sopenharmony_ci if (adapter->flags & FULL_INIT_DONE) 198962306a36Sopenharmony_ci return -EBUSY; 199062306a36Sopenharmony_ci 199162306a36Sopenharmony_ci q = &adapter->params.sge.qset[pi->first_qset]; 199262306a36Sopenharmony_ci for (i = 0; i < pi->nqsets; ++i, ++q) { 199362306a36Sopenharmony_ci q->rspq_size = e->rx_mini_pending; 199462306a36Sopenharmony_ci q->fl_size = e->rx_pending; 199562306a36Sopenharmony_ci q->jumbo_size = e->rx_jumbo_pending; 199662306a36Sopenharmony_ci q->txq_size[0] = e->tx_pending; 199762306a36Sopenharmony_ci q->txq_size[1] = e->tx_pending; 199862306a36Sopenharmony_ci q->txq_size[2] = e->tx_pending; 199962306a36Sopenharmony_ci } 200062306a36Sopenharmony_ci return 0; 200162306a36Sopenharmony_ci} 200262306a36Sopenharmony_ci 200362306a36Sopenharmony_cistatic int set_coalesce(struct net_device *dev, struct ethtool_coalesce *c, 200462306a36Sopenharmony_ci struct kernel_ethtool_coalesce *kernel_coal, 200562306a36Sopenharmony_ci struct netlink_ext_ack *extack) 200662306a36Sopenharmony_ci{ 200762306a36Sopenharmony_ci struct port_info *pi = netdev_priv(dev); 200862306a36Sopenharmony_ci struct adapter *adapter = pi->adapter; 200962306a36Sopenharmony_ci struct qset_params *qsp; 201062306a36Sopenharmony_ci struct sge_qset *qs; 201162306a36Sopenharmony_ci int i; 201262306a36Sopenharmony_ci 201362306a36Sopenharmony_ci if (c->rx_coalesce_usecs * 10 > M_NEWTIMER) 201462306a36Sopenharmony_ci return -EINVAL; 201562306a36Sopenharmony_ci 201662306a36Sopenharmony_ci for (i = 0; i < pi->nqsets; i++) { 201762306a36Sopenharmony_ci qsp = &adapter->params.sge.qset[i]; 201862306a36Sopenharmony_ci qs = &adapter->sge.qs[i]; 201962306a36Sopenharmony_ci qsp->coalesce_usecs = c->rx_coalesce_usecs; 202062306a36Sopenharmony_ci t3_update_qset_coalesce(qs, qsp); 202162306a36Sopenharmony_ci } 202262306a36Sopenharmony_ci 202362306a36Sopenharmony_ci return 0; 202462306a36Sopenharmony_ci} 202562306a36Sopenharmony_ci 202662306a36Sopenharmony_cistatic int get_coalesce(struct net_device *dev, struct ethtool_coalesce *c, 202762306a36Sopenharmony_ci struct kernel_ethtool_coalesce *kernel_coal, 202862306a36Sopenharmony_ci struct netlink_ext_ack *extack) 202962306a36Sopenharmony_ci{ 203062306a36Sopenharmony_ci struct port_info *pi = netdev_priv(dev); 203162306a36Sopenharmony_ci struct adapter *adapter = pi->adapter; 203262306a36Sopenharmony_ci struct qset_params *q = adapter->params.sge.qset; 203362306a36Sopenharmony_ci 203462306a36Sopenharmony_ci c->rx_coalesce_usecs = q->coalesce_usecs; 203562306a36Sopenharmony_ci return 0; 203662306a36Sopenharmony_ci} 203762306a36Sopenharmony_ci 203862306a36Sopenharmony_cistatic int get_eeprom(struct net_device *dev, struct ethtool_eeprom *e, 203962306a36Sopenharmony_ci u8 * data) 204062306a36Sopenharmony_ci{ 204162306a36Sopenharmony_ci struct port_info *pi = netdev_priv(dev); 204262306a36Sopenharmony_ci struct adapter *adapter = pi->adapter; 204362306a36Sopenharmony_ci int cnt; 204462306a36Sopenharmony_ci 204562306a36Sopenharmony_ci e->magic = EEPROM_MAGIC; 204662306a36Sopenharmony_ci cnt = pci_read_vpd(adapter->pdev, e->offset, e->len, data); 204762306a36Sopenharmony_ci if (cnt < 0) 204862306a36Sopenharmony_ci return cnt; 204962306a36Sopenharmony_ci 205062306a36Sopenharmony_ci e->len = cnt; 205162306a36Sopenharmony_ci 205262306a36Sopenharmony_ci return 0; 205362306a36Sopenharmony_ci} 205462306a36Sopenharmony_ci 205562306a36Sopenharmony_cistatic int set_eeprom(struct net_device *dev, struct ethtool_eeprom *eeprom, 205662306a36Sopenharmony_ci u8 * data) 205762306a36Sopenharmony_ci{ 205862306a36Sopenharmony_ci struct port_info *pi = netdev_priv(dev); 205962306a36Sopenharmony_ci struct adapter *adapter = pi->adapter; 206062306a36Sopenharmony_ci u32 aligned_offset, aligned_len; 206162306a36Sopenharmony_ci u8 *buf; 206262306a36Sopenharmony_ci int err; 206362306a36Sopenharmony_ci 206462306a36Sopenharmony_ci if (eeprom->magic != EEPROM_MAGIC) 206562306a36Sopenharmony_ci return -EINVAL; 206662306a36Sopenharmony_ci 206762306a36Sopenharmony_ci aligned_offset = eeprom->offset & ~3; 206862306a36Sopenharmony_ci aligned_len = (eeprom->len + (eeprom->offset & 3) + 3) & ~3; 206962306a36Sopenharmony_ci 207062306a36Sopenharmony_ci if (aligned_offset != eeprom->offset || aligned_len != eeprom->len) { 207162306a36Sopenharmony_ci buf = kmalloc(aligned_len, GFP_KERNEL); 207262306a36Sopenharmony_ci if (!buf) 207362306a36Sopenharmony_ci return -ENOMEM; 207462306a36Sopenharmony_ci err = pci_read_vpd(adapter->pdev, aligned_offset, aligned_len, 207562306a36Sopenharmony_ci buf); 207662306a36Sopenharmony_ci if (err < 0) 207762306a36Sopenharmony_ci goto out; 207862306a36Sopenharmony_ci memcpy(buf + (eeprom->offset & 3), data, eeprom->len); 207962306a36Sopenharmony_ci } else 208062306a36Sopenharmony_ci buf = data; 208162306a36Sopenharmony_ci 208262306a36Sopenharmony_ci err = t3_seeprom_wp(adapter, 0); 208362306a36Sopenharmony_ci if (err) 208462306a36Sopenharmony_ci goto out; 208562306a36Sopenharmony_ci 208662306a36Sopenharmony_ci err = pci_write_vpd(adapter->pdev, aligned_offset, aligned_len, buf); 208762306a36Sopenharmony_ci if (err >= 0) 208862306a36Sopenharmony_ci err = t3_seeprom_wp(adapter, 1); 208962306a36Sopenharmony_ciout: 209062306a36Sopenharmony_ci if (buf != data) 209162306a36Sopenharmony_ci kfree(buf); 209262306a36Sopenharmony_ci return err < 0 ? err : 0; 209362306a36Sopenharmony_ci} 209462306a36Sopenharmony_ci 209562306a36Sopenharmony_cistatic void get_wol(struct net_device *dev, struct ethtool_wolinfo *wol) 209662306a36Sopenharmony_ci{ 209762306a36Sopenharmony_ci wol->supported = 0; 209862306a36Sopenharmony_ci wol->wolopts = 0; 209962306a36Sopenharmony_ci memset(&wol->sopass, 0, sizeof(wol->sopass)); 210062306a36Sopenharmony_ci} 210162306a36Sopenharmony_ci 210262306a36Sopenharmony_cistatic const struct ethtool_ops cxgb_ethtool_ops = { 210362306a36Sopenharmony_ci .supported_coalesce_params = ETHTOOL_COALESCE_RX_USECS, 210462306a36Sopenharmony_ci .get_drvinfo = get_drvinfo, 210562306a36Sopenharmony_ci .get_msglevel = get_msglevel, 210662306a36Sopenharmony_ci .set_msglevel = set_msglevel, 210762306a36Sopenharmony_ci .get_ringparam = get_sge_param, 210862306a36Sopenharmony_ci .set_ringparam = set_sge_param, 210962306a36Sopenharmony_ci .get_coalesce = get_coalesce, 211062306a36Sopenharmony_ci .set_coalesce = set_coalesce, 211162306a36Sopenharmony_ci .get_eeprom_len = get_eeprom_len, 211262306a36Sopenharmony_ci .get_eeprom = get_eeprom, 211362306a36Sopenharmony_ci .set_eeprom = set_eeprom, 211462306a36Sopenharmony_ci .get_pauseparam = get_pauseparam, 211562306a36Sopenharmony_ci .set_pauseparam = set_pauseparam, 211662306a36Sopenharmony_ci .get_link = ethtool_op_get_link, 211762306a36Sopenharmony_ci .get_strings = get_strings, 211862306a36Sopenharmony_ci .set_phys_id = set_phys_id, 211962306a36Sopenharmony_ci .nway_reset = restart_autoneg, 212062306a36Sopenharmony_ci .get_sset_count = get_sset_count, 212162306a36Sopenharmony_ci .get_ethtool_stats = get_stats, 212262306a36Sopenharmony_ci .get_regs_len = get_regs_len, 212362306a36Sopenharmony_ci .get_regs = get_regs, 212462306a36Sopenharmony_ci .get_wol = get_wol, 212562306a36Sopenharmony_ci .get_link_ksettings = get_link_ksettings, 212662306a36Sopenharmony_ci .set_link_ksettings = set_link_ksettings, 212762306a36Sopenharmony_ci}; 212862306a36Sopenharmony_ci 212962306a36Sopenharmony_cistatic int cxgb_in_range(int val, int lo, int hi) 213062306a36Sopenharmony_ci{ 213162306a36Sopenharmony_ci return val < 0 || (val <= hi && val >= lo); 213262306a36Sopenharmony_ci} 213362306a36Sopenharmony_ci 213462306a36Sopenharmony_cistatic int cxgb_siocdevprivate(struct net_device *dev, 213562306a36Sopenharmony_ci struct ifreq *ifreq, 213662306a36Sopenharmony_ci void __user *useraddr, 213762306a36Sopenharmony_ci int cmd) 213862306a36Sopenharmony_ci{ 213962306a36Sopenharmony_ci struct port_info *pi = netdev_priv(dev); 214062306a36Sopenharmony_ci struct adapter *adapter = pi->adapter; 214162306a36Sopenharmony_ci int ret; 214262306a36Sopenharmony_ci 214362306a36Sopenharmony_ci if (cmd != SIOCCHIOCTL) 214462306a36Sopenharmony_ci return -EOPNOTSUPP; 214562306a36Sopenharmony_ci 214662306a36Sopenharmony_ci if (copy_from_user(&cmd, useraddr, sizeof(cmd))) 214762306a36Sopenharmony_ci return -EFAULT; 214862306a36Sopenharmony_ci 214962306a36Sopenharmony_ci switch (cmd) { 215062306a36Sopenharmony_ci case CHELSIO_SET_QSET_PARAMS:{ 215162306a36Sopenharmony_ci int i; 215262306a36Sopenharmony_ci struct qset_params *q; 215362306a36Sopenharmony_ci struct ch_qset_params t; 215462306a36Sopenharmony_ci int q1 = pi->first_qset; 215562306a36Sopenharmony_ci int nqsets = pi->nqsets; 215662306a36Sopenharmony_ci 215762306a36Sopenharmony_ci if (!capable(CAP_NET_ADMIN)) 215862306a36Sopenharmony_ci return -EPERM; 215962306a36Sopenharmony_ci if (copy_from_user(&t, useraddr, sizeof(t))) 216062306a36Sopenharmony_ci return -EFAULT; 216162306a36Sopenharmony_ci if (t.cmd != CHELSIO_SET_QSET_PARAMS) 216262306a36Sopenharmony_ci return -EINVAL; 216362306a36Sopenharmony_ci if (t.qset_idx >= SGE_QSETS) 216462306a36Sopenharmony_ci return -EINVAL; 216562306a36Sopenharmony_ci if (!cxgb_in_range(t.intr_lat, 0, M_NEWTIMER) || 216662306a36Sopenharmony_ci !cxgb_in_range(t.cong_thres, 0, 255) || 216762306a36Sopenharmony_ci !cxgb_in_range(t.txq_size[0], MIN_TXQ_ENTRIES, 216862306a36Sopenharmony_ci MAX_TXQ_ENTRIES) || 216962306a36Sopenharmony_ci !cxgb_in_range(t.txq_size[1], MIN_TXQ_ENTRIES, 217062306a36Sopenharmony_ci MAX_TXQ_ENTRIES) || 217162306a36Sopenharmony_ci !cxgb_in_range(t.txq_size[2], MIN_CTRL_TXQ_ENTRIES, 217262306a36Sopenharmony_ci MAX_CTRL_TXQ_ENTRIES) || 217362306a36Sopenharmony_ci !cxgb_in_range(t.fl_size[0], MIN_FL_ENTRIES, 217462306a36Sopenharmony_ci MAX_RX_BUFFERS) || 217562306a36Sopenharmony_ci !cxgb_in_range(t.fl_size[1], MIN_FL_ENTRIES, 217662306a36Sopenharmony_ci MAX_RX_JUMBO_BUFFERS) || 217762306a36Sopenharmony_ci !cxgb_in_range(t.rspq_size, MIN_RSPQ_ENTRIES, 217862306a36Sopenharmony_ci MAX_RSPQ_ENTRIES)) 217962306a36Sopenharmony_ci return -EINVAL; 218062306a36Sopenharmony_ci 218162306a36Sopenharmony_ci if ((adapter->flags & FULL_INIT_DONE) && 218262306a36Sopenharmony_ci (t.rspq_size >= 0 || t.fl_size[0] >= 0 || 218362306a36Sopenharmony_ci t.fl_size[1] >= 0 || t.txq_size[0] >= 0 || 218462306a36Sopenharmony_ci t.txq_size[1] >= 0 || t.txq_size[2] >= 0 || 218562306a36Sopenharmony_ci t.polling >= 0 || t.cong_thres >= 0)) 218662306a36Sopenharmony_ci return -EBUSY; 218762306a36Sopenharmony_ci 218862306a36Sopenharmony_ci /* Allow setting of any available qset when offload enabled */ 218962306a36Sopenharmony_ci if (test_bit(OFFLOAD_DEVMAP_BIT, &adapter->open_device_map)) { 219062306a36Sopenharmony_ci q1 = 0; 219162306a36Sopenharmony_ci for_each_port(adapter, i) { 219262306a36Sopenharmony_ci pi = adap2pinfo(adapter, i); 219362306a36Sopenharmony_ci nqsets += pi->first_qset + pi->nqsets; 219462306a36Sopenharmony_ci } 219562306a36Sopenharmony_ci } 219662306a36Sopenharmony_ci 219762306a36Sopenharmony_ci if (t.qset_idx < q1) 219862306a36Sopenharmony_ci return -EINVAL; 219962306a36Sopenharmony_ci if (t.qset_idx > q1 + nqsets - 1) 220062306a36Sopenharmony_ci return -EINVAL; 220162306a36Sopenharmony_ci 220262306a36Sopenharmony_ci q = &adapter->params.sge.qset[t.qset_idx]; 220362306a36Sopenharmony_ci 220462306a36Sopenharmony_ci if (t.rspq_size >= 0) 220562306a36Sopenharmony_ci q->rspq_size = t.rspq_size; 220662306a36Sopenharmony_ci if (t.fl_size[0] >= 0) 220762306a36Sopenharmony_ci q->fl_size = t.fl_size[0]; 220862306a36Sopenharmony_ci if (t.fl_size[1] >= 0) 220962306a36Sopenharmony_ci q->jumbo_size = t.fl_size[1]; 221062306a36Sopenharmony_ci if (t.txq_size[0] >= 0) 221162306a36Sopenharmony_ci q->txq_size[0] = t.txq_size[0]; 221262306a36Sopenharmony_ci if (t.txq_size[1] >= 0) 221362306a36Sopenharmony_ci q->txq_size[1] = t.txq_size[1]; 221462306a36Sopenharmony_ci if (t.txq_size[2] >= 0) 221562306a36Sopenharmony_ci q->txq_size[2] = t.txq_size[2]; 221662306a36Sopenharmony_ci if (t.cong_thres >= 0) 221762306a36Sopenharmony_ci q->cong_thres = t.cong_thres; 221862306a36Sopenharmony_ci if (t.intr_lat >= 0) { 221962306a36Sopenharmony_ci struct sge_qset *qs = 222062306a36Sopenharmony_ci &adapter->sge.qs[t.qset_idx]; 222162306a36Sopenharmony_ci 222262306a36Sopenharmony_ci q->coalesce_usecs = t.intr_lat; 222362306a36Sopenharmony_ci t3_update_qset_coalesce(qs, q); 222462306a36Sopenharmony_ci } 222562306a36Sopenharmony_ci if (t.polling >= 0) { 222662306a36Sopenharmony_ci if (adapter->flags & USING_MSIX) 222762306a36Sopenharmony_ci q->polling = t.polling; 222862306a36Sopenharmony_ci else { 222962306a36Sopenharmony_ci /* No polling with INTx for T3A */ 223062306a36Sopenharmony_ci if (adapter->params.rev == 0 && 223162306a36Sopenharmony_ci !(adapter->flags & USING_MSI)) 223262306a36Sopenharmony_ci t.polling = 0; 223362306a36Sopenharmony_ci 223462306a36Sopenharmony_ci for (i = 0; i < SGE_QSETS; i++) { 223562306a36Sopenharmony_ci q = &adapter->params.sge. 223662306a36Sopenharmony_ci qset[i]; 223762306a36Sopenharmony_ci q->polling = t.polling; 223862306a36Sopenharmony_ci } 223962306a36Sopenharmony_ci } 224062306a36Sopenharmony_ci } 224162306a36Sopenharmony_ci 224262306a36Sopenharmony_ci if (t.lro >= 0) { 224362306a36Sopenharmony_ci if (t.lro) 224462306a36Sopenharmony_ci dev->wanted_features |= NETIF_F_GRO; 224562306a36Sopenharmony_ci else 224662306a36Sopenharmony_ci dev->wanted_features &= ~NETIF_F_GRO; 224762306a36Sopenharmony_ci netdev_update_features(dev); 224862306a36Sopenharmony_ci } 224962306a36Sopenharmony_ci 225062306a36Sopenharmony_ci break; 225162306a36Sopenharmony_ci } 225262306a36Sopenharmony_ci case CHELSIO_GET_QSET_PARAMS:{ 225362306a36Sopenharmony_ci struct qset_params *q; 225462306a36Sopenharmony_ci struct ch_qset_params t; 225562306a36Sopenharmony_ci int q1 = pi->first_qset; 225662306a36Sopenharmony_ci int nqsets = pi->nqsets; 225762306a36Sopenharmony_ci int i; 225862306a36Sopenharmony_ci 225962306a36Sopenharmony_ci if (copy_from_user(&t, useraddr, sizeof(t))) 226062306a36Sopenharmony_ci return -EFAULT; 226162306a36Sopenharmony_ci 226262306a36Sopenharmony_ci if (t.cmd != CHELSIO_GET_QSET_PARAMS) 226362306a36Sopenharmony_ci return -EINVAL; 226462306a36Sopenharmony_ci 226562306a36Sopenharmony_ci /* Display qsets for all ports when offload enabled */ 226662306a36Sopenharmony_ci if (test_bit(OFFLOAD_DEVMAP_BIT, &adapter->open_device_map)) { 226762306a36Sopenharmony_ci q1 = 0; 226862306a36Sopenharmony_ci for_each_port(adapter, i) { 226962306a36Sopenharmony_ci pi = adap2pinfo(adapter, i); 227062306a36Sopenharmony_ci nqsets = pi->first_qset + pi->nqsets; 227162306a36Sopenharmony_ci } 227262306a36Sopenharmony_ci } 227362306a36Sopenharmony_ci 227462306a36Sopenharmony_ci if (t.qset_idx >= nqsets) 227562306a36Sopenharmony_ci return -EINVAL; 227662306a36Sopenharmony_ci t.qset_idx = array_index_nospec(t.qset_idx, nqsets); 227762306a36Sopenharmony_ci 227862306a36Sopenharmony_ci q = &adapter->params.sge.qset[q1 + t.qset_idx]; 227962306a36Sopenharmony_ci t.rspq_size = q->rspq_size; 228062306a36Sopenharmony_ci t.txq_size[0] = q->txq_size[0]; 228162306a36Sopenharmony_ci t.txq_size[1] = q->txq_size[1]; 228262306a36Sopenharmony_ci t.txq_size[2] = q->txq_size[2]; 228362306a36Sopenharmony_ci t.fl_size[0] = q->fl_size; 228462306a36Sopenharmony_ci t.fl_size[1] = q->jumbo_size; 228562306a36Sopenharmony_ci t.polling = q->polling; 228662306a36Sopenharmony_ci t.lro = !!(dev->features & NETIF_F_GRO); 228762306a36Sopenharmony_ci t.intr_lat = q->coalesce_usecs; 228862306a36Sopenharmony_ci t.cong_thres = q->cong_thres; 228962306a36Sopenharmony_ci t.qnum = q1; 229062306a36Sopenharmony_ci 229162306a36Sopenharmony_ci if (adapter->flags & USING_MSIX) 229262306a36Sopenharmony_ci t.vector = adapter->msix_info[q1 + t.qset_idx + 1].vec; 229362306a36Sopenharmony_ci else 229462306a36Sopenharmony_ci t.vector = adapter->pdev->irq; 229562306a36Sopenharmony_ci 229662306a36Sopenharmony_ci if (copy_to_user(useraddr, &t, sizeof(t))) 229762306a36Sopenharmony_ci return -EFAULT; 229862306a36Sopenharmony_ci break; 229962306a36Sopenharmony_ci } 230062306a36Sopenharmony_ci case CHELSIO_SET_QSET_NUM:{ 230162306a36Sopenharmony_ci struct ch_reg edata; 230262306a36Sopenharmony_ci unsigned int i, first_qset = 0, other_qsets = 0; 230362306a36Sopenharmony_ci 230462306a36Sopenharmony_ci if (!capable(CAP_NET_ADMIN)) 230562306a36Sopenharmony_ci return -EPERM; 230662306a36Sopenharmony_ci if (adapter->flags & FULL_INIT_DONE) 230762306a36Sopenharmony_ci return -EBUSY; 230862306a36Sopenharmony_ci if (copy_from_user(&edata, useraddr, sizeof(edata))) 230962306a36Sopenharmony_ci return -EFAULT; 231062306a36Sopenharmony_ci if (edata.cmd != CHELSIO_SET_QSET_NUM) 231162306a36Sopenharmony_ci return -EINVAL; 231262306a36Sopenharmony_ci if (edata.val < 1 || 231362306a36Sopenharmony_ci (edata.val > 1 && !(adapter->flags & USING_MSIX))) 231462306a36Sopenharmony_ci return -EINVAL; 231562306a36Sopenharmony_ci 231662306a36Sopenharmony_ci for_each_port(adapter, i) 231762306a36Sopenharmony_ci if (adapter->port[i] && adapter->port[i] != dev) 231862306a36Sopenharmony_ci other_qsets += adap2pinfo(adapter, i)->nqsets; 231962306a36Sopenharmony_ci 232062306a36Sopenharmony_ci if (edata.val + other_qsets > SGE_QSETS) 232162306a36Sopenharmony_ci return -EINVAL; 232262306a36Sopenharmony_ci 232362306a36Sopenharmony_ci pi->nqsets = edata.val; 232462306a36Sopenharmony_ci 232562306a36Sopenharmony_ci for_each_port(adapter, i) 232662306a36Sopenharmony_ci if (adapter->port[i]) { 232762306a36Sopenharmony_ci pi = adap2pinfo(adapter, i); 232862306a36Sopenharmony_ci pi->first_qset = first_qset; 232962306a36Sopenharmony_ci first_qset += pi->nqsets; 233062306a36Sopenharmony_ci } 233162306a36Sopenharmony_ci break; 233262306a36Sopenharmony_ci } 233362306a36Sopenharmony_ci case CHELSIO_GET_QSET_NUM:{ 233462306a36Sopenharmony_ci struct ch_reg edata; 233562306a36Sopenharmony_ci 233662306a36Sopenharmony_ci memset(&edata, 0, sizeof(struct ch_reg)); 233762306a36Sopenharmony_ci 233862306a36Sopenharmony_ci edata.cmd = CHELSIO_GET_QSET_NUM; 233962306a36Sopenharmony_ci edata.val = pi->nqsets; 234062306a36Sopenharmony_ci if (copy_to_user(useraddr, &edata, sizeof(edata))) 234162306a36Sopenharmony_ci return -EFAULT; 234262306a36Sopenharmony_ci break; 234362306a36Sopenharmony_ci } 234462306a36Sopenharmony_ci case CHELSIO_LOAD_FW:{ 234562306a36Sopenharmony_ci u8 *fw_data; 234662306a36Sopenharmony_ci struct ch_mem_range t; 234762306a36Sopenharmony_ci 234862306a36Sopenharmony_ci if (!capable(CAP_SYS_RAWIO)) 234962306a36Sopenharmony_ci return -EPERM; 235062306a36Sopenharmony_ci if (copy_from_user(&t, useraddr, sizeof(t))) 235162306a36Sopenharmony_ci return -EFAULT; 235262306a36Sopenharmony_ci if (t.cmd != CHELSIO_LOAD_FW) 235362306a36Sopenharmony_ci return -EINVAL; 235462306a36Sopenharmony_ci /* Check t.len sanity ? */ 235562306a36Sopenharmony_ci fw_data = memdup_user(useraddr + sizeof(t), t.len); 235662306a36Sopenharmony_ci if (IS_ERR(fw_data)) 235762306a36Sopenharmony_ci return PTR_ERR(fw_data); 235862306a36Sopenharmony_ci 235962306a36Sopenharmony_ci ret = t3_load_fw(adapter, fw_data, t.len); 236062306a36Sopenharmony_ci kfree(fw_data); 236162306a36Sopenharmony_ci if (ret) 236262306a36Sopenharmony_ci return ret; 236362306a36Sopenharmony_ci break; 236462306a36Sopenharmony_ci } 236562306a36Sopenharmony_ci case CHELSIO_SETMTUTAB:{ 236662306a36Sopenharmony_ci struct ch_mtus m; 236762306a36Sopenharmony_ci int i; 236862306a36Sopenharmony_ci 236962306a36Sopenharmony_ci if (!is_offload(adapter)) 237062306a36Sopenharmony_ci return -EOPNOTSUPP; 237162306a36Sopenharmony_ci if (!capable(CAP_NET_ADMIN)) 237262306a36Sopenharmony_ci return -EPERM; 237362306a36Sopenharmony_ci if (offload_running(adapter)) 237462306a36Sopenharmony_ci return -EBUSY; 237562306a36Sopenharmony_ci if (copy_from_user(&m, useraddr, sizeof(m))) 237662306a36Sopenharmony_ci return -EFAULT; 237762306a36Sopenharmony_ci if (m.cmd != CHELSIO_SETMTUTAB) 237862306a36Sopenharmony_ci return -EINVAL; 237962306a36Sopenharmony_ci if (m.nmtus != NMTUS) 238062306a36Sopenharmony_ci return -EINVAL; 238162306a36Sopenharmony_ci if (m.mtus[0] < 81) /* accommodate SACK */ 238262306a36Sopenharmony_ci return -EINVAL; 238362306a36Sopenharmony_ci 238462306a36Sopenharmony_ci /* MTUs must be in ascending order */ 238562306a36Sopenharmony_ci for (i = 1; i < NMTUS; ++i) 238662306a36Sopenharmony_ci if (m.mtus[i] < m.mtus[i - 1]) 238762306a36Sopenharmony_ci return -EINVAL; 238862306a36Sopenharmony_ci 238962306a36Sopenharmony_ci memcpy(adapter->params.mtus, m.mtus, 239062306a36Sopenharmony_ci sizeof(adapter->params.mtus)); 239162306a36Sopenharmony_ci break; 239262306a36Sopenharmony_ci } 239362306a36Sopenharmony_ci case CHELSIO_GET_PM:{ 239462306a36Sopenharmony_ci struct tp_params *p = &adapter->params.tp; 239562306a36Sopenharmony_ci struct ch_pm m = {.cmd = CHELSIO_GET_PM }; 239662306a36Sopenharmony_ci 239762306a36Sopenharmony_ci if (!is_offload(adapter)) 239862306a36Sopenharmony_ci return -EOPNOTSUPP; 239962306a36Sopenharmony_ci m.tx_pg_sz = p->tx_pg_size; 240062306a36Sopenharmony_ci m.tx_num_pg = p->tx_num_pgs; 240162306a36Sopenharmony_ci m.rx_pg_sz = p->rx_pg_size; 240262306a36Sopenharmony_ci m.rx_num_pg = p->rx_num_pgs; 240362306a36Sopenharmony_ci m.pm_total = p->pmtx_size + p->chan_rx_size * p->nchan; 240462306a36Sopenharmony_ci if (copy_to_user(useraddr, &m, sizeof(m))) 240562306a36Sopenharmony_ci return -EFAULT; 240662306a36Sopenharmony_ci break; 240762306a36Sopenharmony_ci } 240862306a36Sopenharmony_ci case CHELSIO_SET_PM:{ 240962306a36Sopenharmony_ci struct ch_pm m; 241062306a36Sopenharmony_ci struct tp_params *p = &adapter->params.tp; 241162306a36Sopenharmony_ci 241262306a36Sopenharmony_ci if (!is_offload(adapter)) 241362306a36Sopenharmony_ci return -EOPNOTSUPP; 241462306a36Sopenharmony_ci if (!capable(CAP_NET_ADMIN)) 241562306a36Sopenharmony_ci return -EPERM; 241662306a36Sopenharmony_ci if (adapter->flags & FULL_INIT_DONE) 241762306a36Sopenharmony_ci return -EBUSY; 241862306a36Sopenharmony_ci if (copy_from_user(&m, useraddr, sizeof(m))) 241962306a36Sopenharmony_ci return -EFAULT; 242062306a36Sopenharmony_ci if (m.cmd != CHELSIO_SET_PM) 242162306a36Sopenharmony_ci return -EINVAL; 242262306a36Sopenharmony_ci if (!is_power_of_2(m.rx_pg_sz) || 242362306a36Sopenharmony_ci !is_power_of_2(m.tx_pg_sz)) 242462306a36Sopenharmony_ci return -EINVAL; /* not power of 2 */ 242562306a36Sopenharmony_ci if (!(m.rx_pg_sz & 0x14000)) 242662306a36Sopenharmony_ci return -EINVAL; /* not 16KB or 64KB */ 242762306a36Sopenharmony_ci if (!(m.tx_pg_sz & 0x1554000)) 242862306a36Sopenharmony_ci return -EINVAL; 242962306a36Sopenharmony_ci if (m.tx_num_pg == -1) 243062306a36Sopenharmony_ci m.tx_num_pg = p->tx_num_pgs; 243162306a36Sopenharmony_ci if (m.rx_num_pg == -1) 243262306a36Sopenharmony_ci m.rx_num_pg = p->rx_num_pgs; 243362306a36Sopenharmony_ci if (m.tx_num_pg % 24 || m.rx_num_pg % 24) 243462306a36Sopenharmony_ci return -EINVAL; 243562306a36Sopenharmony_ci if (m.rx_num_pg * m.rx_pg_sz > p->chan_rx_size || 243662306a36Sopenharmony_ci m.tx_num_pg * m.tx_pg_sz > p->chan_tx_size) 243762306a36Sopenharmony_ci return -EINVAL; 243862306a36Sopenharmony_ci p->rx_pg_size = m.rx_pg_sz; 243962306a36Sopenharmony_ci p->tx_pg_size = m.tx_pg_sz; 244062306a36Sopenharmony_ci p->rx_num_pgs = m.rx_num_pg; 244162306a36Sopenharmony_ci p->tx_num_pgs = m.tx_num_pg; 244262306a36Sopenharmony_ci break; 244362306a36Sopenharmony_ci } 244462306a36Sopenharmony_ci case CHELSIO_GET_MEM:{ 244562306a36Sopenharmony_ci struct ch_mem_range t; 244662306a36Sopenharmony_ci struct mc7 *mem; 244762306a36Sopenharmony_ci u64 buf[32]; 244862306a36Sopenharmony_ci 244962306a36Sopenharmony_ci if (!is_offload(adapter)) 245062306a36Sopenharmony_ci return -EOPNOTSUPP; 245162306a36Sopenharmony_ci if (!capable(CAP_NET_ADMIN)) 245262306a36Sopenharmony_ci return -EPERM; 245362306a36Sopenharmony_ci if (!(adapter->flags & FULL_INIT_DONE)) 245462306a36Sopenharmony_ci return -EIO; /* need the memory controllers */ 245562306a36Sopenharmony_ci if (copy_from_user(&t, useraddr, sizeof(t))) 245662306a36Sopenharmony_ci return -EFAULT; 245762306a36Sopenharmony_ci if (t.cmd != CHELSIO_GET_MEM) 245862306a36Sopenharmony_ci return -EINVAL; 245962306a36Sopenharmony_ci if ((t.addr & 7) || (t.len & 7)) 246062306a36Sopenharmony_ci return -EINVAL; 246162306a36Sopenharmony_ci if (t.mem_id == MEM_CM) 246262306a36Sopenharmony_ci mem = &adapter->cm; 246362306a36Sopenharmony_ci else if (t.mem_id == MEM_PMRX) 246462306a36Sopenharmony_ci mem = &adapter->pmrx; 246562306a36Sopenharmony_ci else if (t.mem_id == MEM_PMTX) 246662306a36Sopenharmony_ci mem = &adapter->pmtx; 246762306a36Sopenharmony_ci else 246862306a36Sopenharmony_ci return -EINVAL; 246962306a36Sopenharmony_ci 247062306a36Sopenharmony_ci /* 247162306a36Sopenharmony_ci * Version scheme: 247262306a36Sopenharmony_ci * bits 0..9: chip version 247362306a36Sopenharmony_ci * bits 10..15: chip revision 247462306a36Sopenharmony_ci */ 247562306a36Sopenharmony_ci t.version = 3 | (adapter->params.rev << 10); 247662306a36Sopenharmony_ci if (copy_to_user(useraddr, &t, sizeof(t))) 247762306a36Sopenharmony_ci return -EFAULT; 247862306a36Sopenharmony_ci 247962306a36Sopenharmony_ci /* 248062306a36Sopenharmony_ci * Read 256 bytes at a time as len can be large and we don't 248162306a36Sopenharmony_ci * want to use huge intermediate buffers. 248262306a36Sopenharmony_ci */ 248362306a36Sopenharmony_ci useraddr += sizeof(t); /* advance to start of buffer */ 248462306a36Sopenharmony_ci while (t.len) { 248562306a36Sopenharmony_ci unsigned int chunk = 248662306a36Sopenharmony_ci min_t(unsigned int, t.len, sizeof(buf)); 248762306a36Sopenharmony_ci 248862306a36Sopenharmony_ci ret = 248962306a36Sopenharmony_ci t3_mc7_bd_read(mem, t.addr / 8, chunk / 8, 249062306a36Sopenharmony_ci buf); 249162306a36Sopenharmony_ci if (ret) 249262306a36Sopenharmony_ci return ret; 249362306a36Sopenharmony_ci if (copy_to_user(useraddr, buf, chunk)) 249462306a36Sopenharmony_ci return -EFAULT; 249562306a36Sopenharmony_ci useraddr += chunk; 249662306a36Sopenharmony_ci t.addr += chunk; 249762306a36Sopenharmony_ci t.len -= chunk; 249862306a36Sopenharmony_ci } 249962306a36Sopenharmony_ci break; 250062306a36Sopenharmony_ci } 250162306a36Sopenharmony_ci case CHELSIO_SET_TRACE_FILTER:{ 250262306a36Sopenharmony_ci struct ch_trace t; 250362306a36Sopenharmony_ci const struct trace_params *tp; 250462306a36Sopenharmony_ci 250562306a36Sopenharmony_ci if (!capable(CAP_NET_ADMIN)) 250662306a36Sopenharmony_ci return -EPERM; 250762306a36Sopenharmony_ci if (!offload_running(adapter)) 250862306a36Sopenharmony_ci return -EAGAIN; 250962306a36Sopenharmony_ci if (copy_from_user(&t, useraddr, sizeof(t))) 251062306a36Sopenharmony_ci return -EFAULT; 251162306a36Sopenharmony_ci if (t.cmd != CHELSIO_SET_TRACE_FILTER) 251262306a36Sopenharmony_ci return -EINVAL; 251362306a36Sopenharmony_ci 251462306a36Sopenharmony_ci tp = (const struct trace_params *)&t.sip; 251562306a36Sopenharmony_ci if (t.config_tx) 251662306a36Sopenharmony_ci t3_config_trace_filter(adapter, tp, 0, 251762306a36Sopenharmony_ci t.invert_match, 251862306a36Sopenharmony_ci t.trace_tx); 251962306a36Sopenharmony_ci if (t.config_rx) 252062306a36Sopenharmony_ci t3_config_trace_filter(adapter, tp, 1, 252162306a36Sopenharmony_ci t.invert_match, 252262306a36Sopenharmony_ci t.trace_rx); 252362306a36Sopenharmony_ci break; 252462306a36Sopenharmony_ci } 252562306a36Sopenharmony_ci default: 252662306a36Sopenharmony_ci return -EOPNOTSUPP; 252762306a36Sopenharmony_ci } 252862306a36Sopenharmony_ci return 0; 252962306a36Sopenharmony_ci} 253062306a36Sopenharmony_ci 253162306a36Sopenharmony_cistatic int cxgb_ioctl(struct net_device *dev, struct ifreq *req, int cmd) 253262306a36Sopenharmony_ci{ 253362306a36Sopenharmony_ci struct mii_ioctl_data *data = if_mii(req); 253462306a36Sopenharmony_ci struct port_info *pi = netdev_priv(dev); 253562306a36Sopenharmony_ci struct adapter *adapter = pi->adapter; 253662306a36Sopenharmony_ci 253762306a36Sopenharmony_ci switch (cmd) { 253862306a36Sopenharmony_ci case SIOCGMIIREG: 253962306a36Sopenharmony_ci case SIOCSMIIREG: 254062306a36Sopenharmony_ci /* Convert phy_id from older PRTAD/DEVAD format */ 254162306a36Sopenharmony_ci if (is_10G(adapter) && 254262306a36Sopenharmony_ci !mdio_phy_id_is_c45(data->phy_id) && 254362306a36Sopenharmony_ci (data->phy_id & 0x1f00) && 254462306a36Sopenharmony_ci !(data->phy_id & 0xe0e0)) 254562306a36Sopenharmony_ci data->phy_id = mdio_phy_id_c45(data->phy_id >> 8, 254662306a36Sopenharmony_ci data->phy_id & 0x1f); 254762306a36Sopenharmony_ci fallthrough; 254862306a36Sopenharmony_ci case SIOCGMIIPHY: 254962306a36Sopenharmony_ci return mdio_mii_ioctl(&pi->phy.mdio, data, cmd); 255062306a36Sopenharmony_ci default: 255162306a36Sopenharmony_ci return -EOPNOTSUPP; 255262306a36Sopenharmony_ci } 255362306a36Sopenharmony_ci} 255462306a36Sopenharmony_ci 255562306a36Sopenharmony_cistatic int cxgb_change_mtu(struct net_device *dev, int new_mtu) 255662306a36Sopenharmony_ci{ 255762306a36Sopenharmony_ci struct port_info *pi = netdev_priv(dev); 255862306a36Sopenharmony_ci struct adapter *adapter = pi->adapter; 255962306a36Sopenharmony_ci int ret; 256062306a36Sopenharmony_ci 256162306a36Sopenharmony_ci if ((ret = t3_mac_set_mtu(&pi->mac, new_mtu))) 256262306a36Sopenharmony_ci return ret; 256362306a36Sopenharmony_ci dev->mtu = new_mtu; 256462306a36Sopenharmony_ci init_port_mtus(adapter); 256562306a36Sopenharmony_ci if (adapter->params.rev == 0 && offload_running(adapter)) 256662306a36Sopenharmony_ci t3_load_mtus(adapter, adapter->params.mtus, 256762306a36Sopenharmony_ci adapter->params.a_wnd, adapter->params.b_wnd, 256862306a36Sopenharmony_ci adapter->port[0]->mtu); 256962306a36Sopenharmony_ci return 0; 257062306a36Sopenharmony_ci} 257162306a36Sopenharmony_ci 257262306a36Sopenharmony_cistatic int cxgb_set_mac_addr(struct net_device *dev, void *p) 257362306a36Sopenharmony_ci{ 257462306a36Sopenharmony_ci struct port_info *pi = netdev_priv(dev); 257562306a36Sopenharmony_ci struct adapter *adapter = pi->adapter; 257662306a36Sopenharmony_ci struct sockaddr *addr = p; 257762306a36Sopenharmony_ci 257862306a36Sopenharmony_ci if (!is_valid_ether_addr(addr->sa_data)) 257962306a36Sopenharmony_ci return -EADDRNOTAVAIL; 258062306a36Sopenharmony_ci 258162306a36Sopenharmony_ci eth_hw_addr_set(dev, addr->sa_data); 258262306a36Sopenharmony_ci t3_mac_set_address(&pi->mac, LAN_MAC_IDX, dev->dev_addr); 258362306a36Sopenharmony_ci if (offload_running(adapter)) 258462306a36Sopenharmony_ci write_smt_entry(adapter, pi->port_id); 258562306a36Sopenharmony_ci return 0; 258662306a36Sopenharmony_ci} 258762306a36Sopenharmony_ci 258862306a36Sopenharmony_cistatic netdev_features_t cxgb_fix_features(struct net_device *dev, 258962306a36Sopenharmony_ci netdev_features_t features) 259062306a36Sopenharmony_ci{ 259162306a36Sopenharmony_ci /* 259262306a36Sopenharmony_ci * Since there is no support for separate rx/tx vlan accel 259362306a36Sopenharmony_ci * enable/disable make sure tx flag is always in same state as rx. 259462306a36Sopenharmony_ci */ 259562306a36Sopenharmony_ci if (features & NETIF_F_HW_VLAN_CTAG_RX) 259662306a36Sopenharmony_ci features |= NETIF_F_HW_VLAN_CTAG_TX; 259762306a36Sopenharmony_ci else 259862306a36Sopenharmony_ci features &= ~NETIF_F_HW_VLAN_CTAG_TX; 259962306a36Sopenharmony_ci 260062306a36Sopenharmony_ci return features; 260162306a36Sopenharmony_ci} 260262306a36Sopenharmony_ci 260362306a36Sopenharmony_cistatic int cxgb_set_features(struct net_device *dev, netdev_features_t features) 260462306a36Sopenharmony_ci{ 260562306a36Sopenharmony_ci netdev_features_t changed = dev->features ^ features; 260662306a36Sopenharmony_ci 260762306a36Sopenharmony_ci if (changed & NETIF_F_HW_VLAN_CTAG_RX) 260862306a36Sopenharmony_ci cxgb_vlan_mode(dev, features); 260962306a36Sopenharmony_ci 261062306a36Sopenharmony_ci return 0; 261162306a36Sopenharmony_ci} 261262306a36Sopenharmony_ci 261362306a36Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER 261462306a36Sopenharmony_cistatic void cxgb_netpoll(struct net_device *dev) 261562306a36Sopenharmony_ci{ 261662306a36Sopenharmony_ci struct port_info *pi = netdev_priv(dev); 261762306a36Sopenharmony_ci struct adapter *adapter = pi->adapter; 261862306a36Sopenharmony_ci int qidx; 261962306a36Sopenharmony_ci 262062306a36Sopenharmony_ci for (qidx = pi->first_qset; qidx < pi->first_qset + pi->nqsets; qidx++) { 262162306a36Sopenharmony_ci struct sge_qset *qs = &adapter->sge.qs[qidx]; 262262306a36Sopenharmony_ci void *source; 262362306a36Sopenharmony_ci 262462306a36Sopenharmony_ci if (adapter->flags & USING_MSIX) 262562306a36Sopenharmony_ci source = qs; 262662306a36Sopenharmony_ci else 262762306a36Sopenharmony_ci source = adapter; 262862306a36Sopenharmony_ci 262962306a36Sopenharmony_ci t3_intr_handler(adapter, qs->rspq.polling) (0, source); 263062306a36Sopenharmony_ci } 263162306a36Sopenharmony_ci} 263262306a36Sopenharmony_ci#endif 263362306a36Sopenharmony_ci 263462306a36Sopenharmony_ci/* 263562306a36Sopenharmony_ci * Periodic accumulation of MAC statistics. 263662306a36Sopenharmony_ci */ 263762306a36Sopenharmony_cistatic void mac_stats_update(struct adapter *adapter) 263862306a36Sopenharmony_ci{ 263962306a36Sopenharmony_ci int i; 264062306a36Sopenharmony_ci 264162306a36Sopenharmony_ci for_each_port(adapter, i) { 264262306a36Sopenharmony_ci struct net_device *dev = adapter->port[i]; 264362306a36Sopenharmony_ci struct port_info *p = netdev_priv(dev); 264462306a36Sopenharmony_ci 264562306a36Sopenharmony_ci if (netif_running(dev)) { 264662306a36Sopenharmony_ci spin_lock(&adapter->stats_lock); 264762306a36Sopenharmony_ci t3_mac_update_stats(&p->mac); 264862306a36Sopenharmony_ci spin_unlock(&adapter->stats_lock); 264962306a36Sopenharmony_ci } 265062306a36Sopenharmony_ci } 265162306a36Sopenharmony_ci} 265262306a36Sopenharmony_ci 265362306a36Sopenharmony_cistatic void check_link_status(struct adapter *adapter) 265462306a36Sopenharmony_ci{ 265562306a36Sopenharmony_ci int i; 265662306a36Sopenharmony_ci 265762306a36Sopenharmony_ci for_each_port(adapter, i) { 265862306a36Sopenharmony_ci struct net_device *dev = adapter->port[i]; 265962306a36Sopenharmony_ci struct port_info *p = netdev_priv(dev); 266062306a36Sopenharmony_ci int link_fault; 266162306a36Sopenharmony_ci 266262306a36Sopenharmony_ci spin_lock_irq(&adapter->work_lock); 266362306a36Sopenharmony_ci link_fault = p->link_fault; 266462306a36Sopenharmony_ci spin_unlock_irq(&adapter->work_lock); 266562306a36Sopenharmony_ci 266662306a36Sopenharmony_ci if (link_fault) { 266762306a36Sopenharmony_ci t3_link_fault(adapter, i); 266862306a36Sopenharmony_ci continue; 266962306a36Sopenharmony_ci } 267062306a36Sopenharmony_ci 267162306a36Sopenharmony_ci if (!(p->phy.caps & SUPPORTED_IRQ) && netif_running(dev)) { 267262306a36Sopenharmony_ci t3_xgm_intr_disable(adapter, i); 267362306a36Sopenharmony_ci t3_read_reg(adapter, A_XGM_INT_STATUS + p->mac.offset); 267462306a36Sopenharmony_ci 267562306a36Sopenharmony_ci t3_link_changed(adapter, i); 267662306a36Sopenharmony_ci t3_xgm_intr_enable(adapter, i); 267762306a36Sopenharmony_ci } 267862306a36Sopenharmony_ci } 267962306a36Sopenharmony_ci} 268062306a36Sopenharmony_ci 268162306a36Sopenharmony_cistatic void check_t3b2_mac(struct adapter *adapter) 268262306a36Sopenharmony_ci{ 268362306a36Sopenharmony_ci int i; 268462306a36Sopenharmony_ci 268562306a36Sopenharmony_ci if (!rtnl_trylock()) /* synchronize with ifdown */ 268662306a36Sopenharmony_ci return; 268762306a36Sopenharmony_ci 268862306a36Sopenharmony_ci for_each_port(adapter, i) { 268962306a36Sopenharmony_ci struct net_device *dev = adapter->port[i]; 269062306a36Sopenharmony_ci struct port_info *p = netdev_priv(dev); 269162306a36Sopenharmony_ci int status; 269262306a36Sopenharmony_ci 269362306a36Sopenharmony_ci if (!netif_running(dev)) 269462306a36Sopenharmony_ci continue; 269562306a36Sopenharmony_ci 269662306a36Sopenharmony_ci status = 0; 269762306a36Sopenharmony_ci if (netif_running(dev) && netif_carrier_ok(dev)) 269862306a36Sopenharmony_ci status = t3b2_mac_watchdog_task(&p->mac); 269962306a36Sopenharmony_ci if (status == 1) 270062306a36Sopenharmony_ci p->mac.stats.num_toggled++; 270162306a36Sopenharmony_ci else if (status == 2) { 270262306a36Sopenharmony_ci struct cmac *mac = &p->mac; 270362306a36Sopenharmony_ci 270462306a36Sopenharmony_ci t3_mac_set_mtu(mac, dev->mtu); 270562306a36Sopenharmony_ci t3_mac_set_address(mac, LAN_MAC_IDX, dev->dev_addr); 270662306a36Sopenharmony_ci cxgb_set_rxmode(dev); 270762306a36Sopenharmony_ci t3_link_start(&p->phy, mac, &p->link_config); 270862306a36Sopenharmony_ci t3_mac_enable(mac, MAC_DIRECTION_RX | MAC_DIRECTION_TX); 270962306a36Sopenharmony_ci t3_port_intr_enable(adapter, p->port_id); 271062306a36Sopenharmony_ci p->mac.stats.num_resets++; 271162306a36Sopenharmony_ci } 271262306a36Sopenharmony_ci } 271362306a36Sopenharmony_ci rtnl_unlock(); 271462306a36Sopenharmony_ci} 271562306a36Sopenharmony_ci 271662306a36Sopenharmony_ci 271762306a36Sopenharmony_cistatic void t3_adap_check_task(struct work_struct *work) 271862306a36Sopenharmony_ci{ 271962306a36Sopenharmony_ci struct adapter *adapter = container_of(work, struct adapter, 272062306a36Sopenharmony_ci adap_check_task.work); 272162306a36Sopenharmony_ci const struct adapter_params *p = &adapter->params; 272262306a36Sopenharmony_ci int port; 272362306a36Sopenharmony_ci unsigned int v, status, reset; 272462306a36Sopenharmony_ci 272562306a36Sopenharmony_ci adapter->check_task_cnt++; 272662306a36Sopenharmony_ci 272762306a36Sopenharmony_ci check_link_status(adapter); 272862306a36Sopenharmony_ci 272962306a36Sopenharmony_ci /* Accumulate MAC stats if needed */ 273062306a36Sopenharmony_ci if (!p->linkpoll_period || 273162306a36Sopenharmony_ci (adapter->check_task_cnt * p->linkpoll_period) / 10 >= 273262306a36Sopenharmony_ci p->stats_update_period) { 273362306a36Sopenharmony_ci mac_stats_update(adapter); 273462306a36Sopenharmony_ci adapter->check_task_cnt = 0; 273562306a36Sopenharmony_ci } 273662306a36Sopenharmony_ci 273762306a36Sopenharmony_ci if (p->rev == T3_REV_B2) 273862306a36Sopenharmony_ci check_t3b2_mac(adapter); 273962306a36Sopenharmony_ci 274062306a36Sopenharmony_ci /* 274162306a36Sopenharmony_ci * Scan the XGMAC's to check for various conditions which we want to 274262306a36Sopenharmony_ci * monitor in a periodic polling manner rather than via an interrupt 274362306a36Sopenharmony_ci * condition. This is used for conditions which would otherwise flood 274462306a36Sopenharmony_ci * the system with interrupts and we only really need to know that the 274562306a36Sopenharmony_ci * conditions are "happening" ... For each condition we count the 274662306a36Sopenharmony_ci * detection of the condition and reset it for the next polling loop. 274762306a36Sopenharmony_ci */ 274862306a36Sopenharmony_ci for_each_port(adapter, port) { 274962306a36Sopenharmony_ci struct cmac *mac = &adap2pinfo(adapter, port)->mac; 275062306a36Sopenharmony_ci u32 cause; 275162306a36Sopenharmony_ci 275262306a36Sopenharmony_ci cause = t3_read_reg(adapter, A_XGM_INT_CAUSE + mac->offset); 275362306a36Sopenharmony_ci reset = 0; 275462306a36Sopenharmony_ci if (cause & F_RXFIFO_OVERFLOW) { 275562306a36Sopenharmony_ci mac->stats.rx_fifo_ovfl++; 275662306a36Sopenharmony_ci reset |= F_RXFIFO_OVERFLOW; 275762306a36Sopenharmony_ci } 275862306a36Sopenharmony_ci 275962306a36Sopenharmony_ci t3_write_reg(adapter, A_XGM_INT_CAUSE + mac->offset, reset); 276062306a36Sopenharmony_ci } 276162306a36Sopenharmony_ci 276262306a36Sopenharmony_ci /* 276362306a36Sopenharmony_ci * We do the same as above for FL_EMPTY interrupts. 276462306a36Sopenharmony_ci */ 276562306a36Sopenharmony_ci status = t3_read_reg(adapter, A_SG_INT_CAUSE); 276662306a36Sopenharmony_ci reset = 0; 276762306a36Sopenharmony_ci 276862306a36Sopenharmony_ci if (status & F_FLEMPTY) { 276962306a36Sopenharmony_ci struct sge_qset *qs = &adapter->sge.qs[0]; 277062306a36Sopenharmony_ci int i = 0; 277162306a36Sopenharmony_ci 277262306a36Sopenharmony_ci reset |= F_FLEMPTY; 277362306a36Sopenharmony_ci 277462306a36Sopenharmony_ci v = (t3_read_reg(adapter, A_SG_RSPQ_FL_STATUS) >> S_FL0EMPTY) & 277562306a36Sopenharmony_ci 0xffff; 277662306a36Sopenharmony_ci 277762306a36Sopenharmony_ci while (v) { 277862306a36Sopenharmony_ci qs->fl[i].empty += (v & 1); 277962306a36Sopenharmony_ci if (i) 278062306a36Sopenharmony_ci qs++; 278162306a36Sopenharmony_ci i ^= 1; 278262306a36Sopenharmony_ci v >>= 1; 278362306a36Sopenharmony_ci } 278462306a36Sopenharmony_ci } 278562306a36Sopenharmony_ci 278662306a36Sopenharmony_ci t3_write_reg(adapter, A_SG_INT_CAUSE, reset); 278762306a36Sopenharmony_ci 278862306a36Sopenharmony_ci /* Schedule the next check update if any port is active. */ 278962306a36Sopenharmony_ci spin_lock_irq(&adapter->work_lock); 279062306a36Sopenharmony_ci if (adapter->open_device_map & PORT_MASK) 279162306a36Sopenharmony_ci schedule_chk_task(adapter); 279262306a36Sopenharmony_ci spin_unlock_irq(&adapter->work_lock); 279362306a36Sopenharmony_ci} 279462306a36Sopenharmony_ci 279562306a36Sopenharmony_cistatic void db_full_task(struct work_struct *work) 279662306a36Sopenharmony_ci{ 279762306a36Sopenharmony_ci struct adapter *adapter = container_of(work, struct adapter, 279862306a36Sopenharmony_ci db_full_task); 279962306a36Sopenharmony_ci 280062306a36Sopenharmony_ci cxgb3_event_notify(&adapter->tdev, OFFLOAD_DB_FULL, 0); 280162306a36Sopenharmony_ci} 280262306a36Sopenharmony_ci 280362306a36Sopenharmony_cistatic void db_empty_task(struct work_struct *work) 280462306a36Sopenharmony_ci{ 280562306a36Sopenharmony_ci struct adapter *adapter = container_of(work, struct adapter, 280662306a36Sopenharmony_ci db_empty_task); 280762306a36Sopenharmony_ci 280862306a36Sopenharmony_ci cxgb3_event_notify(&adapter->tdev, OFFLOAD_DB_EMPTY, 0); 280962306a36Sopenharmony_ci} 281062306a36Sopenharmony_ci 281162306a36Sopenharmony_cistatic void db_drop_task(struct work_struct *work) 281262306a36Sopenharmony_ci{ 281362306a36Sopenharmony_ci struct adapter *adapter = container_of(work, struct adapter, 281462306a36Sopenharmony_ci db_drop_task); 281562306a36Sopenharmony_ci unsigned long delay = 1000; 281662306a36Sopenharmony_ci unsigned short r; 281762306a36Sopenharmony_ci 281862306a36Sopenharmony_ci cxgb3_event_notify(&adapter->tdev, OFFLOAD_DB_DROP, 0); 281962306a36Sopenharmony_ci 282062306a36Sopenharmony_ci /* 282162306a36Sopenharmony_ci * Sleep a while before ringing the driver qset dbs. 282262306a36Sopenharmony_ci * The delay is between 1000-2023 usecs. 282362306a36Sopenharmony_ci */ 282462306a36Sopenharmony_ci get_random_bytes(&r, 2); 282562306a36Sopenharmony_ci delay += r & 1023; 282662306a36Sopenharmony_ci set_current_state(TASK_UNINTERRUPTIBLE); 282762306a36Sopenharmony_ci schedule_timeout(usecs_to_jiffies(delay)); 282862306a36Sopenharmony_ci ring_dbs(adapter); 282962306a36Sopenharmony_ci} 283062306a36Sopenharmony_ci 283162306a36Sopenharmony_ci/* 283262306a36Sopenharmony_ci * Processes external (PHY) interrupts in process context. 283362306a36Sopenharmony_ci */ 283462306a36Sopenharmony_cistatic void ext_intr_task(struct work_struct *work) 283562306a36Sopenharmony_ci{ 283662306a36Sopenharmony_ci struct adapter *adapter = container_of(work, struct adapter, 283762306a36Sopenharmony_ci ext_intr_handler_task); 283862306a36Sopenharmony_ci int i; 283962306a36Sopenharmony_ci 284062306a36Sopenharmony_ci /* Disable link fault interrupts */ 284162306a36Sopenharmony_ci for_each_port(adapter, i) { 284262306a36Sopenharmony_ci struct net_device *dev = adapter->port[i]; 284362306a36Sopenharmony_ci struct port_info *p = netdev_priv(dev); 284462306a36Sopenharmony_ci 284562306a36Sopenharmony_ci t3_xgm_intr_disable(adapter, i); 284662306a36Sopenharmony_ci t3_read_reg(adapter, A_XGM_INT_STATUS + p->mac.offset); 284762306a36Sopenharmony_ci } 284862306a36Sopenharmony_ci 284962306a36Sopenharmony_ci /* Re-enable link fault interrupts */ 285062306a36Sopenharmony_ci t3_phy_intr_handler(adapter); 285162306a36Sopenharmony_ci 285262306a36Sopenharmony_ci for_each_port(adapter, i) 285362306a36Sopenharmony_ci t3_xgm_intr_enable(adapter, i); 285462306a36Sopenharmony_ci 285562306a36Sopenharmony_ci /* Now reenable external interrupts */ 285662306a36Sopenharmony_ci spin_lock_irq(&adapter->work_lock); 285762306a36Sopenharmony_ci if (adapter->slow_intr_mask) { 285862306a36Sopenharmony_ci adapter->slow_intr_mask |= F_T3DBG; 285962306a36Sopenharmony_ci t3_write_reg(adapter, A_PL_INT_CAUSE0, F_T3DBG); 286062306a36Sopenharmony_ci t3_write_reg(adapter, A_PL_INT_ENABLE0, 286162306a36Sopenharmony_ci adapter->slow_intr_mask); 286262306a36Sopenharmony_ci } 286362306a36Sopenharmony_ci spin_unlock_irq(&adapter->work_lock); 286462306a36Sopenharmony_ci} 286562306a36Sopenharmony_ci 286662306a36Sopenharmony_ci/* 286762306a36Sopenharmony_ci * Interrupt-context handler for external (PHY) interrupts. 286862306a36Sopenharmony_ci */ 286962306a36Sopenharmony_civoid t3_os_ext_intr_handler(struct adapter *adapter) 287062306a36Sopenharmony_ci{ 287162306a36Sopenharmony_ci /* 287262306a36Sopenharmony_ci * Schedule a task to handle external interrupts as they may be slow 287362306a36Sopenharmony_ci * and we use a mutex to protect MDIO registers. We disable PHY 287462306a36Sopenharmony_ci * interrupts in the meantime and let the task reenable them when 287562306a36Sopenharmony_ci * it's done. 287662306a36Sopenharmony_ci */ 287762306a36Sopenharmony_ci spin_lock(&adapter->work_lock); 287862306a36Sopenharmony_ci if (adapter->slow_intr_mask) { 287962306a36Sopenharmony_ci adapter->slow_intr_mask &= ~F_T3DBG; 288062306a36Sopenharmony_ci t3_write_reg(adapter, A_PL_INT_ENABLE0, 288162306a36Sopenharmony_ci adapter->slow_intr_mask); 288262306a36Sopenharmony_ci queue_work(cxgb3_wq, &adapter->ext_intr_handler_task); 288362306a36Sopenharmony_ci } 288462306a36Sopenharmony_ci spin_unlock(&adapter->work_lock); 288562306a36Sopenharmony_ci} 288662306a36Sopenharmony_ci 288762306a36Sopenharmony_civoid t3_os_link_fault_handler(struct adapter *adapter, int port_id) 288862306a36Sopenharmony_ci{ 288962306a36Sopenharmony_ci struct net_device *netdev = adapter->port[port_id]; 289062306a36Sopenharmony_ci struct port_info *pi = netdev_priv(netdev); 289162306a36Sopenharmony_ci 289262306a36Sopenharmony_ci spin_lock(&adapter->work_lock); 289362306a36Sopenharmony_ci pi->link_fault = 1; 289462306a36Sopenharmony_ci spin_unlock(&adapter->work_lock); 289562306a36Sopenharmony_ci} 289662306a36Sopenharmony_ci 289762306a36Sopenharmony_cistatic int t3_adapter_error(struct adapter *adapter, int reset, int on_wq) 289862306a36Sopenharmony_ci{ 289962306a36Sopenharmony_ci int i, ret = 0; 290062306a36Sopenharmony_ci 290162306a36Sopenharmony_ci if (is_offload(adapter) && 290262306a36Sopenharmony_ci test_bit(OFFLOAD_DEVMAP_BIT, &adapter->open_device_map)) { 290362306a36Sopenharmony_ci cxgb3_event_notify(&adapter->tdev, OFFLOAD_STATUS_DOWN, 0); 290462306a36Sopenharmony_ci offload_close(&adapter->tdev); 290562306a36Sopenharmony_ci } 290662306a36Sopenharmony_ci 290762306a36Sopenharmony_ci /* Stop all ports */ 290862306a36Sopenharmony_ci for_each_port(adapter, i) { 290962306a36Sopenharmony_ci struct net_device *netdev = adapter->port[i]; 291062306a36Sopenharmony_ci 291162306a36Sopenharmony_ci if (netif_running(netdev)) 291262306a36Sopenharmony_ci __cxgb_close(netdev, on_wq); 291362306a36Sopenharmony_ci } 291462306a36Sopenharmony_ci 291562306a36Sopenharmony_ci /* Stop SGE timers */ 291662306a36Sopenharmony_ci t3_stop_sge_timers(adapter); 291762306a36Sopenharmony_ci 291862306a36Sopenharmony_ci adapter->flags &= ~FULL_INIT_DONE; 291962306a36Sopenharmony_ci 292062306a36Sopenharmony_ci if (reset) 292162306a36Sopenharmony_ci ret = t3_reset_adapter(adapter); 292262306a36Sopenharmony_ci 292362306a36Sopenharmony_ci pci_disable_device(adapter->pdev); 292462306a36Sopenharmony_ci 292562306a36Sopenharmony_ci return ret; 292662306a36Sopenharmony_ci} 292762306a36Sopenharmony_ci 292862306a36Sopenharmony_cistatic int t3_reenable_adapter(struct adapter *adapter) 292962306a36Sopenharmony_ci{ 293062306a36Sopenharmony_ci if (pci_enable_device(adapter->pdev)) { 293162306a36Sopenharmony_ci dev_err(&adapter->pdev->dev, 293262306a36Sopenharmony_ci "Cannot re-enable PCI device after reset.\n"); 293362306a36Sopenharmony_ci goto err; 293462306a36Sopenharmony_ci } 293562306a36Sopenharmony_ci pci_set_master(adapter->pdev); 293662306a36Sopenharmony_ci pci_restore_state(adapter->pdev); 293762306a36Sopenharmony_ci pci_save_state(adapter->pdev); 293862306a36Sopenharmony_ci 293962306a36Sopenharmony_ci /* Free sge resources */ 294062306a36Sopenharmony_ci t3_free_sge_resources(adapter); 294162306a36Sopenharmony_ci 294262306a36Sopenharmony_ci if (t3_replay_prep_adapter(adapter)) 294362306a36Sopenharmony_ci goto err; 294462306a36Sopenharmony_ci 294562306a36Sopenharmony_ci return 0; 294662306a36Sopenharmony_cierr: 294762306a36Sopenharmony_ci return -1; 294862306a36Sopenharmony_ci} 294962306a36Sopenharmony_ci 295062306a36Sopenharmony_cistatic void t3_resume_ports(struct adapter *adapter) 295162306a36Sopenharmony_ci{ 295262306a36Sopenharmony_ci int i; 295362306a36Sopenharmony_ci 295462306a36Sopenharmony_ci /* Restart the ports */ 295562306a36Sopenharmony_ci for_each_port(adapter, i) { 295662306a36Sopenharmony_ci struct net_device *netdev = adapter->port[i]; 295762306a36Sopenharmony_ci 295862306a36Sopenharmony_ci if (netif_running(netdev)) { 295962306a36Sopenharmony_ci if (cxgb_open(netdev)) { 296062306a36Sopenharmony_ci dev_err(&adapter->pdev->dev, 296162306a36Sopenharmony_ci "can't bring device back up" 296262306a36Sopenharmony_ci " after reset\n"); 296362306a36Sopenharmony_ci continue; 296462306a36Sopenharmony_ci } 296562306a36Sopenharmony_ci } 296662306a36Sopenharmony_ci } 296762306a36Sopenharmony_ci 296862306a36Sopenharmony_ci if (is_offload(adapter) && !ofld_disable) 296962306a36Sopenharmony_ci cxgb3_event_notify(&adapter->tdev, OFFLOAD_STATUS_UP, 0); 297062306a36Sopenharmony_ci} 297162306a36Sopenharmony_ci 297262306a36Sopenharmony_ci/* 297362306a36Sopenharmony_ci * processes a fatal error. 297462306a36Sopenharmony_ci * Bring the ports down, reset the chip, bring the ports back up. 297562306a36Sopenharmony_ci */ 297662306a36Sopenharmony_cistatic void fatal_error_task(struct work_struct *work) 297762306a36Sopenharmony_ci{ 297862306a36Sopenharmony_ci struct adapter *adapter = container_of(work, struct adapter, 297962306a36Sopenharmony_ci fatal_error_handler_task); 298062306a36Sopenharmony_ci int err = 0; 298162306a36Sopenharmony_ci 298262306a36Sopenharmony_ci rtnl_lock(); 298362306a36Sopenharmony_ci err = t3_adapter_error(adapter, 1, 1); 298462306a36Sopenharmony_ci if (!err) 298562306a36Sopenharmony_ci err = t3_reenable_adapter(adapter); 298662306a36Sopenharmony_ci if (!err) 298762306a36Sopenharmony_ci t3_resume_ports(adapter); 298862306a36Sopenharmony_ci 298962306a36Sopenharmony_ci CH_ALERT(adapter, "adapter reset %s\n", err ? "failed" : "succeeded"); 299062306a36Sopenharmony_ci rtnl_unlock(); 299162306a36Sopenharmony_ci} 299262306a36Sopenharmony_ci 299362306a36Sopenharmony_civoid t3_fatal_err(struct adapter *adapter) 299462306a36Sopenharmony_ci{ 299562306a36Sopenharmony_ci unsigned int fw_status[4]; 299662306a36Sopenharmony_ci 299762306a36Sopenharmony_ci if (adapter->flags & FULL_INIT_DONE) { 299862306a36Sopenharmony_ci t3_sge_stop_dma(adapter); 299962306a36Sopenharmony_ci t3_write_reg(adapter, A_XGM_TX_CTRL, 0); 300062306a36Sopenharmony_ci t3_write_reg(adapter, A_XGM_RX_CTRL, 0); 300162306a36Sopenharmony_ci t3_write_reg(adapter, XGM_REG(A_XGM_TX_CTRL, 1), 0); 300262306a36Sopenharmony_ci t3_write_reg(adapter, XGM_REG(A_XGM_RX_CTRL, 1), 0); 300362306a36Sopenharmony_ci 300462306a36Sopenharmony_ci spin_lock(&adapter->work_lock); 300562306a36Sopenharmony_ci t3_intr_disable(adapter); 300662306a36Sopenharmony_ci queue_work(cxgb3_wq, &adapter->fatal_error_handler_task); 300762306a36Sopenharmony_ci spin_unlock(&adapter->work_lock); 300862306a36Sopenharmony_ci } 300962306a36Sopenharmony_ci CH_ALERT(adapter, "encountered fatal error, operation suspended\n"); 301062306a36Sopenharmony_ci if (!t3_cim_ctl_blk_read(adapter, 0xa0, 4, fw_status)) 301162306a36Sopenharmony_ci CH_ALERT(adapter, "FW status: 0x%x, 0x%x, 0x%x, 0x%x\n", 301262306a36Sopenharmony_ci fw_status[0], fw_status[1], 301362306a36Sopenharmony_ci fw_status[2], fw_status[3]); 301462306a36Sopenharmony_ci} 301562306a36Sopenharmony_ci 301662306a36Sopenharmony_ci/** 301762306a36Sopenharmony_ci * t3_io_error_detected - called when PCI error is detected 301862306a36Sopenharmony_ci * @pdev: Pointer to PCI device 301962306a36Sopenharmony_ci * @state: The current pci connection state 302062306a36Sopenharmony_ci * 302162306a36Sopenharmony_ci * This function is called after a PCI bus error affecting 302262306a36Sopenharmony_ci * this device has been detected. 302362306a36Sopenharmony_ci */ 302462306a36Sopenharmony_cistatic pci_ers_result_t t3_io_error_detected(struct pci_dev *pdev, 302562306a36Sopenharmony_ci pci_channel_state_t state) 302662306a36Sopenharmony_ci{ 302762306a36Sopenharmony_ci struct adapter *adapter = pci_get_drvdata(pdev); 302862306a36Sopenharmony_ci 302962306a36Sopenharmony_ci if (state == pci_channel_io_perm_failure) 303062306a36Sopenharmony_ci return PCI_ERS_RESULT_DISCONNECT; 303162306a36Sopenharmony_ci 303262306a36Sopenharmony_ci t3_adapter_error(adapter, 0, 0); 303362306a36Sopenharmony_ci 303462306a36Sopenharmony_ci /* Request a slot reset. */ 303562306a36Sopenharmony_ci return PCI_ERS_RESULT_NEED_RESET; 303662306a36Sopenharmony_ci} 303762306a36Sopenharmony_ci 303862306a36Sopenharmony_ci/** 303962306a36Sopenharmony_ci * t3_io_slot_reset - called after the pci bus has been reset. 304062306a36Sopenharmony_ci * @pdev: Pointer to PCI device 304162306a36Sopenharmony_ci * 304262306a36Sopenharmony_ci * Restart the card from scratch, as if from a cold-boot. 304362306a36Sopenharmony_ci */ 304462306a36Sopenharmony_cistatic pci_ers_result_t t3_io_slot_reset(struct pci_dev *pdev) 304562306a36Sopenharmony_ci{ 304662306a36Sopenharmony_ci struct adapter *adapter = pci_get_drvdata(pdev); 304762306a36Sopenharmony_ci 304862306a36Sopenharmony_ci if (!t3_reenable_adapter(adapter)) 304962306a36Sopenharmony_ci return PCI_ERS_RESULT_RECOVERED; 305062306a36Sopenharmony_ci 305162306a36Sopenharmony_ci return PCI_ERS_RESULT_DISCONNECT; 305262306a36Sopenharmony_ci} 305362306a36Sopenharmony_ci 305462306a36Sopenharmony_ci/** 305562306a36Sopenharmony_ci * t3_io_resume - called when traffic can start flowing again. 305662306a36Sopenharmony_ci * @pdev: Pointer to PCI device 305762306a36Sopenharmony_ci * 305862306a36Sopenharmony_ci * This callback is called when the error recovery driver tells us that 305962306a36Sopenharmony_ci * its OK to resume normal operation. 306062306a36Sopenharmony_ci */ 306162306a36Sopenharmony_cistatic void t3_io_resume(struct pci_dev *pdev) 306262306a36Sopenharmony_ci{ 306362306a36Sopenharmony_ci struct adapter *adapter = pci_get_drvdata(pdev); 306462306a36Sopenharmony_ci 306562306a36Sopenharmony_ci CH_ALERT(adapter, "adapter recovering, PEX ERR 0x%x\n", 306662306a36Sopenharmony_ci t3_read_reg(adapter, A_PCIE_PEX_ERR)); 306762306a36Sopenharmony_ci 306862306a36Sopenharmony_ci rtnl_lock(); 306962306a36Sopenharmony_ci t3_resume_ports(adapter); 307062306a36Sopenharmony_ci rtnl_unlock(); 307162306a36Sopenharmony_ci} 307262306a36Sopenharmony_ci 307362306a36Sopenharmony_cistatic const struct pci_error_handlers t3_err_handler = { 307462306a36Sopenharmony_ci .error_detected = t3_io_error_detected, 307562306a36Sopenharmony_ci .slot_reset = t3_io_slot_reset, 307662306a36Sopenharmony_ci .resume = t3_io_resume, 307762306a36Sopenharmony_ci}; 307862306a36Sopenharmony_ci 307962306a36Sopenharmony_ci/* 308062306a36Sopenharmony_ci * Set the number of qsets based on the number of CPUs and the number of ports, 308162306a36Sopenharmony_ci * not to exceed the number of available qsets, assuming there are enough qsets 308262306a36Sopenharmony_ci * per port in HW. 308362306a36Sopenharmony_ci */ 308462306a36Sopenharmony_cistatic void set_nqsets(struct adapter *adap) 308562306a36Sopenharmony_ci{ 308662306a36Sopenharmony_ci int i, j = 0; 308762306a36Sopenharmony_ci int num_cpus = netif_get_num_default_rss_queues(); 308862306a36Sopenharmony_ci int hwports = adap->params.nports; 308962306a36Sopenharmony_ci int nqsets = adap->msix_nvectors - 1; 309062306a36Sopenharmony_ci 309162306a36Sopenharmony_ci if (adap->params.rev > 0 && adap->flags & USING_MSIX) { 309262306a36Sopenharmony_ci if (hwports == 2 && 309362306a36Sopenharmony_ci (hwports * nqsets > SGE_QSETS || 309462306a36Sopenharmony_ci num_cpus >= nqsets / hwports)) 309562306a36Sopenharmony_ci nqsets /= hwports; 309662306a36Sopenharmony_ci if (nqsets > num_cpus) 309762306a36Sopenharmony_ci nqsets = num_cpus; 309862306a36Sopenharmony_ci if (nqsets < 1 || hwports == 4) 309962306a36Sopenharmony_ci nqsets = 1; 310062306a36Sopenharmony_ci } else { 310162306a36Sopenharmony_ci nqsets = 1; 310262306a36Sopenharmony_ci } 310362306a36Sopenharmony_ci 310462306a36Sopenharmony_ci for_each_port(adap, i) { 310562306a36Sopenharmony_ci struct port_info *pi = adap2pinfo(adap, i); 310662306a36Sopenharmony_ci 310762306a36Sopenharmony_ci pi->first_qset = j; 310862306a36Sopenharmony_ci pi->nqsets = nqsets; 310962306a36Sopenharmony_ci j = pi->first_qset + nqsets; 311062306a36Sopenharmony_ci 311162306a36Sopenharmony_ci dev_info(&adap->pdev->dev, 311262306a36Sopenharmony_ci "Port %d using %d queue sets.\n", i, nqsets); 311362306a36Sopenharmony_ci } 311462306a36Sopenharmony_ci} 311562306a36Sopenharmony_ci 311662306a36Sopenharmony_cistatic int cxgb_enable_msix(struct adapter *adap) 311762306a36Sopenharmony_ci{ 311862306a36Sopenharmony_ci struct msix_entry entries[SGE_QSETS + 1]; 311962306a36Sopenharmony_ci int vectors; 312062306a36Sopenharmony_ci int i; 312162306a36Sopenharmony_ci 312262306a36Sopenharmony_ci vectors = ARRAY_SIZE(entries); 312362306a36Sopenharmony_ci for (i = 0; i < vectors; ++i) 312462306a36Sopenharmony_ci entries[i].entry = i; 312562306a36Sopenharmony_ci 312662306a36Sopenharmony_ci vectors = pci_enable_msix_range(adap->pdev, entries, 312762306a36Sopenharmony_ci adap->params.nports + 1, vectors); 312862306a36Sopenharmony_ci if (vectors < 0) 312962306a36Sopenharmony_ci return vectors; 313062306a36Sopenharmony_ci 313162306a36Sopenharmony_ci for (i = 0; i < vectors; ++i) 313262306a36Sopenharmony_ci adap->msix_info[i].vec = entries[i].vector; 313362306a36Sopenharmony_ci adap->msix_nvectors = vectors; 313462306a36Sopenharmony_ci 313562306a36Sopenharmony_ci return 0; 313662306a36Sopenharmony_ci} 313762306a36Sopenharmony_ci 313862306a36Sopenharmony_cistatic void print_port_info(struct adapter *adap, const struct adapter_info *ai) 313962306a36Sopenharmony_ci{ 314062306a36Sopenharmony_ci static const char *pci_variant[] = { 314162306a36Sopenharmony_ci "PCI", "PCI-X", "PCI-X ECC", "PCI-X 266", "PCI Express" 314262306a36Sopenharmony_ci }; 314362306a36Sopenharmony_ci 314462306a36Sopenharmony_ci int i; 314562306a36Sopenharmony_ci char buf[80]; 314662306a36Sopenharmony_ci 314762306a36Sopenharmony_ci if (is_pcie(adap)) 314862306a36Sopenharmony_ci snprintf(buf, sizeof(buf), "%s x%d", 314962306a36Sopenharmony_ci pci_variant[adap->params.pci.variant], 315062306a36Sopenharmony_ci adap->params.pci.width); 315162306a36Sopenharmony_ci else 315262306a36Sopenharmony_ci snprintf(buf, sizeof(buf), "%s %dMHz/%d-bit", 315362306a36Sopenharmony_ci pci_variant[adap->params.pci.variant], 315462306a36Sopenharmony_ci adap->params.pci.speed, adap->params.pci.width); 315562306a36Sopenharmony_ci 315662306a36Sopenharmony_ci for_each_port(adap, i) { 315762306a36Sopenharmony_ci struct net_device *dev = adap->port[i]; 315862306a36Sopenharmony_ci const struct port_info *pi = netdev_priv(dev); 315962306a36Sopenharmony_ci 316062306a36Sopenharmony_ci if (!test_bit(i, &adap->registered_device_map)) 316162306a36Sopenharmony_ci continue; 316262306a36Sopenharmony_ci netdev_info(dev, "%s %s %sNIC (rev %d) %s%s\n", 316362306a36Sopenharmony_ci ai->desc, pi->phy.desc, 316462306a36Sopenharmony_ci is_offload(adap) ? "R" : "", adap->params.rev, buf, 316562306a36Sopenharmony_ci (adap->flags & USING_MSIX) ? " MSI-X" : 316662306a36Sopenharmony_ci (adap->flags & USING_MSI) ? " MSI" : ""); 316762306a36Sopenharmony_ci if (adap->name == dev->name && adap->params.vpd.mclk) 316862306a36Sopenharmony_ci pr_info("%s: %uMB CM, %uMB PMTX, %uMB PMRX, S/N: %s\n", 316962306a36Sopenharmony_ci adap->name, t3_mc7_size(&adap->cm) >> 20, 317062306a36Sopenharmony_ci t3_mc7_size(&adap->pmtx) >> 20, 317162306a36Sopenharmony_ci t3_mc7_size(&adap->pmrx) >> 20, 317262306a36Sopenharmony_ci adap->params.vpd.sn); 317362306a36Sopenharmony_ci } 317462306a36Sopenharmony_ci} 317562306a36Sopenharmony_ci 317662306a36Sopenharmony_cistatic const struct net_device_ops cxgb_netdev_ops = { 317762306a36Sopenharmony_ci .ndo_open = cxgb_open, 317862306a36Sopenharmony_ci .ndo_stop = cxgb_close, 317962306a36Sopenharmony_ci .ndo_start_xmit = t3_eth_xmit, 318062306a36Sopenharmony_ci .ndo_get_stats = cxgb_get_stats, 318162306a36Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 318262306a36Sopenharmony_ci .ndo_set_rx_mode = cxgb_set_rxmode, 318362306a36Sopenharmony_ci .ndo_eth_ioctl = cxgb_ioctl, 318462306a36Sopenharmony_ci .ndo_siocdevprivate = cxgb_siocdevprivate, 318562306a36Sopenharmony_ci .ndo_change_mtu = cxgb_change_mtu, 318662306a36Sopenharmony_ci .ndo_set_mac_address = cxgb_set_mac_addr, 318762306a36Sopenharmony_ci .ndo_fix_features = cxgb_fix_features, 318862306a36Sopenharmony_ci .ndo_set_features = cxgb_set_features, 318962306a36Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER 319062306a36Sopenharmony_ci .ndo_poll_controller = cxgb_netpoll, 319162306a36Sopenharmony_ci#endif 319262306a36Sopenharmony_ci}; 319362306a36Sopenharmony_ci 319462306a36Sopenharmony_cistatic void cxgb3_init_iscsi_mac(struct net_device *dev) 319562306a36Sopenharmony_ci{ 319662306a36Sopenharmony_ci struct port_info *pi = netdev_priv(dev); 319762306a36Sopenharmony_ci 319862306a36Sopenharmony_ci memcpy(pi->iscsic.mac_addr, dev->dev_addr, ETH_ALEN); 319962306a36Sopenharmony_ci pi->iscsic.mac_addr[3] |= 0x80; 320062306a36Sopenharmony_ci} 320162306a36Sopenharmony_ci 320262306a36Sopenharmony_ci#define TSO_FLAGS (NETIF_F_TSO | NETIF_F_TSO6 | NETIF_F_TSO_ECN) 320362306a36Sopenharmony_ci#define VLAN_FEAT (NETIF_F_SG | NETIF_F_IP_CSUM | TSO_FLAGS | \ 320462306a36Sopenharmony_ci NETIF_F_IPV6_CSUM | NETIF_F_HIGHDMA) 320562306a36Sopenharmony_cistatic int init_one(struct pci_dev *pdev, const struct pci_device_id *ent) 320662306a36Sopenharmony_ci{ 320762306a36Sopenharmony_ci int i, err; 320862306a36Sopenharmony_ci resource_size_t mmio_start, mmio_len; 320962306a36Sopenharmony_ci const struct adapter_info *ai; 321062306a36Sopenharmony_ci struct adapter *adapter = NULL; 321162306a36Sopenharmony_ci struct port_info *pi; 321262306a36Sopenharmony_ci 321362306a36Sopenharmony_ci if (!cxgb3_wq) { 321462306a36Sopenharmony_ci cxgb3_wq = create_singlethread_workqueue(DRV_NAME); 321562306a36Sopenharmony_ci if (!cxgb3_wq) { 321662306a36Sopenharmony_ci pr_err("cannot initialize work queue\n"); 321762306a36Sopenharmony_ci return -ENOMEM; 321862306a36Sopenharmony_ci } 321962306a36Sopenharmony_ci } 322062306a36Sopenharmony_ci 322162306a36Sopenharmony_ci err = pci_enable_device(pdev); 322262306a36Sopenharmony_ci if (err) { 322362306a36Sopenharmony_ci dev_err(&pdev->dev, "cannot enable PCI device\n"); 322462306a36Sopenharmony_ci goto out; 322562306a36Sopenharmony_ci } 322662306a36Sopenharmony_ci 322762306a36Sopenharmony_ci err = pci_request_regions(pdev, DRV_NAME); 322862306a36Sopenharmony_ci if (err) { 322962306a36Sopenharmony_ci /* Just info, some other driver may have claimed the device. */ 323062306a36Sopenharmony_ci dev_info(&pdev->dev, "cannot obtain PCI resources\n"); 323162306a36Sopenharmony_ci goto out_disable_device; 323262306a36Sopenharmony_ci } 323362306a36Sopenharmony_ci 323462306a36Sopenharmony_ci err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); 323562306a36Sopenharmony_ci if (err) { 323662306a36Sopenharmony_ci dev_err(&pdev->dev, "no usable DMA configuration\n"); 323762306a36Sopenharmony_ci goto out_release_regions; 323862306a36Sopenharmony_ci } 323962306a36Sopenharmony_ci 324062306a36Sopenharmony_ci pci_set_master(pdev); 324162306a36Sopenharmony_ci pci_save_state(pdev); 324262306a36Sopenharmony_ci 324362306a36Sopenharmony_ci mmio_start = pci_resource_start(pdev, 0); 324462306a36Sopenharmony_ci mmio_len = pci_resource_len(pdev, 0); 324562306a36Sopenharmony_ci ai = t3_get_adapter_info(ent->driver_data); 324662306a36Sopenharmony_ci 324762306a36Sopenharmony_ci adapter = kzalloc(sizeof(*adapter), GFP_KERNEL); 324862306a36Sopenharmony_ci if (!adapter) { 324962306a36Sopenharmony_ci err = -ENOMEM; 325062306a36Sopenharmony_ci goto out_release_regions; 325162306a36Sopenharmony_ci } 325262306a36Sopenharmony_ci 325362306a36Sopenharmony_ci adapter->nofail_skb = 325462306a36Sopenharmony_ci alloc_skb(sizeof(struct cpl_set_tcb_field), GFP_KERNEL); 325562306a36Sopenharmony_ci if (!adapter->nofail_skb) { 325662306a36Sopenharmony_ci dev_err(&pdev->dev, "cannot allocate nofail buffer\n"); 325762306a36Sopenharmony_ci err = -ENOMEM; 325862306a36Sopenharmony_ci goto out_free_adapter; 325962306a36Sopenharmony_ci } 326062306a36Sopenharmony_ci 326162306a36Sopenharmony_ci adapter->regs = ioremap(mmio_start, mmio_len); 326262306a36Sopenharmony_ci if (!adapter->regs) { 326362306a36Sopenharmony_ci dev_err(&pdev->dev, "cannot map device registers\n"); 326462306a36Sopenharmony_ci err = -ENOMEM; 326562306a36Sopenharmony_ci goto out_free_adapter_nofail; 326662306a36Sopenharmony_ci } 326762306a36Sopenharmony_ci 326862306a36Sopenharmony_ci adapter->pdev = pdev; 326962306a36Sopenharmony_ci adapter->name = pci_name(pdev); 327062306a36Sopenharmony_ci adapter->msg_enable = dflt_msg_enable; 327162306a36Sopenharmony_ci adapter->mmio_len = mmio_len; 327262306a36Sopenharmony_ci 327362306a36Sopenharmony_ci mutex_init(&adapter->mdio_lock); 327462306a36Sopenharmony_ci spin_lock_init(&adapter->work_lock); 327562306a36Sopenharmony_ci spin_lock_init(&adapter->stats_lock); 327662306a36Sopenharmony_ci 327762306a36Sopenharmony_ci INIT_LIST_HEAD(&adapter->adapter_list); 327862306a36Sopenharmony_ci INIT_WORK(&adapter->ext_intr_handler_task, ext_intr_task); 327962306a36Sopenharmony_ci INIT_WORK(&adapter->fatal_error_handler_task, fatal_error_task); 328062306a36Sopenharmony_ci 328162306a36Sopenharmony_ci INIT_WORK(&adapter->db_full_task, db_full_task); 328262306a36Sopenharmony_ci INIT_WORK(&adapter->db_empty_task, db_empty_task); 328362306a36Sopenharmony_ci INIT_WORK(&adapter->db_drop_task, db_drop_task); 328462306a36Sopenharmony_ci 328562306a36Sopenharmony_ci INIT_DELAYED_WORK(&adapter->adap_check_task, t3_adap_check_task); 328662306a36Sopenharmony_ci 328762306a36Sopenharmony_ci for (i = 0; i < ai->nports0 + ai->nports1; ++i) { 328862306a36Sopenharmony_ci struct net_device *netdev; 328962306a36Sopenharmony_ci 329062306a36Sopenharmony_ci netdev = alloc_etherdev_mq(sizeof(struct port_info), SGE_QSETS); 329162306a36Sopenharmony_ci if (!netdev) { 329262306a36Sopenharmony_ci err = -ENOMEM; 329362306a36Sopenharmony_ci goto out_free_dev; 329462306a36Sopenharmony_ci } 329562306a36Sopenharmony_ci 329662306a36Sopenharmony_ci SET_NETDEV_DEV(netdev, &pdev->dev); 329762306a36Sopenharmony_ci 329862306a36Sopenharmony_ci adapter->port[i] = netdev; 329962306a36Sopenharmony_ci pi = netdev_priv(netdev); 330062306a36Sopenharmony_ci pi->adapter = adapter; 330162306a36Sopenharmony_ci pi->port_id = i; 330262306a36Sopenharmony_ci netif_carrier_off(netdev); 330362306a36Sopenharmony_ci netdev->irq = pdev->irq; 330462306a36Sopenharmony_ci netdev->mem_start = mmio_start; 330562306a36Sopenharmony_ci netdev->mem_end = mmio_start + mmio_len - 1; 330662306a36Sopenharmony_ci netdev->hw_features = NETIF_F_SG | NETIF_F_IP_CSUM | 330762306a36Sopenharmony_ci NETIF_F_TSO | NETIF_F_RXCSUM | NETIF_F_HW_VLAN_CTAG_RX; 330862306a36Sopenharmony_ci netdev->features |= netdev->hw_features | 330962306a36Sopenharmony_ci NETIF_F_HW_VLAN_CTAG_TX; 331062306a36Sopenharmony_ci netdev->vlan_features |= netdev->features & VLAN_FEAT; 331162306a36Sopenharmony_ci 331262306a36Sopenharmony_ci netdev->features |= NETIF_F_HIGHDMA; 331362306a36Sopenharmony_ci 331462306a36Sopenharmony_ci netdev->netdev_ops = &cxgb_netdev_ops; 331562306a36Sopenharmony_ci netdev->ethtool_ops = &cxgb_ethtool_ops; 331662306a36Sopenharmony_ci netdev->min_mtu = 81; 331762306a36Sopenharmony_ci netdev->max_mtu = ETH_MAX_MTU; 331862306a36Sopenharmony_ci netdev->dev_port = pi->port_id; 331962306a36Sopenharmony_ci } 332062306a36Sopenharmony_ci 332162306a36Sopenharmony_ci pci_set_drvdata(pdev, adapter); 332262306a36Sopenharmony_ci if (t3_prep_adapter(adapter, ai, 1) < 0) { 332362306a36Sopenharmony_ci err = -ENODEV; 332462306a36Sopenharmony_ci goto out_free_dev; 332562306a36Sopenharmony_ci } 332662306a36Sopenharmony_ci 332762306a36Sopenharmony_ci /* 332862306a36Sopenharmony_ci * The card is now ready to go. If any errors occur during device 332962306a36Sopenharmony_ci * registration we do not fail the whole card but rather proceed only 333062306a36Sopenharmony_ci * with the ports we manage to register successfully. However we must 333162306a36Sopenharmony_ci * register at least one net device. 333262306a36Sopenharmony_ci */ 333362306a36Sopenharmony_ci for_each_port(adapter, i) { 333462306a36Sopenharmony_ci err = register_netdev(adapter->port[i]); 333562306a36Sopenharmony_ci if (err) 333662306a36Sopenharmony_ci dev_warn(&pdev->dev, 333762306a36Sopenharmony_ci "cannot register net device %s, skipping\n", 333862306a36Sopenharmony_ci adapter->port[i]->name); 333962306a36Sopenharmony_ci else { 334062306a36Sopenharmony_ci /* 334162306a36Sopenharmony_ci * Change the name we use for messages to the name of 334262306a36Sopenharmony_ci * the first successfully registered interface. 334362306a36Sopenharmony_ci */ 334462306a36Sopenharmony_ci if (!adapter->registered_device_map) 334562306a36Sopenharmony_ci adapter->name = adapter->port[i]->name; 334662306a36Sopenharmony_ci 334762306a36Sopenharmony_ci __set_bit(i, &adapter->registered_device_map); 334862306a36Sopenharmony_ci } 334962306a36Sopenharmony_ci } 335062306a36Sopenharmony_ci if (!adapter->registered_device_map) { 335162306a36Sopenharmony_ci dev_err(&pdev->dev, "could not register any net devices\n"); 335262306a36Sopenharmony_ci err = -ENODEV; 335362306a36Sopenharmony_ci goto out_free_dev; 335462306a36Sopenharmony_ci } 335562306a36Sopenharmony_ci 335662306a36Sopenharmony_ci for_each_port(adapter, i) 335762306a36Sopenharmony_ci cxgb3_init_iscsi_mac(adapter->port[i]); 335862306a36Sopenharmony_ci 335962306a36Sopenharmony_ci /* Driver's ready. Reflect it on LEDs */ 336062306a36Sopenharmony_ci t3_led_ready(adapter); 336162306a36Sopenharmony_ci 336262306a36Sopenharmony_ci if (is_offload(adapter)) { 336362306a36Sopenharmony_ci __set_bit(OFFLOAD_DEVMAP_BIT, &adapter->registered_device_map); 336462306a36Sopenharmony_ci cxgb3_adapter_ofld(adapter); 336562306a36Sopenharmony_ci } 336662306a36Sopenharmony_ci 336762306a36Sopenharmony_ci /* See what interrupts we'll be using */ 336862306a36Sopenharmony_ci if (msi > 1 && cxgb_enable_msix(adapter) == 0) 336962306a36Sopenharmony_ci adapter->flags |= USING_MSIX; 337062306a36Sopenharmony_ci else if (msi > 0 && pci_enable_msi(pdev) == 0) 337162306a36Sopenharmony_ci adapter->flags |= USING_MSI; 337262306a36Sopenharmony_ci 337362306a36Sopenharmony_ci set_nqsets(adapter); 337462306a36Sopenharmony_ci 337562306a36Sopenharmony_ci err = sysfs_create_group(&adapter->port[0]->dev.kobj, 337662306a36Sopenharmony_ci &cxgb3_attr_group); 337762306a36Sopenharmony_ci if (err) { 337862306a36Sopenharmony_ci dev_err(&pdev->dev, "cannot create sysfs group\n"); 337962306a36Sopenharmony_ci goto out_close_led; 338062306a36Sopenharmony_ci } 338162306a36Sopenharmony_ci 338262306a36Sopenharmony_ci print_port_info(adapter, ai); 338362306a36Sopenharmony_ci return 0; 338462306a36Sopenharmony_ci 338562306a36Sopenharmony_ciout_close_led: 338662306a36Sopenharmony_ci t3_set_reg_field(adapter, A_T3DBG_GPIO_EN, F_GPIO0_OUT_VAL, 0); 338762306a36Sopenharmony_ci 338862306a36Sopenharmony_ciout_free_dev: 338962306a36Sopenharmony_ci iounmap(adapter->regs); 339062306a36Sopenharmony_ci for (i = ai->nports0 + ai->nports1 - 1; i >= 0; --i) 339162306a36Sopenharmony_ci if (adapter->port[i]) 339262306a36Sopenharmony_ci free_netdev(adapter->port[i]); 339362306a36Sopenharmony_ci 339462306a36Sopenharmony_ciout_free_adapter_nofail: 339562306a36Sopenharmony_ci kfree_skb(adapter->nofail_skb); 339662306a36Sopenharmony_ci 339762306a36Sopenharmony_ciout_free_adapter: 339862306a36Sopenharmony_ci kfree(adapter); 339962306a36Sopenharmony_ci 340062306a36Sopenharmony_ciout_release_regions: 340162306a36Sopenharmony_ci pci_release_regions(pdev); 340262306a36Sopenharmony_ciout_disable_device: 340362306a36Sopenharmony_ci pci_disable_device(pdev); 340462306a36Sopenharmony_ciout: 340562306a36Sopenharmony_ci return err; 340662306a36Sopenharmony_ci} 340762306a36Sopenharmony_ci 340862306a36Sopenharmony_cistatic void remove_one(struct pci_dev *pdev) 340962306a36Sopenharmony_ci{ 341062306a36Sopenharmony_ci struct adapter *adapter = pci_get_drvdata(pdev); 341162306a36Sopenharmony_ci 341262306a36Sopenharmony_ci if (adapter) { 341362306a36Sopenharmony_ci int i; 341462306a36Sopenharmony_ci 341562306a36Sopenharmony_ci t3_sge_stop(adapter); 341662306a36Sopenharmony_ci sysfs_remove_group(&adapter->port[0]->dev.kobj, 341762306a36Sopenharmony_ci &cxgb3_attr_group); 341862306a36Sopenharmony_ci 341962306a36Sopenharmony_ci if (is_offload(adapter)) { 342062306a36Sopenharmony_ci cxgb3_adapter_unofld(adapter); 342162306a36Sopenharmony_ci if (test_bit(OFFLOAD_DEVMAP_BIT, 342262306a36Sopenharmony_ci &adapter->open_device_map)) 342362306a36Sopenharmony_ci offload_close(&adapter->tdev); 342462306a36Sopenharmony_ci } 342562306a36Sopenharmony_ci 342662306a36Sopenharmony_ci for_each_port(adapter, i) 342762306a36Sopenharmony_ci if (test_bit(i, &adapter->registered_device_map)) 342862306a36Sopenharmony_ci unregister_netdev(adapter->port[i]); 342962306a36Sopenharmony_ci 343062306a36Sopenharmony_ci t3_stop_sge_timers(adapter); 343162306a36Sopenharmony_ci t3_free_sge_resources(adapter); 343262306a36Sopenharmony_ci cxgb_disable_msi(adapter); 343362306a36Sopenharmony_ci 343462306a36Sopenharmony_ci for_each_port(adapter, i) 343562306a36Sopenharmony_ci if (adapter->port[i]) 343662306a36Sopenharmony_ci free_netdev(adapter->port[i]); 343762306a36Sopenharmony_ci 343862306a36Sopenharmony_ci iounmap(adapter->regs); 343962306a36Sopenharmony_ci kfree_skb(adapter->nofail_skb); 344062306a36Sopenharmony_ci kfree(adapter); 344162306a36Sopenharmony_ci pci_release_regions(pdev); 344262306a36Sopenharmony_ci pci_disable_device(pdev); 344362306a36Sopenharmony_ci } 344462306a36Sopenharmony_ci} 344562306a36Sopenharmony_ci 344662306a36Sopenharmony_cistatic struct pci_driver driver = { 344762306a36Sopenharmony_ci .name = DRV_NAME, 344862306a36Sopenharmony_ci .id_table = cxgb3_pci_tbl, 344962306a36Sopenharmony_ci .probe = init_one, 345062306a36Sopenharmony_ci .remove = remove_one, 345162306a36Sopenharmony_ci .err_handler = &t3_err_handler, 345262306a36Sopenharmony_ci}; 345362306a36Sopenharmony_ci 345462306a36Sopenharmony_cistatic int __init cxgb3_init_module(void) 345562306a36Sopenharmony_ci{ 345662306a36Sopenharmony_ci int ret; 345762306a36Sopenharmony_ci 345862306a36Sopenharmony_ci cxgb3_offload_init(); 345962306a36Sopenharmony_ci 346062306a36Sopenharmony_ci ret = pci_register_driver(&driver); 346162306a36Sopenharmony_ci return ret; 346262306a36Sopenharmony_ci} 346362306a36Sopenharmony_ci 346462306a36Sopenharmony_cistatic void __exit cxgb3_cleanup_module(void) 346562306a36Sopenharmony_ci{ 346662306a36Sopenharmony_ci pci_unregister_driver(&driver); 346762306a36Sopenharmony_ci if (cxgb3_wq) 346862306a36Sopenharmony_ci destroy_workqueue(cxgb3_wq); 346962306a36Sopenharmony_ci} 347062306a36Sopenharmony_ci 347162306a36Sopenharmony_cimodule_init(cxgb3_init_module); 347262306a36Sopenharmony_cimodule_exit(cxgb3_cleanup_module); 3473