162306a36Sopenharmony_ci/***************************************************************************** 262306a36Sopenharmony_ci * * 362306a36Sopenharmony_ci * File: cxgb2.c * 462306a36Sopenharmony_ci * $Revision: 1.25 $ * 562306a36Sopenharmony_ci * $Date: 2005/06/22 00:43:25 $ * 662306a36Sopenharmony_ci * Description: * 762306a36Sopenharmony_ci * Chelsio 10Gb Ethernet Driver. * 862306a36Sopenharmony_ci * * 962306a36Sopenharmony_ci * This program is free software; you can redistribute it and/or modify * 1062306a36Sopenharmony_ci * it under the terms of the GNU General Public License, version 2, as * 1162306a36Sopenharmony_ci * published by the Free Software Foundation. * 1262306a36Sopenharmony_ci * * 1362306a36Sopenharmony_ci * You should have received a copy of the GNU General Public License along * 1462306a36Sopenharmony_ci * with this program; if not, see <http://www.gnu.org/licenses/>. * 1562306a36Sopenharmony_ci * * 1662306a36Sopenharmony_ci * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED * 1762306a36Sopenharmony_ci * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * 1862306a36Sopenharmony_ci * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. * 1962306a36Sopenharmony_ci * * 2062306a36Sopenharmony_ci * http://www.chelsio.com * 2162306a36Sopenharmony_ci * * 2262306a36Sopenharmony_ci * Copyright (c) 2003 - 2005 Chelsio Communications, Inc. * 2362306a36Sopenharmony_ci * All rights reserved. * 2462306a36Sopenharmony_ci * * 2562306a36Sopenharmony_ci * Maintainers: maintainers@chelsio.com * 2662306a36Sopenharmony_ci * * 2762306a36Sopenharmony_ci * Authors: Dimitrios Michailidis <dm@chelsio.com> * 2862306a36Sopenharmony_ci * Tina Yang <tainay@chelsio.com> * 2962306a36Sopenharmony_ci * Felix Marti <felix@chelsio.com> * 3062306a36Sopenharmony_ci * Scott Bardone <sbardone@chelsio.com> * 3162306a36Sopenharmony_ci * Kurt Ottaway <kottaway@chelsio.com> * 3262306a36Sopenharmony_ci * Frank DiMambro <frank@chelsio.com> * 3362306a36Sopenharmony_ci * * 3462306a36Sopenharmony_ci * History: * 3562306a36Sopenharmony_ci * * 3662306a36Sopenharmony_ci ****************************************************************************/ 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci#include "common.h" 3962306a36Sopenharmony_ci#include <linux/module.h> 4062306a36Sopenharmony_ci#include <linux/pci.h> 4162306a36Sopenharmony_ci#include <linux/netdevice.h> 4262306a36Sopenharmony_ci#include <linux/etherdevice.h> 4362306a36Sopenharmony_ci#include <linux/if_vlan.h> 4462306a36Sopenharmony_ci#include <linux/mii.h> 4562306a36Sopenharmony_ci#include <linux/sockios.h> 4662306a36Sopenharmony_ci#include <linux/dma-mapping.h> 4762306a36Sopenharmony_ci#include <linux/uaccess.h> 4862306a36Sopenharmony_ci 4962306a36Sopenharmony_ci#include "cpl5_cmd.h" 5062306a36Sopenharmony_ci#include "regs.h" 5162306a36Sopenharmony_ci#include "gmac.h" 5262306a36Sopenharmony_ci#include "cphy.h" 5362306a36Sopenharmony_ci#include "sge.h" 5462306a36Sopenharmony_ci#include "tp.h" 5562306a36Sopenharmony_ci#include "espi.h" 5662306a36Sopenharmony_ci#include "elmer0.h" 5762306a36Sopenharmony_ci 5862306a36Sopenharmony_ci#include <linux/workqueue.h> 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_cistatic inline void schedule_mac_stats_update(struct adapter *ap, int secs) 6162306a36Sopenharmony_ci{ 6262306a36Sopenharmony_ci schedule_delayed_work(&ap->stats_update_task, secs * HZ); 6362306a36Sopenharmony_ci} 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_cistatic inline void cancel_mac_stats_update(struct adapter *ap) 6662306a36Sopenharmony_ci{ 6762306a36Sopenharmony_ci cancel_delayed_work(&ap->stats_update_task); 6862306a36Sopenharmony_ci} 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci#define MAX_CMDQ_ENTRIES 16384 7162306a36Sopenharmony_ci#define MAX_CMDQ1_ENTRIES 1024 7262306a36Sopenharmony_ci#define MAX_RX_BUFFERS 16384 7362306a36Sopenharmony_ci#define MAX_RX_JUMBO_BUFFERS 16384 7462306a36Sopenharmony_ci#define MAX_TX_BUFFERS_HIGH 16384U 7562306a36Sopenharmony_ci#define MAX_TX_BUFFERS_LOW 1536U 7662306a36Sopenharmony_ci#define MAX_TX_BUFFERS 1460U 7762306a36Sopenharmony_ci#define MIN_FL_ENTRIES 32 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/* 8462306a36Sopenharmony_ci * The EEPROM is actually bigger but only the first few bytes are used so we 8562306a36Sopenharmony_ci * only report those. 8662306a36Sopenharmony_ci */ 8762306a36Sopenharmony_ci#define EEPROM_SIZE 32 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ciMODULE_DESCRIPTION(DRV_DESCRIPTION); 9062306a36Sopenharmony_ciMODULE_AUTHOR("Chelsio Communications"); 9162306a36Sopenharmony_ciMODULE_LICENSE("GPL"); 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_cistatic int dflt_msg_enable = DFLT_MSG_ENABLE; 9462306a36Sopenharmony_ci 9562306a36Sopenharmony_cimodule_param(dflt_msg_enable, int, 0); 9662306a36Sopenharmony_ciMODULE_PARM_DESC(dflt_msg_enable, "Chelsio T1 default message enable bitmap"); 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci#define HCLOCK 0x0 9962306a36Sopenharmony_ci#define LCLOCK 0x1 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci/* T1 cards powersave mode */ 10262306a36Sopenharmony_cistatic int t1_clock(struct adapter *adapter, int mode); 10362306a36Sopenharmony_cistatic int t1powersave = 1; /* HW default is powersave mode. */ 10462306a36Sopenharmony_ci 10562306a36Sopenharmony_cimodule_param(t1powersave, int, 0); 10662306a36Sopenharmony_ciMODULE_PARM_DESC(t1powersave, "Enable/Disable T1 powersaving mode"); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_cistatic int disable_msi = 0; 10962306a36Sopenharmony_cimodule_param(disable_msi, int, 0); 11062306a36Sopenharmony_ciMODULE_PARM_DESC(disable_msi, "Disable Message Signaled Interrupt (MSI)"); 11162306a36Sopenharmony_ci 11262306a36Sopenharmony_ci/* 11362306a36Sopenharmony_ci * Setup MAC to receive the types of packets we want. 11462306a36Sopenharmony_ci */ 11562306a36Sopenharmony_cistatic void t1_set_rxmode(struct net_device *dev) 11662306a36Sopenharmony_ci{ 11762306a36Sopenharmony_ci struct adapter *adapter = dev->ml_priv; 11862306a36Sopenharmony_ci struct cmac *mac = adapter->port[dev->if_port].mac; 11962306a36Sopenharmony_ci struct t1_rx_mode rm; 12062306a36Sopenharmony_ci 12162306a36Sopenharmony_ci rm.dev = dev; 12262306a36Sopenharmony_ci mac->ops->set_rx_mode(mac, &rm); 12362306a36Sopenharmony_ci} 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_cistatic void link_report(struct port_info *p) 12662306a36Sopenharmony_ci{ 12762306a36Sopenharmony_ci if (!netif_carrier_ok(p->dev)) 12862306a36Sopenharmony_ci netdev_info(p->dev, "link down\n"); 12962306a36Sopenharmony_ci else { 13062306a36Sopenharmony_ci const char *s = "10Mbps"; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci switch (p->link_config.speed) { 13362306a36Sopenharmony_ci case SPEED_10000: s = "10Gbps"; break; 13462306a36Sopenharmony_ci case SPEED_1000: s = "1000Mbps"; break; 13562306a36Sopenharmony_ci case SPEED_100: s = "100Mbps"; break; 13662306a36Sopenharmony_ci } 13762306a36Sopenharmony_ci 13862306a36Sopenharmony_ci netdev_info(p->dev, "link up, %s, %s-duplex\n", 13962306a36Sopenharmony_ci s, p->link_config.duplex == DUPLEX_FULL 14062306a36Sopenharmony_ci ? "full" : "half"); 14162306a36Sopenharmony_ci } 14262306a36Sopenharmony_ci} 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_civoid t1_link_negotiated(struct adapter *adapter, int port_id, int link_stat, 14562306a36Sopenharmony_ci int speed, int duplex, int pause) 14662306a36Sopenharmony_ci{ 14762306a36Sopenharmony_ci struct port_info *p = &adapter->port[port_id]; 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci if (link_stat != netif_carrier_ok(p->dev)) { 15062306a36Sopenharmony_ci if (link_stat) 15162306a36Sopenharmony_ci netif_carrier_on(p->dev); 15262306a36Sopenharmony_ci else 15362306a36Sopenharmony_ci netif_carrier_off(p->dev); 15462306a36Sopenharmony_ci link_report(p); 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci /* multi-ports: inform toe */ 15762306a36Sopenharmony_ci if ((speed > 0) && (adapter->params.nports > 1)) { 15862306a36Sopenharmony_ci unsigned int sched_speed = 10; 15962306a36Sopenharmony_ci switch (speed) { 16062306a36Sopenharmony_ci case SPEED_1000: 16162306a36Sopenharmony_ci sched_speed = 1000; 16262306a36Sopenharmony_ci break; 16362306a36Sopenharmony_ci case SPEED_100: 16462306a36Sopenharmony_ci sched_speed = 100; 16562306a36Sopenharmony_ci break; 16662306a36Sopenharmony_ci case SPEED_10: 16762306a36Sopenharmony_ci sched_speed = 10; 16862306a36Sopenharmony_ci break; 16962306a36Sopenharmony_ci } 17062306a36Sopenharmony_ci t1_sched_update_parms(adapter->sge, port_id, 0, sched_speed); 17162306a36Sopenharmony_ci } 17262306a36Sopenharmony_ci } 17362306a36Sopenharmony_ci} 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_cistatic void link_start(struct port_info *p) 17662306a36Sopenharmony_ci{ 17762306a36Sopenharmony_ci struct cmac *mac = p->mac; 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_ci mac->ops->reset(mac); 18062306a36Sopenharmony_ci if (mac->ops->macaddress_set) 18162306a36Sopenharmony_ci mac->ops->macaddress_set(mac, p->dev->dev_addr); 18262306a36Sopenharmony_ci t1_set_rxmode(p->dev); 18362306a36Sopenharmony_ci t1_link_start(p->phy, mac, &p->link_config); 18462306a36Sopenharmony_ci mac->ops->enable(mac, MAC_DIRECTION_RX | MAC_DIRECTION_TX); 18562306a36Sopenharmony_ci} 18662306a36Sopenharmony_ci 18762306a36Sopenharmony_cistatic void enable_hw_csum(struct adapter *adapter) 18862306a36Sopenharmony_ci{ 18962306a36Sopenharmony_ci if (adapter->port[0].dev->hw_features & NETIF_F_TSO) 19062306a36Sopenharmony_ci t1_tp_set_ip_checksum_offload(adapter->tp, 1); /* for TSO only */ 19162306a36Sopenharmony_ci t1_tp_set_tcp_checksum_offload(adapter->tp, 1); 19262306a36Sopenharmony_ci} 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci/* 19562306a36Sopenharmony_ci * Things to do upon first use of a card. 19662306a36Sopenharmony_ci * This must run with the rtnl lock held. 19762306a36Sopenharmony_ci */ 19862306a36Sopenharmony_cistatic int cxgb_up(struct adapter *adapter) 19962306a36Sopenharmony_ci{ 20062306a36Sopenharmony_ci int err = 0; 20162306a36Sopenharmony_ci 20262306a36Sopenharmony_ci if (!(adapter->flags & FULL_INIT_DONE)) { 20362306a36Sopenharmony_ci err = t1_init_hw_modules(adapter); 20462306a36Sopenharmony_ci if (err) 20562306a36Sopenharmony_ci goto out_err; 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_ci enable_hw_csum(adapter); 20862306a36Sopenharmony_ci adapter->flags |= FULL_INIT_DONE; 20962306a36Sopenharmony_ci } 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci t1_interrupts_clear(adapter); 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci adapter->params.has_msi = !disable_msi && !pci_enable_msi(adapter->pdev); 21462306a36Sopenharmony_ci err = request_threaded_irq(adapter->pdev->irq, t1_interrupt, 21562306a36Sopenharmony_ci t1_interrupt_thread, 21662306a36Sopenharmony_ci adapter->params.has_msi ? 0 : IRQF_SHARED, 21762306a36Sopenharmony_ci adapter->name, adapter); 21862306a36Sopenharmony_ci if (err) { 21962306a36Sopenharmony_ci if (adapter->params.has_msi) 22062306a36Sopenharmony_ci pci_disable_msi(adapter->pdev); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci goto out_err; 22362306a36Sopenharmony_ci } 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci t1_sge_start(adapter->sge); 22662306a36Sopenharmony_ci t1_interrupts_enable(adapter); 22762306a36Sopenharmony_ciout_err: 22862306a36Sopenharmony_ci return err; 22962306a36Sopenharmony_ci} 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci/* 23262306a36Sopenharmony_ci * Release resources when all the ports have been stopped. 23362306a36Sopenharmony_ci */ 23462306a36Sopenharmony_cistatic void cxgb_down(struct adapter *adapter) 23562306a36Sopenharmony_ci{ 23662306a36Sopenharmony_ci t1_sge_stop(adapter->sge); 23762306a36Sopenharmony_ci t1_interrupts_disable(adapter); 23862306a36Sopenharmony_ci free_irq(adapter->pdev->irq, adapter); 23962306a36Sopenharmony_ci if (adapter->params.has_msi) 24062306a36Sopenharmony_ci pci_disable_msi(adapter->pdev); 24162306a36Sopenharmony_ci} 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_cistatic int cxgb_open(struct net_device *dev) 24462306a36Sopenharmony_ci{ 24562306a36Sopenharmony_ci int err; 24662306a36Sopenharmony_ci struct adapter *adapter = dev->ml_priv; 24762306a36Sopenharmony_ci int other_ports = adapter->open_device_map & PORT_MASK; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_ci napi_enable(&adapter->napi); 25062306a36Sopenharmony_ci if (!adapter->open_device_map && (err = cxgb_up(adapter)) < 0) { 25162306a36Sopenharmony_ci napi_disable(&adapter->napi); 25262306a36Sopenharmony_ci return err; 25362306a36Sopenharmony_ci } 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci __set_bit(dev->if_port, &adapter->open_device_map); 25662306a36Sopenharmony_ci link_start(&adapter->port[dev->if_port]); 25762306a36Sopenharmony_ci netif_start_queue(dev); 25862306a36Sopenharmony_ci if (!other_ports && adapter->params.stats_update_period) 25962306a36Sopenharmony_ci schedule_mac_stats_update(adapter, 26062306a36Sopenharmony_ci adapter->params.stats_update_period); 26162306a36Sopenharmony_ci 26262306a36Sopenharmony_ci t1_vlan_mode(adapter, dev->features); 26362306a36Sopenharmony_ci return 0; 26462306a36Sopenharmony_ci} 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_cistatic int cxgb_close(struct net_device *dev) 26762306a36Sopenharmony_ci{ 26862306a36Sopenharmony_ci struct adapter *adapter = dev->ml_priv; 26962306a36Sopenharmony_ci struct port_info *p = &adapter->port[dev->if_port]; 27062306a36Sopenharmony_ci struct cmac *mac = p->mac; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci netif_stop_queue(dev); 27362306a36Sopenharmony_ci napi_disable(&adapter->napi); 27462306a36Sopenharmony_ci mac->ops->disable(mac, MAC_DIRECTION_TX | MAC_DIRECTION_RX); 27562306a36Sopenharmony_ci netif_carrier_off(dev); 27662306a36Sopenharmony_ci 27762306a36Sopenharmony_ci clear_bit(dev->if_port, &adapter->open_device_map); 27862306a36Sopenharmony_ci if (adapter->params.stats_update_period && 27962306a36Sopenharmony_ci !(adapter->open_device_map & PORT_MASK)) { 28062306a36Sopenharmony_ci /* Stop statistics accumulation. */ 28162306a36Sopenharmony_ci smp_mb__after_atomic(); 28262306a36Sopenharmony_ci spin_lock(&adapter->work_lock); /* sync with update task */ 28362306a36Sopenharmony_ci spin_unlock(&adapter->work_lock); 28462306a36Sopenharmony_ci cancel_mac_stats_update(adapter); 28562306a36Sopenharmony_ci } 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci if (!adapter->open_device_map) 28862306a36Sopenharmony_ci cxgb_down(adapter); 28962306a36Sopenharmony_ci return 0; 29062306a36Sopenharmony_ci} 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_cistatic struct net_device_stats *t1_get_stats(struct net_device *dev) 29362306a36Sopenharmony_ci{ 29462306a36Sopenharmony_ci struct adapter *adapter = dev->ml_priv; 29562306a36Sopenharmony_ci struct port_info *p = &adapter->port[dev->if_port]; 29662306a36Sopenharmony_ci struct net_device_stats *ns = &dev->stats; 29762306a36Sopenharmony_ci const struct cmac_statistics *pstats; 29862306a36Sopenharmony_ci 29962306a36Sopenharmony_ci /* Do a full update of the MAC stats */ 30062306a36Sopenharmony_ci pstats = p->mac->ops->statistics_update(p->mac, 30162306a36Sopenharmony_ci MAC_STATS_UPDATE_FULL); 30262306a36Sopenharmony_ci 30362306a36Sopenharmony_ci ns->tx_packets = pstats->TxUnicastFramesOK + 30462306a36Sopenharmony_ci pstats->TxMulticastFramesOK + pstats->TxBroadcastFramesOK; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci ns->rx_packets = pstats->RxUnicastFramesOK + 30762306a36Sopenharmony_ci pstats->RxMulticastFramesOK + pstats->RxBroadcastFramesOK; 30862306a36Sopenharmony_ci 30962306a36Sopenharmony_ci ns->tx_bytes = pstats->TxOctetsOK; 31062306a36Sopenharmony_ci ns->rx_bytes = pstats->RxOctetsOK; 31162306a36Sopenharmony_ci 31262306a36Sopenharmony_ci ns->tx_errors = pstats->TxLateCollisions + pstats->TxLengthErrors + 31362306a36Sopenharmony_ci pstats->TxUnderrun + pstats->TxFramesAbortedDueToXSCollisions; 31462306a36Sopenharmony_ci ns->rx_errors = pstats->RxDataErrors + pstats->RxJabberErrors + 31562306a36Sopenharmony_ci pstats->RxFCSErrors + pstats->RxAlignErrors + 31662306a36Sopenharmony_ci pstats->RxSequenceErrors + pstats->RxFrameTooLongErrors + 31762306a36Sopenharmony_ci pstats->RxSymbolErrors + pstats->RxRuntErrors; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci ns->multicast = pstats->RxMulticastFramesOK; 32062306a36Sopenharmony_ci ns->collisions = pstats->TxTotalCollisions; 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci /* detailed rx_errors */ 32362306a36Sopenharmony_ci ns->rx_length_errors = pstats->RxFrameTooLongErrors + 32462306a36Sopenharmony_ci pstats->RxJabberErrors; 32562306a36Sopenharmony_ci ns->rx_over_errors = 0; 32662306a36Sopenharmony_ci ns->rx_crc_errors = pstats->RxFCSErrors; 32762306a36Sopenharmony_ci ns->rx_frame_errors = pstats->RxAlignErrors; 32862306a36Sopenharmony_ci ns->rx_fifo_errors = 0; 32962306a36Sopenharmony_ci ns->rx_missed_errors = 0; 33062306a36Sopenharmony_ci 33162306a36Sopenharmony_ci /* detailed tx_errors */ 33262306a36Sopenharmony_ci ns->tx_aborted_errors = pstats->TxFramesAbortedDueToXSCollisions; 33362306a36Sopenharmony_ci ns->tx_carrier_errors = 0; 33462306a36Sopenharmony_ci ns->tx_fifo_errors = pstats->TxUnderrun; 33562306a36Sopenharmony_ci ns->tx_heartbeat_errors = 0; 33662306a36Sopenharmony_ci ns->tx_window_errors = pstats->TxLateCollisions; 33762306a36Sopenharmony_ci return ns; 33862306a36Sopenharmony_ci} 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_cistatic u32 get_msglevel(struct net_device *dev) 34162306a36Sopenharmony_ci{ 34262306a36Sopenharmony_ci struct adapter *adapter = dev->ml_priv; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci return adapter->msg_enable; 34562306a36Sopenharmony_ci} 34662306a36Sopenharmony_ci 34762306a36Sopenharmony_cistatic void set_msglevel(struct net_device *dev, u32 val) 34862306a36Sopenharmony_ci{ 34962306a36Sopenharmony_ci struct adapter *adapter = dev->ml_priv; 35062306a36Sopenharmony_ci 35162306a36Sopenharmony_ci adapter->msg_enable = val; 35262306a36Sopenharmony_ci} 35362306a36Sopenharmony_ci 35462306a36Sopenharmony_cistatic const char stats_strings[][ETH_GSTRING_LEN] = { 35562306a36Sopenharmony_ci "TxOctetsOK", 35662306a36Sopenharmony_ci "TxOctetsBad", 35762306a36Sopenharmony_ci "TxUnicastFramesOK", 35862306a36Sopenharmony_ci "TxMulticastFramesOK", 35962306a36Sopenharmony_ci "TxBroadcastFramesOK", 36062306a36Sopenharmony_ci "TxPauseFrames", 36162306a36Sopenharmony_ci "TxFramesWithDeferredXmissions", 36262306a36Sopenharmony_ci "TxLateCollisions", 36362306a36Sopenharmony_ci "TxTotalCollisions", 36462306a36Sopenharmony_ci "TxFramesAbortedDueToXSCollisions", 36562306a36Sopenharmony_ci "TxUnderrun", 36662306a36Sopenharmony_ci "TxLengthErrors", 36762306a36Sopenharmony_ci "TxInternalMACXmitError", 36862306a36Sopenharmony_ci "TxFramesWithExcessiveDeferral", 36962306a36Sopenharmony_ci "TxFCSErrors", 37062306a36Sopenharmony_ci "TxJumboFramesOk", 37162306a36Sopenharmony_ci "TxJumboOctetsOk", 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci "RxOctetsOK", 37462306a36Sopenharmony_ci "RxOctetsBad", 37562306a36Sopenharmony_ci "RxUnicastFramesOK", 37662306a36Sopenharmony_ci "RxMulticastFramesOK", 37762306a36Sopenharmony_ci "RxBroadcastFramesOK", 37862306a36Sopenharmony_ci "RxPauseFrames", 37962306a36Sopenharmony_ci "RxFCSErrors", 38062306a36Sopenharmony_ci "RxAlignErrors", 38162306a36Sopenharmony_ci "RxSymbolErrors", 38262306a36Sopenharmony_ci "RxDataErrors", 38362306a36Sopenharmony_ci "RxSequenceErrors", 38462306a36Sopenharmony_ci "RxRuntErrors", 38562306a36Sopenharmony_ci "RxJabberErrors", 38662306a36Sopenharmony_ci "RxInternalMACRcvError", 38762306a36Sopenharmony_ci "RxInRangeLengthErrors", 38862306a36Sopenharmony_ci "RxOutOfRangeLengthField", 38962306a36Sopenharmony_ci "RxFrameTooLongErrors", 39062306a36Sopenharmony_ci "RxJumboFramesOk", 39162306a36Sopenharmony_ci "RxJumboOctetsOk", 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci /* Port stats */ 39462306a36Sopenharmony_ci "RxCsumGood", 39562306a36Sopenharmony_ci "TxCsumOffload", 39662306a36Sopenharmony_ci "TxTso", 39762306a36Sopenharmony_ci "RxVlan", 39862306a36Sopenharmony_ci "TxVlan", 39962306a36Sopenharmony_ci "TxNeedHeadroom", 40062306a36Sopenharmony_ci 40162306a36Sopenharmony_ci /* Interrupt stats */ 40262306a36Sopenharmony_ci "rx drops", 40362306a36Sopenharmony_ci "pure_rsps", 40462306a36Sopenharmony_ci "unhandled irqs", 40562306a36Sopenharmony_ci "respQ_empty", 40662306a36Sopenharmony_ci "respQ_overflow", 40762306a36Sopenharmony_ci "freelistQ_empty", 40862306a36Sopenharmony_ci "pkt_too_big", 40962306a36Sopenharmony_ci "pkt_mismatch", 41062306a36Sopenharmony_ci "cmdQ_full0", 41162306a36Sopenharmony_ci "cmdQ_full1", 41262306a36Sopenharmony_ci 41362306a36Sopenharmony_ci "espi_DIP2ParityErr", 41462306a36Sopenharmony_ci "espi_DIP4Err", 41562306a36Sopenharmony_ci "espi_RxDrops", 41662306a36Sopenharmony_ci "espi_TxDrops", 41762306a36Sopenharmony_ci "espi_RxOvfl", 41862306a36Sopenharmony_ci "espi_ParityErr" 41962306a36Sopenharmony_ci}; 42062306a36Sopenharmony_ci 42162306a36Sopenharmony_ci#define T2_REGMAP_SIZE (3 * 1024) 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_cistatic int get_regs_len(struct net_device *dev) 42462306a36Sopenharmony_ci{ 42562306a36Sopenharmony_ci return T2_REGMAP_SIZE; 42662306a36Sopenharmony_ci} 42762306a36Sopenharmony_ci 42862306a36Sopenharmony_cistatic void get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info) 42962306a36Sopenharmony_ci{ 43062306a36Sopenharmony_ci struct adapter *adapter = dev->ml_priv; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci strscpy(info->driver, DRV_NAME, sizeof(info->driver)); 43362306a36Sopenharmony_ci strscpy(info->bus_info, pci_name(adapter->pdev), 43462306a36Sopenharmony_ci sizeof(info->bus_info)); 43562306a36Sopenharmony_ci} 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_cistatic int get_sset_count(struct net_device *dev, int sset) 43862306a36Sopenharmony_ci{ 43962306a36Sopenharmony_ci switch (sset) { 44062306a36Sopenharmony_ci case ETH_SS_STATS: 44162306a36Sopenharmony_ci return ARRAY_SIZE(stats_strings); 44262306a36Sopenharmony_ci default: 44362306a36Sopenharmony_ci return -EOPNOTSUPP; 44462306a36Sopenharmony_ci } 44562306a36Sopenharmony_ci} 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_cistatic void get_strings(struct net_device *dev, u32 stringset, u8 *data) 44862306a36Sopenharmony_ci{ 44962306a36Sopenharmony_ci if (stringset == ETH_SS_STATS) 45062306a36Sopenharmony_ci memcpy(data, stats_strings, sizeof(stats_strings)); 45162306a36Sopenharmony_ci} 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_cistatic void get_stats(struct net_device *dev, struct ethtool_stats *stats, 45462306a36Sopenharmony_ci u64 *data) 45562306a36Sopenharmony_ci{ 45662306a36Sopenharmony_ci struct adapter *adapter = dev->ml_priv; 45762306a36Sopenharmony_ci struct cmac *mac = adapter->port[dev->if_port].mac; 45862306a36Sopenharmony_ci const struct cmac_statistics *s; 45962306a36Sopenharmony_ci const struct sge_intr_counts *t; 46062306a36Sopenharmony_ci struct sge_port_stats ss; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci s = mac->ops->statistics_update(mac, MAC_STATS_UPDATE_FULL); 46362306a36Sopenharmony_ci t = t1_sge_get_intr_counts(adapter->sge); 46462306a36Sopenharmony_ci t1_sge_get_port_stats(adapter->sge, dev->if_port, &ss); 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci *data++ = s->TxOctetsOK; 46762306a36Sopenharmony_ci *data++ = s->TxOctetsBad; 46862306a36Sopenharmony_ci *data++ = s->TxUnicastFramesOK; 46962306a36Sopenharmony_ci *data++ = s->TxMulticastFramesOK; 47062306a36Sopenharmony_ci *data++ = s->TxBroadcastFramesOK; 47162306a36Sopenharmony_ci *data++ = s->TxPauseFrames; 47262306a36Sopenharmony_ci *data++ = s->TxFramesWithDeferredXmissions; 47362306a36Sopenharmony_ci *data++ = s->TxLateCollisions; 47462306a36Sopenharmony_ci *data++ = s->TxTotalCollisions; 47562306a36Sopenharmony_ci *data++ = s->TxFramesAbortedDueToXSCollisions; 47662306a36Sopenharmony_ci *data++ = s->TxUnderrun; 47762306a36Sopenharmony_ci *data++ = s->TxLengthErrors; 47862306a36Sopenharmony_ci *data++ = s->TxInternalMACXmitError; 47962306a36Sopenharmony_ci *data++ = s->TxFramesWithExcessiveDeferral; 48062306a36Sopenharmony_ci *data++ = s->TxFCSErrors; 48162306a36Sopenharmony_ci *data++ = s->TxJumboFramesOK; 48262306a36Sopenharmony_ci *data++ = s->TxJumboOctetsOK; 48362306a36Sopenharmony_ci 48462306a36Sopenharmony_ci *data++ = s->RxOctetsOK; 48562306a36Sopenharmony_ci *data++ = s->RxOctetsBad; 48662306a36Sopenharmony_ci *data++ = s->RxUnicastFramesOK; 48762306a36Sopenharmony_ci *data++ = s->RxMulticastFramesOK; 48862306a36Sopenharmony_ci *data++ = s->RxBroadcastFramesOK; 48962306a36Sopenharmony_ci *data++ = s->RxPauseFrames; 49062306a36Sopenharmony_ci *data++ = s->RxFCSErrors; 49162306a36Sopenharmony_ci *data++ = s->RxAlignErrors; 49262306a36Sopenharmony_ci *data++ = s->RxSymbolErrors; 49362306a36Sopenharmony_ci *data++ = s->RxDataErrors; 49462306a36Sopenharmony_ci *data++ = s->RxSequenceErrors; 49562306a36Sopenharmony_ci *data++ = s->RxRuntErrors; 49662306a36Sopenharmony_ci *data++ = s->RxJabberErrors; 49762306a36Sopenharmony_ci *data++ = s->RxInternalMACRcvError; 49862306a36Sopenharmony_ci *data++ = s->RxInRangeLengthErrors; 49962306a36Sopenharmony_ci *data++ = s->RxOutOfRangeLengthField; 50062306a36Sopenharmony_ci *data++ = s->RxFrameTooLongErrors; 50162306a36Sopenharmony_ci *data++ = s->RxJumboFramesOK; 50262306a36Sopenharmony_ci *data++ = s->RxJumboOctetsOK; 50362306a36Sopenharmony_ci 50462306a36Sopenharmony_ci *data++ = ss.rx_cso_good; 50562306a36Sopenharmony_ci *data++ = ss.tx_cso; 50662306a36Sopenharmony_ci *data++ = ss.tx_tso; 50762306a36Sopenharmony_ci *data++ = ss.vlan_xtract; 50862306a36Sopenharmony_ci *data++ = ss.vlan_insert; 50962306a36Sopenharmony_ci *data++ = ss.tx_need_hdrroom; 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci *data++ = t->rx_drops; 51262306a36Sopenharmony_ci *data++ = t->pure_rsps; 51362306a36Sopenharmony_ci *data++ = t->unhandled_irqs; 51462306a36Sopenharmony_ci *data++ = t->respQ_empty; 51562306a36Sopenharmony_ci *data++ = t->respQ_overflow; 51662306a36Sopenharmony_ci *data++ = t->freelistQ_empty; 51762306a36Sopenharmony_ci *data++ = t->pkt_too_big; 51862306a36Sopenharmony_ci *data++ = t->pkt_mismatch; 51962306a36Sopenharmony_ci *data++ = t->cmdQ_full[0]; 52062306a36Sopenharmony_ci *data++ = t->cmdQ_full[1]; 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci if (adapter->espi) { 52362306a36Sopenharmony_ci const struct espi_intr_counts *e; 52462306a36Sopenharmony_ci 52562306a36Sopenharmony_ci e = t1_espi_get_intr_counts(adapter->espi); 52662306a36Sopenharmony_ci *data++ = e->DIP2_parity_err; 52762306a36Sopenharmony_ci *data++ = e->DIP4_err; 52862306a36Sopenharmony_ci *data++ = e->rx_drops; 52962306a36Sopenharmony_ci *data++ = e->tx_drops; 53062306a36Sopenharmony_ci *data++ = e->rx_ovflw; 53162306a36Sopenharmony_ci *data++ = e->parity_err; 53262306a36Sopenharmony_ci } 53362306a36Sopenharmony_ci} 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_cistatic inline void reg_block_dump(struct adapter *ap, void *buf, 53662306a36Sopenharmony_ci unsigned int start, unsigned int end) 53762306a36Sopenharmony_ci{ 53862306a36Sopenharmony_ci u32 *p = buf + start; 53962306a36Sopenharmony_ci 54062306a36Sopenharmony_ci for ( ; start <= end; start += sizeof(u32)) 54162306a36Sopenharmony_ci *p++ = readl(ap->regs + start); 54262306a36Sopenharmony_ci} 54362306a36Sopenharmony_ci 54462306a36Sopenharmony_cistatic void get_regs(struct net_device *dev, struct ethtool_regs *regs, 54562306a36Sopenharmony_ci void *buf) 54662306a36Sopenharmony_ci{ 54762306a36Sopenharmony_ci struct adapter *ap = dev->ml_priv; 54862306a36Sopenharmony_ci 54962306a36Sopenharmony_ci /* 55062306a36Sopenharmony_ci * Version scheme: bits 0..9: chip version, bits 10..15: chip revision 55162306a36Sopenharmony_ci */ 55262306a36Sopenharmony_ci regs->version = 2; 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci memset(buf, 0, T2_REGMAP_SIZE); 55562306a36Sopenharmony_ci reg_block_dump(ap, buf, 0, A_SG_RESPACCUTIMER); 55662306a36Sopenharmony_ci reg_block_dump(ap, buf, A_MC3_CFG, A_MC4_INT_CAUSE); 55762306a36Sopenharmony_ci reg_block_dump(ap, buf, A_TPI_ADDR, A_TPI_PAR); 55862306a36Sopenharmony_ci reg_block_dump(ap, buf, A_TP_IN_CONFIG, A_TP_TX_DROP_COUNT); 55962306a36Sopenharmony_ci reg_block_dump(ap, buf, A_RAT_ROUTE_CONTROL, A_RAT_INTR_CAUSE); 56062306a36Sopenharmony_ci reg_block_dump(ap, buf, A_CSPI_RX_AE_WM, A_CSPI_INTR_ENABLE); 56162306a36Sopenharmony_ci reg_block_dump(ap, buf, A_ESPI_SCH_TOKEN0, A_ESPI_GOSTAT); 56262306a36Sopenharmony_ci reg_block_dump(ap, buf, A_ULP_ULIMIT, A_ULP_PIO_CTRL); 56362306a36Sopenharmony_ci reg_block_dump(ap, buf, A_PL_ENABLE, A_PL_CAUSE); 56462306a36Sopenharmony_ci reg_block_dump(ap, buf, A_MC5_CONFIG, A_MC5_MASK_WRITE_CMD); 56562306a36Sopenharmony_ci} 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_cistatic int get_link_ksettings(struct net_device *dev, 56862306a36Sopenharmony_ci struct ethtool_link_ksettings *cmd) 56962306a36Sopenharmony_ci{ 57062306a36Sopenharmony_ci struct adapter *adapter = dev->ml_priv; 57162306a36Sopenharmony_ci struct port_info *p = &adapter->port[dev->if_port]; 57262306a36Sopenharmony_ci u32 supported, advertising; 57362306a36Sopenharmony_ci 57462306a36Sopenharmony_ci supported = p->link_config.supported; 57562306a36Sopenharmony_ci advertising = p->link_config.advertising; 57662306a36Sopenharmony_ci 57762306a36Sopenharmony_ci if (netif_carrier_ok(dev)) { 57862306a36Sopenharmony_ci cmd->base.speed = p->link_config.speed; 57962306a36Sopenharmony_ci cmd->base.duplex = p->link_config.duplex; 58062306a36Sopenharmony_ci } else { 58162306a36Sopenharmony_ci cmd->base.speed = SPEED_UNKNOWN; 58262306a36Sopenharmony_ci cmd->base.duplex = DUPLEX_UNKNOWN; 58362306a36Sopenharmony_ci } 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci cmd->base.port = (supported & SUPPORTED_TP) ? PORT_TP : PORT_FIBRE; 58662306a36Sopenharmony_ci cmd->base.phy_address = p->phy->mdio.prtad; 58762306a36Sopenharmony_ci cmd->base.autoneg = p->link_config.autoneg; 58862306a36Sopenharmony_ci 58962306a36Sopenharmony_ci ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.supported, 59062306a36Sopenharmony_ci supported); 59162306a36Sopenharmony_ci ethtool_convert_legacy_u32_to_link_mode(cmd->link_modes.advertising, 59262306a36Sopenharmony_ci advertising); 59362306a36Sopenharmony_ci 59462306a36Sopenharmony_ci return 0; 59562306a36Sopenharmony_ci} 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_cistatic int speed_duplex_to_caps(int speed, int duplex) 59862306a36Sopenharmony_ci{ 59962306a36Sopenharmony_ci int cap = 0; 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci switch (speed) { 60262306a36Sopenharmony_ci case SPEED_10: 60362306a36Sopenharmony_ci if (duplex == DUPLEX_FULL) 60462306a36Sopenharmony_ci cap = SUPPORTED_10baseT_Full; 60562306a36Sopenharmony_ci else 60662306a36Sopenharmony_ci cap = SUPPORTED_10baseT_Half; 60762306a36Sopenharmony_ci break; 60862306a36Sopenharmony_ci case SPEED_100: 60962306a36Sopenharmony_ci if (duplex == DUPLEX_FULL) 61062306a36Sopenharmony_ci cap = SUPPORTED_100baseT_Full; 61162306a36Sopenharmony_ci else 61262306a36Sopenharmony_ci cap = SUPPORTED_100baseT_Half; 61362306a36Sopenharmony_ci break; 61462306a36Sopenharmony_ci case SPEED_1000: 61562306a36Sopenharmony_ci if (duplex == DUPLEX_FULL) 61662306a36Sopenharmony_ci cap = SUPPORTED_1000baseT_Full; 61762306a36Sopenharmony_ci else 61862306a36Sopenharmony_ci cap = SUPPORTED_1000baseT_Half; 61962306a36Sopenharmony_ci break; 62062306a36Sopenharmony_ci case SPEED_10000: 62162306a36Sopenharmony_ci if (duplex == DUPLEX_FULL) 62262306a36Sopenharmony_ci cap = SUPPORTED_10000baseT_Full; 62362306a36Sopenharmony_ci } 62462306a36Sopenharmony_ci return cap; 62562306a36Sopenharmony_ci} 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci#define ADVERTISED_MASK (ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full | \ 62862306a36Sopenharmony_ci ADVERTISED_100baseT_Half | ADVERTISED_100baseT_Full | \ 62962306a36Sopenharmony_ci ADVERTISED_1000baseT_Half | ADVERTISED_1000baseT_Full | \ 63062306a36Sopenharmony_ci ADVERTISED_10000baseT_Full) 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_cistatic int set_link_ksettings(struct net_device *dev, 63362306a36Sopenharmony_ci const struct ethtool_link_ksettings *cmd) 63462306a36Sopenharmony_ci{ 63562306a36Sopenharmony_ci struct adapter *adapter = dev->ml_priv; 63662306a36Sopenharmony_ci struct port_info *p = &adapter->port[dev->if_port]; 63762306a36Sopenharmony_ci struct link_config *lc = &p->link_config; 63862306a36Sopenharmony_ci u32 advertising; 63962306a36Sopenharmony_ci 64062306a36Sopenharmony_ci ethtool_convert_link_mode_to_legacy_u32(&advertising, 64162306a36Sopenharmony_ci cmd->link_modes.advertising); 64262306a36Sopenharmony_ci 64362306a36Sopenharmony_ci if (!(lc->supported & SUPPORTED_Autoneg)) 64462306a36Sopenharmony_ci return -EOPNOTSUPP; /* can't change speed/duplex */ 64562306a36Sopenharmony_ci 64662306a36Sopenharmony_ci if (cmd->base.autoneg == AUTONEG_DISABLE) { 64762306a36Sopenharmony_ci u32 speed = cmd->base.speed; 64862306a36Sopenharmony_ci int cap = speed_duplex_to_caps(speed, cmd->base.duplex); 64962306a36Sopenharmony_ci 65062306a36Sopenharmony_ci if (!(lc->supported & cap) || (speed == SPEED_1000)) 65162306a36Sopenharmony_ci return -EINVAL; 65262306a36Sopenharmony_ci lc->requested_speed = speed; 65362306a36Sopenharmony_ci lc->requested_duplex = cmd->base.duplex; 65462306a36Sopenharmony_ci lc->advertising = 0; 65562306a36Sopenharmony_ci } else { 65662306a36Sopenharmony_ci advertising &= ADVERTISED_MASK; 65762306a36Sopenharmony_ci if (advertising & (advertising - 1)) 65862306a36Sopenharmony_ci advertising = lc->supported; 65962306a36Sopenharmony_ci advertising &= lc->supported; 66062306a36Sopenharmony_ci if (!advertising) 66162306a36Sopenharmony_ci return -EINVAL; 66262306a36Sopenharmony_ci lc->requested_speed = SPEED_INVALID; 66362306a36Sopenharmony_ci lc->requested_duplex = DUPLEX_INVALID; 66462306a36Sopenharmony_ci lc->advertising = advertising | ADVERTISED_Autoneg; 66562306a36Sopenharmony_ci } 66662306a36Sopenharmony_ci lc->autoneg = cmd->base.autoneg; 66762306a36Sopenharmony_ci if (netif_running(dev)) 66862306a36Sopenharmony_ci t1_link_start(p->phy, p->mac, lc); 66962306a36Sopenharmony_ci return 0; 67062306a36Sopenharmony_ci} 67162306a36Sopenharmony_ci 67262306a36Sopenharmony_cistatic void get_pauseparam(struct net_device *dev, 67362306a36Sopenharmony_ci struct ethtool_pauseparam *epause) 67462306a36Sopenharmony_ci{ 67562306a36Sopenharmony_ci struct adapter *adapter = dev->ml_priv; 67662306a36Sopenharmony_ci struct port_info *p = &adapter->port[dev->if_port]; 67762306a36Sopenharmony_ci 67862306a36Sopenharmony_ci epause->autoneg = (p->link_config.requested_fc & PAUSE_AUTONEG) != 0; 67962306a36Sopenharmony_ci epause->rx_pause = (p->link_config.fc & PAUSE_RX) != 0; 68062306a36Sopenharmony_ci epause->tx_pause = (p->link_config.fc & PAUSE_TX) != 0; 68162306a36Sopenharmony_ci} 68262306a36Sopenharmony_ci 68362306a36Sopenharmony_cistatic int set_pauseparam(struct net_device *dev, 68462306a36Sopenharmony_ci struct ethtool_pauseparam *epause) 68562306a36Sopenharmony_ci{ 68662306a36Sopenharmony_ci struct adapter *adapter = dev->ml_priv; 68762306a36Sopenharmony_ci struct port_info *p = &adapter->port[dev->if_port]; 68862306a36Sopenharmony_ci struct link_config *lc = &p->link_config; 68962306a36Sopenharmony_ci 69062306a36Sopenharmony_ci if (epause->autoneg == AUTONEG_DISABLE) 69162306a36Sopenharmony_ci lc->requested_fc = 0; 69262306a36Sopenharmony_ci else if (lc->supported & SUPPORTED_Autoneg) 69362306a36Sopenharmony_ci lc->requested_fc = PAUSE_AUTONEG; 69462306a36Sopenharmony_ci else 69562306a36Sopenharmony_ci return -EINVAL; 69662306a36Sopenharmony_ci 69762306a36Sopenharmony_ci if (epause->rx_pause) 69862306a36Sopenharmony_ci lc->requested_fc |= PAUSE_RX; 69962306a36Sopenharmony_ci if (epause->tx_pause) 70062306a36Sopenharmony_ci lc->requested_fc |= PAUSE_TX; 70162306a36Sopenharmony_ci if (lc->autoneg == AUTONEG_ENABLE) { 70262306a36Sopenharmony_ci if (netif_running(dev)) 70362306a36Sopenharmony_ci t1_link_start(p->phy, p->mac, lc); 70462306a36Sopenharmony_ci } else { 70562306a36Sopenharmony_ci lc->fc = lc->requested_fc & (PAUSE_RX | PAUSE_TX); 70662306a36Sopenharmony_ci if (netif_running(dev)) 70762306a36Sopenharmony_ci p->mac->ops->set_speed_duplex_fc(p->mac, -1, -1, 70862306a36Sopenharmony_ci lc->fc); 70962306a36Sopenharmony_ci } 71062306a36Sopenharmony_ci return 0; 71162306a36Sopenharmony_ci} 71262306a36Sopenharmony_ci 71362306a36Sopenharmony_cistatic void get_sge_param(struct net_device *dev, struct ethtool_ringparam *e, 71462306a36Sopenharmony_ci struct kernel_ethtool_ringparam *kernel_e, 71562306a36Sopenharmony_ci struct netlink_ext_ack *extack) 71662306a36Sopenharmony_ci{ 71762306a36Sopenharmony_ci struct adapter *adapter = dev->ml_priv; 71862306a36Sopenharmony_ci int jumbo_fl = t1_is_T1B(adapter) ? 1 : 0; 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci e->rx_max_pending = MAX_RX_BUFFERS; 72162306a36Sopenharmony_ci e->rx_jumbo_max_pending = MAX_RX_JUMBO_BUFFERS; 72262306a36Sopenharmony_ci e->tx_max_pending = MAX_CMDQ_ENTRIES; 72362306a36Sopenharmony_ci 72462306a36Sopenharmony_ci e->rx_pending = adapter->params.sge.freelQ_size[!jumbo_fl]; 72562306a36Sopenharmony_ci e->rx_jumbo_pending = adapter->params.sge.freelQ_size[jumbo_fl]; 72662306a36Sopenharmony_ci e->tx_pending = adapter->params.sge.cmdQ_size[0]; 72762306a36Sopenharmony_ci} 72862306a36Sopenharmony_ci 72962306a36Sopenharmony_cistatic int set_sge_param(struct net_device *dev, struct ethtool_ringparam *e, 73062306a36Sopenharmony_ci struct kernel_ethtool_ringparam *kernel_e, 73162306a36Sopenharmony_ci struct netlink_ext_ack *extack) 73262306a36Sopenharmony_ci{ 73362306a36Sopenharmony_ci struct adapter *adapter = dev->ml_priv; 73462306a36Sopenharmony_ci int jumbo_fl = t1_is_T1B(adapter) ? 1 : 0; 73562306a36Sopenharmony_ci 73662306a36Sopenharmony_ci if (e->rx_pending > MAX_RX_BUFFERS || e->rx_mini_pending || 73762306a36Sopenharmony_ci e->rx_jumbo_pending > MAX_RX_JUMBO_BUFFERS || 73862306a36Sopenharmony_ci e->tx_pending > MAX_CMDQ_ENTRIES || 73962306a36Sopenharmony_ci e->rx_pending < MIN_FL_ENTRIES || 74062306a36Sopenharmony_ci e->rx_jumbo_pending < MIN_FL_ENTRIES || 74162306a36Sopenharmony_ci e->tx_pending < (adapter->params.nports + 1) * (MAX_SKB_FRAGS + 1)) 74262306a36Sopenharmony_ci return -EINVAL; 74362306a36Sopenharmony_ci 74462306a36Sopenharmony_ci if (adapter->flags & FULL_INIT_DONE) 74562306a36Sopenharmony_ci return -EBUSY; 74662306a36Sopenharmony_ci 74762306a36Sopenharmony_ci adapter->params.sge.freelQ_size[!jumbo_fl] = e->rx_pending; 74862306a36Sopenharmony_ci adapter->params.sge.freelQ_size[jumbo_fl] = e->rx_jumbo_pending; 74962306a36Sopenharmony_ci adapter->params.sge.cmdQ_size[0] = e->tx_pending; 75062306a36Sopenharmony_ci adapter->params.sge.cmdQ_size[1] = e->tx_pending > MAX_CMDQ1_ENTRIES ? 75162306a36Sopenharmony_ci MAX_CMDQ1_ENTRIES : e->tx_pending; 75262306a36Sopenharmony_ci return 0; 75362306a36Sopenharmony_ci} 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_cistatic int set_coalesce(struct net_device *dev, struct ethtool_coalesce *c, 75662306a36Sopenharmony_ci struct kernel_ethtool_coalesce *kernel_coal, 75762306a36Sopenharmony_ci struct netlink_ext_ack *extack) 75862306a36Sopenharmony_ci{ 75962306a36Sopenharmony_ci struct adapter *adapter = dev->ml_priv; 76062306a36Sopenharmony_ci 76162306a36Sopenharmony_ci adapter->params.sge.rx_coalesce_usecs = c->rx_coalesce_usecs; 76262306a36Sopenharmony_ci adapter->params.sge.coalesce_enable = c->use_adaptive_rx_coalesce; 76362306a36Sopenharmony_ci adapter->params.sge.sample_interval_usecs = c->rate_sample_interval; 76462306a36Sopenharmony_ci t1_sge_set_coalesce_params(adapter->sge, &adapter->params.sge); 76562306a36Sopenharmony_ci return 0; 76662306a36Sopenharmony_ci} 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_cistatic int get_coalesce(struct net_device *dev, struct ethtool_coalesce *c, 76962306a36Sopenharmony_ci struct kernel_ethtool_coalesce *kernel_coal, 77062306a36Sopenharmony_ci struct netlink_ext_ack *extack) 77162306a36Sopenharmony_ci{ 77262306a36Sopenharmony_ci struct adapter *adapter = dev->ml_priv; 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci c->rx_coalesce_usecs = adapter->params.sge.rx_coalesce_usecs; 77562306a36Sopenharmony_ci c->rate_sample_interval = adapter->params.sge.sample_interval_usecs; 77662306a36Sopenharmony_ci c->use_adaptive_rx_coalesce = adapter->params.sge.coalesce_enable; 77762306a36Sopenharmony_ci return 0; 77862306a36Sopenharmony_ci} 77962306a36Sopenharmony_ci 78062306a36Sopenharmony_cistatic int get_eeprom_len(struct net_device *dev) 78162306a36Sopenharmony_ci{ 78262306a36Sopenharmony_ci struct adapter *adapter = dev->ml_priv; 78362306a36Sopenharmony_ci 78462306a36Sopenharmony_ci return t1_is_asic(adapter) ? EEPROM_SIZE : 0; 78562306a36Sopenharmony_ci} 78662306a36Sopenharmony_ci 78762306a36Sopenharmony_ci#define EEPROM_MAGIC(ap) \ 78862306a36Sopenharmony_ci (PCI_VENDOR_ID_CHELSIO | ((ap)->params.chip_version << 16)) 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_cistatic int get_eeprom(struct net_device *dev, struct ethtool_eeprom *e, 79162306a36Sopenharmony_ci u8 *data) 79262306a36Sopenharmony_ci{ 79362306a36Sopenharmony_ci int i; 79462306a36Sopenharmony_ci u8 buf[EEPROM_SIZE] __attribute__((aligned(4))); 79562306a36Sopenharmony_ci struct adapter *adapter = dev->ml_priv; 79662306a36Sopenharmony_ci 79762306a36Sopenharmony_ci e->magic = EEPROM_MAGIC(adapter); 79862306a36Sopenharmony_ci for (i = e->offset & ~3; i < e->offset + e->len; i += sizeof(u32)) 79962306a36Sopenharmony_ci t1_seeprom_read(adapter, i, (__le32 *)&buf[i]); 80062306a36Sopenharmony_ci memcpy(data, buf + e->offset, e->len); 80162306a36Sopenharmony_ci return 0; 80262306a36Sopenharmony_ci} 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_cistatic const struct ethtool_ops t1_ethtool_ops = { 80562306a36Sopenharmony_ci .supported_coalesce_params = ETHTOOL_COALESCE_RX_USECS | 80662306a36Sopenharmony_ci ETHTOOL_COALESCE_USE_ADAPTIVE_RX | 80762306a36Sopenharmony_ci ETHTOOL_COALESCE_RATE_SAMPLE_INTERVAL, 80862306a36Sopenharmony_ci .get_drvinfo = get_drvinfo, 80962306a36Sopenharmony_ci .get_msglevel = get_msglevel, 81062306a36Sopenharmony_ci .set_msglevel = set_msglevel, 81162306a36Sopenharmony_ci .get_ringparam = get_sge_param, 81262306a36Sopenharmony_ci .set_ringparam = set_sge_param, 81362306a36Sopenharmony_ci .get_coalesce = get_coalesce, 81462306a36Sopenharmony_ci .set_coalesce = set_coalesce, 81562306a36Sopenharmony_ci .get_eeprom_len = get_eeprom_len, 81662306a36Sopenharmony_ci .get_eeprom = get_eeprom, 81762306a36Sopenharmony_ci .get_pauseparam = get_pauseparam, 81862306a36Sopenharmony_ci .set_pauseparam = set_pauseparam, 81962306a36Sopenharmony_ci .get_link = ethtool_op_get_link, 82062306a36Sopenharmony_ci .get_strings = get_strings, 82162306a36Sopenharmony_ci .get_sset_count = get_sset_count, 82262306a36Sopenharmony_ci .get_ethtool_stats = get_stats, 82362306a36Sopenharmony_ci .get_regs_len = get_regs_len, 82462306a36Sopenharmony_ci .get_regs = get_regs, 82562306a36Sopenharmony_ci .get_link_ksettings = get_link_ksettings, 82662306a36Sopenharmony_ci .set_link_ksettings = set_link_ksettings, 82762306a36Sopenharmony_ci}; 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_cistatic int t1_ioctl(struct net_device *dev, struct ifreq *req, int cmd) 83062306a36Sopenharmony_ci{ 83162306a36Sopenharmony_ci struct adapter *adapter = dev->ml_priv; 83262306a36Sopenharmony_ci struct mdio_if_info *mdio = &adapter->port[dev->if_port].phy->mdio; 83362306a36Sopenharmony_ci 83462306a36Sopenharmony_ci return mdio_mii_ioctl(mdio, if_mii(req), cmd); 83562306a36Sopenharmony_ci} 83662306a36Sopenharmony_ci 83762306a36Sopenharmony_cistatic int t1_change_mtu(struct net_device *dev, int new_mtu) 83862306a36Sopenharmony_ci{ 83962306a36Sopenharmony_ci int ret; 84062306a36Sopenharmony_ci struct adapter *adapter = dev->ml_priv; 84162306a36Sopenharmony_ci struct cmac *mac = adapter->port[dev->if_port].mac; 84262306a36Sopenharmony_ci 84362306a36Sopenharmony_ci if (!mac->ops->set_mtu) 84462306a36Sopenharmony_ci return -EOPNOTSUPP; 84562306a36Sopenharmony_ci if ((ret = mac->ops->set_mtu(mac, new_mtu))) 84662306a36Sopenharmony_ci return ret; 84762306a36Sopenharmony_ci dev->mtu = new_mtu; 84862306a36Sopenharmony_ci return 0; 84962306a36Sopenharmony_ci} 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_cistatic int t1_set_mac_addr(struct net_device *dev, void *p) 85262306a36Sopenharmony_ci{ 85362306a36Sopenharmony_ci struct adapter *adapter = dev->ml_priv; 85462306a36Sopenharmony_ci struct cmac *mac = adapter->port[dev->if_port].mac; 85562306a36Sopenharmony_ci struct sockaddr *addr = p; 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci if (!mac->ops->macaddress_set) 85862306a36Sopenharmony_ci return -EOPNOTSUPP; 85962306a36Sopenharmony_ci 86062306a36Sopenharmony_ci eth_hw_addr_set(dev, addr->sa_data); 86162306a36Sopenharmony_ci mac->ops->macaddress_set(mac, dev->dev_addr); 86262306a36Sopenharmony_ci return 0; 86362306a36Sopenharmony_ci} 86462306a36Sopenharmony_ci 86562306a36Sopenharmony_cistatic netdev_features_t t1_fix_features(struct net_device *dev, 86662306a36Sopenharmony_ci netdev_features_t features) 86762306a36Sopenharmony_ci{ 86862306a36Sopenharmony_ci /* 86962306a36Sopenharmony_ci * Since there is no support for separate rx/tx vlan accel 87062306a36Sopenharmony_ci * enable/disable make sure tx flag is always in same state as rx. 87162306a36Sopenharmony_ci */ 87262306a36Sopenharmony_ci if (features & NETIF_F_HW_VLAN_CTAG_RX) 87362306a36Sopenharmony_ci features |= NETIF_F_HW_VLAN_CTAG_TX; 87462306a36Sopenharmony_ci else 87562306a36Sopenharmony_ci features &= ~NETIF_F_HW_VLAN_CTAG_TX; 87662306a36Sopenharmony_ci 87762306a36Sopenharmony_ci return features; 87862306a36Sopenharmony_ci} 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_cistatic int t1_set_features(struct net_device *dev, netdev_features_t features) 88162306a36Sopenharmony_ci{ 88262306a36Sopenharmony_ci netdev_features_t changed = dev->features ^ features; 88362306a36Sopenharmony_ci struct adapter *adapter = dev->ml_priv; 88462306a36Sopenharmony_ci 88562306a36Sopenharmony_ci if (changed & NETIF_F_HW_VLAN_CTAG_RX) 88662306a36Sopenharmony_ci t1_vlan_mode(adapter, features); 88762306a36Sopenharmony_ci 88862306a36Sopenharmony_ci return 0; 88962306a36Sopenharmony_ci} 89062306a36Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER 89162306a36Sopenharmony_cistatic void t1_netpoll(struct net_device *dev) 89262306a36Sopenharmony_ci{ 89362306a36Sopenharmony_ci unsigned long flags; 89462306a36Sopenharmony_ci struct adapter *adapter = dev->ml_priv; 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ci local_irq_save(flags); 89762306a36Sopenharmony_ci t1_interrupt(adapter->pdev->irq, adapter); 89862306a36Sopenharmony_ci local_irq_restore(flags); 89962306a36Sopenharmony_ci} 90062306a36Sopenharmony_ci#endif 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_ci/* 90362306a36Sopenharmony_ci * Periodic accumulation of MAC statistics. This is used only if the MAC 90462306a36Sopenharmony_ci * does not have any other way to prevent stats counter overflow. 90562306a36Sopenharmony_ci */ 90662306a36Sopenharmony_cistatic void mac_stats_task(struct work_struct *work) 90762306a36Sopenharmony_ci{ 90862306a36Sopenharmony_ci int i; 90962306a36Sopenharmony_ci struct adapter *adapter = 91062306a36Sopenharmony_ci container_of(work, struct adapter, stats_update_task.work); 91162306a36Sopenharmony_ci 91262306a36Sopenharmony_ci for_each_port(adapter, i) { 91362306a36Sopenharmony_ci struct port_info *p = &adapter->port[i]; 91462306a36Sopenharmony_ci 91562306a36Sopenharmony_ci if (netif_running(p->dev)) 91662306a36Sopenharmony_ci p->mac->ops->statistics_update(p->mac, 91762306a36Sopenharmony_ci MAC_STATS_UPDATE_FAST); 91862306a36Sopenharmony_ci } 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci /* Schedule the next statistics update if any port is active. */ 92162306a36Sopenharmony_ci spin_lock(&adapter->work_lock); 92262306a36Sopenharmony_ci if (adapter->open_device_map & PORT_MASK) 92362306a36Sopenharmony_ci schedule_mac_stats_update(adapter, 92462306a36Sopenharmony_ci adapter->params.stats_update_period); 92562306a36Sopenharmony_ci spin_unlock(&adapter->work_lock); 92662306a36Sopenharmony_ci} 92762306a36Sopenharmony_ci 92862306a36Sopenharmony_cistatic const struct net_device_ops cxgb_netdev_ops = { 92962306a36Sopenharmony_ci .ndo_open = cxgb_open, 93062306a36Sopenharmony_ci .ndo_stop = cxgb_close, 93162306a36Sopenharmony_ci .ndo_start_xmit = t1_start_xmit, 93262306a36Sopenharmony_ci .ndo_get_stats = t1_get_stats, 93362306a36Sopenharmony_ci .ndo_validate_addr = eth_validate_addr, 93462306a36Sopenharmony_ci .ndo_set_rx_mode = t1_set_rxmode, 93562306a36Sopenharmony_ci .ndo_eth_ioctl = t1_ioctl, 93662306a36Sopenharmony_ci .ndo_change_mtu = t1_change_mtu, 93762306a36Sopenharmony_ci .ndo_set_mac_address = t1_set_mac_addr, 93862306a36Sopenharmony_ci .ndo_fix_features = t1_fix_features, 93962306a36Sopenharmony_ci .ndo_set_features = t1_set_features, 94062306a36Sopenharmony_ci#ifdef CONFIG_NET_POLL_CONTROLLER 94162306a36Sopenharmony_ci .ndo_poll_controller = t1_netpoll, 94262306a36Sopenharmony_ci#endif 94362306a36Sopenharmony_ci}; 94462306a36Sopenharmony_ci 94562306a36Sopenharmony_cistatic int init_one(struct pci_dev *pdev, const struct pci_device_id *ent) 94662306a36Sopenharmony_ci{ 94762306a36Sopenharmony_ci unsigned long mmio_start, mmio_len; 94862306a36Sopenharmony_ci const struct board_info *bi; 94962306a36Sopenharmony_ci struct adapter *adapter = NULL; 95062306a36Sopenharmony_ci struct port_info *pi; 95162306a36Sopenharmony_ci int i, err; 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_ci err = pci_enable_device(pdev); 95462306a36Sopenharmony_ci if (err) 95562306a36Sopenharmony_ci return err; 95662306a36Sopenharmony_ci 95762306a36Sopenharmony_ci if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)) { 95862306a36Sopenharmony_ci pr_err("%s: cannot find PCI device memory base address\n", 95962306a36Sopenharmony_ci pci_name(pdev)); 96062306a36Sopenharmony_ci err = -ENODEV; 96162306a36Sopenharmony_ci goto out_disable_pdev; 96262306a36Sopenharmony_ci } 96362306a36Sopenharmony_ci 96462306a36Sopenharmony_ci err = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64)); 96562306a36Sopenharmony_ci if (err) { 96662306a36Sopenharmony_ci pr_err("%s: no usable DMA configuration\n", pci_name(pdev)); 96762306a36Sopenharmony_ci goto out_disable_pdev; 96862306a36Sopenharmony_ci } 96962306a36Sopenharmony_ci 97062306a36Sopenharmony_ci err = pci_request_regions(pdev, DRV_NAME); 97162306a36Sopenharmony_ci if (err) { 97262306a36Sopenharmony_ci pr_err("%s: cannot obtain PCI resources\n", pci_name(pdev)); 97362306a36Sopenharmony_ci goto out_disable_pdev; 97462306a36Sopenharmony_ci } 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_ci pci_set_master(pdev); 97762306a36Sopenharmony_ci 97862306a36Sopenharmony_ci mmio_start = pci_resource_start(pdev, 0); 97962306a36Sopenharmony_ci mmio_len = pci_resource_len(pdev, 0); 98062306a36Sopenharmony_ci bi = t1_get_board_info(ent->driver_data); 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci for (i = 0; i < bi->port_number; ++i) { 98362306a36Sopenharmony_ci struct net_device *netdev; 98462306a36Sopenharmony_ci 98562306a36Sopenharmony_ci netdev = alloc_etherdev(adapter ? 0 : sizeof(*adapter)); 98662306a36Sopenharmony_ci if (!netdev) { 98762306a36Sopenharmony_ci err = -ENOMEM; 98862306a36Sopenharmony_ci goto out_free_dev; 98962306a36Sopenharmony_ci } 99062306a36Sopenharmony_ci 99162306a36Sopenharmony_ci SET_NETDEV_DEV(netdev, &pdev->dev); 99262306a36Sopenharmony_ci 99362306a36Sopenharmony_ci if (!adapter) { 99462306a36Sopenharmony_ci adapter = netdev_priv(netdev); 99562306a36Sopenharmony_ci adapter->pdev = pdev; 99662306a36Sopenharmony_ci adapter->port[0].dev = netdev; /* so we don't leak it */ 99762306a36Sopenharmony_ci 99862306a36Sopenharmony_ci adapter->regs = ioremap(mmio_start, mmio_len); 99962306a36Sopenharmony_ci if (!adapter->regs) { 100062306a36Sopenharmony_ci pr_err("%s: cannot map device registers\n", 100162306a36Sopenharmony_ci pci_name(pdev)); 100262306a36Sopenharmony_ci err = -ENOMEM; 100362306a36Sopenharmony_ci goto out_free_dev; 100462306a36Sopenharmony_ci } 100562306a36Sopenharmony_ci 100662306a36Sopenharmony_ci if (t1_get_board_rev(adapter, bi, &adapter->params)) { 100762306a36Sopenharmony_ci err = -ENODEV; /* Can't handle this chip rev */ 100862306a36Sopenharmony_ci goto out_free_dev; 100962306a36Sopenharmony_ci } 101062306a36Sopenharmony_ci 101162306a36Sopenharmony_ci adapter->name = pci_name(pdev); 101262306a36Sopenharmony_ci adapter->msg_enable = dflt_msg_enable; 101362306a36Sopenharmony_ci adapter->mmio_len = mmio_len; 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci spin_lock_init(&adapter->tpi_lock); 101662306a36Sopenharmony_ci spin_lock_init(&adapter->work_lock); 101762306a36Sopenharmony_ci spin_lock_init(&adapter->async_lock); 101862306a36Sopenharmony_ci spin_lock_init(&adapter->mac_lock); 101962306a36Sopenharmony_ci 102062306a36Sopenharmony_ci INIT_DELAYED_WORK(&adapter->stats_update_task, 102162306a36Sopenharmony_ci mac_stats_task); 102262306a36Sopenharmony_ci 102362306a36Sopenharmony_ci pci_set_drvdata(pdev, netdev); 102462306a36Sopenharmony_ci } 102562306a36Sopenharmony_ci 102662306a36Sopenharmony_ci pi = &adapter->port[i]; 102762306a36Sopenharmony_ci pi->dev = netdev; 102862306a36Sopenharmony_ci netif_carrier_off(netdev); 102962306a36Sopenharmony_ci netdev->irq = pdev->irq; 103062306a36Sopenharmony_ci netdev->if_port = i; 103162306a36Sopenharmony_ci netdev->mem_start = mmio_start; 103262306a36Sopenharmony_ci netdev->mem_end = mmio_start + mmio_len - 1; 103362306a36Sopenharmony_ci netdev->ml_priv = adapter; 103462306a36Sopenharmony_ci netdev->hw_features |= NETIF_F_SG | NETIF_F_IP_CSUM | 103562306a36Sopenharmony_ci NETIF_F_RXCSUM; 103662306a36Sopenharmony_ci netdev->features |= NETIF_F_SG | NETIF_F_IP_CSUM | 103762306a36Sopenharmony_ci NETIF_F_RXCSUM | NETIF_F_LLTX | NETIF_F_HIGHDMA; 103862306a36Sopenharmony_ci 103962306a36Sopenharmony_ci if (vlan_tso_capable(adapter)) { 104062306a36Sopenharmony_ci netdev->features |= 104162306a36Sopenharmony_ci NETIF_F_HW_VLAN_CTAG_TX | 104262306a36Sopenharmony_ci NETIF_F_HW_VLAN_CTAG_RX; 104362306a36Sopenharmony_ci netdev->hw_features |= NETIF_F_HW_VLAN_CTAG_RX; 104462306a36Sopenharmony_ci 104562306a36Sopenharmony_ci /* T204: disable TSO */ 104662306a36Sopenharmony_ci if (!(is_T2(adapter)) || bi->port_number != 4) { 104762306a36Sopenharmony_ci netdev->hw_features |= NETIF_F_TSO; 104862306a36Sopenharmony_ci netdev->features |= NETIF_F_TSO; 104962306a36Sopenharmony_ci } 105062306a36Sopenharmony_ci } 105162306a36Sopenharmony_ci 105262306a36Sopenharmony_ci netdev->netdev_ops = &cxgb_netdev_ops; 105362306a36Sopenharmony_ci netdev->hard_header_len += (netdev->hw_features & NETIF_F_TSO) ? 105462306a36Sopenharmony_ci sizeof(struct cpl_tx_pkt_lso) : sizeof(struct cpl_tx_pkt); 105562306a36Sopenharmony_ci 105662306a36Sopenharmony_ci netif_napi_add(netdev, &adapter->napi, t1_poll); 105762306a36Sopenharmony_ci 105862306a36Sopenharmony_ci netdev->ethtool_ops = &t1_ethtool_ops; 105962306a36Sopenharmony_ci 106062306a36Sopenharmony_ci switch (bi->board) { 106162306a36Sopenharmony_ci case CHBT_BOARD_CHT110: 106262306a36Sopenharmony_ci case CHBT_BOARD_N110: 106362306a36Sopenharmony_ci case CHBT_BOARD_N210: 106462306a36Sopenharmony_ci case CHBT_BOARD_CHT210: 106562306a36Sopenharmony_ci netdev->max_mtu = PM3393_MAX_FRAME_SIZE - 106662306a36Sopenharmony_ci (ETH_HLEN + ETH_FCS_LEN); 106762306a36Sopenharmony_ci break; 106862306a36Sopenharmony_ci case CHBT_BOARD_CHN204: 106962306a36Sopenharmony_ci netdev->max_mtu = VSC7326_MAX_MTU; 107062306a36Sopenharmony_ci break; 107162306a36Sopenharmony_ci default: 107262306a36Sopenharmony_ci netdev->max_mtu = ETH_DATA_LEN; 107362306a36Sopenharmony_ci break; 107462306a36Sopenharmony_ci } 107562306a36Sopenharmony_ci } 107662306a36Sopenharmony_ci 107762306a36Sopenharmony_ci if (t1_init_sw_modules(adapter, bi) < 0) { 107862306a36Sopenharmony_ci err = -ENODEV; 107962306a36Sopenharmony_ci goto out_free_dev; 108062306a36Sopenharmony_ci } 108162306a36Sopenharmony_ci 108262306a36Sopenharmony_ci /* 108362306a36Sopenharmony_ci * The card is now ready to go. If any errors occur during device 108462306a36Sopenharmony_ci * registration we do not fail the whole card but rather proceed only 108562306a36Sopenharmony_ci * with the ports we manage to register successfully. However we must 108662306a36Sopenharmony_ci * register at least one net device. 108762306a36Sopenharmony_ci */ 108862306a36Sopenharmony_ci for (i = 0; i < bi->port_number; ++i) { 108962306a36Sopenharmony_ci err = register_netdev(adapter->port[i].dev); 109062306a36Sopenharmony_ci if (err) 109162306a36Sopenharmony_ci pr_warn("%s: cannot register net device %s, skipping\n", 109262306a36Sopenharmony_ci pci_name(pdev), adapter->port[i].dev->name); 109362306a36Sopenharmony_ci else { 109462306a36Sopenharmony_ci /* 109562306a36Sopenharmony_ci * Change the name we use for messages to the name of 109662306a36Sopenharmony_ci * the first successfully registered interface. 109762306a36Sopenharmony_ci */ 109862306a36Sopenharmony_ci if (!adapter->registered_device_map) 109962306a36Sopenharmony_ci adapter->name = adapter->port[i].dev->name; 110062306a36Sopenharmony_ci 110162306a36Sopenharmony_ci __set_bit(i, &adapter->registered_device_map); 110262306a36Sopenharmony_ci } 110362306a36Sopenharmony_ci } 110462306a36Sopenharmony_ci if (!adapter->registered_device_map) { 110562306a36Sopenharmony_ci pr_err("%s: could not register any net devices\n", 110662306a36Sopenharmony_ci pci_name(pdev)); 110762306a36Sopenharmony_ci err = -EINVAL; 110862306a36Sopenharmony_ci goto out_release_adapter_res; 110962306a36Sopenharmony_ci } 111062306a36Sopenharmony_ci 111162306a36Sopenharmony_ci pr_info("%s: %s (rev %d), %s %dMHz/%d-bit\n", 111262306a36Sopenharmony_ci adapter->name, bi->desc, adapter->params.chip_revision, 111362306a36Sopenharmony_ci adapter->params.pci.is_pcix ? "PCIX" : "PCI", 111462306a36Sopenharmony_ci adapter->params.pci.speed, adapter->params.pci.width); 111562306a36Sopenharmony_ci 111662306a36Sopenharmony_ci /* 111762306a36Sopenharmony_ci * Set the T1B ASIC and memory clocks. 111862306a36Sopenharmony_ci */ 111962306a36Sopenharmony_ci if (t1powersave) 112062306a36Sopenharmony_ci adapter->t1powersave = LCLOCK; /* HW default is powersave mode. */ 112162306a36Sopenharmony_ci else 112262306a36Sopenharmony_ci adapter->t1powersave = HCLOCK; 112362306a36Sopenharmony_ci if (t1_is_T1B(adapter)) 112462306a36Sopenharmony_ci t1_clock(adapter, t1powersave); 112562306a36Sopenharmony_ci 112662306a36Sopenharmony_ci return 0; 112762306a36Sopenharmony_ci 112862306a36Sopenharmony_ciout_release_adapter_res: 112962306a36Sopenharmony_ci t1_free_sw_modules(adapter); 113062306a36Sopenharmony_ciout_free_dev: 113162306a36Sopenharmony_ci if (adapter) { 113262306a36Sopenharmony_ci if (adapter->regs) 113362306a36Sopenharmony_ci iounmap(adapter->regs); 113462306a36Sopenharmony_ci for (i = bi->port_number - 1; i >= 0; --i) 113562306a36Sopenharmony_ci if (adapter->port[i].dev) 113662306a36Sopenharmony_ci free_netdev(adapter->port[i].dev); 113762306a36Sopenharmony_ci } 113862306a36Sopenharmony_ci pci_release_regions(pdev); 113962306a36Sopenharmony_ciout_disable_pdev: 114062306a36Sopenharmony_ci pci_disable_device(pdev); 114162306a36Sopenharmony_ci return err; 114262306a36Sopenharmony_ci} 114362306a36Sopenharmony_ci 114462306a36Sopenharmony_cistatic void bit_bang(struct adapter *adapter, int bitdata, int nbits) 114562306a36Sopenharmony_ci{ 114662306a36Sopenharmony_ci int data; 114762306a36Sopenharmony_ci int i; 114862306a36Sopenharmony_ci u32 val; 114962306a36Sopenharmony_ci 115062306a36Sopenharmony_ci enum { 115162306a36Sopenharmony_ci S_CLOCK = 1 << 3, 115262306a36Sopenharmony_ci S_DATA = 1 << 4 115362306a36Sopenharmony_ci }; 115462306a36Sopenharmony_ci 115562306a36Sopenharmony_ci for (i = (nbits - 1); i > -1; i--) { 115662306a36Sopenharmony_ci 115762306a36Sopenharmony_ci udelay(50); 115862306a36Sopenharmony_ci 115962306a36Sopenharmony_ci data = ((bitdata >> i) & 0x1); 116062306a36Sopenharmony_ci __t1_tpi_read(adapter, A_ELMER0_GPO, &val); 116162306a36Sopenharmony_ci 116262306a36Sopenharmony_ci if (data) 116362306a36Sopenharmony_ci val |= S_DATA; 116462306a36Sopenharmony_ci else 116562306a36Sopenharmony_ci val &= ~S_DATA; 116662306a36Sopenharmony_ci 116762306a36Sopenharmony_ci udelay(50); 116862306a36Sopenharmony_ci 116962306a36Sopenharmony_ci /* Set SCLOCK low */ 117062306a36Sopenharmony_ci val &= ~S_CLOCK; 117162306a36Sopenharmony_ci __t1_tpi_write(adapter, A_ELMER0_GPO, val); 117262306a36Sopenharmony_ci 117362306a36Sopenharmony_ci udelay(50); 117462306a36Sopenharmony_ci 117562306a36Sopenharmony_ci /* Write SCLOCK high */ 117662306a36Sopenharmony_ci val |= S_CLOCK; 117762306a36Sopenharmony_ci __t1_tpi_write(adapter, A_ELMER0_GPO, val); 117862306a36Sopenharmony_ci 117962306a36Sopenharmony_ci } 118062306a36Sopenharmony_ci} 118162306a36Sopenharmony_ci 118262306a36Sopenharmony_cistatic int t1_clock(struct adapter *adapter, int mode) 118362306a36Sopenharmony_ci{ 118462306a36Sopenharmony_ci u32 val; 118562306a36Sopenharmony_ci int M_CORE_VAL; 118662306a36Sopenharmony_ci int M_MEM_VAL; 118762306a36Sopenharmony_ci 118862306a36Sopenharmony_ci enum { 118962306a36Sopenharmony_ci M_CORE_BITS = 9, 119062306a36Sopenharmony_ci T_CORE_VAL = 0, 119162306a36Sopenharmony_ci T_CORE_BITS = 2, 119262306a36Sopenharmony_ci N_CORE_VAL = 0, 119362306a36Sopenharmony_ci N_CORE_BITS = 2, 119462306a36Sopenharmony_ci M_MEM_BITS = 9, 119562306a36Sopenharmony_ci T_MEM_VAL = 0, 119662306a36Sopenharmony_ci T_MEM_BITS = 2, 119762306a36Sopenharmony_ci N_MEM_VAL = 0, 119862306a36Sopenharmony_ci N_MEM_BITS = 2, 119962306a36Sopenharmony_ci NP_LOAD = 1 << 17, 120062306a36Sopenharmony_ci S_LOAD_MEM = 1 << 5, 120162306a36Sopenharmony_ci S_LOAD_CORE = 1 << 6, 120262306a36Sopenharmony_ci S_CLOCK = 1 << 3 120362306a36Sopenharmony_ci }; 120462306a36Sopenharmony_ci 120562306a36Sopenharmony_ci if (!t1_is_T1B(adapter)) 120662306a36Sopenharmony_ci return -ENODEV; /* Can't re-clock this chip. */ 120762306a36Sopenharmony_ci 120862306a36Sopenharmony_ci if (mode & 2) 120962306a36Sopenharmony_ci return 0; /* show current mode. */ 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_ci if ((adapter->t1powersave & 1) == (mode & 1)) 121262306a36Sopenharmony_ci return -EALREADY; /* ASIC already running in mode. */ 121362306a36Sopenharmony_ci 121462306a36Sopenharmony_ci if ((mode & 1) == HCLOCK) { 121562306a36Sopenharmony_ci M_CORE_VAL = 0x14; 121662306a36Sopenharmony_ci M_MEM_VAL = 0x18; 121762306a36Sopenharmony_ci adapter->t1powersave = HCLOCK; /* overclock */ 121862306a36Sopenharmony_ci } else { 121962306a36Sopenharmony_ci M_CORE_VAL = 0xe; 122062306a36Sopenharmony_ci M_MEM_VAL = 0x10; 122162306a36Sopenharmony_ci adapter->t1powersave = LCLOCK; /* underclock */ 122262306a36Sopenharmony_ci } 122362306a36Sopenharmony_ci 122462306a36Sopenharmony_ci /* Don't interrupt this serial stream! */ 122562306a36Sopenharmony_ci spin_lock(&adapter->tpi_lock); 122662306a36Sopenharmony_ci 122762306a36Sopenharmony_ci /* Initialize for ASIC core */ 122862306a36Sopenharmony_ci __t1_tpi_read(adapter, A_ELMER0_GPO, &val); 122962306a36Sopenharmony_ci val |= NP_LOAD; 123062306a36Sopenharmony_ci udelay(50); 123162306a36Sopenharmony_ci __t1_tpi_write(adapter, A_ELMER0_GPO, val); 123262306a36Sopenharmony_ci udelay(50); 123362306a36Sopenharmony_ci __t1_tpi_read(adapter, A_ELMER0_GPO, &val); 123462306a36Sopenharmony_ci val &= ~S_LOAD_CORE; 123562306a36Sopenharmony_ci val &= ~S_CLOCK; 123662306a36Sopenharmony_ci __t1_tpi_write(adapter, A_ELMER0_GPO, val); 123762306a36Sopenharmony_ci udelay(50); 123862306a36Sopenharmony_ci 123962306a36Sopenharmony_ci /* Serial program the ASIC clock synthesizer */ 124062306a36Sopenharmony_ci bit_bang(adapter, T_CORE_VAL, T_CORE_BITS); 124162306a36Sopenharmony_ci bit_bang(adapter, N_CORE_VAL, N_CORE_BITS); 124262306a36Sopenharmony_ci bit_bang(adapter, M_CORE_VAL, M_CORE_BITS); 124362306a36Sopenharmony_ci udelay(50); 124462306a36Sopenharmony_ci 124562306a36Sopenharmony_ci /* Finish ASIC core */ 124662306a36Sopenharmony_ci __t1_tpi_read(adapter, A_ELMER0_GPO, &val); 124762306a36Sopenharmony_ci val |= S_LOAD_CORE; 124862306a36Sopenharmony_ci udelay(50); 124962306a36Sopenharmony_ci __t1_tpi_write(adapter, A_ELMER0_GPO, val); 125062306a36Sopenharmony_ci udelay(50); 125162306a36Sopenharmony_ci __t1_tpi_read(adapter, A_ELMER0_GPO, &val); 125262306a36Sopenharmony_ci val &= ~S_LOAD_CORE; 125362306a36Sopenharmony_ci udelay(50); 125462306a36Sopenharmony_ci __t1_tpi_write(adapter, A_ELMER0_GPO, val); 125562306a36Sopenharmony_ci udelay(50); 125662306a36Sopenharmony_ci 125762306a36Sopenharmony_ci /* Initialize for memory */ 125862306a36Sopenharmony_ci __t1_tpi_read(adapter, A_ELMER0_GPO, &val); 125962306a36Sopenharmony_ci val |= NP_LOAD; 126062306a36Sopenharmony_ci udelay(50); 126162306a36Sopenharmony_ci __t1_tpi_write(adapter, A_ELMER0_GPO, val); 126262306a36Sopenharmony_ci udelay(50); 126362306a36Sopenharmony_ci __t1_tpi_read(adapter, A_ELMER0_GPO, &val); 126462306a36Sopenharmony_ci val &= ~S_LOAD_MEM; 126562306a36Sopenharmony_ci val &= ~S_CLOCK; 126662306a36Sopenharmony_ci udelay(50); 126762306a36Sopenharmony_ci __t1_tpi_write(adapter, A_ELMER0_GPO, val); 126862306a36Sopenharmony_ci udelay(50); 126962306a36Sopenharmony_ci 127062306a36Sopenharmony_ci /* Serial program the memory clock synthesizer */ 127162306a36Sopenharmony_ci bit_bang(adapter, T_MEM_VAL, T_MEM_BITS); 127262306a36Sopenharmony_ci bit_bang(adapter, N_MEM_VAL, N_MEM_BITS); 127362306a36Sopenharmony_ci bit_bang(adapter, M_MEM_VAL, M_MEM_BITS); 127462306a36Sopenharmony_ci udelay(50); 127562306a36Sopenharmony_ci 127662306a36Sopenharmony_ci /* Finish memory */ 127762306a36Sopenharmony_ci __t1_tpi_read(adapter, A_ELMER0_GPO, &val); 127862306a36Sopenharmony_ci val |= S_LOAD_MEM; 127962306a36Sopenharmony_ci udelay(50); 128062306a36Sopenharmony_ci __t1_tpi_write(adapter, A_ELMER0_GPO, val); 128162306a36Sopenharmony_ci udelay(50); 128262306a36Sopenharmony_ci __t1_tpi_read(adapter, A_ELMER0_GPO, &val); 128362306a36Sopenharmony_ci val &= ~S_LOAD_MEM; 128462306a36Sopenharmony_ci udelay(50); 128562306a36Sopenharmony_ci __t1_tpi_write(adapter, A_ELMER0_GPO, val); 128662306a36Sopenharmony_ci 128762306a36Sopenharmony_ci spin_unlock(&adapter->tpi_lock); 128862306a36Sopenharmony_ci 128962306a36Sopenharmony_ci return 0; 129062306a36Sopenharmony_ci} 129162306a36Sopenharmony_ci 129262306a36Sopenharmony_cistatic inline void t1_sw_reset(struct pci_dev *pdev) 129362306a36Sopenharmony_ci{ 129462306a36Sopenharmony_ci pci_write_config_dword(pdev, A_PCICFG_PM_CSR, 3); 129562306a36Sopenharmony_ci pci_write_config_dword(pdev, A_PCICFG_PM_CSR, 0); 129662306a36Sopenharmony_ci} 129762306a36Sopenharmony_ci 129862306a36Sopenharmony_cistatic void remove_one(struct pci_dev *pdev) 129962306a36Sopenharmony_ci{ 130062306a36Sopenharmony_ci struct net_device *dev = pci_get_drvdata(pdev); 130162306a36Sopenharmony_ci struct adapter *adapter = dev->ml_priv; 130262306a36Sopenharmony_ci int i; 130362306a36Sopenharmony_ci 130462306a36Sopenharmony_ci for_each_port(adapter, i) { 130562306a36Sopenharmony_ci if (test_bit(i, &adapter->registered_device_map)) 130662306a36Sopenharmony_ci unregister_netdev(adapter->port[i].dev); 130762306a36Sopenharmony_ci } 130862306a36Sopenharmony_ci 130962306a36Sopenharmony_ci t1_free_sw_modules(adapter); 131062306a36Sopenharmony_ci iounmap(adapter->regs); 131162306a36Sopenharmony_ci 131262306a36Sopenharmony_ci while (--i >= 0) { 131362306a36Sopenharmony_ci if (adapter->port[i].dev) 131462306a36Sopenharmony_ci free_netdev(adapter->port[i].dev); 131562306a36Sopenharmony_ci } 131662306a36Sopenharmony_ci 131762306a36Sopenharmony_ci pci_release_regions(pdev); 131862306a36Sopenharmony_ci pci_disable_device(pdev); 131962306a36Sopenharmony_ci t1_sw_reset(pdev); 132062306a36Sopenharmony_ci} 132162306a36Sopenharmony_ci 132262306a36Sopenharmony_cistatic struct pci_driver cxgb_pci_driver = { 132362306a36Sopenharmony_ci .name = DRV_NAME, 132462306a36Sopenharmony_ci .id_table = t1_pci_tbl, 132562306a36Sopenharmony_ci .probe = init_one, 132662306a36Sopenharmony_ci .remove = remove_one, 132762306a36Sopenharmony_ci}; 132862306a36Sopenharmony_ci 132962306a36Sopenharmony_cimodule_pci_driver(cxgb_pci_driver); 1330