Commit 8199d3a7 authored by Christoph Lameter's avatar Christoph Lameter Committed by Jeff Garzik

[PATCH] A new 10GB Ethernet Driver by Chelsio Communications

A Linux driver for the Chelsio 10Gb Ethernet Network Controller by Chelsio
(http://www.chelsio.com).  This driver supports the Chelsio N210 NIC and is
backward compatible with the Chelsio N110 model 10Gb NICs.  It supports
AMD64, EM64T and x86 systems.
Signed-off-by: default avatarTina Yang <tinay@chelsio.com>
Signed-off-by: default avatarScott Bardone <sbardone@chelsio.com>
Signed-off-by: default avatarChristoph Lameter <christoph@lameter.com>

Adrian said:

- my3126.c is unused (because t1_my3126_ops isn't used anywhere)
- what are the EXTRA_CFLAGS in drivers/net/chelsio/Makefile for?
- $(cxgb-y) in drivers/net/chelsio/Makefile seems to be unneeded
- completely unused global functions:
  - espi.c: t1_espi_get_intr_counts
  - sge.c: t1_sge_get_intr_counts
- the following functions can be made static:
  - sge.c: t1_espi_workaround
  - sge.c: t1_sge_tx
  - subr.c: __t1_tpi_read
  - subr.c: __t1_tpi_write
  - subr.c: t1_wait_op_done

shemminger said:

The performance recommendations in cxgb.txt are common to all fast devices,
and should be in one file rather than just for this device. I would rather
see ip-sysctl.txt updated or a new file on tuning recommendations started.
Some of them have consequences that aren't documented well.
For example, turning off TCP timestamps risks data corruption from sequence wrap.

A new driver shouldn't need so may #ifdef's unless you want to putit on older
vendor versions of 2.4

Some accessor and wrapper functions like:
        t1_pci_read_config_4
        adapter_name
        t1_malloc
are just annoying noise.

Why have useless dead code like:

/* Interrupt handler */
+static int pm3393_interrupt_handler(struct cmac *cmac)
+{
+       u32 master_intr_status;
+/*
+    1. Read master interrupt register.
+    2. Read BLOCK's interrupt status registers.
+    3. Handle BLOCK interrupts.
+*/

Jeff said:

step 1:  kill all the OS wrappers.

 And do you really need hooks for multiple MACs, when only one MAC is
 really supported?  Typically these hooks are at a higher level anyway --
 struct net_device.

From: Christoph Lameter <christoph@lameter

Driver modified as suggested by Pekka Enberg, Stephen Hemminger and Andrian
Bunk.  Reduces the size of the driver to ~260k.

- clean up tabs
- removed my3126.c
- removed 85% of suni1x10gexp_regs.h
- removed 80% of regs.h
- removed various calls, renamed variables/functions.
- removed system specific and other wrappers (usleep, msleep)
- removed dead code
- dropped redundant casts in osdep.h
- dropped redundant check of kfree
- dropped weird code (MODVERSIONS stuff)
- reduced number of #ifdefs
- use kcalloc now instead of kmalloc
- Add information about known issues with the driver
- Add information about authors
Signed-off-by: default avatarScott Bardone <sbardone@chelsio.com>
Signed-off-by: default avatarChristoph Lameter <christoph@lameter.com>
Signed-off-by: default avatarAndrew Morton <akpm@osdl.org>

diff -puN /dev/null Documentation/networking/cxgb.txt
parent 88d7bd8c
Chelsio N210 10Gb Ethernet Network Controller
Driver Release Notes for Linux
Version 2.1.0
March 8, 2005
CONTENTS
========
INTRODUCTION
FEATURES
PERFORMANCE
DRIVER MESSAGES
KNOWN ISSUES
SUPPORT
INTRODUCTION
============
This document describes the Linux driver for Chelsio 10Gb Ethernet Network
Controller. This driver supports the Chelsio N210 NIC and is backward
compatible with the Chelsio N110 model 10Gb NICs. This driver supports AMD64
and EM64T, and x86 systems.
FEATURES
========
Adaptive Interrupts (adaptive-rx)
---------------------------------
This feature provides an adaptive algorithm that adjusts the interrupt
coalescing parameters, allowing the driver to dynamically adapt the latency
settings to achieve the highest performance during various types of network
load.
The interface used to control this feature is ethtool. Please see the
ethtool manpage for additional usage information.
By default, adaptive-rx is disabled.
To enable adaptive-rx:
ethtool -C <interface> adaptive-rx on
To disable adaptive-rx, use ethtool:
ethtool -C <interface> adaptive-rx off
After disabling adaptive-rx, the timer latency value will be set to 50us.
You may set the timer latency after disabling adaptive-rx:
ethtool -C <interface> rx-usecs <microseconds>
An example to set the timer latency value to 100us on eth0:
ethtool -C eth0 rx-usecs 100
You may also provide a timer latency value while disabling adpative-rx:
ethtool -C <interface> adaptive-rx off rx-usecs <microseconds>
If adaptive-rx is disabled and a timer latency value is specified, the timer
will be set to the specified value until changed by the user or until
adaptive-rx is enabled.
To view the status of the adaptive-rx and timer latency values:
ethtool -c <interface>
TCP Segmentation Offloading (TSO) Support
-----------------------------------------
This feature, also known as "large send", enables a system's protocol stack
to offload portions of outbound TCP processing to a network interface card
thereby reducing system CPU utilization and enhancing performance.
The interface used to control this feature is ethtool version 1.8 or higher.
Please see the ethtool manpage for additional usage information.
By default, TSO is enabled.
To disable TSO:
ethtool -K <interface> tso off
To enable TSO:
ethtool -K <interface> tso on
To view the status of TSO:
ethtool -k <interface>
PERFORMANCE
===========
The following information is provided as an example of how to change system
parameters for "performance tuning" an what value to use. You may or may not
want to change these system parameters, depending on your server/workstation
application. Doing so is not warranted in any way by Chelsio Communications,
and is done at "YOUR OWN RISK". Chelsio will not be held responsible for loss
of data or damage to equipment.
Your distribution may have a different way of doing things, or you may prefer
a different method. These commands are shown only to provide an example of
what to do and are by no means definitive.
Making any of the following system changes will only last until you reboot
your system. You may want to write a script that runs at boot-up which
includes the optimal settings for your system.
Setting PCI Latency Timer:
setpci -d 1425:* 0x0c.l=0x0000F800
Disabling TCP timestamp:
sysctl -w net.ipv4.tcp_timestamps=0
Disabling SACK:
sysctl -w net.ipv4.tcp_sack=0
Setting TCP read buffers (min/default/max):
sysctl -w net.ipv4.tcp_rmem="10000000 10000000 10000000"
Setting TCP write buffers (min/pressure/max):
sysctl -w net.ipv4.tcp_wmem="10000000 10000000 10000000"
Setting TCP buffer space (min/pressure/max):
sysctl -w net.ipv4.tcp_mem="10000000 10000000 10000000"
Setting large number of incoming connection requests (2.6.x only):
sysctl -w net.ipv4.tcp_max_syn_backlog=3000
Setting maximum receive socket buffer size:
sysctl -w net.core.rmem_max=524287
Setting maximum send socket buffer size:
sysctl -w net.core.wmem_max=524287
Setting default receive socket buffer size:
sysctl -w net.core.rmem_default=524287
Setting default send socket buffer size:
sysctl -w net.core.wmem_default=524287
Setting maximum option memory buffers:
sysctl -w net.core.optmem_max=524287
Setting maximum backlog (# of unprocessed packets before kernel drops):
sysctl -w net.core.netdev_max_backlog=300000
Set smp_affinity (on a multiprocessor system) to a single CPU:
echo 00000001 > /proc/irq/<interrupt_number>/smp_affinity
TCP window size for single connections:
The receive buffer (RX_WINDOW) size must be at least as large as the
Bandwidth-Delay Product of the communication link between the sender and
receiver. Due to the variations of RTT, you may want to increase the buffer
size up to 2 times the Bandwidth-Delay Product. Reference page 289 of
"TCP/IP Illustrated, Volume 1, The Protocols" by W. Richard Stevens.
At 10Gb speeds, use the following formula:
RX_WINDOW >= 1.25MBytes * RTT(in milliseconds)
Example for RTT with 100us: RX_WINDOW = (1,250,000 * 0.1) = 125,000
RX_WINDOW sizes of 256KB - 512KB should be sufficient.
Setting the min, max, and default receive buffer (RX_WINDOW) size:
sysctl -w net.ipv4.tcp_rmem="<min> <default> <max>"
TCP window size for multiple connections:
The receive buffer (RX_WINDOW) size may be calculated the same as single
connections, but should be divided by the number of connections. The
smaller window prevents congestion and facilitates better pacing,
especially if/when MAC level flow control does not work well or when it is
not supported on the machine. Experimentation may be necessary to attain
the correct value. This method is provided as a starting point fot the
correct receive buffer size.
Setting the min, max, and default receive buffer (RX_WINDOW) size is
performed in the same manner as single connection.
DRIVER MESSAGES
===============
The following messages are the most common messages logged by syslog. These
may be found in /var/log/messages.
Driver up:
Chelsio Network Driver - version 2.1.0
NIC detected:
eth#: Chelsio N210 1x10GBaseX NIC (rev #), PCIX 133MHz/64-bit
Link up:
eth#: link is up at 10 Gbps, full duplex
Link down:
eth#: link is down
KNOWN ISSUES
============
These issues have been identified during testing. The following information
is provided as a workaround to the problem. In some cases, this problem is
inherent to Linux or to a particular Linux Distribution and/or hardware
platform.
1. Large number of TCP retransmits on a multiprocessor (SMP) system.
On a system with multiple CPUs, the interrupt (IRQ) for the network
controller may be bound to more than one CPU. This will cause TCP
retransmits if the packet data were to be split across different CPUs
and re-assembled in a different order than expected.
To eliminate the TCP retransmits, set smp_affinity on the particular
interrupt to a single CPU. You can locate the interrupt (IRQ) used on
the N110/N210 by using ifconfig:
ifconfig <dev_name> | grep Interrupt
Set the smp_affinity to a single CPU:
echo 1 > /proc/irq/<interrupt_number>/smp_affinity
It is highly suggested that you do not run the irqbalance daemon on your
system, as this will change any smp_affinity setting you have applied.
The irqbalance daemon runs on a 10 second interval and binds interrupts
to the least loaded CPU determined by the daemon. To disable this daemon:
chkconfig --level 2345 irqbalance off
By default, some Linux distributions enable the kernel feature,
irqbalance, which performs the same function as the daemon. To disable
this feature, add the following line to your bootloader:
noirqbalance
Example using the Grub bootloader:
title Red Hat Enterprise Linux AS (2.4.21-27.ELsmp)
root (hd0,0)
kernel /vmlinuz-2.4.21-27.ELsmp ro root=/dev/hda3 noirqbalance
initrd /initrd-2.4.21-27.ELsmp.img
2. After running insmod, the driver is loaded and the incorrect network
interface is brought up without running ifup.
When using 2.4.x kernels, including RHEL kernels, the Linux kernel
invokes a script named "hotplug". This script is primarily used to
automatically bring up USB devices when they are plugged in, however,
the script also attempts to automatically bring up a network interface
after loading the kernel module. The hotplug script does this by scanning
the ifcfg-eth# config files in /etc/sysconfig/network-scripts, looking
for HWADDR=<mac_address>.
If the hotplug script does not find the HWADDRR within any of the
ifcfg-eth# files, it will bring up the device with the next available
interface name. If this interface is already configured for a different
network card, your new interface will have incorrect IP address and
network settings.
To solve this issue, you can add the HWADDR=<mac_address> key to the
interface config file of your network controller.
To disable this "hotplug" feature, you may add the driver (module name)
to the "blacklist" file located in /etc/hotplug. It has been noted that
this does not work for network devices because the net.agent script
does not use the blacklist file. Simply remove, or rename, the net.agent
script located in /etc/hotplug to disable this feature.
3. Transport Protocol (TP) hangs when running heavy multi-connection traffic
on an AMD Opteron system with HyperTransport PCI-X Tunnel chipset.
If your AMD Opteron system uses the AMD-8131 HyperTransport PCI-X Tunnel
chipset, you may experience the "133-Mhz Mode Split Completion Data
Corruption" bug identified by AMD while using a 133Mhz PCI-X card on the
bus PCI-X bus.
AMD states, "Under highly specific conditions, the AMD-8131 PCI-X Tunnel
can provide stale data via split completion cycles to a PCI-X card that
is operating at 133 Mhz", causing data corruption.
AMD's provides three workarounds for this problem, however, Chelsio
recommends the first option for best performance with this bug:
For 133Mhz secondary bus operation, limit the transaction length and
the number of outstanding transactions, via BIOS configuration
programming of the PCI-X card, to the following:
Data Length (bytes): 2k
Total allowed outstanding transactions: 1
Please refer to AMD 8131-HT/PCI-X Errata 26310 Rev 3.08 August 2004,
section 56, "133-MHz Mode Split Completion Data Corruption" for more
details with this bug and workarounds suggested by AMD.
SUPPORT
=======
If you have problems with the software or hardware, please contact our
customer support team via email at support@chelsio.com or check our website
at http://www.chelsio.com
===============================================================================
Chelsio Communications
370 San Aleso Ave.
Suite 100
Sunnyvale, CA 94085
http://www.chelsio.com
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License, version 2, as
published by the Free Software Foundation.
You should have received a copy of the GNU General Public License along
with this program; if not, write to the Free Software Foundation, Inc.,
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
Copyright (c) 2003-2005 Chelsio Communications. All rights reserved.
===============================================================================
......@@ -2080,6 +2080,25 @@ endmenu
menu "Ethernet (10000 Mbit)"
depends on NETDEVICES && !UML
config CHELSIO_T1
tristate "Chelsio 10Gb Ethernet support"
depends on PCI
help
This driver supports Chelsio N110 and N210 models 10Gb Ethernet
cards. More information about adapter features and performance
tuning is in <file:Documentation/networking/cxgb.txt>.
For general information about Chelsio and our products, visit
our website at <http://www.chelsio.com>.
For customer support, please visit our customer support page at
<http://www.chelsio.com/support.htm>.
Please send feedback to <linux-bugs@chelsio.com>.
To compile this driver as a module, choose M here: the module
will be called cxgb.
config IXGB
tristate "Intel(R) PRO/10GbE support"
depends on PCI
......
......@@ -9,6 +9,7 @@ endif
obj-$(CONFIG_E1000) += e1000/
obj-$(CONFIG_IBM_EMAC) += ibm_emac/
obj-$(CONFIG_IXGB) += ixgb/
obj-$(CONFIG_CHELSIO_T1) += chelsio/
obj-$(CONFIG_BONDING) += bonding/
obj-$(CONFIG_GIANFAR) += gianfar_driver.o
......
#
# Chelsio 10Gb NIC driver for Linux.
#
obj-$(CONFIG_CHELSIO_T1) += cxgb.o
EXTRA_CFLAGS += -I$(TOPDIR)/drivers/net/chelsio $(DEBUG_FLAGS)
cxgb-objs := cxgb2.o espi.o tp.o pm3393.o sge.o subr.o mv88x201x.o
/*****************************************************************************
* *
* File: ch_ethtool.h *
* $Revision: 1.5 $ *
* $Date: 2005/03/23 07:15:58 $ *
* Description: *
* part of the Chelsio 10Gb Ethernet Driver. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License, version 2, as *
* published by the Free Software Foundation. *
* *
* You should have received a copy of the GNU General Public License along *
* with this program; if not, write to the Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
* *
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED *
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF *
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. *
* *
* http://www.chelsio.com *
* *
* Copyright (c) 2003 - 2005 Chelsio Communications, Inc. *
* All rights reserved. *
* *
* Maintainers: maintainers@chelsio.com *
* *
* Authors: Dimitrios Michailidis <dm@chelsio.com> *
* Tina Yang <tainay@chelsio.com> *
* Felix Marti <felix@chelsio.com> *
* Scott Bardone <sbardone@chelsio.com> *
* Kurt Ottaway <kottaway@chelsio.com> *
* Frank DiMambro <frank@chelsio.com> *
* *
* History: *
* *
****************************************************************************/
#ifndef __CHETHTOOL_LINUX_H__
#define __CHETHTOOL_LINUX_H__
/* TCB size in 32-bit words */
#define TCB_WORDS (TCB_SIZE / 4)
enum {
ETHTOOL_SETREG,
ETHTOOL_GETREG,
ETHTOOL_SETTPI,
ETHTOOL_GETTPI,
ETHTOOL_DEVUP,
ETHTOOL_GETMTUTAB,
ETHTOOL_SETMTUTAB,
ETHTOOL_GETMTU,
ETHTOOL_SET_PM,
ETHTOOL_GET_PM,
ETHTOOL_GET_TCAM,
ETHTOOL_SET_TCAM,
ETHTOOL_GET_TCB,
ETHTOOL_READ_TCAM_WORD,
};
struct ethtool_reg {
uint32_t cmd;
uint32_t addr;
uint32_t val;
};
struct ethtool_mtus {
uint32_t cmd;
uint16_t mtus[NMTUS];
};
struct ethtool_pm {
uint32_t cmd;
uint32_t tx_pg_sz;
uint32_t tx_num_pg;
uint32_t rx_pg_sz;
uint32_t rx_num_pg;
uint32_t pm_total;
};
struct ethtool_tcam {
uint32_t cmd;
uint32_t tcam_size;
uint32_t nservers;
uint32_t nroutes;
};
struct ethtool_tcb {
uint32_t cmd;
uint32_t tcb_index;
uint32_t tcb_data[TCB_WORDS];
};
struct ethtool_tcam_word {
uint32_t cmd;
uint32_t addr;
uint32_t buf[3];
};
#define SIOCCHETHTOOL SIOCDEVPRIVATE
#endif
/*****************************************************************************
* *
* File: common.h *
* $Revision: 1.5 $ *
* $Date: 2005/03/23 07:41:27 $ *
* Description: *
* part of the Chelsio 10Gb Ethernet Driver. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License, version 2, as *
* published by the Free Software Foundation. *
* *
* You should have received a copy of the GNU General Public License along *
* with this program; if not, write to the Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
* *
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED *
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF *
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. *
* *
* http://www.chelsio.com *
* *
* Copyright (c) 2003 - 2005 Chelsio Communications, Inc. *
* All rights reserved. *
* *
* Maintainers: maintainers@chelsio.com *
* *
* Authors: Dimitrios Michailidis <dm@chelsio.com> *
* Tina Yang <tainay@chelsio.com> *
* Felix Marti <felix@chelsio.com> *
* Scott Bardone <sbardone@chelsio.com> *
* Kurt Ottaway <kottaway@chelsio.com> *
* Frank DiMambro <frank@chelsio.com> *
* *
* History: *
* *
****************************************************************************/
#ifndef CHELSIO_COMMON_H
#define CHELSIO_COMMON_H
#define DIMOF(x) (sizeof(x)/sizeof(x[0]))
#define NMTUS 8
#define MAX_NPORTS 4
#define TCB_SIZE 128
enum {
CHBT_BOARD_7500,
CHBT_BOARD_8000,
CHBT_BOARD_CHT101,
CHBT_BOARD_CHT110,
CHBT_BOARD_CHT210,
CHBT_BOARD_CHT204,
CHBT_BOARD_N110,
CHBT_BOARD_N210,
CHBT_BOARD_COUGAR,
CHBT_BOARD_6800,
CHBT_BOARD_SIMUL
};
enum {
CHBT_TERM_FPGA,
CHBT_TERM_T1,
CHBT_TERM_T2,
CHBT_TERM_T3
};
enum {
CHBT_MAC_CHELSIO_A,
CHBT_MAC_IXF1010,
CHBT_MAC_PM3393,
CHBT_MAC_VSC7321,
CHBT_MAC_DUMMY
};
enum {
CHBT_PHY_88E1041,
CHBT_PHY_88E1111,
CHBT_PHY_88X2010,
CHBT_PHY_XPAK,
CHBT_PHY_MY3126,
CHBT_PHY_DUMMY
};
enum {
PAUSE_RX = 1,
PAUSE_TX = 2,
PAUSE_AUTONEG = 4
};
/* Revisions of T1 chip */
#define TERM_T1A 0
#define TERM_T1B 1
#define TERM_T2 3
struct tp_params {
unsigned int pm_size;
unsigned int cm_size;
unsigned int pm_rx_base;
unsigned int pm_tx_base;
unsigned int pm_rx_pg_size;
unsigned int pm_tx_pg_size;
unsigned int pm_rx_num_pgs;
unsigned int pm_tx_num_pgs;
unsigned int use_5tuple_mode;
};
struct sge_params {
unsigned int cmdQ_size[2];
unsigned int freelQ_size[2];
unsigned int large_buf_capacity;
unsigned int rx_coalesce_usecs;
unsigned int last_rx_coalesce_raw;
unsigned int default_rx_coalesce_usecs;
unsigned int sample_interval_usecs;
unsigned int coalesce_enable;
unsigned int polling;
};
struct mc5_params {
unsigned int mode; /* selects MC5 width */
unsigned int nservers; /* size of server region */
unsigned int nroutes; /* size of routing region */
};
/* Default MC5 region sizes */
#define DEFAULT_SERVER_REGION_LEN 256
#define DEFAULT_RT_REGION_LEN 1024
struct pci_params {
unsigned short speed;
unsigned char width;
unsigned char is_pcix;
};
struct adapter_params {
struct sge_params sge;
struct mc5_params mc5;
struct tp_params tp;
struct pci_params pci;
const struct board_info *brd_info;
unsigned short mtus[NMTUS];
unsigned int nports; /* # of ethernet ports */
unsigned int stats_update_period;
unsigned short chip_revision;
unsigned char chip_version;
unsigned char is_asic;
};
struct pci_err_cnt {
unsigned int master_parity_err;
unsigned int sig_target_abort;
unsigned int rcv_target_abort;
unsigned int rcv_master_abort;
unsigned int sig_sys_err;
unsigned int det_parity_err;
unsigned int pio_parity_err;
unsigned int wf_parity_err;
unsigned int rf_parity_err;
unsigned int cf_parity_err;
};
struct link_config {
unsigned int supported; /* link capabilities */
unsigned int advertising; /* advertised capabilities */
unsigned short requested_speed; /* speed user has requested */
unsigned short speed; /* actual link speed */
unsigned char requested_duplex; /* duplex user has requested */
unsigned char duplex; /* actual link duplex */
unsigned char requested_fc; /* flow control user has requested */
unsigned char fc; /* actual link flow control */
unsigned char autoneg; /* autonegotiating? */
};
#define SPEED_INVALID 0xffff
#define DUPLEX_INVALID 0xff
struct mdio_ops;
struct gmac;
struct gphy;
struct board_info {
unsigned char board;
unsigned char port_number;
unsigned long caps;
unsigned char chip_term;
unsigned char chip_mac;
unsigned char chip_phy;
unsigned int clock_core;
unsigned int clock_mc3;
unsigned int clock_mc4;
unsigned int espi_nports;
unsigned int clock_cspi;
unsigned int clock_elmer0;
unsigned char mdio_mdien;
unsigned char mdio_mdiinv;
unsigned char mdio_mdc;
unsigned char mdio_phybaseaddr;
struct gmac *gmac;
struct gphy *gphy;
struct mdio_ops *mdio_ops;
const char *desc;
};
#include "osdep.h"
#ifndef PCI_VENDOR_ID_CHELSIO
#define PCI_VENDOR_ID_CHELSIO 0x1425
#endif
extern struct pci_device_id t1_pci_tbl[];
static inline int t1_is_asic(const adapter_t *adapter)
{
return adapter->params.is_asic;
}
static inline int adapter_matches_type(const adapter_t *adapter,
int version, int revision)
{
return adapter->params.chip_version == version &&
adapter->params.chip_revision == revision;
}
#define t1_is_T1B(adap) adapter_matches_type(adap, CHBT_TERM_T1, TERM_T1B)
#define is_T2(adap) adapter_matches_type(adap, CHBT_TERM_T2, TERM_T2)
/* Returns true if an adapter supports VLAN acceleration and TSO */
static inline int vlan_tso_capable(const adapter_t *adapter)
{
return !t1_is_T1B(adapter);
}
#define for_each_port(adapter, iter) \
for (iter = 0; iter < (adapter)->params.nports; ++iter)
#define board_info(adapter) ((adapter)->params.brd_info)
#define is_10G(adapter) (board_info(adapter)->caps & SUPPORTED_10000baseT_Full)
static inline unsigned int core_ticks_per_usec(const adapter_t *adap)
{
return board_info(adap)->clock_core / 1000000;
}
int t1_tpi_write(adapter_t *adapter, u32 addr, u32 value);
int t1_tpi_read(adapter_t *adapter, u32 addr, u32 *value);
void t1_interrupts_enable(adapter_t *adapter);
void t1_interrupts_disable(adapter_t *adapter);
void t1_interrupts_clear(adapter_t *adapter);
int elmer0_ext_intr_handler(adapter_t *adapter);
int t1_slow_intr_handler(adapter_t *adapter);
int t1_link_start(struct cphy *phy, struct cmac *mac, struct link_config *lc);
const struct board_info *t1_get_board_info(unsigned int board_id);
const struct board_info *t1_get_board_info_from_ids(unsigned int devid,
unsigned short ssid);
int t1_seeprom_read(adapter_t *adapter, u32 addr, u32 *data);
int t1_get_board_rev(adapter_t *adapter, const struct board_info *bi,
struct adapter_params *p);
int t1_init_hw_modules(adapter_t *adapter);
int t1_init_sw_modules(adapter_t *adapter, const struct board_info *bi);
void t1_free_sw_modules(adapter_t *adapter);
void t1_fatal_err(adapter_t *adapter);
#endif
/*****************************************************************************
* *
* File: cphy.h *
* $Revision: 1.4 $ *
* $Date: 2005/03/23 07:41:27 $ *
* Description: *
* part of the Chelsio 10Gb Ethernet Driver. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License, version 2, as *
* published by the Free Software Foundation. *
* *
* You should have received a copy of the GNU General Public License along *
* with this program; if not, write to the Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
* *
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED *
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF *
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. *
* *
* http://www.chelsio.com *
* *
* Copyright (c) 2003 - 2005 Chelsio Communications, Inc. *
* All rights reserved. *
* *
* Maintainers: maintainers@chelsio.com *
* *
* Authors: Dimitrios Michailidis <dm@chelsio.com> *
* Tina Yang <tainay@chelsio.com> *
* Felix Marti <felix@chelsio.com> *
* Scott Bardone <sbardone@chelsio.com> *
* Kurt Ottaway <kottaway@chelsio.com> *
* Frank DiMambro <frank@chelsio.com> *
* *
* History: *
* *
****************************************************************************/
#ifndef CHELSIO_CPHY_H
#define CHELSIO_CPHY_H
#include "common.h"
struct mdio_ops {
void (*init)(adapter_t *adapter, const struct board_info *bi);
int (*read)(adapter_t *adapter, int phy_addr, int mmd_addr,
int reg_addr, unsigned int *val);
int (*write)(adapter_t *adapter, int phy_addr, int mmd_addr,
int reg_addr, unsigned int val);
};
/* PHY interrupt types */
enum {
cphy_cause_link_change = 0x1,
cphy_cause_error = 0x2
};
struct cphy;
/* PHY operations */
struct cphy_ops {
void (*destroy)(struct cphy *);
int (*reset)(struct cphy *, int wait);
int (*interrupt_enable)(struct cphy *);
int (*interrupt_disable)(struct cphy *);
int (*interrupt_clear)(struct cphy *);
int (*interrupt_handler)(struct cphy *);
int (*autoneg_enable)(struct cphy *);
int (*autoneg_disable)(struct cphy *);
int (*autoneg_restart)(struct cphy *);
int (*advertise)(struct cphy *phy, unsigned int advertise_map);
int (*set_loopback)(struct cphy *, int on);
int (*set_speed_duplex)(struct cphy *phy, int speed, int duplex);
int (*get_link_status)(struct cphy *phy, int *link_ok, int *speed,
int *duplex, int *fc);
};
/* A PHY instance */
struct cphy {
int addr; /* PHY address */
adapter_t *adapter; /* associated adapter */
struct cphy_ops *ops; /* PHY operations */
int (*mdio_read)(adapter_t *adapter, int phy_addr, int mmd_addr,
int reg_addr, unsigned int *val);
int (*mdio_write)(adapter_t *adapter, int phy_addr, int mmd_addr,
int reg_addr, unsigned int val);
struct cphy_instance *instance;
};
/* Convenience MDIO read/write wrappers */
static inline int mdio_read(struct cphy *cphy, int mmd, int reg,
unsigned int *valp)
{
return cphy->mdio_read(cphy->adapter, cphy->addr, mmd, reg, valp);
}
static inline int mdio_write(struct cphy *cphy, int mmd, int reg,
unsigned int val)
{
return cphy->mdio_write(cphy->adapter, cphy->addr, mmd, reg, val);
}
static inline int simple_mdio_read(struct cphy *cphy, int reg,
unsigned int *valp)
{
return mdio_read(cphy, 0, reg, valp);
}
static inline int simple_mdio_write(struct cphy *cphy, int reg,
unsigned int val)
{
return mdio_write(cphy, 0, reg, val);
}
/* Convenience initializer */
static inline void cphy_init(struct cphy *phy, adapter_t *adapter,
int phy_addr, struct cphy_ops *phy_ops,
struct mdio_ops *mdio_ops)
{
phy->adapter = adapter;
phy->addr = phy_addr;
phy->ops = phy_ops;
if (mdio_ops) {
phy->mdio_read = mdio_ops->read;
phy->mdio_write = mdio_ops->write;
}
}
/* Operations of the PHY-instance factory */
struct gphy {
/* Construct a PHY instance with the given PHY address */
struct cphy *(*create)(adapter_t *adapter, int phy_addr,
struct mdio_ops *mdio_ops);
/*
* Reset the PHY chip. This resets the whole PHY chip, not individual
* ports.
*/
int (*reset)(adapter_t *adapter);
};
extern struct gphy t1_my3126_ops;
extern struct gphy t1_mv88e1xxx_ops;
extern struct gphy t1_xpak_ops;
extern struct gphy t1_mv88x201x_ops;
extern struct gphy t1_dummy_phy_ops;
#endif
/*****************************************************************************
* *
* File: cpl5_cmd.h *
* $Revision: 1.4 $ *
* $Date: 2005/03/23 07:15:58 $ *
* Description: *
* part of the Chelsio 10Gb Ethernet Driver. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License, version 2, as *
* published by the Free Software Foundation. *
* *
* You should have received a copy of the GNU General Public License along *
* with this program; if not, write to the Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
* *
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED *
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF *
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. *
* *
* http://www.chelsio.com *
* *
* Copyright (c) 2003 - 2005 Chelsio Communications, Inc. *
* All rights reserved. *
* *
* Maintainers: maintainers@chelsio.com *
* *
* Authors: Dimitrios Michailidis <dm@chelsio.com> *
* Tina Yang <tainay@chelsio.com> *
* Felix Marti <felix@chelsio.com> *
* Scott Bardone <sbardone@chelsio.com> *
* Kurt Ottaway <kottaway@chelsio.com> *
* Frank DiMambro <frank@chelsio.com> *
* *
* History: *
* *
****************************************************************************/
#ifndef _CPL5_CMD_H
#define _CPL5_CMD_H
#include <asm/byteorder.h>
#if !defined(__LITTLE_ENDIAN_BITFIELD) && !defined(__BIG_ENDIAN_BITFIELD)
#error "Adjust your <asm/byteorder.h> defines"
#endif
enum CPL_opcode {
CPL_RX_PKT = 0xAD,
CPL_TX_PKT = 0xB2,
CPL_TX_PKT_LSO = 0xB6,
};
enum { /* TX_PKT_LSO ethernet types */
CPL_ETH_II,
CPL_ETH_II_VLAN,
CPL_ETH_802_3,
CPL_ETH_802_3_VLAN
};
struct cpl_rx_data {
__u32 rsvd0;
__u32 len;
__u32 seq;
__u16 urg;
__u8 rsvd1;
__u8 status;
};
/*
* We want this header's alignment to be no more stringent than 2-byte aligned.
* All fields are u8 or u16 except for the length. However that field is not
* used so we break it into 2 16-bit parts to easily meet our alignment needs.
*/
struct cpl_tx_pkt {
__u8 opcode;
#if defined(__LITTLE_ENDIAN_BITFIELD)
__u8 iff:4;
__u8 ip_csum_dis:1;
__u8 l4_csum_dis:1;
__u8 vlan_valid:1;
__u8 rsvd:1;
#else
__u8 rsvd:1;
__u8 vlan_valid:1;
__u8 l4_csum_dis:1;
__u8 ip_csum_dis:1;
__u8 iff:4;
#endif
__u16 vlan;
__u16 len_hi;
__u16 len_lo;
};
struct cpl_tx_pkt_lso {
__u8 opcode;
#if defined(__LITTLE_ENDIAN_BITFIELD)
__u8 iff:4;
__u8 ip_csum_dis:1;
__u8 l4_csum_dis:1;
__u8 vlan_valid:1;
__u8 rsvd:1;
#else
__u8 rsvd:1;
__u8 vlan_valid:1;
__u8 l4_csum_dis:1;
__u8 ip_csum_dis:1;
__u8 iff:4;
#endif
__u16 vlan;
__u32 len;
__u32 rsvd2;
__u8 rsvd3;
#if defined(__LITTLE_ENDIAN_BITFIELD)
__u8 tcp_hdr_words:4;
__u8 ip_hdr_words:4;
#else
__u8 ip_hdr_words:4;
__u8 tcp_hdr_words:4;
#endif
__u16 eth_type_mss;
};
struct cpl_rx_pkt {
__u8 opcode;
#if defined(__LITTLE_ENDIAN_BITFIELD)
__u8 iff:4;
__u8 csum_valid:1;
__u8 bad_pkt:1;
__u8 vlan_valid:1;
__u8 rsvd:1;
#else
__u8 rsvd:1;
__u8 vlan_valid:1;
__u8 bad_pkt:1;
__u8 csum_valid:1;
__u8 iff:4;
#endif
__u16 csum;
__u16 vlan;
__u16 len;
};
#endif
/*****************************************************************************
* *
* File: cxgb2.c *
* $Revision: 1.11 $ *
* $Date: 2005/03/23 07:41:27 $ *
* Description: *
* Chelsio 10Gb Ethernet Driver. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License, version 2, as *
* published by the Free Software Foundation. *
* *
* You should have received a copy of the GNU General Public License along *
* with this program; if not, write to the Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
* *
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED *
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF *
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. *
* *
* http://www.chelsio.com *
* *
* Copyright (c) 2003 - 2005 Chelsio Communications, Inc. *
* All rights reserved. *
* *
* Maintainers: maintainers@chelsio.com *
* *
* Authors: Dimitrios Michailidis <dm@chelsio.com> *
* Tina Yang <tainay@chelsio.com> *
* Felix Marti <felix@chelsio.com> *
* Scott Bardone <sbardone@chelsio.com> *
* Kurt Ottaway <kottaway@chelsio.com> *
* Frank DiMambro <frank@chelsio.com> *
* *
* History: *
* *
****************************************************************************/
#include "common.h"
#include <linux/config.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/pci.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/if_vlan.h>
#include <linux/mii.h>
#include <linux/sockios.h>
#include <linux/proc_fs.h>
#include <linux/version.h>
#include <linux/workqueue.h>
#include <asm/uaccess.h>
#include "ch_ethtool.h"
#include "cpl5_cmd.h"
#include "regs.h"
#include "gmac.h"
#include "cphy.h"
#include "sge.h"
#include "tp.h"
#include "espi.h"
static inline void schedule_mac_stats_update(struct adapter *ap, int secs)
{
schedule_delayed_work(&ap->stats_update_task, secs * HZ);
}
static inline void cancel_mac_stats_update(struct adapter *ap)
{
cancel_delayed_work(&ap->stats_update_task);
}
#if BITS_PER_LONG == 64 && !defined(CONFIG_X86_64)
# define FMT64 "l"
#else
# define FMT64 "ll"
#endif
# define DRV_TYPE ""
# define MODULE_DESC "Chelsio Network Driver"
static char driver_name[] = DRV_NAME;
static char driver_string[] = "Chelsio " DRV_TYPE "Network Driver";
static char driver_version[] = "2.1.0";
#define PCI_DMA_64BIT ~0ULL
#define PCI_DMA_32BIT 0xffffffffULL
#define MAX_CMDQ_ENTRIES 16384
#define MAX_CMDQ1_ENTRIES 1024
#define MAX_RX_BUFFERS 16384
#define MAX_RX_JUMBO_BUFFERS 16384
#define MAX_TX_BUFFERS_HIGH 16384U
#define MAX_TX_BUFFERS_LOW 1536U
#define MIN_FL_ENTRIES 32
#define PORT_MASK ((1 << MAX_NPORTS) - 1)
#define DFLT_MSG_ENABLE (NETIF_MSG_DRV | NETIF_MSG_PROBE | NETIF_MSG_LINK | \
NETIF_MSG_TIMER | NETIF_MSG_IFDOWN | NETIF_MSG_IFUP |\
NETIF_MSG_RX_ERR | NETIF_MSG_TX_ERR)
/*
* The EEPROM is actually bigger but only the first few bytes are used so we
* only report those.
*/
#define EEPROM_SIZE 32
MODULE_DESCRIPTION(MODULE_DESC);
MODULE_AUTHOR("Chelsio Communications");
MODULE_LICENSE("GPL");
MODULE_DEVICE_TABLE(pci, t1_pci_tbl);
static int dflt_msg_enable = DFLT_MSG_ENABLE;
MODULE_PARM(dflt_msg_enable, "i");
MODULE_PARM_DESC(dflt_msg_enable, "Chelsio T1 message enable bitmap");
static const char pci_speed[][4] = {
"33", "66", "100", "133"
};
/*
* Setup MAC to receive the types of packets we want.
*/
static void t1_set_rxmode(struct net_device *dev)
{
struct adapter *adapter = dev->priv;
struct cmac *mac = adapter->port[dev->if_port].mac;
struct t1_rx_mode rm;
rm.dev = dev;
rm.idx = 0;
rm.list = dev->mc_list;
mac->ops->set_rx_mode(mac, &rm);
}
static void link_report(struct port_info *p)
{
if (!netif_carrier_ok(p->dev))
printk(KERN_INFO "%s: link is down\n", p->dev->name);
else {
const char *s = "10 Mbps";
switch (p->link_config.speed) {
case SPEED_10000: s = "10 Gbps"; break;
case SPEED_1000: s = "1000 Mbps"; break;
case SPEED_100: s = "100 Mbps"; break;
}
printk(KERN_INFO "%s: link is up at %s, %s duplex\n",
p->dev->name, s,
p->link_config.duplex == DUPLEX_FULL ? "full" : "half");
}
}
void t1_link_changed(struct adapter *adapter, int port_id, int link_stat,
int speed, int duplex, int pause)
{
struct port_info *p = &adapter->port[port_id];
if (link_stat != netif_carrier_ok(p->dev)) {
if (link_stat)
netif_carrier_on(p->dev);
else
netif_carrier_off(p->dev);
link_report(p);
}
}
static void link_start(struct port_info *p)
{
struct cmac *mac = p->mac;
mac->ops->reset(mac);
if (mac->ops->macaddress_set)
mac->ops->macaddress_set(mac, p->dev->dev_addr);
t1_set_rxmode(p->dev);
t1_link_start(p->phy, mac, &p->link_config);
mac->ops->enable(mac, MAC_DIRECTION_RX | MAC_DIRECTION_TX);
}
static void enable_hw_csum(struct adapter *adapter)
{
if (adapter->flags & TSO_CAPABLE)
t1_tp_set_ip_checksum_offload(adapter->tp, 1); /* for TSO only */
if (adapter->flags & UDP_CSUM_CAPABLE)
t1_tp_set_udp_checksum_offload(adapter->tp, 1);
t1_tp_set_tcp_checksum_offload(adapter->tp, 1);
}
/*
* Things to do upon first use of a card.
* This must run with the rtnl lock held.
*/
static int cxgb_up(struct adapter *adapter)
{
int err = 0;
if (!(adapter->flags & FULL_INIT_DONE)) {
err = t1_init_hw_modules(adapter);
if (err)
goto out_err;
enable_hw_csum(adapter);
adapter->flags |= FULL_INIT_DONE;
}
t1_interrupts_clear(adapter);
if ((err = request_irq(adapter->pdev->irq, &t1_interrupt, SA_SHIRQ,
adapter->name, adapter)))
goto out_err;
t1_sge_start(adapter->sge);
t1_interrupts_enable(adapter);
err = 0;
out_err:
return err;
}
/*
* Release resources when all the ports have been stopped.
*/
static void cxgb_down(struct adapter *adapter)
{
t1_sge_stop(adapter->sge);
t1_interrupts_disable(adapter);
free_irq(adapter->pdev->irq, adapter);
}
static int cxgb_open(struct net_device *dev)
{
int err;
struct adapter *adapter = dev->priv;
int other_ports = adapter->open_device_map & PORT_MASK;
if (!adapter->open_device_map && (err = cxgb_up(adapter)) < 0)
return err;
__set_bit(dev->if_port, &adapter->open_device_map);
link_start(&adapter->port[dev->if_port]);
netif_start_queue(dev);
if (!other_ports && adapter->params.stats_update_period)
schedule_mac_stats_update(adapter,
adapter->params.stats_update_period);
return 0;
}
static int cxgb_close(struct net_device *dev)
{
struct adapter *adapter = dev->priv;
struct port_info *p = &adapter->port[dev->if_port];
struct cmac *mac = p->mac;
netif_stop_queue(dev);
mac->ops->disable(mac, MAC_DIRECTION_TX | MAC_DIRECTION_RX);
netif_carrier_off(dev);
clear_bit(dev->if_port, &adapter->open_device_map);
if (adapter->params.stats_update_period &&
!(adapter->open_device_map & PORT_MASK)) {
/* Stop statistics accumulation. */
smp_mb__after_clear_bit();
spin_lock(&adapter->work_lock); /* sync with update task */
spin_unlock(&adapter->work_lock);
cancel_mac_stats_update(adapter);
}
if (!adapter->open_device_map)
cxgb_down(adapter);
return 0;
}
static struct net_device_stats *t1_get_stats(struct net_device *dev)
{
struct adapter *adapter = dev->priv;
struct port_info *p = &adapter->port[dev->if_port];
struct net_device_stats *ns = &p->netstats;
const struct cmac_statistics *pstats;
/* Do a full update of the MAC stats */
pstats = p->mac->ops->statistics_update(p->mac,
MAC_STATS_UPDATE_FULL);
ns->tx_packets = pstats->TxUnicastFramesOK +
pstats->TxMulticastFramesOK + pstats->TxBroadcastFramesOK;
ns->rx_packets = pstats->RxUnicastFramesOK +
pstats->RxMulticastFramesOK + pstats->RxBroadcastFramesOK;
ns->tx_bytes = pstats->TxOctetsOK;
ns->rx_bytes = pstats->RxOctetsOK;
ns->tx_errors = pstats->TxLateCollisions + pstats->TxLengthErrors +
pstats->TxUnderrun + pstats->TxFramesAbortedDueToXSCollisions;
ns->rx_errors = pstats->RxDataErrors + pstats->RxJabberErrors +
pstats->RxFCSErrors + pstats->RxAlignErrors +
pstats->RxSequenceErrors + pstats->RxFrameTooLongErrors +
pstats->RxSymbolErrors + pstats->RxRuntErrors;
ns->multicast = pstats->RxMulticastFramesOK;
ns->collisions = pstats->TxTotalCollisions;
/* detailed rx_errors */
ns->rx_length_errors = pstats->RxFrameTooLongErrors +
pstats->RxJabberErrors;
ns->rx_over_errors = 0;
ns->rx_crc_errors = pstats->RxFCSErrors;
ns->rx_frame_errors = pstats->RxAlignErrors;
ns->rx_fifo_errors = 0;
ns->rx_missed_errors = 0;
/* detailed tx_errors */
ns->tx_aborted_errors = pstats->TxFramesAbortedDueToXSCollisions;
ns->tx_carrier_errors = 0;
ns->tx_fifo_errors = pstats->TxUnderrun;
ns->tx_heartbeat_errors = 0;
ns->tx_window_errors = pstats->TxLateCollisions;
return ns;
}
static u32 get_msglevel(struct net_device *dev)
{
struct adapter *adapter = dev->priv;
return adapter->msg_enable;
}
static void set_msglevel(struct net_device *dev, u32 val)
{
struct adapter *adapter = dev->priv;
adapter->msg_enable = val;
}
static char stats_strings[][ETH_GSTRING_LEN] = {
"TxOctetsOK",
"TxOctetsBad",
"TxUnicastFramesOK",
"TxMulticastFramesOK",
"TxBroadcastFramesOK",
"TxPauseFrames",
"TxFramesWithDeferredXmissions",
"TxLateCollisions",
"TxTotalCollisions",
"TxFramesAbortedDueToXSCollisions",
"TxUnderrun",
"TxLengthErrors",
"TxInternalMACXmitError",
"TxFramesWithExcessiveDeferral",
"TxFCSErrors",
"RxOctetsOK",
"RxOctetsBad",
"RxUnicastFramesOK",
"RxMulticastFramesOK",
"RxBroadcastFramesOK",
"RxPauseFrames",
"RxFCSErrors",
"RxAlignErrors",
"RxSymbolErrors",
"RxDataErrors",
"RxSequenceErrors",
"RxRuntErrors",
"RxJabberErrors",
"RxInternalMACRcvError",
"RxInRangeLengthErrors",
"RxOutOfRangeLengthField",
"RxFrameTooLongErrors"
};
static void get_drvinfo(struct net_device *dev, struct ethtool_drvinfo *info)
{
struct adapter *adapter = dev->priv;
strcpy(info->driver, driver_name);
strcpy(info->version, driver_version);
strcpy(info->fw_version, "N/A");
strcpy(info->bus_info, pci_name(adapter->pdev));
}
static int get_stats_count(struct net_device *dev)
{
return ARRAY_SIZE(stats_strings);
}
static void get_strings(struct net_device *dev, u32 stringset, u8 *data)
{
if (stringset == ETH_SS_STATS)
memcpy(data, stats_strings, sizeof(stats_strings));
}
static void get_stats(struct net_device *dev, struct ethtool_stats *stats,
u64 *data)
{
struct adapter *adapter = dev->priv;
struct cmac *mac = adapter->port[dev->if_port].mac;
const struct cmac_statistics *s;
s = mac->ops->statistics_update(mac, MAC_STATS_UPDATE_FULL);
*data++ = s->TxOctetsOK;
*data++ = s->TxOctetsBad;
*data++ = s->TxUnicastFramesOK;
*data++ = s->TxMulticastFramesOK;
*data++ = s->TxBroadcastFramesOK;
*data++ = s->TxPauseFrames;
*data++ = s->TxFramesWithDeferredXmissions;
*data++ = s->TxLateCollisions;
*data++ = s->TxTotalCollisions;
*data++ = s->TxFramesAbortedDueToXSCollisions;
*data++ = s->TxUnderrun;
*data++ = s->TxLengthErrors;
*data++ = s->TxInternalMACXmitError;
*data++ = s->TxFramesWithExcessiveDeferral;
*data++ = s->TxFCSErrors;
*data++ = s->RxOctetsOK;
*data++ = s->RxOctetsBad;
*data++ = s->RxUnicastFramesOK;
*data++ = s->RxMulticastFramesOK;
*data++ = s->RxBroadcastFramesOK;
*data++ = s->RxPauseFrames;
*data++ = s->RxFCSErrors;
*data++ = s->RxAlignErrors;
*data++ = s->RxSymbolErrors;
*data++ = s->RxDataErrors;
*data++ = s->RxSequenceErrors;
*data++ = s->RxRuntErrors;
*data++ = s->RxJabberErrors;
*data++ = s->RxInternalMACRcvError;
*data++ = s->RxInRangeLengthErrors;
*data++ = s->RxOutOfRangeLengthField;
*data++ = s->RxFrameTooLongErrors;
}
static int get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
{
struct adapter *adapter = dev->priv;
struct port_info *p = &adapter->port[dev->if_port];
cmd->supported = p->link_config.supported;
cmd->advertising = p->link_config.advertising;
if (netif_carrier_ok(dev)) {
cmd->speed = p->link_config.speed;
cmd->duplex = p->link_config.duplex;
} else {
cmd->speed = -1;
cmd->duplex = -1;
}
cmd->port = (cmd->supported & SUPPORTED_TP) ? PORT_TP : PORT_FIBRE;
cmd->phy_address = p->phy->addr;
cmd->transceiver = XCVR_EXTERNAL;
cmd->autoneg = p->link_config.autoneg;
cmd->maxtxpkt = 0;
cmd->maxrxpkt = 0;
return 0;
}
static int speed_duplex_to_caps(int speed, int duplex)
{
int cap = 0;
switch (speed) {
case SPEED_10:
if (duplex == DUPLEX_FULL)
cap = SUPPORTED_10baseT_Full;
else
cap = SUPPORTED_10baseT_Half;
break;
case SPEED_100:
if (duplex == DUPLEX_FULL)
cap = SUPPORTED_100baseT_Full;
else
cap = SUPPORTED_100baseT_Half;
break;
case SPEED_1000:
if (duplex == DUPLEX_FULL)
cap = SUPPORTED_1000baseT_Full;
else
cap = SUPPORTED_1000baseT_Half;
break;
case SPEED_10000:
if (duplex == DUPLEX_FULL)
cap = SUPPORTED_10000baseT_Full;
}
return cap;
}
#define ADVERTISED_MASK (ADVERTISED_10baseT_Half | ADVERTISED_10baseT_Full | \
ADVERTISED_100baseT_Half | ADVERTISED_100baseT_Full | \
ADVERTISED_1000baseT_Half | ADVERTISED_1000baseT_Full | \
ADVERTISED_10000baseT_Full)
static int set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
{
struct adapter *adapter = dev->priv;
struct port_info *p = &adapter->port[dev->if_port];
struct link_config *lc = &p->link_config;
if (!(lc->supported & SUPPORTED_Autoneg))
return -EOPNOTSUPP; /* can't change speed/duplex */
if (cmd->autoneg == AUTONEG_DISABLE) {
int cap = speed_duplex_to_caps(cmd->speed, cmd->duplex);
if (!(lc->supported & cap) || cmd->speed == SPEED_1000)
return -EINVAL;
lc->requested_speed = cmd->speed;
lc->requested_duplex = cmd->duplex;
lc->advertising = 0;
} else {
cmd->advertising &= ADVERTISED_MASK;
if (cmd->advertising & (cmd->advertising - 1))
cmd->advertising = lc->supported;
cmd->advertising &= lc->supported;
if (!cmd->advertising)
return -EINVAL;
lc->requested_speed = SPEED_INVALID;
lc->requested_duplex = DUPLEX_INVALID;
lc->advertising = cmd->advertising | ADVERTISED_Autoneg;
}
lc->autoneg = cmd->autoneg;
if (netif_running(dev))
t1_link_start(p->phy, p->mac, lc);
return 0;
}
static void get_pauseparam(struct net_device *dev,
struct ethtool_pauseparam *epause)
{
struct adapter *adapter = dev->priv;
struct port_info *p = &adapter->port[dev->if_port];
epause->autoneg = (p->link_config.requested_fc & PAUSE_AUTONEG) != 0;
epause->rx_pause = (p->link_config.fc & PAUSE_RX) != 0;
epause->tx_pause = (p->link_config.fc & PAUSE_TX) != 0;
}
static int set_pauseparam(struct net_device *dev,
struct ethtool_pauseparam *epause)
{
struct adapter *adapter = dev->priv;
struct port_info *p = &adapter->port[dev->if_port];
struct link_config *lc = &p->link_config;
if (epause->autoneg == AUTONEG_DISABLE)
lc->requested_fc = 0;
else if (lc->supported & SUPPORTED_Autoneg)
lc->requested_fc = PAUSE_AUTONEG;
else
return -EINVAL;
if (epause->rx_pause)
lc->requested_fc |= PAUSE_RX;
if (epause->tx_pause)
lc->requested_fc |= PAUSE_TX;
if (lc->autoneg == AUTONEG_ENABLE) {
if (netif_running(dev))
t1_link_start(p->phy, p->mac, lc);
} else {
lc->fc = lc->requested_fc & (PAUSE_RX | PAUSE_TX);
if (netif_running(dev))
p->mac->ops->set_speed_duplex_fc(p->mac, -1, -1,
lc->fc);
}
return 0;
}
static u32 get_rx_csum(struct net_device *dev)
{
struct adapter *adapter = dev->priv;
return (adapter->flags & RX_CSUM_ENABLED) != 0;
}
static int set_rx_csum(struct net_device *dev, u32 data)
{
struct adapter *adapter = dev->priv;
if (data)
adapter->flags |= RX_CSUM_ENABLED;
else
adapter->flags &= ~RX_CSUM_ENABLED;
return 0;
}
static int set_tso(struct net_device *dev, u32 value)
{
struct adapter *adapter = dev->priv;
if (!(adapter->flags & TSO_CAPABLE))
return value ? -EOPNOTSUPP : 0;
return ethtool_op_set_tso(dev, value);
}
static void get_sge_param(struct net_device *dev, struct ethtool_ringparam *e)
{
struct adapter *adapter = dev->priv;
int jumbo_fl = t1_is_T1B(adapter) ? 1 : 0;
e->rx_max_pending = MAX_RX_BUFFERS;
e->rx_mini_max_pending = 0;
e->rx_jumbo_max_pending = MAX_RX_JUMBO_BUFFERS;
e->tx_max_pending = MAX_CMDQ_ENTRIES;
e->rx_pending = adapter->params.sge.freelQ_size[!jumbo_fl];
e->rx_mini_pending = 0;
e->rx_jumbo_pending = adapter->params.sge.freelQ_size[jumbo_fl];
e->tx_pending = adapter->params.sge.cmdQ_size[0];
}
static int set_sge_param(struct net_device *dev, struct ethtool_ringparam *e)
{
struct adapter *adapter = dev->priv;
int jumbo_fl = t1_is_T1B(adapter) ? 1 : 0;
if (e->rx_pending > MAX_RX_BUFFERS || e->rx_mini_pending ||
e->rx_jumbo_pending > MAX_RX_JUMBO_BUFFERS ||
e->tx_pending > MAX_CMDQ_ENTRIES ||
e->rx_pending < MIN_FL_ENTRIES ||
e->rx_jumbo_pending < MIN_FL_ENTRIES ||
e->tx_pending < (adapter->params.nports + 1) * (MAX_SKB_FRAGS + 1))
return -EINVAL;
if (adapter->flags & FULL_INIT_DONE)
return -EBUSY;
adapter->params.sge.freelQ_size[!jumbo_fl] = e->rx_pending;
adapter->params.sge.freelQ_size[jumbo_fl] = e->rx_jumbo_pending;
adapter->params.sge.cmdQ_size[0] = e->tx_pending;
adapter->params.sge.cmdQ_size[1] = e->tx_pending > MAX_CMDQ1_ENTRIES ?
MAX_CMDQ1_ENTRIES : e->tx_pending;
return 0;
}
static int set_coalesce(struct net_device *dev, struct ethtool_coalesce *c)
{
struct adapter *adapter = dev->priv;
unsigned int sge_coalesce_usecs = 0;
sge_coalesce_usecs = adapter->params.sge.last_rx_coalesce_raw;
sge_coalesce_usecs /= board_info(adapter)->clock_core / 1000000;
if ( (adapter->params.sge.coalesce_enable && !c->use_adaptive_rx_coalesce) &&
(c->rx_coalesce_usecs == sge_coalesce_usecs) ) {
adapter->params.sge.rx_coalesce_usecs =
adapter->params.sge.default_rx_coalesce_usecs;
} else {
adapter->params.sge.rx_coalesce_usecs = c->rx_coalesce_usecs;
}
adapter->params.sge.last_rx_coalesce_raw = adapter->params.sge.rx_coalesce_usecs;
adapter->params.sge.last_rx_coalesce_raw *= (board_info(adapter)->clock_core / 1000000);
adapter->params.sge.sample_interval_usecs = c->rate_sample_interval;
adapter->params.sge.coalesce_enable = c->use_adaptive_rx_coalesce;
t1_sge_set_coalesce_params(adapter->sge, &adapter->params.sge);
return 0;
}
static int get_coalesce(struct net_device *dev, struct ethtool_coalesce *c)
{
struct adapter *adapter = dev->priv;
if (adapter->params.sge.coalesce_enable) { /* Adaptive algorithm on */
c->rx_coalesce_usecs = adapter->params.sge.last_rx_coalesce_raw;
c->rx_coalesce_usecs /= board_info(adapter)->clock_core / 1000000;
} else {
c->rx_coalesce_usecs = adapter->params.sge.rx_coalesce_usecs;
}
c->rate_sample_interval = adapter->params.sge.sample_interval_usecs;
c->use_adaptive_rx_coalesce = adapter->params.sge.coalesce_enable;
return 0;
}
static int get_eeprom_len(struct net_device *dev)
{
struct adapter *adapter = dev->priv;
return t1_is_asic(adapter) ? EEPROM_SIZE : 0;
}
#define EEPROM_MAGIC(ap) \
(PCI_VENDOR_ID_CHELSIO | ((ap)->params.chip_version << 16))
static int get_eeprom(struct net_device *dev, struct ethtool_eeprom *e,
u8 *data)
{
int i;
u8 buf[EEPROM_SIZE] __attribute__((aligned(4)));
struct adapter *adapter = dev->priv;
e->magic = EEPROM_MAGIC(adapter);
for (i = e->offset & ~3; i < e->offset + e->len; i += sizeof(u32))
t1_seeprom_read(adapter, i, (u32 *)&buf[i]);
memcpy(data, buf + e->offset, e->len);
return 0;
}
static struct ethtool_ops t1_ethtool_ops = {
.get_settings = get_settings,
.set_settings = set_settings,
.get_drvinfo = get_drvinfo,
.get_msglevel = get_msglevel,
.set_msglevel = set_msglevel,
.get_ringparam = get_sge_param,
.set_ringparam = set_sge_param,
.get_coalesce = get_coalesce,
.set_coalesce = set_coalesce,
.get_eeprom_len = get_eeprom_len,
.get_eeprom = get_eeprom,
.get_pauseparam = get_pauseparam,
.set_pauseparam = set_pauseparam,
.get_rx_csum = get_rx_csum,
.set_rx_csum = set_rx_csum,
.get_tx_csum = ethtool_op_get_tx_csum,
.set_tx_csum = ethtool_op_set_tx_csum,
.get_sg = ethtool_op_get_sg,
.set_sg = ethtool_op_set_sg,
.get_link = ethtool_op_get_link,
.get_strings = get_strings,
.get_stats_count = get_stats_count,
.get_ethtool_stats = get_stats,
.get_tso = ethtool_op_get_tso,
.set_tso = set_tso,
};
static int ethtool_ioctl(struct net_device *dev, void *useraddr)
{
u32 cmd;
struct adapter *adapter = dev->priv;
if (copy_from_user(&cmd, useraddr, sizeof(cmd)))
return -EFAULT;
switch (cmd) {
case ETHTOOL_SETREG: {
struct ethtool_reg edata;
if (!capable(CAP_NET_ADMIN))
return -EPERM;
if (copy_from_user(&edata, useraddr, sizeof(edata)))
return -EFAULT;
if ((edata.addr & 3) != 0 || edata.addr >= adapter->mmio_len)
return -EINVAL;
if (edata.addr == A_ESPI_MISC_CONTROL)
t1_espi_set_misc_ctrl(adapter, edata.val);
else {
if (edata.addr == 0x950)
t1_sge_set_ptimeout(adapter, edata.val);
else
writel(edata.val, adapter->regs + edata.addr);
}
break;
}
case ETHTOOL_GETREG: {
struct ethtool_reg edata;
if (copy_from_user(&edata, useraddr, sizeof(edata)))
return -EFAULT;
if ((edata.addr & 3) != 0 || edata.addr >= adapter->mmio_len)
return -EINVAL;
if (edata.addr >= 0x900 && edata.addr <= 0x93c)
edata.val = t1_espi_get_mon(adapter, edata.addr, 1);
else {
if (edata.addr == 0x950)
edata.val = t1_sge_get_ptimeout(adapter);
else
edata.val = readl(adapter->regs + edata.addr);
}
if (copy_to_user(useraddr, &edata, sizeof(edata)))
return -EFAULT;
break;
}
case ETHTOOL_SETTPI: {
struct ethtool_reg edata;
if (!capable(CAP_NET_ADMIN))
return -EPERM;
if (copy_from_user(&edata, useraddr, sizeof(edata)))
return -EFAULT;
if ((edata.addr & 3) != 0)
return -EINVAL;
t1_tpi_write(adapter, edata.addr, edata.val);
break;
}
case ETHTOOL_GETTPI: {
struct ethtool_reg edata;
if (copy_from_user(&edata, useraddr, sizeof(edata)))
return -EFAULT;
if ((edata.addr & 3) != 0)
return -EINVAL;
t1_tpi_read(adapter, edata.addr, &edata.val);
if (copy_to_user(useraddr, &edata, sizeof(edata)))
return -EFAULT;
break;
}
default:
return -EOPNOTSUPP;
}
return 0;
}
static int t1_ioctl(struct net_device *dev, struct ifreq *req, int cmd)
{
struct adapter *adapter = dev->priv;
struct mii_ioctl_data *data = (struct mii_ioctl_data *)&req->ifr_data;
switch (cmd) {
case SIOCGMIIPHY:
data->phy_id = adapter->port[dev->if_port].phy->addr;
/* FALLTHRU */
case SIOCGMIIREG: {
struct cphy *phy = adapter->port[dev->if_port].phy;
u32 val;
if (!phy->mdio_read) return -EOPNOTSUPP;
phy->mdio_read(adapter, data->phy_id, 0, data->reg_num & 0x1f,
&val);
data->val_out = val;
break;
}
case SIOCSMIIREG: {
struct cphy *phy = adapter->port[dev->if_port].phy;
if (!capable(CAP_NET_ADMIN)) return -EPERM;
if (!phy->mdio_write) return -EOPNOTSUPP;
phy->mdio_write(adapter, data->phy_id, 0, data->reg_num & 0x1f,
data->val_in);
break;
}
case SIOCCHETHTOOL:
return ethtool_ioctl(dev, (void *)req->ifr_data);
default:
return -EOPNOTSUPP;
}
return 0;
}
static int t1_change_mtu(struct net_device *dev, int new_mtu)
{
int ret;
struct adapter *adapter = dev->priv;
struct cmac *mac = adapter->port[dev->if_port].mac;
if (!mac->ops->set_mtu)
return -EOPNOTSUPP;
if (new_mtu < 68)
return -EINVAL;
if ((ret = mac->ops->set_mtu(mac, new_mtu)))
return ret;
dev->mtu = new_mtu;
return 0;
}
static int t1_set_mac_addr(struct net_device *dev, void *p)
{
struct adapter *adapter = dev->priv;
struct cmac *mac = adapter->port[dev->if_port].mac;
struct sockaddr *addr = p;
if (!mac->ops->macaddress_set)
return -EOPNOTSUPP;
memcpy(dev->dev_addr, addr->sa_data, dev->addr_len);
mac->ops->macaddress_set(mac, dev->dev_addr);
return 0;
}
#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
static void vlan_rx_register(struct net_device *dev,
struct vlan_group *grp)
{
struct adapter *adapter = dev->priv;
spin_lock_irq(&adapter->async_lock);
adapter->vlan_grp = grp;
t1_set_vlan_accel(adapter, grp != NULL);
spin_unlock_irq(&adapter->async_lock);
}
static void vlan_rx_kill_vid(struct net_device *dev, unsigned short vid)
{
struct adapter *adapter = dev->priv;
spin_lock_irq(&adapter->async_lock);
if (adapter->vlan_grp)
adapter->vlan_grp->vlan_devices[vid] = NULL;
spin_unlock_irq(&adapter->async_lock);
}
#endif
#ifdef CONFIG_NET_POLL_CONTROLLER
static void t1_netpoll(struct net_device *dev)
{
struct adapter *adapter = dev->priv;
t1_interrupt(adapter->pdev->irq, adapter, NULL);
}
#endif
/*
* Periodic accumulation of MAC statistics. This is used only if the MAC
* does not have any other way to prevent stats counter overflow.
*/
static void mac_stats_task(void *data)
{
int i;
struct adapter *adapter = data;
for_each_port(adapter, i) {
struct port_info *p = &adapter->port[i];
if (netif_running(p->dev))
p->mac->ops->statistics_update(p->mac,
MAC_STATS_UPDATE_FAST);
}
/* Schedule the next statistics update if any port is active. */
spin_lock(&adapter->work_lock);
if (adapter->open_device_map & PORT_MASK)
schedule_mac_stats_update(adapter,
adapter->params.stats_update_period);
spin_unlock(&adapter->work_lock);
}
/*
* Processes elmer0 external interrupts in process context.
*/
static void ext_intr_task(void *data)
{
u32 enable;
struct adapter *adapter = data;
elmer0_ext_intr_handler(adapter);
/* Now reenable external interrupts */
t1_write_reg_4(adapter, A_PL_CAUSE, F_PL_INTR_EXT);
enable = t1_read_reg_4(adapter, A_PL_ENABLE);
t1_write_reg_4(adapter, A_PL_ENABLE, enable | F_PL_INTR_EXT);
adapter->slow_intr_mask |= F_PL_INTR_EXT;
}
/*
* Interrupt-context handler for elmer0 external interrupts.
*/
void t1_elmer0_ext_intr(struct adapter *adapter)
{
u32 enable = t1_read_reg_4(adapter, A_PL_ENABLE);
/*
* Schedule a task to handle external interrupts as we require
* a process context. We disable EXT interrupts in the interim
* and let the task reenable them when it's done.
*/
adapter->slow_intr_mask &= ~F_PL_INTR_EXT;
t1_write_reg_4(adapter, A_PL_ENABLE, enable & ~F_PL_INTR_EXT);
schedule_work(&adapter->ext_intr_handler_task);
}
void t1_fatal_err(struct adapter *adapter)
{
if (adapter->flags & FULL_INIT_DONE) {
t1_sge_stop(adapter->sge);
t1_interrupts_disable(adapter);
}
CH_ALERT("%s: encountered fatal error, operation suspended\n",
adapter->name);
}
static int __devinit init_one(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
static int version_printed;
int i, err, pci_using_dac = 0;
unsigned long mmio_start, mmio_len;
const struct board_info *bi;
struct adapter *adapter = NULL;
struct port_info *pi;
if (!version_printed) {
printk(KERN_INFO "%s - version %s\n", driver_string,
driver_version);
++version_printed;
}
err = pci_enable_device(pdev);
if (err)
return err;
if (!(pci_resource_flags(pdev, 0) & IORESOURCE_MEM)) {
CH_ERR("%s: cannot find PCI device memory base address\n",
pci_name(pdev));
err = -ENODEV;
goto out_disable_pdev;
}
if (!pci_set_dma_mask(pdev, PCI_DMA_64BIT)) {
pci_using_dac = 1;
if (pci_set_consistent_dma_mask(pdev, PCI_DMA_64BIT)) {
CH_ERR("%s: unable to obtain 64-bit DMA for"
"consistent allocations\n", pci_name(pdev));
err = -ENODEV;
goto out_disable_pdev;
}
} else if ((err = pci_set_dma_mask(pdev, PCI_DMA_32BIT)) != 0) {
CH_ERR("%s: no usable DMA configuration\n", pci_name(pdev));
goto out_disable_pdev;
}
err = pci_request_regions(pdev, driver_name);
if (err) {
CH_ERR("%s: cannot obtain PCI resources\n", pci_name(pdev));
goto out_disable_pdev;
}
pci_set_master(pdev);
mmio_start = pci_resource_start(pdev, 0);
mmio_len = pci_resource_len(pdev, 0);
bi = t1_get_board_info(ent->driver_data);
for (i = 0; i < bi->port_number; ++i) {
struct net_device *netdev;
netdev = alloc_etherdev(adapter ? 0 : sizeof(*adapter));
if (!netdev) {
err = -ENOMEM;
goto out_free_dev;
}
SET_MODULE_OWNER(netdev);
SET_NETDEV_DEV(netdev, &pdev->dev);
if (!adapter) {
adapter = netdev->priv;
adapter->pdev = pdev;
adapter->port[0].dev = netdev; /* so we don't leak it */
adapter->regs = ioremap(mmio_start, mmio_len);
if (!adapter->regs) {
CH_ERR("%s: cannot map device registers\n",
pci_name(pdev));
err = -ENOMEM;
goto out_free_dev;
}
if (t1_get_board_rev(adapter, bi, &adapter->params)) {
err = -ENODEV; /* Can't handle this chip rev */
goto out_free_dev;
}
adapter->name = pci_name(pdev);
adapter->msg_enable = dflt_msg_enable;
adapter->mmio_len = mmio_len;
init_MUTEX(&adapter->mib_mutex);
spin_lock_init(&adapter->tpi_lock);
spin_lock_init(&adapter->work_lock);
spin_lock_init(&adapter->async_lock);
INIT_WORK(&adapter->ext_intr_handler_task,
ext_intr_task, adapter);
INIT_WORK(&adapter->stats_update_task, mac_stats_task,
adapter);
pci_set_drvdata(pdev, netdev);
}
pi = &adapter->port[i];
pi->dev = netdev;
netif_carrier_off(netdev);
netdev->irq = pdev->irq;
netdev->if_port = i;
netdev->mem_start = mmio_start;
netdev->mem_end = mmio_start + mmio_len - 1;
netdev->priv = adapter;
netdev->features |= NETIF_F_SG | NETIF_F_IP_CSUM;
adapter->flags |= RX_CSUM_ENABLED | TCP_CSUM_CAPABLE;
if (pci_using_dac)
netdev->features |= NETIF_F_HIGHDMA;
if (vlan_tso_capable(adapter)) {
adapter->flags |= UDP_CSUM_CAPABLE;
#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
adapter->flags |= VLAN_ACCEL_CAPABLE;
netdev->features |=
NETIF_F_HW_VLAN_TX | NETIF_F_HW_VLAN_RX;
netdev->vlan_rx_register = vlan_rx_register;
netdev->vlan_rx_kill_vid = vlan_rx_kill_vid;
#endif
adapter->flags |= TSO_CAPABLE;
netdev->features |= NETIF_F_TSO;
}
netdev->open = cxgb_open;
netdev->stop = cxgb_close;
netdev->hard_start_xmit = t1_start_xmit;
netdev->hard_header_len += (adapter->flags & TSO_CAPABLE) ?
sizeof(struct cpl_tx_pkt_lso) :
sizeof(struct cpl_tx_pkt);
netdev->get_stats = t1_get_stats;
netdev->set_multicast_list = t1_set_rxmode;
netdev->do_ioctl = t1_ioctl;
netdev->change_mtu = t1_change_mtu;
netdev->set_mac_address = t1_set_mac_addr;
#ifdef CONFIG_NET_POLL_CONTROLLER
netdev->poll_controller = t1_netpoll;
#endif
netdev->weight = 64;
SET_ETHTOOL_OPS(netdev, &t1_ethtool_ops);
}
if (t1_init_sw_modules(adapter, bi) < 0) {
err = -ENODEV;
goto out_free_dev;
}
/*
* The card is now ready to go. If any errors occur during device
* registration we do not fail the whole card but rather proceed only
* with the ports we manage to register successfully. However we must
* register at least one net device.
*/
for (i = 0; i < bi->port_number; ++i) {
err = register_netdev(adapter->port[i].dev);
if (err)
CH_WARN("%s: cannot register net device %s, skipping\n",
pci_name(pdev), adapter->port[i].dev->name);
else {
/*
* Change the name we use for messages to the name of
* the first successfully registered interface.
*/
if (!adapter->registered_device_map)
adapter->name = adapter->port[i].dev->name;
__set_bit(i, &adapter->registered_device_map);
}
}
if (!adapter->registered_device_map) {
CH_ERR("%s: could not register any net devices\n",
pci_name(pdev));
goto out_release_adapter_res;
}
printk(KERN_INFO "%s: %s (rev %d), %s %dMHz/%d-bit\n", adapter->name,
bi->desc, adapter->params.chip_revision,
adapter->params.pci.is_pcix ? "PCIX" : "PCI",
adapter->params.pci.speed, adapter->params.pci.width);
return 0;
out_release_adapter_res:
t1_free_sw_modules(adapter);
out_free_dev:
if (adapter) {
if (adapter->regs)
iounmap(adapter->regs);
for (i = bi->port_number - 1; i >= 0; --i)
if (adapter->port[i].dev)
free_netdev(adapter->port[i].dev);
}
pci_release_regions(pdev);
out_disable_pdev:
pci_disable_device(pdev);
pci_set_drvdata(pdev, NULL);
return err;
}
static inline void t1_sw_reset(struct pci_dev *pdev)
{
pci_write_config_dword(pdev, A_PCICFG_PM_CSR, 3);
pci_write_config_dword(pdev, A_PCICFG_PM_CSR, 0);
}
static void __devexit remove_one(struct pci_dev *pdev)
{
struct net_device *dev = pci_get_drvdata(pdev);
if (dev) {
int i;
struct adapter *adapter = dev->priv;
for_each_port(adapter, i)
if (test_bit(i, &adapter->registered_device_map))
unregister_netdev(adapter->port[i].dev);
t1_free_sw_modules(adapter);
iounmap(adapter->regs);
while (--i >= 0)
if (adapter->port[i].dev)
free_netdev(adapter->port[i].dev);
pci_release_regions(pdev);
pci_disable_device(pdev);
pci_set_drvdata(pdev, NULL);
t1_sw_reset(pdev);
}
}
static struct pci_driver driver = {
.name = driver_name,
.id_table = t1_pci_tbl,
.probe = init_one,
.remove = __devexit_p(remove_one),
};
static int __init t1_init_module(void)
{
return pci_module_init(&driver);
}
static void __exit t1_cleanup_module(void)
{
pci_unregister_driver(&driver);
}
module_init(t1_init_module);
module_exit(t1_cleanup_module);
/*****************************************************************************
* *
* File: cxgb2.h *
* $Revision: 1.8 $ *
* $Date: 2005/03/23 07:41:27 $ *
* Description: *
* part of the Chelsio 10Gb Ethernet Driver. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License, version 2, as *
* published by the Free Software Foundation. *
* *
* You should have received a copy of the GNU General Public License along *
* with this program; if not, write to the Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
* *
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED *
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF *
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. *
* *
* http://www.chelsio.com *
* *
* Copyright (c) 2003 - 2005 Chelsio Communications, Inc. *
* All rights reserved. *
* *
* Maintainers: maintainers@chelsio.com *
* *
* Authors: Dimitrios Michailidis <dm@chelsio.com> *
* Tina Yang <tainay@chelsio.com> *
* Felix Marti <felix@chelsio.com> *
* Scott Bardone <sbardone@chelsio.com> *
* Kurt Ottaway <kottaway@chelsio.com> *
* Frank DiMambro <frank@chelsio.com> *
* *
* History: *
* *
****************************************************************************/
#ifndef __CXGB_LINUX_H__
#define __CXGB_LINUX_H__
#include <linux/netdevice.h>
#include <linux/skbuff.h>
#include <linux/version.h>
#include <asm/semaphore.h>
#include <asm/bitops.h>
/* This belongs in if_ether.h */
#define ETH_P_CPL5 0xf
struct cmac;
struct cphy;
struct port_info {
struct net_device *dev;
struct cmac *mac;
struct cphy *phy;
struct link_config link_config;
struct net_device_stats netstats;
};
struct cxgbdev;
struct t1_sge;
struct pemc3;
struct pemc4;
struct pemc5;
struct peulp;
struct petp;
struct pecspi;
struct peespi;
struct work_struct;
struct vlan_group;
enum { /* adapter flags */
FULL_INIT_DONE = 0x1,
USING_MSI = 0x2,
TSO_CAPABLE = 0x4,
TCP_CSUM_CAPABLE = 0x8,
UDP_CSUM_CAPABLE = 0x10,
VLAN_ACCEL_CAPABLE = 0x20,
RX_CSUM_ENABLED = 0x40,
};
struct adapter {
u8 *regs;
struct pci_dev *pdev;
unsigned long registered_device_map;
unsigned long open_device_map;
unsigned int flags;
const char *name;
int msg_enable;
u32 mmio_len;
struct work_struct ext_intr_handler_task;
struct adapter_params params;
struct vlan_group *vlan_grp;
/* Terminator modules. */
struct sge *sge;
struct pemc3 *mc3;
struct pemc4 *mc4;
struct pemc5 *mc5;
struct petp *tp;
struct pecspi *cspi;
struct peespi *espi;
struct peulp *ulp;
struct port_info port[MAX_NPORTS];
struct work_struct stats_update_task;
struct timer_list stats_update_timer;
struct semaphore mib_mutex;
spinlock_t tpi_lock;
spinlock_t work_lock;
spinlock_t async_lock ____cacheline_aligned; /* guards async operations */
u32 slow_intr_mask;
};
#endif
/*****************************************************************************
* *
* File: elmer0.h *
* $Revision: 1.3 $ *
* $Date: 2005/03/23 07:15:58 $ *
* Description: *
* part of the Chelsio 10Gb Ethernet Driver. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License, version 2, as *
* published by the Free Software Foundation. *
* *
* You should have received a copy of the GNU General Public License along *
* with this program; if not, write to the Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
* *
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED *
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF *
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. *
* *
* http://www.chelsio.com *
* *
* Copyright (c) 2003 - 2005 Chelsio Communications, Inc. *
* All rights reserved. *
* *
* Maintainers: maintainers@chelsio.com *
* *
* Authors: Dimitrios Michailidis <dm@chelsio.com> *
* Tina Yang <tainay@chelsio.com> *
* Felix Marti <felix@chelsio.com> *
* Scott Bardone <sbardone@chelsio.com> *
* Kurt Ottaway <kottaway@chelsio.com> *
* Frank DiMambro <frank@chelsio.com> *
* *
* History: *
* *
****************************************************************************/
#ifndef CHELSIO_ELMER0_H
#define CHELSIO_ELMER0_H
/* ELMER0 flavors */
enum {
ELMER0_XC2S300E_6FT256_C,
ELMER0_XC2S100E_6TQ144_C
};
/* ELMER0 registers */
#define A_ELMER0_VERSION 0x100000
#define A_ELMER0_PHY_CFG 0x100004
#define A_ELMER0_INT_ENABLE 0x100008
#define A_ELMER0_INT_CAUSE 0x10000c
#define A_ELMER0_GPI_CFG 0x100010
#define A_ELMER0_GPI_STAT 0x100014
#define A_ELMER0_GPO 0x100018
#define A_ELMER0_PORT0_MI1_CFG 0x400000
#define S_MI1_MDI_ENABLE 0
#define V_MI1_MDI_ENABLE(x) ((x) << S_MI1_MDI_ENABLE)
#define F_MI1_MDI_ENABLE V_MI1_MDI_ENABLE(1U)
#define S_MI1_MDI_INVERT 1
#define V_MI1_MDI_INVERT(x) ((x) << S_MI1_MDI_INVERT)
#define F_MI1_MDI_INVERT V_MI1_MDI_INVERT(1U)
#define S_MI1_PREAMBLE_ENABLE 2
#define V_MI1_PREAMBLE_ENABLE(x) ((x) << S_MI1_PREAMBLE_ENABLE)
#define F_MI1_PREAMBLE_ENABLE V_MI1_PREAMBLE_ENABLE(1U)
#define S_MI1_SOF 3
#define M_MI1_SOF 0x3
#define V_MI1_SOF(x) ((x) << S_MI1_SOF)
#define G_MI1_SOF(x) (((x) >> S_MI1_SOF) & M_MI1_SOF)
#define S_MI1_CLK_DIV 5
#define M_MI1_CLK_DIV 0xff
#define V_MI1_CLK_DIV(x) ((x) << S_MI1_CLK_DIV)
#define G_MI1_CLK_DIV(x) (((x) >> S_MI1_CLK_DIV) & M_MI1_CLK_DIV)
#define A_ELMER0_PORT0_MI1_ADDR 0x400004
#define S_MI1_REG_ADDR 0
#define M_MI1_REG_ADDR 0x1f
#define V_MI1_REG_ADDR(x) ((x) << S_MI1_REG_ADDR)
#define G_MI1_REG_ADDR(x) (((x) >> S_MI1_REG_ADDR) & M_MI1_REG_ADDR)
#define S_MI1_PHY_ADDR 5
#define M_MI1_PHY_ADDR 0x1f
#define V_MI1_PHY_ADDR(x) ((x) << S_MI1_PHY_ADDR)
#define G_MI1_PHY_ADDR(x) (((x) >> S_MI1_PHY_ADDR) & M_MI1_PHY_ADDR)
#define A_ELMER0_PORT0_MI1_DATA 0x400008
#define S_MI1_DATA 0
#define M_MI1_DATA 0xffff
#define V_MI1_DATA(x) ((x) << S_MI1_DATA)
#define G_MI1_DATA(x) (((x) >> S_MI1_DATA) & M_MI1_DATA)
#define A_ELMER0_PORT0_MI1_OP 0x40000c
#define S_MI1_OP 0
#define M_MI1_OP 0x3
#define V_MI1_OP(x) ((x) << S_MI1_OP)
#define G_MI1_OP(x) (((x) >> S_MI1_OP) & M_MI1_OP)
#define S_MI1_ADDR_AUTOINC 2
#define V_MI1_ADDR_AUTOINC(x) ((x) << S_MI1_ADDR_AUTOINC)
#define F_MI1_ADDR_AUTOINC V_MI1_ADDR_AUTOINC(1U)
#define S_MI1_OP_BUSY 31
#define V_MI1_OP_BUSY(x) ((x) << S_MI1_OP_BUSY)
#define F_MI1_OP_BUSY V_MI1_OP_BUSY(1U)
#define A_ELMER0_PORT1_MI1_CFG 0x500000
#define A_ELMER0_PORT1_MI1_ADDR 0x500004
#define A_ELMER0_PORT1_MI1_DATA 0x500008
#define A_ELMER0_PORT1_MI1_OP 0x50000c
#define A_ELMER0_PORT2_MI1_CFG 0x600000
#define A_ELMER0_PORT2_MI1_ADDR 0x600004
#define A_ELMER0_PORT2_MI1_DATA 0x600008
#define A_ELMER0_PORT2_MI1_OP 0x60000c
#define A_ELMER0_PORT3_MI1_CFG 0x700000
#define A_ELMER0_PORT3_MI1_ADDR 0x700004
#define A_ELMER0_PORT3_MI1_DATA 0x700008
#define A_ELMER0_PORT3_MI1_OP 0x70000c
/* Simple bit definition for GPI and GP0 registers. */
#define ELMER0_GP_BIT0 0x0001
#define ELMER0_GP_BIT1 0x0002
#define ELMER0_GP_BIT2 0x0004
#define ELMER0_GP_BIT3 0x0008
#define ELMER0_GP_BIT4 0x0010
#define ELMER0_GP_BIT5 0x0020
#define ELMER0_GP_BIT6 0x0040
#define ELMER0_GP_BIT7 0x0080
#define ELMER0_GP_BIT8 0x0100
#define ELMER0_GP_BIT9 0x0200
#define ELMER0_GP_BIT10 0x0400
#define ELMER0_GP_BIT11 0x0800
#define ELMER0_GP_BIT12 0x1000
#define ELMER0_GP_BIT13 0x2000
#define ELMER0_GP_BIT14 0x4000
#define ELMER0_GP_BIT15 0x8000
#define ELMER0_GP_BIT16 0x10000
#define ELMER0_GP_BIT17 0x20000
#define ELMER0_GP_BIT18 0x40000
#define ELMER0_GP_BIT19 0x80000
#define MI1_OP_DIRECT_WRITE 1
#define MI1_OP_DIRECT_READ 2
#define MI1_OP_INDIRECT_ADDRESS 0
#define MI1_OP_INDIRECT_WRITE 1
#define MI1_OP_INDIRECT_READ_INC 2
#define MI1_OP_INDIRECT_READ 3
#endif
/*****************************************************************************
* *
* File: espi.c *
* $Revision: 1.9 $ *
* $Date: 2005/03/23 07:41:27 $ *
* Description: *
* Ethernet SPI functionality. *
* part of the Chelsio 10Gb Ethernet Driver. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License, version 2, as *
* published by the Free Software Foundation. *
* *
* You should have received a copy of the GNU General Public License along *
* with this program; if not, write to the Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
* *
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED *
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF *
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. *
* *
* http://www.chelsio.com *
* *
* Copyright (c) 2003 - 2005 Chelsio Communications, Inc. *
* All rights reserved. *
* *
* Maintainers: maintainers@chelsio.com *
* *
* Authors: Dimitrios Michailidis <dm@chelsio.com> *
* Tina Yang <tainay@chelsio.com> *
* Felix Marti <felix@chelsio.com> *
* Scott Bardone <sbardone@chelsio.com> *
* Kurt Ottaway <kottaway@chelsio.com> *
* Frank DiMambro <frank@chelsio.com> *
* *
* History: *
* *
****************************************************************************/
#include "common.h"
#include "regs.h"
#include "espi.h"
struct peespi {
adapter_t *adapter;
struct espi_intr_counts intr_cnt;
u32 misc_ctrl;
spinlock_t lock;
};
#define ESPI_INTR_MASK (F_DIP4ERR | F_RXDROP | F_TXDROP | F_RXOVERFLOW | \
F_RAMPARITYERR | F_DIP2PARITYERR)
#define MON_MASK (V_MONITORED_PORT_NUM(3) | F_MONITORED_DIRECTION \
| F_MONITORED_INTERFACE)
#define TRICN_CNFG 14
#define TRICN_CMD_READ 0x11
#define TRICN_CMD_WRITE 0x21
#define TRICN_CMD_ATTEMPTS 10
static int tricn_write(adapter_t *adapter, int bundle_addr, int module_addr,
int ch_addr, int reg_offset, u32 wr_data)
{
int busy, attempts = TRICN_CMD_ATTEMPTS;
t1_write_reg_4(adapter, A_ESPI_CMD_ADDR, V_WRITE_DATA(wr_data) |
V_REGISTER_OFFSET(reg_offset) |
V_CHANNEL_ADDR(ch_addr) | V_MODULE_ADDR(module_addr) |
V_BUNDLE_ADDR(bundle_addr) |
V_SPI4_COMMAND(TRICN_CMD_WRITE));
t1_write_reg_4(adapter, A_ESPI_GOSTAT, 0);
do {
busy = t1_read_reg_4(adapter, A_ESPI_GOSTAT) & F_ESPI_CMD_BUSY;
} while (busy && --attempts);
if (busy)
CH_ERR("%s: TRICN write timed out\n", adapter->name);
return busy;
}
/* 1. Deassert rx_reset_core. */
/* 2. Program TRICN_CNFG registers. */
/* 3. Deassert rx_reset_link */
static int tricn_init(adapter_t *adapter)
{
int i = 0;
int sme = 1;
int stat = 0;
int timeout = 0;
int is_ready = 0;
int dynamic_deskew = 0;
if (dynamic_deskew)
sme = 0;
/* 1 */
timeout=1000;
do {
stat = t1_read_reg_4(adapter, A_ESPI_RX_RESET);
is_ready = (stat & 0x4);
timeout--;
udelay(5);
} while (!is_ready || (timeout==0));
t1_write_reg_4(adapter, A_ESPI_RX_RESET, 0x2);
if (timeout==0)
{
CH_ERR("ESPI : ERROR : Timeout tricn_init() \n");
t1_fatal_err(adapter);
}
/* 2 */
if (sme) {
tricn_write(adapter, 0, 0, 0, TRICN_CNFG, 0x81);
tricn_write(adapter, 0, 1, 0, TRICN_CNFG, 0x81);
tricn_write(adapter, 0, 2, 0, TRICN_CNFG, 0x81);
}
for (i=1; i<= 8; i++) tricn_write(adapter, 0, 0, i, TRICN_CNFG, 0xf1);
for (i=1; i<= 2; i++) tricn_write(adapter, 0, 1, i, TRICN_CNFG, 0xf1);
for (i=1; i<= 3; i++) tricn_write(adapter, 0, 2, i, TRICN_CNFG, 0xe1);
for (i=4; i<= 4; i++) tricn_write(adapter, 0, 2, i, TRICN_CNFG, 0xf1);
for (i=5; i<= 5; i++) tricn_write(adapter, 0, 2, i, TRICN_CNFG, 0xe1);
for (i=6; i<= 6; i++) tricn_write(adapter, 0, 2, i, TRICN_CNFG, 0xf1);
for (i=7; i<= 7; i++) tricn_write(adapter, 0, 2, i, TRICN_CNFG, 0x80);
for (i=8; i<= 8; i++) tricn_write(adapter, 0, 2, i, TRICN_CNFG, 0xf1);
/* 3 */
t1_write_reg_4(adapter, A_ESPI_RX_RESET, 0x3);
return 0;
}
void t1_espi_intr_enable(struct peespi *espi)
{
u32 enable, pl_intr = t1_read_reg_4(espi->adapter, A_PL_ENABLE);
/*
* Cannot enable ESPI interrupts on T1B because HW asserts the
* interrupt incorrectly, namely the driver gets ESPI interrupts
* but no data is actually dropped (can verify this reading the ESPI
* drop registers). Also, once the ESPI interrupt is asserted it
* cannot be cleared (HW bug).
*/
enable = t1_is_T1B(espi->adapter) ? 0 : ESPI_INTR_MASK;
t1_write_reg_4(espi->adapter, A_ESPI_INTR_ENABLE, enable);
t1_write_reg_4(espi->adapter, A_PL_ENABLE, pl_intr | F_PL_INTR_ESPI);
}
void t1_espi_intr_clear(struct peespi *espi)
{
t1_write_reg_4(espi->adapter, A_ESPI_INTR_STATUS, 0xffffffff);
t1_write_reg_4(espi->adapter, A_PL_CAUSE, F_PL_INTR_ESPI);
}
void t1_espi_intr_disable(struct peespi *espi)
{
u32 pl_intr = t1_read_reg_4(espi->adapter, A_PL_ENABLE);
t1_write_reg_4(espi->adapter, A_ESPI_INTR_ENABLE, 0);
t1_write_reg_4(espi->adapter, A_PL_ENABLE, pl_intr & ~F_PL_INTR_ESPI);
}
int t1_espi_intr_handler(struct peespi *espi)
{
u32 cnt;
u32 status = t1_read_reg_4(espi->adapter, A_ESPI_INTR_STATUS);
if (status & F_DIP4ERR)
espi->intr_cnt.DIP4_err++;
if (status & F_RXDROP)
espi->intr_cnt.rx_drops++;
if (status & F_TXDROP)
espi->intr_cnt.tx_drops++;
if (status & F_RXOVERFLOW)
espi->intr_cnt.rx_ovflw++;
if (status & F_RAMPARITYERR)
espi->intr_cnt.parity_err++;
if (status & F_DIP2PARITYERR) {
espi->intr_cnt.DIP2_parity_err++;
/*
* Must read the error count to clear the interrupt
* that it causes.
*/
cnt = t1_read_reg_4(espi->adapter, A_ESPI_DIP2_ERR_COUNT);
}
/*
* For T1B we need to write 1 to clear ESPI interrupts. For T2+ we
* write the status as is.
*/
if (status && t1_is_T1B(espi->adapter))
status = 1;
t1_write_reg_4(espi->adapter, A_ESPI_INTR_STATUS, status);
return 0;
}
static void espi_setup_for_pm3393(adapter_t *adapter)
{
u32 wmark = t1_is_T1B(adapter) ? 0x4000 : 0x3200;
t1_write_reg_4(adapter, A_ESPI_SCH_TOKEN0, 0x1f4);
t1_write_reg_4(adapter, A_ESPI_SCH_TOKEN1, 0x1f4);
t1_write_reg_4(adapter, A_ESPI_SCH_TOKEN2, 0x1f4);
t1_write_reg_4(adapter, A_ESPI_SCH_TOKEN3, 0x1f4);
t1_write_reg_4(adapter, A_ESPI_RX_FIFO_ALMOST_EMPTY_WATERMARK, 0x100);
t1_write_reg_4(adapter, A_ESPI_RX_FIFO_ALMOST_FULL_WATERMARK, wmark);
t1_write_reg_4(adapter, A_ESPI_CALENDAR_LENGTH, 3);
t1_write_reg_4(adapter, A_ESPI_TRAIN, 0x08000008);
t1_write_reg_4(adapter, A_PORT_CONFIG,
V_RX_NPORTS(1) | V_TX_NPORTS(1));
}
static void espi_setup_for_vsc7321(adapter_t *adapter)
{
u32 wmark = t1_is_T1B(adapter) ? 0x4000 : 0x3200;
t1_write_reg_4(adapter, A_ESPI_SCH_TOKEN0, 0x1f4);
t1_write_reg_4(adapter, A_ESPI_SCH_TOKEN1, 0x1f4);
t1_write_reg_4(adapter, A_ESPI_SCH_TOKEN2, 0x1f4);
t1_write_reg_4(adapter, A_ESPI_SCH_TOKEN3, 0x1f4);
t1_write_reg_4(adapter, A_ESPI_RX_FIFO_ALMOST_EMPTY_WATERMARK, 0x100);
t1_write_reg_4(adapter, A_ESPI_RX_FIFO_ALMOST_FULL_WATERMARK, wmark);
t1_write_reg_4(adapter, A_ESPI_CALENDAR_LENGTH, 3);
t1_write_reg_4(adapter, A_ESPI_TRAIN, 0x08000008);
t1_write_reg_4(adapter, A_PORT_CONFIG,
V_RX_NPORTS(1) | V_TX_NPORTS(1));
}
/*
* Note that T1B requires at least 2 ports for IXF1010 due to a HW bug.
*/
static void espi_setup_for_ixf1010(adapter_t *adapter, int nports)
{
t1_write_reg_4(adapter, A_ESPI_CALENDAR_LENGTH, 1);
if (nports == 4) {
if (is_T2(adapter)) {
t1_write_reg_4(adapter, A_ESPI_RX_FIFO_ALMOST_FULL_WATERMARK,
0xf00);
t1_write_reg_4(adapter, A_ESPI_RX_FIFO_ALMOST_EMPTY_WATERMARK,
0x3c0);
} else {
t1_write_reg_4(adapter, A_ESPI_RX_FIFO_ALMOST_FULL_WATERMARK,
0x7ff);
t1_write_reg_4(adapter, A_ESPI_RX_FIFO_ALMOST_EMPTY_WATERMARK,
0x1ff);
}
} else {
t1_write_reg_4(adapter, A_ESPI_RX_FIFO_ALMOST_FULL_WATERMARK,
0x1fff);
t1_write_reg_4(adapter, A_ESPI_RX_FIFO_ALMOST_EMPTY_WATERMARK,
0x7ff);
}
t1_write_reg_4(adapter, A_PORT_CONFIG,
V_RX_NPORTS(nports) | V_TX_NPORTS(nports));
}
/* T2 Init part -- */
/* 1. Set T_ESPI_MISCCTRL_ADDR */
/* 2. Init ESPI registers. */
/* 3. Init TriCN Hard Macro */
int t1_espi_init(struct peespi *espi, int mac_type, int nports)
{
u32 status_enable_extra = 0;
adapter_t *adapter = espi->adapter;
u32 cnt;
u32 status, burstval = 0x800100;
/* Disable ESPI training. MACs that can handle it enable it below. */
t1_write_reg_4(adapter, A_ESPI_TRAIN, 0);
if (is_T2(adapter)) {
t1_write_reg_4(adapter, A_ESPI_MISC_CONTROL,
V_OUT_OF_SYNC_COUNT(4) |
V_DIP2_PARITY_ERR_THRES(3) | V_DIP4_THRES(1));
if (nports == 4) {
/* T204: maxburst1 = 0x40, maxburst2 = 0x20 */
burstval = 0x200040;
}
}
t1_write_reg_4(adapter, A_ESPI_MAXBURST1_MAXBURST2, burstval);
if (mac_type == CHBT_MAC_PM3393)
espi_setup_for_pm3393(adapter);
else if (mac_type == CHBT_MAC_VSC7321)
espi_setup_for_vsc7321(adapter);
else if (mac_type == CHBT_MAC_IXF1010) {
status_enable_extra = F_INTEL1010MODE;
espi_setup_for_ixf1010(adapter, nports);
} else
return -1;
/*
* Make sure any pending interrupts from the SPI are
* Cleared before enabling the interrupt.
*/
t1_write_reg_4(espi->adapter, A_ESPI_INTR_ENABLE, ESPI_INTR_MASK);
status = t1_read_reg_4(espi->adapter, A_ESPI_INTR_STATUS);
if (status & F_DIP2PARITYERR) {
cnt = t1_read_reg_4(espi->adapter, A_ESPI_DIP2_ERR_COUNT);
}
/*
* For T1B we need to write 1 to clear ESPI interrupts. For T2+ we
* write the status as is.
*/
if (status && t1_is_T1B(espi->adapter))
status = 1;
t1_write_reg_4(espi->adapter, A_ESPI_INTR_STATUS, status);
t1_write_reg_4(adapter, A_ESPI_FIFO_STATUS_ENABLE,
status_enable_extra | F_RXSTATUSENABLE);
if (is_T2(adapter)) {
tricn_init(adapter);
/*
* Always position the control at the 1st port egress IN
* (sop,eop) counter to reduce PIOs for T/N210 workaround.
*/
espi->misc_ctrl = (t1_read_reg_4(adapter, A_ESPI_MISC_CONTROL)
& ~MON_MASK) | (F_MONITORED_DIRECTION
| F_MONITORED_INTERFACE);
t1_write_reg_4(adapter, A_ESPI_MISC_CONTROL, espi->misc_ctrl);
spin_lock_init(&espi->lock);
}
return 0;
}
void t1_espi_destroy(struct peespi *espi)
{
kfree(espi);
}
struct peespi *t1_espi_create(adapter_t *adapter)
{
struct peespi *espi = kmalloc(sizeof(*espi), GFP_KERNEL);
memset(espi, 0, sizeof(*espi));
if (espi)
espi->adapter = adapter;
return espi;
}
void t1_espi_set_misc_ctrl(adapter_t *adapter, u32 val)
{
struct peespi *espi = adapter->espi;
if (!is_T2(adapter))
return;
spin_lock(&espi->lock);
espi->misc_ctrl = (val & ~MON_MASK) |
(espi->misc_ctrl & MON_MASK);
t1_write_reg_4(adapter, A_ESPI_MISC_CONTROL, espi->misc_ctrl);
spin_unlock(&espi->lock);
}
u32 t1_espi_get_mon(adapter_t *adapter, u32 addr, u8 wait)
{
struct peespi *espi = adapter->espi;
u32 sel;
if (!is_T2(adapter))
return 0;
sel = V_MONITORED_PORT_NUM((addr & 0x3c) >> 2);
if (!wait) {
if (!spin_trylock(&espi->lock))
return 0;
}
else
spin_lock(&espi->lock);
if ((sel != (espi->misc_ctrl & MON_MASK))) {
t1_write_reg_4(adapter, A_ESPI_MISC_CONTROL,
((espi->misc_ctrl & ~MON_MASK) | sel));
sel = t1_read_reg_4(adapter, A_ESPI_SCH_TOKEN3);
t1_write_reg_4(adapter, A_ESPI_MISC_CONTROL,
espi->misc_ctrl);
}
else
sel = t1_read_reg_4(adapter, A_ESPI_SCH_TOKEN3);
spin_unlock(&espi->lock);
return sel;
}
/*****************************************************************************
* *
* File: espi.h *
* $Revision: 1.4 $ *
* $Date: 2005/03/23 07:15:58 $ *
* Description: *
* part of the Chelsio 10Gb Ethernet Driver. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License, version 2, as *
* published by the Free Software Foundation. *
* *
* You should have received a copy of the GNU General Public License along *
* with this program; if not, write to the Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
* *
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED *
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF *
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. *
* *
* http://www.chelsio.com *
* *
* Copyright (c) 2003 - 2005 Chelsio Communications, Inc. *
* All rights reserved. *
* *
* Maintainers: maintainers@chelsio.com *
* *
* Authors: Dimitrios Michailidis <dm@chelsio.com> *
* Tina Yang <tainay@chelsio.com> *
* Felix Marti <felix@chelsio.com> *
* Scott Bardone <sbardone@chelsio.com> *
* Kurt Ottaway <kottaway@chelsio.com> *
* Frank DiMambro <frank@chelsio.com> *
* *
* History: *
* *
****************************************************************************/
#ifndef CHELSIO_ESPI_H
#define CHELSIO_ESPI_H
#include "common.h"
struct espi_intr_counts {
unsigned int DIP4_err;
unsigned int rx_drops;
unsigned int tx_drops;
unsigned int rx_ovflw;
unsigned int parity_err;
unsigned int DIP2_parity_err;
};
struct peespi;
struct peespi *t1_espi_create(adapter_t *adapter);
void t1_espi_destroy(struct peespi *espi);
int t1_espi_init(struct peespi *espi, int mac_type, int nports);
void t1_espi_intr_enable(struct peespi *);
void t1_espi_intr_clear(struct peespi *);
void t1_espi_intr_disable(struct peespi *);
int t1_espi_intr_handler(struct peespi *);
void t1_espi_set_misc_ctrl(adapter_t *adapter, u32 val);
u32 t1_espi_get_mon(adapter_t *adapter, u32 addr, u8 wait);
#endif
/*****************************************************************************
* *
* File: gmac.h *
* $Revision: 1.3 $ *
* $Date: 2005/03/23 07:15:58 $ *
* Description: *
* Generic MAC functionality. *
* part of the Chelsio 10Gb Ethernet Driver. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License, version 2, as *
* published by the Free Software Foundation. *
* *
* You should have received a copy of the GNU General Public License along *
* with this program; if not, write to the Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
* *
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED *
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF *
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. *
* *
* http://www.chelsio.com *
* *
* Copyright (c) 2003 - 2005 Chelsio Communications, Inc. *
* All rights reserved. *
* *
* Maintainers: maintainers@chelsio.com *
* *
* Authors: Dimitrios Michailidis <dm@chelsio.com> *
* Tina Yang <tainay@chelsio.com> *
* Felix Marti <felix@chelsio.com> *
* Scott Bardone <sbardone@chelsio.com> *
* Kurt Ottaway <kottaway@chelsio.com> *
* Frank DiMambro <frank@chelsio.com> *
* *
* History: *
* *
****************************************************************************/
#ifndef CHELSIO_GMAC_H
#define CHELSIO_GMAC_H
#include "common.h"
enum { MAC_STATS_UPDATE_FAST, MAC_STATS_UPDATE_FULL };
enum { MAC_DIRECTION_RX = 1, MAC_DIRECTION_TX = 2 };
struct cmac_statistics {
/* Transmit */
u64 TxOctetsOK;
u64 TxOctetsBad;
u64 TxUnicastFramesOK;
u64 TxMulticastFramesOK;
u64 TxBroadcastFramesOK;
u64 TxPauseFrames;
u64 TxFramesWithDeferredXmissions;
u64 TxLateCollisions;
u64 TxTotalCollisions;
u64 TxFramesAbortedDueToXSCollisions;
u64 TxUnderrun;
u64 TxLengthErrors;
u64 TxInternalMACXmitError;
u64 TxFramesWithExcessiveDeferral;
u64 TxFCSErrors;
/* Receive */
u64 RxOctetsOK;
u64 RxOctetsBad;
u64 RxUnicastFramesOK;
u64 RxMulticastFramesOK;
u64 RxBroadcastFramesOK;
u64 RxPauseFrames;
u64 RxFCSErrors;
u64 RxAlignErrors;
u64 RxSymbolErrors;
u64 RxDataErrors;
u64 RxSequenceErrors;
u64 RxRuntErrors;
u64 RxJabberErrors;
u64 RxInternalMACRcvError;
u64 RxInRangeLengthErrors;
u64 RxOutOfRangeLengthField;
u64 RxFrameTooLongErrors;
};
struct cmac_ops {
void (*destroy)(struct cmac *);
int (*reset)(struct cmac *);
int (*interrupt_enable)(struct cmac *);
int (*interrupt_disable)(struct cmac *);
int (*interrupt_clear)(struct cmac *);
int (*interrupt_handler)(struct cmac *);
int (*enable)(struct cmac *, int);
int (*disable)(struct cmac *, int);
int (*loopback_enable)(struct cmac *);
int (*loopback_disable)(struct cmac *);
int (*set_mtu)(struct cmac *, int mtu);
int (*set_rx_mode)(struct cmac *, struct t1_rx_mode *rm);
int (*set_speed_duplex_fc)(struct cmac *, int speed, int duplex, int fc);
int (*get_speed_duplex_fc)(struct cmac *, int *speed, int *duplex,
int *fc);
const struct cmac_statistics *(*statistics_update)(struct cmac *, int);
int (*macaddress_get)(struct cmac *, u8 mac_addr[6]);
int (*macaddress_set)(struct cmac *, u8 mac_addr[6]);
};
typedef struct _cmac_instance cmac_instance;
struct cmac {
struct cmac_statistics stats;
adapter_t *adapter;
struct cmac_ops *ops;
cmac_instance *instance;
};
struct gmac {
unsigned int stats_update_period;
struct cmac *(*create)(adapter_t *adapter, int index);
int (*reset)(adapter_t *);
};
extern struct gmac t1_pm3393_ops;
extern struct gmac t1_chelsio_mac_ops;
extern struct gmac t1_vsc7321_ops;
extern struct gmac t1_ixf1010_ops;
extern struct gmac t1_dummy_mac_ops;
#endif
/*****************************************************************************
* *
* File: mv88x201x.c *
* $Revision: 1.7 $ *
* $Date: 2005/03/23 07:15:59 $ *
* Description: *
* Marvell PHY (mv88x201x) functionality. *
* part of the Chelsio 10Gb Ethernet Driver. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License, version 2, as *
* published by the Free Software Foundation. *
* *
* You should have received a copy of the GNU General Public License along *
* with this program; if not, write to the Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
* *
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED *
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF *
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. *
* *
* http://www.chelsio.com *
* *
* Copyright (c) 2003 - 2005 Chelsio Communications, Inc. *
* All rights reserved. *
* *
* Maintainers: maintainers@chelsio.com *
* *
* Authors: Dimitrios Michailidis <dm@chelsio.com> *
* Tina Yang <tainay@chelsio.com> *
* Felix Marti <felix@chelsio.com> *
* Scott Bardone <sbardone@chelsio.com> *
* Kurt Ottaway <kottaway@chelsio.com> *
* Frank DiMambro <frank@chelsio.com> *
* *
* History: *
* *
****************************************************************************/
#include "cphy.h"
#include "elmer0.h"
/*
* The 88x2010 Rev C. requires some link status registers * to be read
* twice in order to get the right values. Future * revisions will fix
* this problem and then this macro * can disappear.
*/
#define MV88x2010_LINK_STATUS_BUGS 1
static int led_init(struct cphy *cphy)
{
/* Setup the LED registers so we can turn on/off.
* Writing these bits maps control to another
* register. mmd(0x1) addr(0x7)
*/
mdio_write(cphy, 0x3, 0x8304, 0xdddd);
return 0;
}
static int led_link(struct cphy *cphy, u32 do_enable)
{
u32 led = 0;
#define LINK_ENABLE_BIT 0x1
mdio_read(cphy, 0x1, 0x7, &led);
if (do_enable & LINK_ENABLE_BIT) {
led |= LINK_ENABLE_BIT;
mdio_write(cphy, 0x1, 0x7, led);
} else {
led &= ~LINK_ENABLE_BIT;
mdio_write(cphy, 0x1, 0x7, led);
}
return 0;
}
/* Port Reset */
static int mv88x201x_reset(struct cphy *cphy, int wait)
{
/* This can be done through registers. It is not required since
* a full chip reset is used.
*/
return 0;
}
static int mv88x201x_interrupt_enable(struct cphy *cphy)
{
/* Enable PHY LASI interrupts. */
mdio_write(cphy, 0x1, 0x9002, 0x1);
/* Enable Marvell interrupts through Elmer0. */
if (t1_is_asic(cphy->adapter)) {
u32 elmer;
t1_tpi_read(cphy->adapter, A_ELMER0_INT_ENABLE, &elmer);
elmer |= ELMER0_GP_BIT6;
t1_tpi_write(cphy->adapter, A_ELMER0_INT_ENABLE, elmer);
}
return 0;
}
static int mv88x201x_interrupt_disable(struct cphy *cphy)
{
/* Disable PHY LASI interrupts. */
mdio_write(cphy, 0x1, 0x9002, 0x0);
/* Disable Marvell interrupts through Elmer0. */
if (t1_is_asic(cphy->adapter)) {
u32 elmer;
t1_tpi_read(cphy->adapter, A_ELMER0_INT_ENABLE, &elmer);
elmer &= ~ELMER0_GP_BIT6;
t1_tpi_write(cphy->adapter, A_ELMER0_INT_ENABLE, elmer);
}
return 0;
}
static int mv88x201x_interrupt_clear(struct cphy *cphy)
{
u32 elmer;
u32 val;
#ifdef MV88x2010_LINK_STATUS_BUGS
/* Required to read twice before clear takes affect. */
mdio_read(cphy, 0x1, 0x9003, &val);
mdio_read(cphy, 0x1, 0x9004, &val);
mdio_read(cphy, 0x1, 0x9005, &val);
/* Read this register after the others above it else
* the register doesn't clear correctly.
*/
mdio_read(cphy, 0x1, 0x1, &val);
#endif
/* Clear link status. */
mdio_read(cphy, 0x1, 0x1, &val);
/* Clear PHY LASI interrupts. */
mdio_read(cphy, 0x1, 0x9005, &val);
#ifdef MV88x2010_LINK_STATUS_BUGS
/* Do it again. */
mdio_read(cphy, 0x1, 0x9003, &val);
mdio_read(cphy, 0x1, 0x9004, &val);
#endif
/* Clear Marvell interrupts through Elmer0. */
if (t1_is_asic(cphy->adapter)) {
t1_tpi_read(cphy->adapter, A_ELMER0_INT_CAUSE, &elmer);
elmer |= ELMER0_GP_BIT6;
t1_tpi_write(cphy->adapter, A_ELMER0_INT_CAUSE, elmer);
}
return 0;
}
static int mv88x201x_interrupt_handler(struct cphy *cphy)
{
/* Clear interrupts */
mv88x201x_interrupt_clear(cphy);
/* We have only enabled link change interrupts and so
* cphy_cause must be a link change interrupt.
*/
return cphy_cause_link_change;
}
static int mv88x201x_set_loopback(struct cphy *cphy, int on)
{
return 0;
}
static int mv88x201x_get_link_status(struct cphy *cphy, int *link_ok,
int *speed, int *duplex, int *fc)
{
u32 val = 0;
#define LINK_STATUS_BIT 0x4
if (link_ok) {
/* Read link status. */
mdio_read(cphy, 0x1, 0x1, &val);
val &= LINK_STATUS_BIT;
*link_ok = (val == LINK_STATUS_BIT);
/* Turn on/off Link LED */
led_link(cphy, *link_ok);
}
if (speed)
*speed = SPEED_10000;
if (duplex)
*duplex = DUPLEX_FULL;
if (fc)
*fc = PAUSE_RX | PAUSE_TX;
return 0;
}
static void mv88x201x_destroy(struct cphy *cphy)
{
kfree(cphy);
}
static struct cphy_ops mv88x201x_ops = {
.destroy = mv88x201x_destroy,
.reset = mv88x201x_reset,
.interrupt_enable = mv88x201x_interrupt_enable,
.interrupt_disable = mv88x201x_interrupt_disable,
.interrupt_clear = mv88x201x_interrupt_clear,
.interrupt_handler = mv88x201x_interrupt_handler,
.get_link_status = mv88x201x_get_link_status,
.set_loopback = mv88x201x_set_loopback,
};
static struct cphy *mv88x201x_phy_create(adapter_t *adapter, int phy_addr,
struct mdio_ops *mdio_ops)
{
u32 val;
struct cphy *cphy = kmalloc(sizeof(*cphy), GFP_KERNEL);
if (!cphy)
return NULL;
memset(cphy, 0, sizeof(*cphy));
cphy_init(cphy, adapter, phy_addr, &mv88x201x_ops, mdio_ops);
/* Commands the PHY to enable XFP's clock. */
mdio_read(cphy, 0x3, 0x8300, &val);
mdio_write(cphy, 0x3, 0x8300, val | 1);
/* Clear link status. Required because of a bug in the PHY. */
mdio_read(cphy, 0x1, 0x8, &val);
mdio_read(cphy, 0x3, 0x8, &val);
/* Allows for Link,Ack LED turn on/off */
led_init(cphy);
return cphy;
}
/* Chip Reset */
static int mv88x201x_phy_reset(adapter_t *adapter)
{
u32 val;
t1_tpi_read(adapter, A_ELMER0_GPO, &val);
val &= ~4;
t1_tpi_write(adapter, A_ELMER0_GPO, val);
msleep(100);
t1_tpi_write(adapter, A_ELMER0_GPO, val | 4);
msleep(1000);
/* Now lets enable the Laser. Delay 100us */
t1_tpi_read(adapter, A_ELMER0_GPO, &val);
val |= 0x8000;
t1_tpi_write(adapter, A_ELMER0_GPO, val);
udelay(100);
return 0;
}
struct gphy t1_mv88x201x_ops = {
mv88x201x_phy_create,
mv88x201x_phy_reset
};
/*****************************************************************************
* *
* File: osdep.h *
* $Revision: 1.9 $ *
* $Date: 2005/03/23 07:41:27 $ *
* Description: *
* part of the Chelsio 10Gb Ethernet Driver. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License, version 2, as *
* published by the Free Software Foundation. *
* *
* You should have received a copy of the GNU General Public License along *
* with this program; if not, write to the Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
* *
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED *
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF *
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. *
* *
* http://www.chelsio.com *
* *
* Copyright (c) 2003 - 2005 Chelsio Communications, Inc. *
* All rights reserved. *
* *
* Maintainers: maintainers@chelsio.com *
* *
* Authors: Dimitrios Michailidis <dm@chelsio.com> *
* Tina Yang <tainay@chelsio.com> *
* Felix Marti <felix@chelsio.com> *
* Scott Bardone <sbardone@chelsio.com> *
* Kurt Ottaway <kottaway@chelsio.com> *
* Frank DiMambro <frank@chelsio.com> *
* *
* History: *
* *
****************************************************************************/
#ifndef __CHELSIO_OSDEP_H
#define __CHELSIO_OSDEP_H
#include <linux/version.h>
#include <linux/module.h>
#include <linux/config.h>
#include <linux/types.h>
#include <linux/delay.h>
#include <linux/pci.h>
#include <linux/ethtool.h>
#include <linux/mii.h>
#include <linux/crc32.h>
#include <linux/init.h>
#include <asm/io.h>
#include "cxgb2.h"
#define DRV_NAME "cxgb"
#define PFX DRV_NAME ": "
#define CH_ERR(fmt, ...) printk(KERN_ERR PFX fmt, ## __VA_ARGS__)
#define CH_WARN(fmt, ...) printk(KERN_WARNING PFX fmt, ## __VA_ARGS__)
#define CH_ALERT(fmt, ...) printk(KERN_ALERT PFX fmt, ## __VA_ARGS__)
/*
* More powerful macro that selectively prints messages based on msg_enable.
* For info and debugging messages.
*/
#define CH_MSG(adapter, level, category, fmt, ...) do { \
if ((adapter)->msg_enable & NETIF_MSG_##category) \
printk(KERN_##level PFX "%s: " fmt, (adapter)->name, \
## __VA_ARGS__); \
} while (0)
#ifdef DEBUG
# define CH_DBG(adapter, category, fmt, ...) \
CH_MSG(adapter, DEBUG, category, fmt, ## __VA_ARGS__)
#else
# define CH_DBG(fmt, ...)
#endif
/* Additional NETIF_MSG_* categories */
#define NETIF_MSG_MMIO 0x8000000
#define CH_DEVICE(devid, ssid, idx) \
{ PCI_VENDOR_ID_CHELSIO, devid, PCI_ANY_ID, ssid, 0, 0, idx }
#define SUPPORTED_PAUSE (1 << 13)
#define SUPPORTED_LOOPBACK (1 << 15)
#define ADVERTISED_PAUSE (1 << 13)
#define ADVERTISED_ASYM_PAUSE (1 << 14)
/*
* Now that we have included the driver's main data structure,
* we typedef it to something the rest of the system understands.
*/
typedef struct adapter adapter_t;
#define TPI_LOCK(adapter) spin_lock(&(adapter)->tpi_lock)
#define TPI_UNLOCK(adapter) spin_unlock(&(adapter)->tpi_lock)
void t1_elmer0_ext_intr(adapter_t *adapter);
void t1_link_changed(adapter_t *adapter, int port_id, int link_status,
int speed, int duplex, int fc);
static inline u16 t1_read_reg_2(adapter_t *adapter, u32 reg_addr)
{
u16 val = readw(adapter->regs + reg_addr);
CH_DBG(adapter, MMIO, "read register 0x%x value 0x%x\n", reg_addr,
val);
return val;
}
static inline void t1_write_reg_2(adapter_t *adapter, u32 reg_addr, u16 val)
{
CH_DBG(adapter, MMIO, "setting register 0x%x to 0x%x\n", reg_addr,
val);
writew(val, adapter->regs + reg_addr);
}
static inline u32 t1_read_reg_4(adapter_t *adapter, u32 reg_addr)
{
u32 val = readl(adapter->regs + reg_addr);
CH_DBG(adapter, MMIO, "read register 0x%x value 0x%x\n", reg_addr,
val);
return val;
}
static inline void t1_write_reg_4(adapter_t *adapter, u32 reg_addr, u32 val)
{
CH_DBG(adapter, MMIO, "setting register 0x%x to 0x%x\n", reg_addr,
val);
writel(val, adapter->regs + reg_addr);
}
static inline const char *port_name(adapter_t *adapter, int port_idx)
{
return adapter->port[port_idx].dev->name;
}
static inline void t1_set_hw_addr(adapter_t *adapter, int port_idx,
u8 hw_addr[])
{
memcpy(adapter->port[port_idx].dev->dev_addr, hw_addr, ETH_ALEN);
}
struct t1_rx_mode {
struct net_device *dev;
u32 idx;
struct dev_mc_list *list;
};
#define t1_rx_mode_promisc(rm) (rm->dev->flags & IFF_PROMISC)
#define t1_rx_mode_allmulti(rm) (rm->dev->flags & IFF_ALLMULTI)
#define t1_rx_mode_mc_cnt(rm) (rm->dev->mc_count)
static inline u8 *t1_get_next_mcaddr(struct t1_rx_mode *rm)
{
u8 *addr = 0;
if (rm->idx++ < rm->dev->mc_count) {
addr = rm->list->dmi_addr;
rm->list = rm->list->next;
}
return addr;
}
#endif
/*****************************************************************************
* *
* File: pm3393.c *
* $Revision: 1.9 $ *
* $Date: 2005/03/23 07:41:27 $ *
* Description: *
* PMC/SIERRA (pm3393) MAC-PHY functionality. *
* part of the Chelsio 10Gb Ethernet Driver. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License, version 2, as *
* published by the Free Software Foundation. *
* *
* You should have received a copy of the GNU General Public License along *
* with this program; if not, write to the Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
* *
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED *
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF *
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. *
* *
* http://www.chelsio.com *
* *
* Copyright (c) 2003 - 2005 Chelsio Communications, Inc. *
* All rights reserved. *
* *
* Maintainers: maintainers@chelsio.com *
* *
* Authors: Dimitrios Michailidis <dm@chelsio.com> *
* Tina Yang <tainay@chelsio.com> *
* Felix Marti <felix@chelsio.com> *
* Scott Bardone <sbardone@chelsio.com> *
* Kurt Ottaway <kottaway@chelsio.com> *
* Frank DiMambro <frank@chelsio.com> *
* *
* History: *
* *
****************************************************************************/
#include "common.h"
#include "regs.h"
#include "gmac.h"
#include "elmer0.h"
#include "suni1x10gexp_regs.h"
/* 802.3ae 10Gb/s MDIO Manageable Device(MMD)
*/
#define MMD_RESERVED 0
#define MMD_PMAPMD 1
#define MMD_WIS 2
#define MMD_PCS 3
#define MMD_PHY_XGXS 4 /* XGMII Extender Sublayer */
#define MMD_DTE_XGXS 5
#define PHY_XGXS_CTRL_1 0
#define PHY_XGXS_STATUS_1 1
#define OFFSET(REG_ADDR) (REG_ADDR << 2)
/* Max frame size PM3393 can handle. Includes Ethernet header and CRC. */
#define MAX_FRAME_SIZE 9600
#define IPG 12
#define TXXG_CONF1_VAL ((IPG << SUNI1x10GEXP_BITOFF_TXXG_IPGT) | \
SUNI1x10GEXP_BITMSK_TXXG_32BIT_ALIGN | SUNI1x10GEXP_BITMSK_TXXG_CRCEN | \
SUNI1x10GEXP_BITMSK_TXXG_PADEN)
#define RXXG_CONF1_VAL (SUNI1x10GEXP_BITMSK_RXXG_PUREP | 0x14 | \
SUNI1x10GEXP_BITMSK_RXXG_FLCHK | SUNI1x10GEXP_BITMSK_RXXG_CRC_STRIP)
/* Update statistics every 15 minutes */
#define STATS_TICK_SECS (15 * 60)
enum { /* RMON registers */
RxOctetsReceivedOK = SUNI1x10GEXP_REG_MSTAT_COUNTER_1_LOW,
RxUnicastFramesReceivedOK = SUNI1x10GEXP_REG_MSTAT_COUNTER_4_LOW,
RxMulticastFramesReceivedOK = SUNI1x10GEXP_REG_MSTAT_COUNTER_5_LOW,
RxBroadcastFramesReceivedOK = SUNI1x10GEXP_REG_MSTAT_COUNTER_6_LOW,
RxPAUSEMACCtrlFramesReceived = SUNI1x10GEXP_REG_MSTAT_COUNTER_8_LOW,
RxFrameCheckSequenceErrors = SUNI1x10GEXP_REG_MSTAT_COUNTER_10_LOW,
RxFramesLostDueToInternalMACErrors = SUNI1x10GEXP_REG_MSTAT_COUNTER_11_LOW,
RxSymbolErrors = SUNI1x10GEXP_REG_MSTAT_COUNTER_12_LOW,
RxInRangeLengthErrors = SUNI1x10GEXP_REG_MSTAT_COUNTER_13_LOW,
RxFramesTooLongErrors = SUNI1x10GEXP_REG_MSTAT_COUNTER_15_LOW,
RxJabbers = SUNI1x10GEXP_REG_MSTAT_COUNTER_16_LOW,
RxFragments = SUNI1x10GEXP_REG_MSTAT_COUNTER_17_LOW,
RxUndersizedFrames = SUNI1x10GEXP_REG_MSTAT_COUNTER_18_LOW,
TxOctetsTransmittedOK = SUNI1x10GEXP_REG_MSTAT_COUNTER_33_LOW,
TxFramesLostDueToInternalMACTransmissionError = SUNI1x10GEXP_REG_MSTAT_COUNTER_35_LOW,
TxTransmitSystemError = SUNI1x10GEXP_REG_MSTAT_COUNTER_36_LOW,
TxUnicastFramesTransmittedOK = SUNI1x10GEXP_REG_MSTAT_COUNTER_38_LOW,
TxMulticastFramesTransmittedOK = SUNI1x10GEXP_REG_MSTAT_COUNTER_40_LOW,
TxBroadcastFramesTransmittedOK = SUNI1x10GEXP_REG_MSTAT_COUNTER_42_LOW,
TxPAUSEMACCtrlFramesTransmitted = SUNI1x10GEXP_REG_MSTAT_COUNTER_43_LOW
};
struct _cmac_instance {
u8 enabled;
u8 fc;
u8 mac_addr[6];
};
static int pmread(struct cmac *cmac, u32 reg, u32 * data32)
{
t1_tpi_read(cmac->adapter, OFFSET(reg), data32);
return 0;
}
static int pmwrite(struct cmac *cmac, u32 reg, u32 data32)
{
t1_tpi_write(cmac->adapter, OFFSET(reg), data32);
return 0;
}
/* Port reset. */
static int pm3393_reset(struct cmac *cmac)
{
return 0;
}
/*
* Enable interrupts for the PM3393
1. Enable PM3393 BLOCK interrupts.
2. Enable PM3393 Master Interrupt bit(INTE)
3. Enable ELMER's PM3393 bit.
4. Enable Terminator external interrupt.
*/
static int pm3393_interrupt_enable(struct cmac *cmac)
{
u32 pl_intr;
/* PM3393 - Enabling all hardware block interrupts.
*/
pmwrite(cmac, SUNI1x10GEXP_REG_SERDES_3125_INTERRUPT_ENABLE, 0xffff);
pmwrite(cmac, SUNI1x10GEXP_REG_XRF_INTERRUPT_ENABLE, 0xffff);
pmwrite(cmac, SUNI1x10GEXP_REG_XRF_DIAG_INTERRUPT_ENABLE, 0xffff);
pmwrite(cmac, SUNI1x10GEXP_REG_RXOAM_INTERRUPT_ENABLE, 0xffff);
/* Don't interrupt on statistics overflow, we are polling */
pmwrite(cmac, SUNI1x10GEXP_REG_MSTAT_INTERRUPT_MASK_0, 0);
pmwrite(cmac, SUNI1x10GEXP_REG_MSTAT_INTERRUPT_MASK_1, 0);
pmwrite(cmac, SUNI1x10GEXP_REG_MSTAT_INTERRUPT_MASK_2, 0);
pmwrite(cmac, SUNI1x10GEXP_REG_MSTAT_INTERRUPT_MASK_3, 0);
pmwrite(cmac, SUNI1x10GEXP_REG_IFLX_FIFO_OVERFLOW_ENABLE, 0xffff);
pmwrite(cmac, SUNI1x10GEXP_REG_PL4ODP_INTERRUPT_MASK, 0xffff);
pmwrite(cmac, SUNI1x10GEXP_REG_XTEF_INTERRUPT_ENABLE, 0xffff);
pmwrite(cmac, SUNI1x10GEXP_REG_TXOAM_INTERRUPT_ENABLE, 0xffff);
pmwrite(cmac, SUNI1x10GEXP_REG_RXXG_CONFIG_3, 0xffff);
pmwrite(cmac, SUNI1x10GEXP_REG_PL4IO_LOCK_DETECT_MASK, 0xffff);
pmwrite(cmac, SUNI1x10GEXP_REG_TXXG_CONFIG_3, 0xffff);
pmwrite(cmac, SUNI1x10GEXP_REG_PL4IDU_INTERRUPT_MASK, 0xffff);
pmwrite(cmac, SUNI1x10GEXP_REG_EFLX_FIFO_OVERFLOW_ERROR_ENABLE, 0xffff);
/* PM3393 - Global interrupt enable
*/
/* TBD XXX Disable for now until we figure out why error interrupts keep asserting. */
pmwrite(cmac, SUNI1x10GEXP_REG_GLOBAL_INTERRUPT_ENABLE,
0 /*SUNI1x10GEXP_BITMSK_TOP_INTE */ );
/* TERMINATOR - PL_INTERUPTS_EXT */
pl_intr = t1_read_reg_4(cmac->adapter, A_PL_ENABLE);
pl_intr |= F_PL_INTR_EXT;
t1_write_reg_4(cmac->adapter, A_PL_ENABLE, pl_intr);
return 0;
}
static int pm3393_interrupt_disable(struct cmac *cmac)
{
u32 elmer;
/* PM3393 - Enabling HW interrupt blocks. */
pmwrite(cmac, SUNI1x10GEXP_REG_SERDES_3125_INTERRUPT_ENABLE, 0);
pmwrite(cmac, SUNI1x10GEXP_REG_XRF_INTERRUPT_ENABLE, 0);
pmwrite(cmac, SUNI1x10GEXP_REG_XRF_DIAG_INTERRUPT_ENABLE, 0);
pmwrite(cmac, SUNI1x10GEXP_REG_RXOAM_INTERRUPT_ENABLE, 0);
pmwrite(cmac, SUNI1x10GEXP_REG_MSTAT_INTERRUPT_MASK_0, 0);
pmwrite(cmac, SUNI1x10GEXP_REG_MSTAT_INTERRUPT_MASK_1, 0);
pmwrite(cmac, SUNI1x10GEXP_REG_MSTAT_INTERRUPT_MASK_2, 0);
pmwrite(cmac, SUNI1x10GEXP_REG_MSTAT_INTERRUPT_MASK_3, 0);
pmwrite(cmac, SUNI1x10GEXP_REG_IFLX_FIFO_OVERFLOW_ENABLE, 0);
pmwrite(cmac, SUNI1x10GEXP_REG_PL4ODP_INTERRUPT_MASK, 0);
pmwrite(cmac, SUNI1x10GEXP_REG_XTEF_INTERRUPT_ENABLE, 0);
pmwrite(cmac, SUNI1x10GEXP_REG_TXOAM_INTERRUPT_ENABLE, 0);
pmwrite(cmac, SUNI1x10GEXP_REG_RXXG_CONFIG_3, 0);
pmwrite(cmac, SUNI1x10GEXP_REG_PL4IO_LOCK_DETECT_MASK, 0);
pmwrite(cmac, SUNI1x10GEXP_REG_TXXG_CONFIG_3, 0);
pmwrite(cmac, SUNI1x10GEXP_REG_PL4IDU_INTERRUPT_MASK, 0);
pmwrite(cmac, SUNI1x10GEXP_REG_EFLX_FIFO_OVERFLOW_ERROR_ENABLE, 0);
/* PM3393 - Global interrupt enable */
pmwrite(cmac, SUNI1x10GEXP_REG_GLOBAL_INTERRUPT_ENABLE, 0);
/* ELMER - External chip interrupts. */
t1_tpi_read(cmac->adapter, A_ELMER0_INT_ENABLE, &elmer);
elmer &= ~ELMER0_GP_BIT1;
t1_tpi_write(cmac->adapter, A_ELMER0_INT_ENABLE, elmer);
/* TERMINATOR - PL_INTERUPTS_EXT */
/* DO NOT DISABLE TERMINATOR's EXTERNAL INTERRUPTS. ANOTHER CHIP
* COULD WANT THEM ENABLED. We disable PM3393 at the ELMER level.
*/
return 0;
}
static int pm3393_interrupt_clear(struct cmac *cmac)
{
u32 elmer;
u32 pl_intr;
u32 val32;
/* PM3393 - Clearing HW interrupt blocks. Note, this assumes
* bit WCIMODE=0 for a clear-on-read.
*/
pmread(cmac, SUNI1x10GEXP_REG_SERDES_3125_INTERRUPT_STATUS, &val32);
pmread(cmac, SUNI1x10GEXP_REG_XRF_INTERRUPT_STATUS, &val32);
pmread(cmac, SUNI1x10GEXP_REG_XRF_DIAG_INTERRUPT_STATUS, &val32);
pmread(cmac, SUNI1x10GEXP_REG_RXOAM_INTERRUPT_STATUS, &val32);
pmread(cmac, SUNI1x10GEXP_REG_PL4ODP_INTERRUPT, &val32);
pmread(cmac, SUNI1x10GEXP_REG_XTEF_INTERRUPT_STATUS, &val32);
pmread(cmac, SUNI1x10GEXP_REG_IFLX_FIFO_OVERFLOW_INTERRUPT, &val32);
pmread(cmac, SUNI1x10GEXP_REG_TXOAM_INTERRUPT_STATUS, &val32);
pmread(cmac, SUNI1x10GEXP_REG_RXXG_INTERRUPT, &val32);
pmread(cmac, SUNI1x10GEXP_REG_TXXG_INTERRUPT, &val32);
pmread(cmac, SUNI1x10GEXP_REG_PL4IDU_INTERRUPT, &val32);
pmread(cmac, SUNI1x10GEXP_REG_EFLX_FIFO_OVERFLOW_ERROR_INDICATION,
&val32);
pmread(cmac, SUNI1x10GEXP_REG_PL4IO_LOCK_DETECT_STATUS, &val32);
pmread(cmac, SUNI1x10GEXP_REG_PL4IO_LOCK_DETECT_CHANGE, &val32);
/* PM3393 - Global interrupt status
*/
pmread(cmac, SUNI1x10GEXP_REG_MASTER_INTERRUPT_STATUS, &val32);
/* ELMER - External chip interrupts.
*/
t1_tpi_read(cmac->adapter, A_ELMER0_INT_CAUSE, &elmer);
elmer |= ELMER0_GP_BIT1;
t1_tpi_write(cmac->adapter, A_ELMER0_INT_CAUSE, elmer);
/* TERMINATOR - PL_INTERUPTS_EXT
*/
pl_intr = t1_read_reg_4(cmac->adapter, A_PL_CAUSE);
pl_intr |= F_PL_INTR_EXT;
t1_write_reg_4(cmac->adapter, A_PL_CAUSE, pl_intr);
return 0;
}
/* Interrupt handler */
static int pm3393_interrupt_handler(struct cmac *cmac)
{
u32 master_intr_status;
/*
1. Read master interrupt register.
2. Read BLOCK's interrupt status registers.
3. Handle BLOCK interrupts.
*/
/* Read the master interrupt status register. */
pmread(cmac, SUNI1x10GEXP_REG_MASTER_INTERRUPT_STATUS,
&master_intr_status);
CH_DBG(cmac->adapter, INTR, "PM3393 intr cause 0x%x\n",
master_intr_status);
/* TBD XXX Lets just clear everything for now */
pm3393_interrupt_clear(cmac);
return 0;
}
static int pm3393_enable(struct cmac *cmac, int which)
{
if (which & MAC_DIRECTION_RX)
pmwrite(cmac, SUNI1x10GEXP_REG_RXXG_CONFIG_1,
(RXXG_CONF1_VAL | SUNI1x10GEXP_BITMSK_RXXG_RXEN));
if (which & MAC_DIRECTION_TX) {
u32 val = TXXG_CONF1_VAL | SUNI1x10GEXP_BITMSK_TXXG_TXEN0;
if (cmac->instance->fc & PAUSE_RX)
val |= SUNI1x10GEXP_BITMSK_TXXG_FCRX;
if (cmac->instance->fc & PAUSE_TX)
val |= SUNI1x10GEXP_BITMSK_TXXG_FCTX;
pmwrite(cmac, SUNI1x10GEXP_REG_TXXG_CONFIG_1, val);
}
cmac->instance->enabled |= which;
return 0;
}
static int pm3393_enable_port(struct cmac *cmac, int which)
{
/* Clear port statistics */
pmwrite(cmac, SUNI1x10GEXP_REG_MSTAT_CONTROL,
SUNI1x10GEXP_BITMSK_MSTAT_CLEAR);
udelay(2);
memset(&cmac->stats, 0, sizeof(struct cmac_statistics));
pm3393_enable(cmac, which);
/*
* XXX This should be done by the PHY and preferrably not at all.
* The PHY doesn't give us link status indication on its own so have
* the link management code query it instead.
*/
{
extern void link_changed(adapter_t *adapter, int port_id);
link_changed(cmac->adapter, 0);
}
return 0;
}
static int pm3393_disable(struct cmac *cmac, int which)
{
if (which & MAC_DIRECTION_RX)
pmwrite(cmac, SUNI1x10GEXP_REG_RXXG_CONFIG_1, RXXG_CONF1_VAL);
if (which & MAC_DIRECTION_TX)
pmwrite(cmac, SUNI1x10GEXP_REG_TXXG_CONFIG_1, TXXG_CONF1_VAL);
/*
* The disable is graceful. Give the PM3393 time. Can't wait very
* long here, we may be holding locks.
*/
udelay(20);
cmac->instance->enabled &= ~which;
return 0;
}
static int pm3393_loopback_enable(struct cmac *cmac)
{
return 0;
}
static int pm3393_loopback_disable(struct cmac *cmac)
{
return 0;
}
static int pm3393_set_mtu(struct cmac *cmac, int mtu)
{
int enabled = cmac->instance->enabled;
/* MAX_FRAME_SIZE includes header + FCS, mtu doesn't */
mtu += 14 + 4;
if (mtu > MAX_FRAME_SIZE)
return -EINVAL;
/* Disable Rx/Tx MAC before configuring it. */
if (enabled)
pm3393_disable(cmac, MAC_DIRECTION_RX | MAC_DIRECTION_TX);
pmwrite(cmac, SUNI1x10GEXP_REG_RXXG_MAX_FRAME_LENGTH, mtu);
pmwrite(cmac, SUNI1x10GEXP_REG_TXXG_MAX_FRAME_SIZE, mtu);
if (enabled)
pm3393_enable(cmac, enabled);
return 0;
}
static u32 calc_crc(u8 *b, int len)
{
int i;
u32 crc = (u32)~0;
/* calculate crc one bit at a time */
while (len--) {
crc ^= *b++;
for (i = 0; i < 8; i++) {
if (crc & 0x1)
crc = (crc >> 1) ^ 0xedb88320;
else
crc = (crc >> 1);
}
}
/* reverse bits */
crc = ((crc >> 4) & 0x0f0f0f0f) | ((crc << 4) & 0xf0f0f0f0);
crc = ((crc >> 2) & 0x33333333) | ((crc << 2) & 0xcccccccc);
crc = ((crc >> 1) & 0x55555555) | ((crc << 1) & 0xaaaaaaaa);
/* swap bytes */
crc = (crc >> 16) | (crc << 16);
crc = (crc >> 8 & 0x00ff00ff) | (crc << 8 & 0xff00ff00);
return crc;
}
static int pm3393_set_rx_mode(struct cmac *cmac, struct t1_rx_mode *rm)
{
int enabled = cmac->instance->enabled & MAC_DIRECTION_RX;
u32 rx_mode;
/* Disable MAC RX before reconfiguring it */
if (enabled)
pm3393_disable(cmac, MAC_DIRECTION_RX);
pmread(cmac, SUNI1x10GEXP_REG_RXXG_ADDRESS_FILTER_CONTROL_2, &rx_mode);
rx_mode &= ~(SUNI1x10GEXP_BITMSK_RXXG_PMODE |
SUNI1x10GEXP_BITMSK_RXXG_MHASH_EN);
pmwrite(cmac, SUNI1x10GEXP_REG_RXXG_ADDRESS_FILTER_CONTROL_2,
(u16)rx_mode);
if (t1_rx_mode_promisc(rm)) {
/* Promiscuous mode. */
rx_mode |= SUNI1x10GEXP_BITMSK_RXXG_PMODE;
}
if (t1_rx_mode_allmulti(rm)) {
/* Accept all multicast. */
pmwrite(cmac, SUNI1x10GEXP_REG_RXXG_MULTICAST_HASH_LOW, 0xffff);
pmwrite(cmac, SUNI1x10GEXP_REG_RXXG_MULTICAST_HASH_MIDLOW, 0xffff);
pmwrite(cmac, SUNI1x10GEXP_REG_RXXG_MULTICAST_HASH_MIDHIGH, 0xffff);
pmwrite(cmac, SUNI1x10GEXP_REG_RXXG_MULTICAST_HASH_HIGH, 0xffff);
rx_mode |= SUNI1x10GEXP_BITMSK_RXXG_MHASH_EN;
} else if (t1_rx_mode_mc_cnt(rm)) {
/* Accept one or more multicast(s). */
u8 *addr;
int bit;
u16 mc_filter[4] = { 0, };
while ((addr = t1_get_next_mcaddr(rm))) {
bit = (calc_crc(addr, ETH_ALEN) >> 23) & 0x3f; /* bit[23:28] */
mc_filter[bit >> 4] |= 1 << (bit & 0xf);
}
pmwrite(cmac, SUNI1x10GEXP_REG_RXXG_MULTICAST_HASH_LOW, mc_filter[0]);
pmwrite(cmac, SUNI1x10GEXP_REG_RXXG_MULTICAST_HASH_MIDLOW, mc_filter[1]);
pmwrite(cmac, SUNI1x10GEXP_REG_RXXG_MULTICAST_HASH_MIDHIGH, mc_filter[2]);
pmwrite(cmac, SUNI1x10GEXP_REG_RXXG_MULTICAST_HASH_HIGH, mc_filter[3]);
rx_mode |= SUNI1x10GEXP_BITMSK_RXXG_MHASH_EN;
}
pmwrite(cmac, SUNI1x10GEXP_REG_RXXG_ADDRESS_FILTER_CONTROL_2, (u16)rx_mode);
if (enabled)
pm3393_enable(cmac, MAC_DIRECTION_RX);
return 0;
}
static int pm3393_get_speed_duplex_fc(struct cmac *cmac, int *speed,
int *duplex, int *fc)
{
if (speed)
*speed = SPEED_10000;
if (duplex)
*duplex = DUPLEX_FULL;
if (fc)
*fc = cmac->instance->fc;
return 0;
}
static int pm3393_set_speed_duplex_fc(struct cmac *cmac, int speed, int duplex,
int fc)
{
if (speed >= 0 && speed != SPEED_10000)
return -1;
if (duplex >= 0 && duplex != DUPLEX_FULL)
return -1;
if (fc & ~(PAUSE_TX | PAUSE_RX))
return -1;
if (fc != cmac->instance->fc) {
cmac->instance->fc = (u8) fc;
if (cmac->instance->enabled & MAC_DIRECTION_TX)
pm3393_enable(cmac, MAC_DIRECTION_TX);
}
return 0;
}
#define RMON_UPDATE(mac, name, stat_name) \
{ \
t1_tpi_read((mac)->adapter, OFFSET(name), &val0); \
t1_tpi_read((mac)->adapter, OFFSET(((name)+1)), &val1); \
t1_tpi_read((mac)->adapter, OFFSET(((name)+2)), &val2); \
(mac)->stats.stat_name = ((u64)val0 & 0xffff) | \
(((u64)val1 & 0xffff) << 16) | \
(((u64)val2 & 0xff) << 32) | \
((mac)->stats.stat_name & \
(~(u64)0 << 40)); \
if (ro & \
((name - SUNI1x10GEXP_REG_MSTAT_COUNTER_0_LOW) >> 2)) \
(mac)->stats.stat_name += ((u64)1 << 40); \
}
static const struct cmac_statistics *pm3393_update_statistics(struct cmac *mac,
int flag)
{
u64 ro;
u32 val0, val1, val2, val3;
/* Snap the counters */
pmwrite(mac, SUNI1x10GEXP_REG_MSTAT_CONTROL,
SUNI1x10GEXP_BITMSK_MSTAT_SNAP);
/* Counter rollover, clear on read */
pmread(mac, SUNI1x10GEXP_REG_MSTAT_COUNTER_ROLLOVER_0, &val0);
pmread(mac, SUNI1x10GEXP_REG_MSTAT_COUNTER_ROLLOVER_1, &val1);
pmread(mac, SUNI1x10GEXP_REG_MSTAT_COUNTER_ROLLOVER_2, &val2);
pmread(mac, SUNI1x10GEXP_REG_MSTAT_COUNTER_ROLLOVER_3, &val3);
ro = ((u64)val0 & 0xffff) | (((u64)val1 & 0xffff) << 16) |
(((u64)val2 & 0xffff) << 32) | (((u64)val3 & 0xffff) << 48);
/* Rx stats */
RMON_UPDATE(mac, RxOctetsReceivedOK, RxOctetsOK);
RMON_UPDATE(mac, RxUnicastFramesReceivedOK, RxUnicastFramesOK);
RMON_UPDATE(mac, RxMulticastFramesReceivedOK, RxMulticastFramesOK);
RMON_UPDATE(mac, RxBroadcastFramesReceivedOK, RxBroadcastFramesOK);
RMON_UPDATE(mac, RxPAUSEMACCtrlFramesReceived, RxPauseFrames);
RMON_UPDATE(mac, RxFrameCheckSequenceErrors, RxFCSErrors);
RMON_UPDATE(mac, RxFramesLostDueToInternalMACErrors,
RxInternalMACRcvError);
RMON_UPDATE(mac, RxSymbolErrors, RxSymbolErrors);
RMON_UPDATE(mac, RxInRangeLengthErrors, RxInRangeLengthErrors);
RMON_UPDATE(mac, RxFramesTooLongErrors , RxFrameTooLongErrors);
RMON_UPDATE(mac, RxJabbers, RxJabberErrors);
RMON_UPDATE(mac, RxFragments, RxRuntErrors);
RMON_UPDATE(mac, RxUndersizedFrames, RxRuntErrors);
/* Tx stats */
RMON_UPDATE(mac, TxOctetsTransmittedOK, TxOctetsOK);
RMON_UPDATE(mac, TxFramesLostDueToInternalMACTransmissionError,
TxInternalMACXmitError);
RMON_UPDATE(mac, TxTransmitSystemError, TxFCSErrors);
RMON_UPDATE(mac, TxUnicastFramesTransmittedOK, TxUnicastFramesOK);
RMON_UPDATE(mac, TxMulticastFramesTransmittedOK, TxMulticastFramesOK);
RMON_UPDATE(mac, TxBroadcastFramesTransmittedOK, TxBroadcastFramesOK);
RMON_UPDATE(mac, TxPAUSEMACCtrlFramesTransmitted, TxPauseFrames);
return &mac->stats;
}
static int pm3393_macaddress_get(struct cmac *cmac, u8 mac_addr[6])
{
memcpy(mac_addr, cmac->instance->mac_addr, 6);
return 0;
}
static int pm3393_macaddress_set(struct cmac *cmac, u8 ma[6])
{
u32 val, lo, mid, hi, enabled = cmac->instance->enabled;
/*
* MAC addr: 00:07:43:00:13:09
*
* ma[5] = 0x09
* ma[4] = 0x13
* ma[3] = 0x00
* ma[2] = 0x43
* ma[1] = 0x07
* ma[0] = 0x00
*
* The PM3393 requires byte swapping and reverse order entry
* when programming MAC addresses:
*
* low_bits[15:0] = ma[1]:ma[0]
* mid_bits[31:16] = ma[3]:ma[2]
* high_bits[47:32] = ma[5]:ma[4]
*/
/* Store local copy */
memcpy(cmac->instance->mac_addr, ma, 6);
lo = ((u32) ma[1] << 8) | (u32) ma[0];
mid = ((u32) ma[3] << 8) | (u32) ma[2];
hi = ((u32) ma[5] << 8) | (u32) ma[4];
/* Disable Rx/Tx MAC before configuring it. */
if (enabled)
pm3393_disable(cmac, MAC_DIRECTION_RX | MAC_DIRECTION_TX);
/* Set RXXG Station Address */
pmwrite(cmac, SUNI1x10GEXP_REG_RXXG_SA_15_0, lo);
pmwrite(cmac, SUNI1x10GEXP_REG_RXXG_SA_31_16, mid);
pmwrite(cmac, SUNI1x10GEXP_REG_RXXG_SA_47_32, hi);
/* Set TXXG Station Address */
pmwrite(cmac, SUNI1x10GEXP_REG_TXXG_SA_15_0, lo);
pmwrite(cmac, SUNI1x10GEXP_REG_TXXG_SA_31_16, mid);
pmwrite(cmac, SUNI1x10GEXP_REG_TXXG_SA_47_32, hi);
/* Setup Exact Match Filter 1 with our MAC address
*
* Must disable exact match filter before configuring it.
*/
pmread(cmac, SUNI1x10GEXP_REG_RXXG_ADDRESS_FILTER_CONTROL_0, &val);
val &= 0xff0f;
pmwrite(cmac, SUNI1x10GEXP_REG_RXXG_ADDRESS_FILTER_CONTROL_0, val);
pmwrite(cmac, SUNI1x10GEXP_REG_RXXG_EXACT_MATCH_ADDR_1_LOW, lo);
pmwrite(cmac, SUNI1x10GEXP_REG_RXXG_EXACT_MATCH_ADDR_1_MID, mid);
pmwrite(cmac, SUNI1x10GEXP_REG_RXXG_EXACT_MATCH_ADDR_1_HIGH, hi);
val |= 0x0090;
pmwrite(cmac, SUNI1x10GEXP_REG_RXXG_ADDRESS_FILTER_CONTROL_0, val);
if (enabled)
pm3393_enable(cmac, enabled);
return 0;
}
static void pm3393_destroy(struct cmac *cmac)
{
kfree(cmac);
}
static struct cmac_ops pm3393_ops = {
.destroy = pm3393_destroy,
.reset = pm3393_reset,
.interrupt_enable = pm3393_interrupt_enable,
.interrupt_disable = pm3393_interrupt_disable,
.interrupt_clear = pm3393_interrupt_clear,
.interrupt_handler = pm3393_interrupt_handler,
.enable = pm3393_enable_port,
.disable = pm3393_disable,
.loopback_enable = pm3393_loopback_enable,
.loopback_disable = pm3393_loopback_disable,
.set_mtu = pm3393_set_mtu,
.set_rx_mode = pm3393_set_rx_mode,
.get_speed_duplex_fc = pm3393_get_speed_duplex_fc,
.set_speed_duplex_fc = pm3393_set_speed_duplex_fc,
.statistics_update = pm3393_update_statistics,
.macaddress_get = pm3393_macaddress_get,
.macaddress_set = pm3393_macaddress_set
};
static struct cmac *pm3393_mac_create(adapter_t *adapter, int index)
{
struct cmac *cmac;
cmac = kmalloc(sizeof(*cmac) + sizeof(cmac_instance), GFP_KERNEL);
if (!cmac)
return NULL;
memset(cmac, 0, sizeof(*cmac));
cmac->ops = &pm3393_ops;
cmac->instance = (cmac_instance *) (cmac + 1);
cmac->adapter = adapter;
cmac->instance->fc = PAUSE_TX | PAUSE_RX;
t1_tpi_write(adapter, OFFSET(0x0001), 0x00008000);
t1_tpi_write(adapter, OFFSET(0x0001), 0x00000000);
t1_tpi_write(adapter, OFFSET(0x2308), 0x00009800);
t1_tpi_write(adapter, OFFSET(0x2305), 0x00001001); /* PL4IO Enable */
t1_tpi_write(adapter, OFFSET(0x2320), 0x00008800);
t1_tpi_write(adapter, OFFSET(0x2321), 0x00008800);
t1_tpi_write(adapter, OFFSET(0x2322), 0x00008800);
t1_tpi_write(adapter, OFFSET(0x2323), 0x00008800);
t1_tpi_write(adapter, OFFSET(0x2324), 0x00008800);
t1_tpi_write(adapter, OFFSET(0x2325), 0x00008800);
t1_tpi_write(adapter, OFFSET(0x2326), 0x00008800);
t1_tpi_write(adapter, OFFSET(0x2327), 0x00008800);
t1_tpi_write(adapter, OFFSET(0x2328), 0x00008800);
t1_tpi_write(adapter, OFFSET(0x2329), 0x00008800);
t1_tpi_write(adapter, OFFSET(0x232a), 0x00008800);
t1_tpi_write(adapter, OFFSET(0x232b), 0x00008800);
t1_tpi_write(adapter, OFFSET(0x232c), 0x00008800);
t1_tpi_write(adapter, OFFSET(0x232d), 0x00008800);
t1_tpi_write(adapter, OFFSET(0x232e), 0x00008800);
t1_tpi_write(adapter, OFFSET(0x232f), 0x00008800);
t1_tpi_write(adapter, OFFSET(0x230d), 0x00009c00);
t1_tpi_write(adapter, OFFSET(0x2304), 0x00000202); /* PL4IO Calendar Repetitions */
t1_tpi_write(adapter, OFFSET(0x3200), 0x00008080); /* EFLX Enable */
t1_tpi_write(adapter, OFFSET(0x3210), 0x00000000); /* EFLX Channel Deprovision */
t1_tpi_write(adapter, OFFSET(0x3203), 0x00000000); /* EFLX Low Limit */
t1_tpi_write(adapter, OFFSET(0x3204), 0x00000040); /* EFLX High Limit */
t1_tpi_write(adapter, OFFSET(0x3205), 0x000002cc); /* EFLX Almost Full */
t1_tpi_write(adapter, OFFSET(0x3206), 0x00000199); /* EFLX Almost Empty */
t1_tpi_write(adapter, OFFSET(0x3207), 0x00000240); /* EFLX Cut Through Threshold */
t1_tpi_write(adapter, OFFSET(0x3202), 0x00000000); /* EFLX Indirect Register Update */
t1_tpi_write(adapter, OFFSET(0x3210), 0x00000001); /* EFLX Channel Provision */
t1_tpi_write(adapter, OFFSET(0x3208), 0x0000ffff); /* EFLX Undocumented */
t1_tpi_write(adapter, OFFSET(0x320a), 0x0000ffff); /* EFLX Undocumented */
t1_tpi_write(adapter, OFFSET(0x320c), 0x0000ffff); /* EFLX enable overflow interrupt The other bit are undocumented */
t1_tpi_write(adapter, OFFSET(0x320e), 0x0000ffff); /* EFLX Undocumented */
t1_tpi_write(adapter, OFFSET(0x2200), 0x0000c000); /* IFLX Configuration - enable */
t1_tpi_write(adapter, OFFSET(0x2201), 0x00000000); /* IFLX Channel Deprovision */
t1_tpi_write(adapter, OFFSET(0x220e), 0x00000000); /* IFLX Low Limit */
t1_tpi_write(adapter, OFFSET(0x220f), 0x00000100); /* IFLX High Limit */
t1_tpi_write(adapter, OFFSET(0x2210), 0x00000c00); /* IFLX Almost Full Limit */
t1_tpi_write(adapter, OFFSET(0x2211), 0x00000599); /* IFLX Almost Empty Limit */
t1_tpi_write(adapter, OFFSET(0x220d), 0x00000000); /* IFLX Indirect Register Update */
t1_tpi_write(adapter, OFFSET(0x2201), 0x00000001); /* IFLX Channel Provision */
t1_tpi_write(adapter, OFFSET(0x2203), 0x0000ffff); /* IFLX Undocumented */
t1_tpi_write(adapter, OFFSET(0x2205), 0x0000ffff); /* IFLX Undocumented */
t1_tpi_write(adapter, OFFSET(0x2209), 0x0000ffff); /* IFLX Enable overflow interrupt. The other bit are undocumented */
t1_tpi_write(adapter, OFFSET(0x2241), 0xfffffffe); /* PL4MOS Undocumented */
t1_tpi_write(adapter, OFFSET(0x2242), 0x0000ffff); /* PL4MOS Undocumented */
t1_tpi_write(adapter, OFFSET(0x2243), 0x00000008); /* PL4MOS Starving Burst Size */
t1_tpi_write(adapter, OFFSET(0x2244), 0x00000008); /* PL4MOS Hungry Burst Size */
t1_tpi_write(adapter, OFFSET(0x2245), 0x00000008); /* PL4MOS Transfer Size */
t1_tpi_write(adapter, OFFSET(0x2240), 0x00000005); /* PL4MOS Disable */
t1_tpi_write(adapter, OFFSET(0x2280), 0x00002103); /* PL4ODP Training Repeat and SOP rule */
t1_tpi_write(adapter, OFFSET(0x2284), 0x00000000); /* PL4ODP MAX_T setting */
t1_tpi_write(adapter, OFFSET(0x3280), 0x00000087); /* PL4IDU Enable data forward, port state machine. Set ALLOW_NON_ZERO_OLB */
t1_tpi_write(adapter, OFFSET(0x3282), 0x0000001f); /* PL4IDU Enable Dip4 check error interrupts */
t1_tpi_write(adapter, OFFSET(0x3040), 0x0c32); /* # TXXG Config */
/* For T1 use timer based Mac flow control. */
if (t1_is_T1B(adapter))
t1_tpi_write(adapter, OFFSET(0x304d), 0x8000);
t1_tpi_write(adapter, OFFSET(0x2040), 0x059c); /* # RXXG Config */
t1_tpi_write(adapter, OFFSET(0x2049), 0x0000); /* # RXXG Cut Through */
t1_tpi_write(adapter, OFFSET(0x2070), 0x0000); /* # Disable promiscuous mode */
/* Setup Exact Match Filter 0 to allow broadcast packets.
*/
t1_tpi_write(adapter, OFFSET(0x206e), 0x0000); /* # Disable Match Enable bit */
t1_tpi_write(adapter, OFFSET(0x204a), 0xffff); /* # low addr */
t1_tpi_write(adapter, OFFSET(0x204b), 0xffff); /* # mid addr */
t1_tpi_write(adapter, OFFSET(0x204c), 0xffff); /* # high addr */
t1_tpi_write(adapter, OFFSET(0x206e), 0x0009); /* # Enable Match Enable bit */
t1_tpi_write(adapter, OFFSET(0x0003), 0x0000); /* # NO SOP/ PAD_EN setup */
t1_tpi_write(adapter, OFFSET(0x0100), 0x0ff0); /* # RXEQB disabled */
t1_tpi_write(adapter, OFFSET(0x0101), 0x0f0f); /* # No Preemphasis */
return cmac;
}
static int pm3393_mac_reset(adapter_t * adapter)
{
u32 val;
u32 x;
u32 is_pl4_reset_finished;
u32 is_pl4_outof_lock;
u32 is_xaui_mabc_pll_locked;
u32 successful_reset;
int i;
/* The following steps are required to properly reset
* the PM3393. This information is provided in the
* PM3393 datasheet (Issue 2: November 2002)
* section 13.1 -- Device Reset.
*
* The PM3393 has three types of components that are
* individually reset:
*
* DRESETB - Digital circuitry
* PL4_ARESETB - PL4 analog circuitry
* XAUI_ARESETB - XAUI bus analog circuitry
*
* Steps to reset PM3393 using RSTB pin:
*
* 1. Assert RSTB pin low ( write 0 )
* 2. Wait at least 1ms to initiate a complete initialization of device.
* 3. Wait until all external clocks and REFSEL are stable.
* 4. Wait minimum of 1ms. (after external clocks and REFEL are stable)
* 5. De-assert RSTB ( write 1 )
* 6. Wait until internal timers to expires after ~14ms.
* - Allows analog clock synthesizer(PL4CSU) to stabilize to
* selected reference frequency before allowing the digital
* portion of the device to operate.
* 7. Wait at least 200us for XAUI interface to stabilize.
* 8. Verify the PM3393 came out of reset successfully.
* Set successful reset flag if everything worked else try again
* a few more times.
*/
successful_reset = 0;
for (i = 0; i < 3 && !successful_reset; i++) {
/* 1 */
t1_tpi_read(adapter, A_ELMER0_GPO, &val);
val &= ~1;
t1_tpi_write(adapter, A_ELMER0_GPO, val);
/* 2 */
msleep(1);
/* 3 */
msleep(1);
/* 4 */
msleep(2 /*1 extra ms for safety */ );
/* 5 */
val |= 1;
t1_tpi_write(adapter, A_ELMER0_GPO, val);
/* 6 */
msleep(15 /*1 extra ms for safety */ );
/* 7 */
msleep(1);
/* 8 */
/* Has PL4 analog block come out of reset correctly? */
t1_tpi_read(adapter, OFFSET(SUNI1x10GEXP_REG_DEVICE_STATUS), &val);
is_pl4_reset_finished = (val & SUNI1x10GEXP_BITMSK_TOP_EXPIRED);
/* TBD XXX SUNI1x10GEXP_BITMSK_TOP_PL4_IS_DOOL gets locked later in the init sequence
* figure out why? */
/* Have all PL4 block clocks locked? */
x = (SUNI1x10GEXP_BITMSK_TOP_PL4_ID_DOOL
/*| SUNI1x10GEXP_BITMSK_TOP_PL4_IS_DOOL */ |
SUNI1x10GEXP_BITMSK_TOP_PL4_ID_ROOL |
SUNI1x10GEXP_BITMSK_TOP_PL4_IS_ROOL |
SUNI1x10GEXP_BITMSK_TOP_PL4_OUT_ROOL);
is_pl4_outof_lock = (val & x);
/* ??? If this fails, might be able to software reset the XAUI part
* and try to recover... thus saving us from doing another HW reset */
/* Has the XAUI MABC PLL circuitry stablized? */
is_xaui_mabc_pll_locked =
(val & SUNI1x10GEXP_BITMSK_TOP_SXRA_EXPIRED);
successful_reset = (is_pl4_reset_finished && !is_pl4_outof_lock
&& is_xaui_mabc_pll_locked);
CH_DBG(adapter, HW,
"PM3393 HW reset %d: pl4_reset 0x%x, val 0x%x, "
"is_pl4_outof_lock 0x%x, xaui_locked 0x%x\n",
i, is_pl4_reset_finished, val, is_pl4_outof_lock,
is_xaui_mabc_pll_locked);
}
return successful_reset ? 0 : 1;
}
struct gmac t1_pm3393_ops = {
STATS_TICK_SECS,
pm3393_mac_create,
pm3393_mac_reset
};
/*****************************************************************************
* *
* File: regs.h *
* $Revision: 1.4 $ *
* $Date: 2005/03/23 07:15:59 $ *
* Description: *
* part of the Chelsio 10Gb Ethernet Driver. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License, version 2, as *
* published by the Free Software Foundation. *
* *
* You should have received a copy of the GNU General Public License along *
* with this program; if not, write to the Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
* *
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED *
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF *
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. *
* *
* http://www.chelsio.com *
* *
* Copyright (c) 2003 - 2005 Chelsio Communications, Inc. *
* All rights reserved. *
* *
* Maintainers: maintainers@chelsio.com *
* *
* Authors: Dimitrios Michailidis <dm@chelsio.com> *
* Tina Yang <tainay@chelsio.com> *
* Felix Marti <felix@chelsio.com> *
* Scott Bardone <sbardone@chelsio.com> *
* Kurt Ottaway <kottaway@chelsio.com> *
* Frank DiMambro <frank@chelsio.com> *
* *
* History: *
* *
****************************************************************************/
/* Do not edit this file */
/* SGE registers */
#define A_SG_CONTROL 0x0
#define S_CMDQ0_ENABLE 0
#define V_CMDQ0_ENABLE(x) ((x) << S_CMDQ0_ENABLE)
#define F_CMDQ0_ENABLE V_CMDQ0_ENABLE(1U)
#define S_CMDQ1_ENABLE 1
#define V_CMDQ1_ENABLE(x) ((x) << S_CMDQ1_ENABLE)
#define F_CMDQ1_ENABLE V_CMDQ1_ENABLE(1U)
#define S_FL0_ENABLE 2
#define V_FL0_ENABLE(x) ((x) << S_FL0_ENABLE)
#define F_FL0_ENABLE V_FL0_ENABLE(1U)
#define S_FL1_ENABLE 3
#define V_FL1_ENABLE(x) ((x) << S_FL1_ENABLE)
#define F_FL1_ENABLE V_FL1_ENABLE(1U)
#define S_CPL_ENABLE 4
#define V_CPL_ENABLE(x) ((x) << S_CPL_ENABLE)
#define F_CPL_ENABLE V_CPL_ENABLE(1U)
#define S_RESPONSE_QUEUE_ENABLE 5
#define V_RESPONSE_QUEUE_ENABLE(x) ((x) << S_RESPONSE_QUEUE_ENABLE)
#define F_RESPONSE_QUEUE_ENABLE V_RESPONSE_QUEUE_ENABLE(1U)
#define S_CMDQ_PRIORITY 6
#define M_CMDQ_PRIORITY 0x3
#define V_CMDQ_PRIORITY(x) ((x) << S_CMDQ_PRIORITY)
#define G_CMDQ_PRIORITY(x) (((x) >> S_CMDQ_PRIORITY) & M_CMDQ_PRIORITY)
#define S_DISABLE_CMDQ1_GTS 9
#define V_DISABLE_CMDQ1_GTS(x) ((x) << S_DISABLE_CMDQ1_GTS)
#define F_DISABLE_CMDQ1_GTS V_DISABLE_CMDQ1_GTS(1U)
#define S_ENABLE_BIG_ENDIAN 12
#define V_ENABLE_BIG_ENDIAN(x) ((x) << S_ENABLE_BIG_ENDIAN)
#define F_ENABLE_BIG_ENDIAN V_ENABLE_BIG_ENDIAN(1U)
#define S_ISCSI_COALESCE 14
#define V_ISCSI_COALESCE(x) ((x) << S_ISCSI_COALESCE)
#define F_ISCSI_COALESCE V_ISCSI_COALESCE(1U)
#define S_RX_PKT_OFFSET 15
#define V_RX_PKT_OFFSET(x) ((x) << S_RX_PKT_OFFSET)
#define S_VLAN_XTRACT 18
#define V_VLAN_XTRACT(x) ((x) << S_VLAN_XTRACT)
#define F_VLAN_XTRACT V_VLAN_XTRACT(1U)
#define A_SG_DOORBELL 0x4
#define A_SG_CMD0BASELWR 0x8
#define A_SG_CMD0BASEUPR 0xc
#define A_SG_CMD1BASELWR 0x10
#define A_SG_CMD1BASEUPR 0x14
#define A_SG_FL0BASELWR 0x18
#define A_SG_FL0BASEUPR 0x1c
#define A_SG_FL1BASELWR 0x20
#define A_SG_FL1BASEUPR 0x24
#define A_SG_CMD0SIZE 0x28
#define A_SG_FL0SIZE 0x2c
#define A_SG_RSPSIZE 0x30
#define A_SG_RSPBASELWR 0x34
#define A_SG_RSPBASEUPR 0x38
#define A_SG_FLTHRESHOLD 0x3c
#define A_SG_RSPQUEUECREDIT 0x40
#define A_SG_SLEEPING 0x48
#define A_SG_INTRTIMER 0x4c
#define A_SG_CMD1SIZE 0xb0
#define A_SG_FL1SIZE 0xb4
#define A_SG_INT_ENABLE 0xb8
#define S_RESPQ_EXHAUSTED 0
#define V_RESPQ_EXHAUSTED(x) ((x) << S_RESPQ_EXHAUSTED)
#define F_RESPQ_EXHAUSTED V_RESPQ_EXHAUSTED(1U)
#define S_RESPQ_OVERFLOW 1
#define V_RESPQ_OVERFLOW(x) ((x) << S_RESPQ_OVERFLOW)
#define F_RESPQ_OVERFLOW V_RESPQ_OVERFLOW(1U)
#define S_FL_EXHAUSTED 2
#define V_FL_EXHAUSTED(x) ((x) << S_FL_EXHAUSTED)
#define F_FL_EXHAUSTED V_FL_EXHAUSTED(1U)
#define S_PACKET_TOO_BIG 3
#define V_PACKET_TOO_BIG(x) ((x) << S_PACKET_TOO_BIG)
#define F_PACKET_TOO_BIG V_PACKET_TOO_BIG(1U)
#define S_PACKET_MISMATCH 4
#define V_PACKET_MISMATCH(x) ((x) << S_PACKET_MISMATCH)
#define F_PACKET_MISMATCH V_PACKET_MISMATCH(1U)
#define A_SG_INT_CAUSE 0xbc
/* MC3 registers */
#define S_READY 1
#define V_READY(x) ((x) << S_READY)
#define F_READY V_READY(1U)
/* MC4 registers */
#define A_MC4_CFG 0x180
#define S_MC4_SLOW 25
#define V_MC4_SLOW(x) ((x) << S_MC4_SLOW)
#define F_MC4_SLOW V_MC4_SLOW(1U)
/* TPI registers */
#define A_TPI_ADDR 0x280
#define A_TPI_WR_DATA 0x284
#define A_TPI_RD_DATA 0x288
#define A_TPI_CSR 0x28c
#define S_TPIWR 0
#define V_TPIWR(x) ((x) << S_TPIWR)
#define F_TPIWR V_TPIWR(1U)
#define S_TPIRDY 1
#define V_TPIRDY(x) ((x) << S_TPIRDY)
#define F_TPIRDY V_TPIRDY(1U)
#define A_TPI_PAR 0x29c
#define S_TPIPAR 0
#define M_TPIPAR 0x7f
#define V_TPIPAR(x) ((x) << S_TPIPAR)
#define G_TPIPAR(x) (((x) >> S_TPIPAR) & M_TPIPAR)
/* TP registers */
#define A_TP_IN_CONFIG 0x300
#define S_TP_IN_CSPI_CPL 3
#define V_TP_IN_CSPI_CPL(x) ((x) << S_TP_IN_CSPI_CPL)
#define F_TP_IN_CSPI_CPL V_TP_IN_CSPI_CPL(1U)
#define S_TP_IN_CSPI_CHECK_IP_CSUM 5
#define V_TP_IN_CSPI_CHECK_IP_CSUM(x) ((x) << S_TP_IN_CSPI_CHECK_IP_CSUM)
#define F_TP_IN_CSPI_CHECK_IP_CSUM V_TP_IN_CSPI_CHECK_IP_CSUM(1U)
#define S_TP_IN_CSPI_CHECK_TCP_CSUM 6
#define V_TP_IN_CSPI_CHECK_TCP_CSUM(x) ((x) << S_TP_IN_CSPI_CHECK_TCP_CSUM)
#define F_TP_IN_CSPI_CHECK_TCP_CSUM V_TP_IN_CSPI_CHECK_TCP_CSUM(1U)
#define S_TP_IN_ESPI_ETHERNET 8
#define V_TP_IN_ESPI_ETHERNET(x) ((x) << S_TP_IN_ESPI_ETHERNET)
#define F_TP_IN_ESPI_ETHERNET V_TP_IN_ESPI_ETHERNET(1U)
#define S_TP_IN_ESPI_CHECK_IP_CSUM 12
#define V_TP_IN_ESPI_CHECK_IP_CSUM(x) ((x) << S_TP_IN_ESPI_CHECK_IP_CSUM)
#define F_TP_IN_ESPI_CHECK_IP_CSUM V_TP_IN_ESPI_CHECK_IP_CSUM(1U)
#define S_TP_IN_ESPI_CHECK_TCP_CSUM 13
#define V_TP_IN_ESPI_CHECK_TCP_CSUM(x) ((x) << S_TP_IN_ESPI_CHECK_TCP_CSUM)
#define F_TP_IN_ESPI_CHECK_TCP_CSUM V_TP_IN_ESPI_CHECK_TCP_CSUM(1U)
#define S_OFFLOAD_DISABLE 14
#define V_OFFLOAD_DISABLE(x) ((x) << S_OFFLOAD_DISABLE)
#define F_OFFLOAD_DISABLE V_OFFLOAD_DISABLE(1U)
#define A_TP_OUT_CONFIG 0x304
#define S_TP_OUT_CSPI_CPL 2
#define V_TP_OUT_CSPI_CPL(x) ((x) << S_TP_OUT_CSPI_CPL)
#define F_TP_OUT_CSPI_CPL V_TP_OUT_CSPI_CPL(1U)
#define S_TP_OUT_ESPI_ETHERNET 6
#define V_TP_OUT_ESPI_ETHERNET(x) ((x) << S_TP_OUT_ESPI_ETHERNET)
#define F_TP_OUT_ESPI_ETHERNET V_TP_OUT_ESPI_ETHERNET(1U)
#define S_TP_OUT_ESPI_GENERATE_IP_CSUM 10
#define V_TP_OUT_ESPI_GENERATE_IP_CSUM(x) ((x) << S_TP_OUT_ESPI_GENERATE_IP_CSUM)
#define F_TP_OUT_ESPI_GENERATE_IP_CSUM V_TP_OUT_ESPI_GENERATE_IP_CSUM(1U)
#define S_TP_OUT_ESPI_GENERATE_TCP_CSUM 11
#define V_TP_OUT_ESPI_GENERATE_TCP_CSUM(x) ((x) << S_TP_OUT_ESPI_GENERATE_TCP_CSUM)
#define F_TP_OUT_ESPI_GENERATE_TCP_CSUM V_TP_OUT_ESPI_GENERATE_TCP_CSUM(1U)
#define A_TP_GLOBAL_CONFIG 0x308
#define S_IP_TTL 0
#define M_IP_TTL 0xff
#define V_IP_TTL(x) ((x) << S_IP_TTL)
#define S_TCP_CSUM 11
#define V_TCP_CSUM(x) ((x) << S_TCP_CSUM)
#define F_TCP_CSUM V_TCP_CSUM(1U)
#define S_UDP_CSUM 12
#define V_UDP_CSUM(x) ((x) << S_UDP_CSUM)
#define F_UDP_CSUM V_UDP_CSUM(1U)
#define S_IP_CSUM 13
#define V_IP_CSUM(x) ((x) << S_IP_CSUM)
#define F_IP_CSUM V_IP_CSUM(1U)
#define S_PATH_MTU 15
#define V_PATH_MTU(x) ((x) << S_PATH_MTU)
#define F_PATH_MTU V_PATH_MTU(1U)
#define S_5TUPLE_LOOKUP 17
#define V_5TUPLE_LOOKUP(x) ((x) << S_5TUPLE_LOOKUP)
#define S_SYN_COOKIE_PARAMETER 26
#define V_SYN_COOKIE_PARAMETER(x) ((x) << S_SYN_COOKIE_PARAMETER)
#define A_TP_PC_CONFIG 0x348
#define S_TP_PC_REV 30
#define M_TP_PC_REV 0x3
#define G_TP_PC_REV(x) (((x) >> S_TP_PC_REV) & M_TP_PC_REV)
#define A_TP_RESET 0x44c
#define S_TP_RESET 0
#define V_TP_RESET(x) ((x) << S_TP_RESET)
#define F_TP_RESET V_TP_RESET(1U)
#define A_TP_INT_ENABLE 0x470
#define A_TP_INT_CAUSE 0x474
#define A_TP_TX_DROP_CONFIG 0x4b8
#define S_ENABLE_TX_DROP 31
#define V_ENABLE_TX_DROP(x) ((x) << S_ENABLE_TX_DROP)
#define F_ENABLE_TX_DROP V_ENABLE_TX_DROP(1U)
#define S_ENABLE_TX_ERROR 30
#define V_ENABLE_TX_ERROR(x) ((x) << S_ENABLE_TX_ERROR)
#define F_ENABLE_TX_ERROR V_ENABLE_TX_ERROR(1U)
#define S_DROP_TICKS_CNT 4
#define V_DROP_TICKS_CNT(x) ((x) << S_DROP_TICKS_CNT)
#define S_NUM_PKTS_DROPPED 0
#define V_NUM_PKTS_DROPPED(x) ((x) << S_NUM_PKTS_DROPPED)
/* CSPI registers */
#define S_DIP4ERR 0
#define V_DIP4ERR(x) ((x) << S_DIP4ERR)
#define F_DIP4ERR V_DIP4ERR(1U)
#define S_RXDROP 1
#define V_RXDROP(x) ((x) << S_RXDROP)
#define F_RXDROP V_RXDROP(1U)
#define S_TXDROP 2
#define V_TXDROP(x) ((x) << S_TXDROP)
#define F_TXDROP V_TXDROP(1U)
#define S_RXOVERFLOW 3
#define V_RXOVERFLOW(x) ((x) << S_RXOVERFLOW)
#define F_RXOVERFLOW V_RXOVERFLOW(1U)
#define S_RAMPARITYERR 4
#define V_RAMPARITYERR(x) ((x) << S_RAMPARITYERR)
#define F_RAMPARITYERR V_RAMPARITYERR(1U)
/* ESPI registers */
#define A_ESPI_SCH_TOKEN0 0x880
#define A_ESPI_SCH_TOKEN1 0x884
#define A_ESPI_SCH_TOKEN2 0x888
#define A_ESPI_SCH_TOKEN3 0x88c
#define A_ESPI_RX_FIFO_ALMOST_EMPTY_WATERMARK 0x890
#define A_ESPI_RX_FIFO_ALMOST_FULL_WATERMARK 0x894
#define A_ESPI_CALENDAR_LENGTH 0x898
#define A_PORT_CONFIG 0x89c
#define S_RX_NPORTS 0
#define V_RX_NPORTS(x) ((x) << S_RX_NPORTS)
#define S_TX_NPORTS 8
#define V_TX_NPORTS(x) ((x) << S_TX_NPORTS)
#define A_ESPI_FIFO_STATUS_ENABLE 0x8a0
#define S_RXSTATUSENABLE 0
#define V_RXSTATUSENABLE(x) ((x) << S_RXSTATUSENABLE)
#define F_RXSTATUSENABLE V_RXSTATUSENABLE(1U)
#define S_INTEL1010MODE 4
#define V_INTEL1010MODE(x) ((x) << S_INTEL1010MODE)
#define F_INTEL1010MODE V_INTEL1010MODE(1U)
#define A_ESPI_MAXBURST1_MAXBURST2 0x8a8
#define A_ESPI_TRAIN 0x8ac
#define A_ESPI_INTR_STATUS 0x8c8
#define S_DIP2PARITYERR 5
#define V_DIP2PARITYERR(x) ((x) << S_DIP2PARITYERR)
#define F_DIP2PARITYERR V_DIP2PARITYERR(1U)
#define A_ESPI_INTR_ENABLE 0x8cc
#define A_RX_DROP_THRESHOLD 0x8d0
#define A_ESPI_RX_RESET 0x8ec
#define A_ESPI_MISC_CONTROL 0x8f0
#define S_OUT_OF_SYNC_COUNT 0
#define V_OUT_OF_SYNC_COUNT(x) ((x) << S_OUT_OF_SYNC_COUNT)
#define S_DIP2_PARITY_ERR_THRES 5
#define V_DIP2_PARITY_ERR_THRES(x) ((x) << S_DIP2_PARITY_ERR_THRES)
#define S_DIP4_THRES 9
#define V_DIP4_THRES(x) ((x) << S_DIP4_THRES)
#define S_MONITORED_PORT_NUM 25
#define V_MONITORED_PORT_NUM(x) ((x) << S_MONITORED_PORT_NUM)
#define S_MONITORED_DIRECTION 27
#define V_MONITORED_DIRECTION(x) ((x) << S_MONITORED_DIRECTION)
#define F_MONITORED_DIRECTION V_MONITORED_DIRECTION(1U)
#define S_MONITORED_INTERFACE 28
#define V_MONITORED_INTERFACE(x) ((x) << S_MONITORED_INTERFACE)
#define F_MONITORED_INTERFACE V_MONITORED_INTERFACE(1U)
#define A_ESPI_DIP2_ERR_COUNT 0x8f4
#define A_ESPI_CMD_ADDR 0x8f8
#define S_WRITE_DATA 0
#define V_WRITE_DATA(x) ((x) << S_WRITE_DATA)
#define S_REGISTER_OFFSET 8
#define V_REGISTER_OFFSET(x) ((x) << S_REGISTER_OFFSET)
#define S_CHANNEL_ADDR 12
#define V_CHANNEL_ADDR(x) ((x) << S_CHANNEL_ADDR)
#define S_MODULE_ADDR 16
#define V_MODULE_ADDR(x) ((x) << S_MODULE_ADDR)
#define S_BUNDLE_ADDR 20
#define V_BUNDLE_ADDR(x) ((x) << S_BUNDLE_ADDR)
#define S_SPI4_COMMAND 24
#define V_SPI4_COMMAND(x) ((x) << S_SPI4_COMMAND)
#define A_ESPI_GOSTAT 0x8fc
#define S_ESPI_CMD_BUSY 8
#define V_ESPI_CMD_BUSY(x) ((x) << S_ESPI_CMD_BUSY)
#define F_ESPI_CMD_BUSY V_ESPI_CMD_BUSY(1U)
/* PL registers */
#define A_PL_ENABLE 0xa00
#define S_PL_INTR_SGE_ERR 0
#define V_PL_INTR_SGE_ERR(x) ((x) << S_PL_INTR_SGE_ERR)
#define F_PL_INTR_SGE_ERR V_PL_INTR_SGE_ERR(1U)
#define S_PL_INTR_SGE_DATA 1
#define V_PL_INTR_SGE_DATA(x) ((x) << S_PL_INTR_SGE_DATA)
#define F_PL_INTR_SGE_DATA V_PL_INTR_SGE_DATA(1U)
#define S_PL_INTR_TP 6
#define V_PL_INTR_TP(x) ((x) << S_PL_INTR_TP)
#define F_PL_INTR_TP V_PL_INTR_TP(1U)
#define S_PL_INTR_ESPI 8
#define V_PL_INTR_ESPI(x) ((x) << S_PL_INTR_ESPI)
#define F_PL_INTR_ESPI V_PL_INTR_ESPI(1U)
#define S_PL_INTR_PCIX 10
#define V_PL_INTR_PCIX(x) ((x) << S_PL_INTR_PCIX)
#define F_PL_INTR_PCIX V_PL_INTR_PCIX(1U)
#define S_PL_INTR_EXT 11
#define V_PL_INTR_EXT(x) ((x) << S_PL_INTR_EXT)
#define F_PL_INTR_EXT V_PL_INTR_EXT(1U)
#define A_PL_CAUSE 0xa04
/* MC5 registers */
#define A_MC5_CONFIG 0xc04
#define S_TCAM_RESET 1
#define V_TCAM_RESET(x) ((x) << S_TCAM_RESET)
#define F_TCAM_RESET V_TCAM_RESET(1U)
#define S_M_BUS_ENABLE 5
#define V_M_BUS_ENABLE(x) ((x) << S_M_BUS_ENABLE)
#define F_M_BUS_ENABLE V_M_BUS_ENABLE(1U)
/* PCICFG registers */
#define A_PCICFG_PM_CSR 0x44
#define A_PCICFG_VPD_ADDR 0x4a
#define S_VPD_OP_FLAG 15
#define V_VPD_OP_FLAG(x) ((x) << S_VPD_OP_FLAG)
#define F_VPD_OP_FLAG V_VPD_OP_FLAG(1U)
#define A_PCICFG_VPD_DATA 0x4c
#define A_PCICFG_INTR_ENABLE 0xf4
#define A_PCICFG_INTR_CAUSE 0xf8
#define A_PCICFG_MODE 0xfc
#define S_PCI_MODE_64BIT 0
#define V_PCI_MODE_64BIT(x) ((x) << S_PCI_MODE_64BIT)
#define F_PCI_MODE_64BIT V_PCI_MODE_64BIT(1U)
#define S_PCI_MODE_PCIX 5
#define V_PCI_MODE_PCIX(x) ((x) << S_PCI_MODE_PCIX)
#define F_PCI_MODE_PCIX V_PCI_MODE_PCIX(1U)
#define S_PCI_MODE_CLK 6
#define M_PCI_MODE_CLK 0x3
#define G_PCI_MODE_CLK(x) (((x) >> S_PCI_MODE_CLK) & M_PCI_MODE_CLK)
/*****************************************************************************
* *
* File: sge.c *
* $Revision: 1.13 $ *
* $Date: 2005/03/23 07:41:27 $ *
* Description: *
* DMA engine. *
* part of the Chelsio 10Gb Ethernet Driver. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License, version 2, as *
* published by the Free Software Foundation. *
* *
* You should have received a copy of the GNU General Public License along *
* with this program; if not, write to the Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
* *
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED *
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF *
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. *
* *
* http://www.chelsio.com *
* *
* Copyright (c) 2003 - 2005 Chelsio Communications, Inc. *
* All rights reserved. *
* *
* Maintainers: maintainers@chelsio.com *
* *
* Authors: Dimitrios Michailidis <dm@chelsio.com> *
* Tina Yang <tainay@chelsio.com> *
* Felix Marti <felix@chelsio.com> *
* Scott Bardone <sbardone@chelsio.com> *
* Kurt Ottaway <kottaway@chelsio.com> *
* Frank DiMambro <frank@chelsio.com> *
* *
* History: *
* *
****************************************************************************/
#include "common.h"
#include <linux/config.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/pci.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/if_vlan.h>
#include <linux/skbuff.h>
#include <linux/init.h>
#include <linux/mm.h>
#include <linux/ip.h>
#include <linux/in.h>
#include <linux/if_arp.h>
#include "cpl5_cmd.h"
#include "sge.h"
#include "regs.h"
#include "espi.h"
#include <linux/tcp.h>
#define SGE_CMDQ_N 2
#define SGE_FREELQ_N 2
#define SGE_CMDQ0_E_N 512
#define SGE_CMDQ1_E_N 128
#define SGE_FREEL_SIZE 4096
#define SGE_JUMBO_FREEL_SIZE 512
#define SGE_FREEL_REFILL_THRESH 16
#define SGE_RESPQ_E_N 1024
#define SGE_INTR_BUCKETSIZE 100
#define SGE_INTR_LATBUCKETS 5
#define SGE_INTR_MAXBUCKETS 11
#define SGE_INTRTIMER0 1
#define SGE_INTRTIMER1 50
#define SGE_INTRTIMER_NRES 10000
#define SGE_RX_COPY_THRESHOLD 256
#define SGE_RX_SM_BUF_SIZE 1536
#define SGE_RESPQ_REPLENISH_THRES ((3 * SGE_RESPQ_E_N) / 4)
#define SGE_RX_OFFSET 2
#ifndef NET_IP_ALIGN
# define NET_IP_ALIGN SGE_RX_OFFSET
#endif
/*
* Memory Mapped HW Command, Freelist and Response Queue Descriptors
*/
#if defined(__BIG_ENDIAN_BITFIELD)
struct cmdQ_e {
u32 AddrLow;
u32 GenerationBit : 1;
u32 BufferLength : 31;
u32 RespQueueSelector : 4;
u32 ResponseTokens : 12;
u32 CmdId : 8;
u32 Reserved : 3;
u32 TokenValid : 1;
u32 Eop : 1;
u32 Sop : 1;
u32 DataValid : 1;
u32 GenerationBit2 : 1;
u32 AddrHigh;
};
struct freelQ_e {
u32 AddrLow;
u32 GenerationBit : 1;
u32 BufferLength : 31;
u32 Reserved : 31;
u32 GenerationBit2 : 1;
u32 AddrHigh;
};
struct respQ_e {
u32 Qsleeping : 4;
u32 Cmdq1CreditReturn : 5;
u32 Cmdq1DmaComplete : 5;
u32 Cmdq0CreditReturn : 5;
u32 Cmdq0DmaComplete : 5;
u32 FreelistQid : 2;
u32 CreditValid : 1;
u32 DataValid : 1;
u32 Offload : 1;
u32 Eop : 1;
u32 Sop : 1;
u32 GenerationBit : 1;
u32 BufferLength;
};
#elif defined(__LITTLE_ENDIAN_BITFIELD)
struct cmdQ_e {
u32 BufferLength : 31;
u32 GenerationBit : 1;
u32 AddrLow;
u32 AddrHigh;
u32 GenerationBit2 : 1;
u32 DataValid : 1;
u32 Sop : 1;
u32 Eop : 1;
u32 TokenValid : 1;
u32 Reserved : 3;
u32 CmdId : 8;
u32 ResponseTokens : 12;
u32 RespQueueSelector : 4;
};
struct freelQ_e {
u32 BufferLength : 31;
u32 GenerationBit : 1;
u32 AddrLow;
u32 AddrHigh;
u32 GenerationBit2 : 1;
u32 Reserved : 31;
};
struct respQ_e {
u32 BufferLength;
u32 GenerationBit : 1;
u32 Sop : 1;
u32 Eop : 1;
u32 Offload : 1;
u32 DataValid : 1;
u32 CreditValid : 1;
u32 FreelistQid : 2;
u32 Cmdq0DmaComplete : 5;
u32 Cmdq0CreditReturn : 5;
u32 Cmdq1DmaComplete : 5;
u32 Cmdq1CreditReturn : 5;
u32 Qsleeping : 4;
} ;
#endif
/*
* SW Context Command and Freelist Queue Descriptors
*/
struct cmdQ_ce {
struct sk_buff *skb;
DECLARE_PCI_UNMAP_ADDR(dma_addr);
DECLARE_PCI_UNMAP_LEN(dma_len);
unsigned int single;
};
struct freelQ_ce {
struct sk_buff *skb;
DECLARE_PCI_UNMAP_ADDR(dma_addr);
DECLARE_PCI_UNMAP_LEN(dma_len);
};
/*
* SW Command, Freelist and Response Queue
*/
struct cmdQ {
atomic_t asleep; /* HW DMA Fetch status */
atomic_t credits; /* # available descriptors for TX */
atomic_t pio_pidx; /* Variable updated on Doorbell */
u16 entries_n; /* # descriptors for TX */
u16 pidx; /* producer index (SW) */
u16 cidx; /* consumer index (HW) */
u8 genbit; /* current generation (=valid) bit */
struct cmdQ_e *entries; /* HW command descriptor Q */
struct cmdQ_ce *centries; /* SW command context descriptor Q */
spinlock_t Qlock; /* Lock to protect cmdQ enqueuing */
dma_addr_t dma_addr; /* DMA addr HW command descriptor Q */
};
struct freelQ {
unsigned int credits; /* # of available RX buffers */
unsigned int entries_n; /* free list capacity */
u16 pidx; /* producer index (SW) */
u16 cidx; /* consumer index (HW) */
u16 rx_buffer_size; /* Buffer size on this free list */
u16 dma_offset; /* DMA offset to align IP headers */
u8 genbit; /* current generation (=valid) bit */
struct freelQ_e *entries; /* HW freelist descriptor Q */
struct freelQ_ce *centries; /* SW freelist conext descriptor Q */
dma_addr_t dma_addr; /* DMA addr HW freelist descriptor Q */
};
struct respQ {
u16 credits; /* # of available respQ descriptors */
u16 credits_pend; /* # of not yet returned descriptors */
u16 entries_n; /* # of response Q descriptors */
u16 pidx; /* producer index (HW) */
u16 cidx; /* consumer index (SW) */
u8 genbit; /* current generation(=valid) bit */
struct respQ_e *entries; /* HW response descriptor Q */
dma_addr_t dma_addr; /* DMA addr HW response descriptor Q */
};
/*
* Main SGE data structure
*
* Interrupts are handled by a single CPU and it is likely that on a MP system
* the application is migrated to another CPU. In that scenario, we try to
* seperate the RX(in irq context) and TX state in order to decrease memory
* contention.
*/
struct sge {
struct adapter *adapter; /* adapter backpointer */
struct freelQ freelQ[SGE_FREELQ_N]; /* freelist Q(s) */
struct respQ respQ; /* response Q instatiation */
unsigned int rx_pkt_pad; /* RX padding for L2 packets */
unsigned int jumbo_fl; /* jumbo freelist Q index */
u32 intrtimer[SGE_INTR_MAXBUCKETS]; /* ! */
u32 currIndex; /* current index into intrtimer[] */
u32 intrtimer_nres; /* no resource interrupt timer value */
u32 sge_control; /* shadow content of sge control reg */
struct sge_intr_counts intr_cnt;
struct timer_list ptimer;
struct sk_buff *pskb;
u32 ptimeout;
struct cmdQ cmdQ[SGE_CMDQ_N] ____cacheline_aligned; /* command Q(s)*/
};
static unsigned int t1_sge_tx(struct sk_buff *skb, struct adapter *adapter,
unsigned int qid);
/*
* PIO to indicate that memory mapped Q contains valid descriptor(s).
*/
static inline void doorbell_pio(struct sge *sge, u32 val)
{
wmb();
t1_write_reg_4(sge->adapter, A_SG_DOORBELL, val);
}
/*
* Disables the DMA engine.
*/
void t1_sge_stop(struct sge *sge)
{
t1_write_reg_4(sge->adapter, A_SG_CONTROL, 0);
t1_read_reg_4(sge->adapter, A_SG_CONTROL); /* flush write */
if (is_T2(sge->adapter))
del_timer_sync(&sge->ptimer);
}
static u8 ch_mac_addr[ETH_ALEN] = {0x0, 0x7, 0x43, 0x0, 0x0, 0x0};
static void t1_espi_workaround(void *data)
{
struct adapter *adapter = (struct adapter *)data;
struct sge *sge = adapter->sge;
if (netif_running(adapter->port[0].dev) &&
atomic_read(&sge->cmdQ[0].asleep)) {
u32 seop = t1_espi_get_mon(adapter, 0x930, 0);
if ((seop & 0xfff0fff) == 0xfff && sge->pskb) {
struct sk_buff *skb = sge->pskb;
if (!skb->cb[0]) {
memcpy(skb->data+sizeof(struct cpl_tx_pkt), ch_mac_addr, ETH_ALEN);
memcpy(skb->data+skb->len-10, ch_mac_addr, ETH_ALEN);
skb->cb[0] = 0xff;
}
t1_sge_tx(skb, adapter,0);
}
}
mod_timer(&adapter->sge->ptimer, jiffies + sge->ptimeout);
}
/*
* Enables the DMA engine.
*/
void t1_sge_start(struct sge *sge)
{
t1_write_reg_4(sge->adapter, A_SG_CONTROL, sge->sge_control);
t1_read_reg_4(sge->adapter, A_SG_CONTROL); /* flush write */
if (is_T2(sge->adapter)) {
init_timer(&sge->ptimer);
sge->ptimer.function = (void *)&t1_espi_workaround;
sge->ptimer.data = (unsigned long)sge->adapter;
sge->ptimer.expires = jiffies + sge->ptimeout;
add_timer(&sge->ptimer);
}
}
/*
* Creates a t1_sge structure and returns suggested resource parameters.
*/
struct sge * __devinit t1_sge_create(struct adapter *adapter,
struct sge_params *p)
{
struct sge *sge = kmalloc(sizeof(*sge), GFP_KERNEL);
if (!sge)
return NULL;
memset(sge, 0, sizeof(*sge));
if (is_T2(adapter))
sge->ptimeout = 1; /* finest allowed */
sge->adapter = adapter;
sge->rx_pkt_pad = t1_is_T1B(adapter) ? 0 : SGE_RX_OFFSET;
sge->jumbo_fl = t1_is_T1B(adapter) ? 1 : 0;
p->cmdQ_size[0] = SGE_CMDQ0_E_N;
p->cmdQ_size[1] = SGE_CMDQ1_E_N;
p->freelQ_size[!sge->jumbo_fl] = SGE_FREEL_SIZE;
p->freelQ_size[sge->jumbo_fl] = SGE_JUMBO_FREEL_SIZE;
p->rx_coalesce_usecs = SGE_INTRTIMER1;
p->last_rx_coalesce_raw = SGE_INTRTIMER1 *
(board_info(sge->adapter)->clock_core / 1000000);
p->default_rx_coalesce_usecs = SGE_INTRTIMER1;
p->coalesce_enable = 0; /* Turn off adaptive algorithm by default */
p->sample_interval_usecs = 0;
return sge;
}
/*
* Frees all RX buffers on the freelist Q. The caller must make sure that
* the SGE is turned off before calling this function.
*/
static void free_freelQ_buffers(struct pci_dev *pdev, struct freelQ *Q)
{
unsigned int cidx = Q->cidx, credits = Q->credits;
while (credits--) {
struct freelQ_ce *ce = &Q->centries[cidx];
pci_unmap_single(pdev, pci_unmap_addr(ce, dma_addr),
pci_unmap_len(ce, dma_len),
PCI_DMA_FROMDEVICE);
dev_kfree_skb(ce->skb);
ce->skb = NULL;
if (++cidx == Q->entries_n)
cidx = 0;
}
}
/*
* Free RX free list and response queue resources.
*/
static void free_rx_resources(struct sge *sge)
{
struct pci_dev *pdev = sge->adapter->pdev;
unsigned int size, i;
if (sge->respQ.entries) {
size = sizeof(struct respQ_e) * sge->respQ.entries_n;
pci_free_consistent(pdev, size, sge->respQ.entries,
sge->respQ.dma_addr);
}
for (i = 0; i < SGE_FREELQ_N; i++) {
struct freelQ *Q = &sge->freelQ[i];
if (Q->centries) {
free_freelQ_buffers(pdev, Q);
kfree(Q->centries);
}
if (Q->entries) {
size = sizeof(struct freelQ_e) * Q->entries_n;
pci_free_consistent(pdev, size, Q->entries,
Q->dma_addr);
}
}
}
/*
* Allocates basic RX resources, consisting of memory mapped freelist Qs and a
* response Q.
*/
static int alloc_rx_resources(struct sge *sge, struct sge_params *p)
{
struct pci_dev *pdev = sge->adapter->pdev;
unsigned int size, i;
for (i = 0; i < SGE_FREELQ_N; i++) {
struct freelQ *Q = &sge->freelQ[i];
Q->genbit = 1;
Q->entries_n = p->freelQ_size[i];
Q->dma_offset = SGE_RX_OFFSET - sge->rx_pkt_pad;
size = sizeof(struct freelQ_e) * Q->entries_n;
Q->entries = (struct freelQ_e *)
pci_alloc_consistent(pdev, size, &Q->dma_addr);
if (!Q->entries)
goto err_no_mem;
memset(Q->entries, 0, size);
Q->centries = kcalloc(Q->entries_n, sizeof(struct freelQ_ce),
GFP_KERNEL);
if (!Q->centries)
goto err_no_mem;
}
/*
* Calculate the buffer sizes for the two free lists. FL0 accommodates
* regular sized Ethernet frames, FL1 is sized not to exceed 16K,
* including all the sk_buff overhead.
*
* Note: For T2 FL0 and FL1 are reversed.
*/
sge->freelQ[!sge->jumbo_fl].rx_buffer_size = SGE_RX_SM_BUF_SIZE +
sizeof(struct cpl_rx_data) +
sge->freelQ[!sge->jumbo_fl].dma_offset;
sge->freelQ[sge->jumbo_fl].rx_buffer_size = (16 * 1024) -
SKB_DATA_ALIGN(sizeof(struct skb_shared_info));
sge->respQ.genbit = 1;
sge->respQ.entries_n = SGE_RESPQ_E_N;
sge->respQ.credits = SGE_RESPQ_E_N;
size = sizeof(struct respQ_e) * sge->respQ.entries_n;
sge->respQ.entries = (struct respQ_e *)
pci_alloc_consistent(pdev, size, &sge->respQ.dma_addr);
if (!sge->respQ.entries)
goto err_no_mem;
memset(sge->respQ.entries, 0, size);
return 0;
err_no_mem:
free_rx_resources(sge);
return -ENOMEM;
}
/*
* Frees 'credits_pend' TX buffers and returns the credits to Q->credits.
*
* The adaptive algorithm receives the total size of the buffers freed
* accumulated in @*totpayload. No initialization of this argument here.
*
*/
static void free_cmdQ_buffers(struct sge *sge, struct cmdQ *Q,
unsigned int credits_pend, unsigned int *totpayload)
{
struct pci_dev *pdev = sge->adapter->pdev;
struct sk_buff *skb;
struct cmdQ_ce *ce, *cq = Q->centries;
unsigned int entries_n = Q->entries_n, cidx = Q->cidx,
i = credits_pend;
ce = &cq[cidx];
while (i--) {
if (ce->single)
pci_unmap_single(pdev, pci_unmap_addr(ce, dma_addr),
pci_unmap_len(ce, dma_len),
PCI_DMA_TODEVICE);
else
pci_unmap_page(pdev, pci_unmap_addr(ce, dma_addr),
pci_unmap_len(ce, dma_len),
PCI_DMA_TODEVICE);
if (totpayload)
*totpayload += pci_unmap_len(ce, dma_len);
skb = ce->skb;
if (skb)
dev_kfree_skb_irq(skb);
ce++;
if (++cidx == entries_n) {
cidx = 0;
ce = cq;
}
}
Q->cidx = cidx;
atomic_add(credits_pend, &Q->credits);
}
/*
* Free TX resources.
*
* Assumes that SGE is stopped and all interrupts are disabled.
*/
static void free_tx_resources(struct sge *sge)
{
struct pci_dev *pdev = sge->adapter->pdev;
unsigned int size, i;
for (i = 0; i < SGE_CMDQ_N; i++) {
struct cmdQ *Q = &sge->cmdQ[i];
if (Q->centries) {
unsigned int pending = Q->entries_n -
atomic_read(&Q->credits);
if (pending)
free_cmdQ_buffers(sge, Q, pending, NULL);
kfree(Q->centries);
}
if (Q->entries) {
size = sizeof(struct cmdQ_e) * Q->entries_n;
pci_free_consistent(pdev, size, Q->entries,
Q->dma_addr);
}
}
}
/*
* Allocates basic TX resources, consisting of memory mapped command Qs.
*/
static int alloc_tx_resources(struct sge *sge, struct sge_params *p)
{
struct pci_dev *pdev = sge->adapter->pdev;
unsigned int size, i;
for (i = 0; i < SGE_CMDQ_N; i++) {
struct cmdQ *Q = &sge->cmdQ[i];
Q->genbit = 1;
Q->entries_n = p->cmdQ_size[i];
atomic_set(&Q->credits, Q->entries_n);
atomic_set(&Q->asleep, 1);
spin_lock_init(&Q->Qlock);
size = sizeof(struct cmdQ_e) * Q->entries_n;
Q->entries = (struct cmdQ_e *)
pci_alloc_consistent(pdev, size, &Q->dma_addr);
if (!Q->entries)
goto err_no_mem;
memset(Q->entries, 0, size);
Q->centries = kcalloc(Q->entries_n, sizeof(struct cmdQ_ce),
GFP_KERNEL);
if (!Q->centries)
goto err_no_mem;
}
return 0;
err_no_mem:
free_tx_resources(sge);
return -ENOMEM;
}
static inline void setup_ring_params(struct adapter *adapter, u64 addr,
u32 size, int base_reg_lo,
int base_reg_hi, int size_reg)
{
t1_write_reg_4(adapter, base_reg_lo, (u32)addr);
t1_write_reg_4(adapter, base_reg_hi, addr >> 32);
t1_write_reg_4(adapter, size_reg, size);
}
/*
* Enable/disable VLAN acceleration.
*/
void t1_set_vlan_accel(struct adapter *adapter, int on_off)
{
struct sge *sge = adapter->sge;
sge->sge_control &= ~F_VLAN_XTRACT;
if (on_off)
sge->sge_control |= F_VLAN_XTRACT;
if (adapter->open_device_map) {
t1_write_reg_4(adapter, A_SG_CONTROL, sge->sge_control);
t1_read_reg_4(adapter, A_SG_CONTROL); /* flush */
}
}
/*
* Sets the interrupt latency timer when the adaptive Rx coalescing
* is turned off. Do nothing when it is turned on again.
*
* This routine relies on the fact that the caller has already set
* the adaptive policy in adapter->sge_params before calling it.
*/
int t1_sge_set_coalesce_params(struct sge *sge, struct sge_params *p)
{
if (!p->coalesce_enable) {
u32 newTimer = p->rx_coalesce_usecs *
(board_info(sge->adapter)->clock_core / 1000000);
t1_write_reg_4(sge->adapter, A_SG_INTRTIMER, newTimer);
}
return 0;
}
/*
* Programs the various SGE registers. However, the engine is not yet enabled,
* but sge->sge_control is setup and ready to go.
*/
static void configure_sge(struct sge *sge, struct sge_params *p)
{
struct adapter *ap = sge->adapter;
int i;
t1_write_reg_4(ap, A_SG_CONTROL, 0);
setup_ring_params(ap, sge->cmdQ[0].dma_addr, sge->cmdQ[0].entries_n,
A_SG_CMD0BASELWR, A_SG_CMD0BASEUPR, A_SG_CMD0SIZE);
setup_ring_params(ap, sge->cmdQ[1].dma_addr, sge->cmdQ[1].entries_n,
A_SG_CMD1BASELWR, A_SG_CMD1BASEUPR, A_SG_CMD1SIZE);
setup_ring_params(ap, sge->freelQ[0].dma_addr,
sge->freelQ[0].entries_n, A_SG_FL0BASELWR,
A_SG_FL0BASEUPR, A_SG_FL0SIZE);
setup_ring_params(ap, sge->freelQ[1].dma_addr,
sge->freelQ[1].entries_n, A_SG_FL1BASELWR,
A_SG_FL1BASEUPR, A_SG_FL1SIZE);
/* The threshold comparison uses <. */
t1_write_reg_4(ap, A_SG_FLTHRESHOLD, SGE_RX_SM_BUF_SIZE + 1);
setup_ring_params(ap, sge->respQ.dma_addr, sge->respQ.entries_n,
A_SG_RSPBASELWR, A_SG_RSPBASEUPR, A_SG_RSPSIZE);
t1_write_reg_4(ap, A_SG_RSPQUEUECREDIT, (u32)sge->respQ.entries_n);
sge->sge_control = F_CMDQ0_ENABLE | F_CMDQ1_ENABLE | F_FL0_ENABLE |
F_FL1_ENABLE | F_CPL_ENABLE | F_RESPONSE_QUEUE_ENABLE |
V_CMDQ_PRIORITY(2) | F_DISABLE_CMDQ1_GTS | F_ISCSI_COALESCE |
V_RX_PKT_OFFSET(sge->rx_pkt_pad);
#if defined(__BIG_ENDIAN_BITFIELD)
sge->sge_control |= F_ENABLE_BIG_ENDIAN;
#endif
/*
* Initialize the SGE Interrupt Timer arrray:
* intrtimer[0] = (SGE_INTRTIMER0) usec
* intrtimer[0<i<5] = (SGE_INTRTIMER0 + i*2) usec
* intrtimer[4<i<10] = ((i - 3) * 6) usec
* intrtimer[10] = (SGE_INTRTIMER1) usec
*
*/
sge->intrtimer[0] = board_info(sge->adapter)->clock_core / 1000000;
for (i = 1; i < SGE_INTR_LATBUCKETS; ++i) {
sge->intrtimer[i] = SGE_INTRTIMER0 + (2 * i);
sge->intrtimer[i] *= sge->intrtimer[0];
}
for (i = SGE_INTR_LATBUCKETS; i < SGE_INTR_MAXBUCKETS - 1; ++i) {
sge->intrtimer[i] = (i - 3) * 6;
sge->intrtimer[i] *= sge->intrtimer[0];
}
sge->intrtimer[SGE_INTR_MAXBUCKETS - 1] =
sge->intrtimer[0] * SGE_INTRTIMER1;
/* Initialize resource timer */
sge->intrtimer_nres = sge->intrtimer[0] * SGE_INTRTIMER_NRES;
/* Finally finish initialization of intrtimer[0] */
sge->intrtimer[0] *= SGE_INTRTIMER0;
/* Initialize for a throughput oriented workload */
sge->currIndex = SGE_INTR_MAXBUCKETS - 1;
if (p->coalesce_enable)
t1_write_reg_4(ap, A_SG_INTRTIMER,
sge->intrtimer[sge->currIndex]);
else
t1_sge_set_coalesce_params(sge, p);
}
/*
* Return the payload capacity of the jumbo free-list buffers.
*/
static inline unsigned int jumbo_payload_capacity(const struct sge *sge)
{
return sge->freelQ[sge->jumbo_fl].rx_buffer_size -
sizeof(struct cpl_rx_data) - SGE_RX_OFFSET + sge->rx_pkt_pad;
}
/*
* Allocates both RX and TX resources and configures the SGE. However,
* the hardware is not enabled yet.
*/
int t1_sge_configure(struct sge *sge, struct sge_params *p)
{
if (alloc_rx_resources(sge, p))
return -ENOMEM;
if (alloc_tx_resources(sge, p)) {
free_rx_resources(sge);
return -ENOMEM;
}
configure_sge(sge, p);
/*
* Now that we have sized the free lists calculate the payload
* capacity of the large buffers. Other parts of the driver use
* this to set the max offload coalescing size so that RX packets
* do not overflow our large buffers.
*/
p->large_buf_capacity = jumbo_payload_capacity(sge);
return 0;
}
/*
* Frees all SGE related resources and the sge structure itself
*/
void t1_sge_destroy(struct sge *sge)
{
if (sge->pskb)
dev_kfree_skb(sge->pskb);
free_tx_resources(sge);
free_rx_resources(sge);
kfree(sge);
}
/*
* Allocates new RX buffers on the freelist Q (and tracks them on the freelist
* context Q) until the Q is full or alloc_skb fails.
*
* It is possible that the generation bits already match, indicating that the
* buffer is already valid and nothing needs to be done. This happens when we
* copied a received buffer into a new sk_buff during the interrupt processing.
*
* If the SGE doesn't automatically align packets properly (!sge->rx_pkt_pad),
* we specify a RX_OFFSET in order to make sure that the IP header is 4B
* aligned.
*/
static void refill_free_list(struct sge *sge, struct freelQ *Q)
{
struct pci_dev *pdev = sge->adapter->pdev;
struct freelQ_ce *ce = &Q->centries[Q->pidx];
struct freelQ_e *e = &Q->entries[Q->pidx];
unsigned int dma_len = Q->rx_buffer_size - Q->dma_offset;
while (Q->credits < Q->entries_n) {
if (e->GenerationBit != Q->genbit) {
struct sk_buff *skb;
dma_addr_t mapping;
skb = alloc_skb(Q->rx_buffer_size, GFP_ATOMIC);
if (!skb)
break;
if (Q->dma_offset)
skb_reserve(skb, Q->dma_offset);
mapping = pci_map_single(pdev, skb->data, dma_len,
PCI_DMA_FROMDEVICE);
ce->skb = skb;
pci_unmap_addr_set(ce, dma_addr, mapping);
pci_unmap_len_set(ce, dma_len, dma_len);
e->AddrLow = (u32)mapping;
e->AddrHigh = (u64)mapping >> 32;
e->BufferLength = dma_len;
e->GenerationBit = e->GenerationBit2 = Q->genbit;
}
e++;
ce++;
if (++Q->pidx == Q->entries_n) {
Q->pidx = 0;
Q->genbit ^= 1;
ce = Q->centries;
e = Q->entries;
}
Q->credits++;
}
}
/*
* Calls refill_free_list for both freelist Qs. If we cannot
* fill at least 1/4 of both Qs, we go into 'few interrupt mode' in order
* to give the system time to free up resources.
*/
static void freelQs_empty(struct sge *sge)
{
u32 irq_reg = t1_read_reg_4(sge->adapter, A_SG_INT_ENABLE);
u32 irqholdoff_reg;
refill_free_list(sge, &sge->freelQ[0]);
refill_free_list(sge, &sge->freelQ[1]);
if (sge->freelQ[0].credits > (sge->freelQ[0].entries_n >> 2) &&
sge->freelQ[1].credits > (sge->freelQ[1].entries_n >> 2)) {
irq_reg |= F_FL_EXHAUSTED;
irqholdoff_reg = sge->intrtimer[sge->currIndex];
} else {
/* Clear the F_FL_EXHAUSTED interrupts for now */
irq_reg &= ~F_FL_EXHAUSTED;
irqholdoff_reg = sge->intrtimer_nres;
}
t1_write_reg_4(sge->adapter, A_SG_INTRTIMER, irqholdoff_reg);
t1_write_reg_4(sge->adapter, A_SG_INT_ENABLE, irq_reg);
/* We reenable the Qs to force a freelist GTS interrupt later */
doorbell_pio(sge, F_FL0_ENABLE | F_FL1_ENABLE);
}
#define SGE_PL_INTR_MASK (F_PL_INTR_SGE_ERR | F_PL_INTR_SGE_DATA)
#define SGE_INT_FATAL (F_RESPQ_OVERFLOW | F_PACKET_TOO_BIG | F_PACKET_MISMATCH)
#define SGE_INT_ENABLE (F_RESPQ_EXHAUSTED | F_RESPQ_OVERFLOW | \
F_FL_EXHAUSTED | F_PACKET_TOO_BIG | F_PACKET_MISMATCH)
/*
* Disable SGE Interrupts
*/
void t1_sge_intr_disable(struct sge *sge)
{
u32 val = t1_read_reg_4(sge->adapter, A_PL_ENABLE);
t1_write_reg_4(sge->adapter, A_PL_ENABLE, val & ~SGE_PL_INTR_MASK);
t1_write_reg_4(sge->adapter, A_SG_INT_ENABLE, 0);
}
/*
* Enable SGE interrupts.
*/
void t1_sge_intr_enable(struct sge *sge)
{
u32 en = SGE_INT_ENABLE;
u32 val = t1_read_reg_4(sge->adapter, A_PL_ENABLE);
if (sge->adapter->flags & TSO_CAPABLE)
en &= ~F_PACKET_TOO_BIG;
t1_write_reg_4(sge->adapter, A_SG_INT_ENABLE, en);
t1_write_reg_4(sge->adapter, A_PL_ENABLE, val | SGE_PL_INTR_MASK);
}
/*
* Clear SGE interrupts.
*/
void t1_sge_intr_clear(struct sge *sge)
{
t1_write_reg_4(sge->adapter, A_PL_CAUSE, SGE_PL_INTR_MASK);
t1_write_reg_4(sge->adapter, A_SG_INT_CAUSE, 0xffffffff);
}
/*
* SGE 'Error' interrupt handler
*/
int t1_sge_intr_error_handler(struct sge *sge)
{
struct adapter *adapter = sge->adapter;
u32 cause = t1_read_reg_4(adapter, A_SG_INT_CAUSE);
if (adapter->flags & TSO_CAPABLE)
cause &= ~F_PACKET_TOO_BIG;
if (cause & F_RESPQ_EXHAUSTED)
sge->intr_cnt.respQ_empty++;
if (cause & F_RESPQ_OVERFLOW) {
sge->intr_cnt.respQ_overflow++;
CH_ALERT("%s: SGE response queue overflow\n",
adapter->name);
}
if (cause & F_FL_EXHAUSTED) {
sge->intr_cnt.freelistQ_empty++;
freelQs_empty(sge);
}
if (cause & F_PACKET_TOO_BIG) {
sge->intr_cnt.pkt_too_big++;
CH_ALERT("%s: SGE max packet size exceeded\n",
adapter->name);
}
if (cause & F_PACKET_MISMATCH) {
sge->intr_cnt.pkt_mismatch++;
CH_ALERT("%s: SGE packet mismatch\n", adapter->name);
}
if (cause & SGE_INT_FATAL)
t1_fatal_err(adapter);
t1_write_reg_4(adapter, A_SG_INT_CAUSE, cause);
return 0;
}
/*
* The following code is copied from 2.6, where the skb_pull is doing the
* right thing and only pulls ETH_HLEN.
*
* Determine the packet's protocol ID. The rule here is that we
* assume 802.3 if the type field is short enough to be a length.
* This is normal practice and works for any 'now in use' protocol.
*/
static unsigned short sge_eth_type_trans(struct sk_buff *skb,
struct net_device *dev)
{
struct ethhdr *eth;
unsigned char *rawp;
skb->mac.raw = skb->data;
skb_pull(skb, ETH_HLEN);
eth = (struct ethhdr *)skb->mac.raw;
if (*eth->h_dest&1) {
if(memcmp(eth->h_dest, dev->broadcast, ETH_ALEN) == 0)
skb->pkt_type = PACKET_BROADCAST;
else
skb->pkt_type = PACKET_MULTICAST;
}
/*
* This ALLMULTI check should be redundant by 1.4
* so don't forget to remove it.
*
* Seems, you forgot to remove it. All silly devices
* seems to set IFF_PROMISC.
*/
else if (1 /*dev->flags&IFF_PROMISC*/)
{
if(memcmp(eth->h_dest,dev->dev_addr, ETH_ALEN))
skb->pkt_type=PACKET_OTHERHOST;
}
if (ntohs(eth->h_proto) >= 1536)
return eth->h_proto;
rawp = skb->data;
/*
* This is a magic hack to spot IPX packets. Older Novell breaks
* the protocol design and runs IPX over 802.3 without an 802.2 LLC
* layer. We look for FFFF which isn't a used 802.2 SSAP/DSAP. This
* won't work for fault tolerant netware but does for the rest.
*/
if (*(unsigned short *)rawp == 0xFFFF)
return htons(ETH_P_802_3);
/*
* Real 802.2 LLC
*/
return htons(ETH_P_802_2);
}
/*
* Prepare the received buffer and pass it up the stack. If it is small enough
* and allocation doesn't fail, we use a new sk_buff and copy the content.
*/
static unsigned int t1_sge_rx(struct sge *sge, struct freelQ *Q,
unsigned int len, unsigned int offload)
{
struct sk_buff *skb;
struct adapter *adapter = sge->adapter;
struct freelQ_ce *ce = &Q->centries[Q->cidx];
if (len <= SGE_RX_COPY_THRESHOLD &&
(skb = alloc_skb(len + NET_IP_ALIGN, GFP_ATOMIC))) {
struct freelQ_e *e;
char *src = ce->skb->data;
pci_dma_sync_single_for_cpu(adapter->pdev,
pci_unmap_addr(ce, dma_addr),
pci_unmap_len(ce, dma_len),
PCI_DMA_FROMDEVICE);
if (!offload) {
skb_reserve(skb, NET_IP_ALIGN);
src += sge->rx_pkt_pad;
}
memcpy(skb->data, src, len);
/* Reuse the entry. */
e = &Q->entries[Q->cidx];
e->GenerationBit ^= 1;
e->GenerationBit2 ^= 1;
} else {
pci_unmap_single(adapter->pdev, pci_unmap_addr(ce, dma_addr),
pci_unmap_len(ce, dma_len),
PCI_DMA_FROMDEVICE);
skb = ce->skb;
if (!offload && sge->rx_pkt_pad)
__skb_pull(skb, sge->rx_pkt_pad);
}
skb_put(skb, len);
if (unlikely(offload)) {
{
printk(KERN_ERR
"%s: unexpected offloaded packet, cmd %u\n",
adapter->name, *skb->data);
dev_kfree_skb_any(skb);
}
} else {
struct cpl_rx_pkt *p = (struct cpl_rx_pkt *)skb->data;
skb_pull(skb, sizeof(*p));
skb->dev = adapter->port[p->iff].dev;
skb->dev->last_rx = jiffies;
skb->protocol = sge_eth_type_trans(skb, skb->dev);
if ((adapter->flags & RX_CSUM_ENABLED) && p->csum == 0xffff &&
skb->protocol == htons(ETH_P_IP) &&
(skb->data[9] == IPPROTO_TCP ||
skb->data[9] == IPPROTO_UDP))
skb->ip_summed = CHECKSUM_UNNECESSARY;
else
skb->ip_summed = CHECKSUM_NONE;
if (adapter->vlan_grp && p->vlan_valid)
vlan_hwaccel_rx(skb, adapter->vlan_grp,
ntohs(p->vlan));
else
netif_rx(skb);
}
if (++Q->cidx == Q->entries_n)
Q->cidx = 0;
if (unlikely(--Q->credits < Q->entries_n - SGE_FREEL_REFILL_THRESH))
refill_free_list(sge, Q);
return 1;
}
/*
* Adaptive interrupt timer logic to keep the CPU utilization to
* manageable levels. Basically, as the Average Packet Size (APS)
* gets higher, the interrupt latency setting gets longer. Every
* SGE_INTR_BUCKETSIZE (of 100B) causes a bump of 2usec to the
* base value of SGE_INTRTIMER0. At large values of payload the
* latency hits the ceiling value of SGE_INTRTIMER1 stored at
* index SGE_INTR_MAXBUCKETS-1 in sge->intrtimer[].
*
* sge->currIndex caches the last index to save unneeded PIOs.
*/
static inline void update_intr_timer(struct sge *sge, unsigned int avg_payload)
{
unsigned int newIndex;
newIndex = avg_payload / SGE_INTR_BUCKETSIZE;
if (newIndex > SGE_INTR_MAXBUCKETS - 1) {
newIndex = SGE_INTR_MAXBUCKETS - 1;
}
/* Save a PIO with this check....maybe */
if (newIndex != sge->currIndex) {
t1_write_reg_4(sge->adapter, A_SG_INTRTIMER,
sge->intrtimer[newIndex]);
sge->currIndex = newIndex;
sge->adapter->params.sge.last_rx_coalesce_raw =
sge->intrtimer[newIndex];
}
}
/*
* Returns true if command queue q_num has enough available descriptors that
* we can resume Tx operation after temporarily disabling its packet queue.
*/
static inline int enough_free_Tx_descs(struct sge *sge, int q_num)
{
return atomic_read(&sge->cmdQ[q_num].credits) >
(sge->cmdQ[q_num].entries_n >> 2);
}
/*
* Main interrupt handler, optimized assuming that we took a 'DATA'
* interrupt.
*
* 1. Clear the interrupt
* 2. Loop while we find valid descriptors and process them; accumulate
* information that can be processed after the loop
* 3. Tell the SGE at which index we stopped processing descriptors
* 4. Bookkeeping; free TX buffers, ring doorbell if there are any
* outstanding TX buffers waiting, replenish RX buffers, potentially
* reenable upper layers if they were turned off due to lack of TX
* resources which are available again.
* 5. If we took an interrupt, but no valid respQ descriptors was found we
* let the slow_intr_handler run and do error handling.
*/
irqreturn_t t1_interrupt(int irq, void *cookie, struct pt_regs *regs)
{
struct net_device *netdev;
struct adapter *adapter = cookie;
struct sge *sge = adapter->sge;
struct respQ *Q = &sge->respQ;
unsigned int credits = Q->credits, flags = 0, ret = 0;
unsigned int tot_rxpayload = 0, tot_txpayload = 0, n_rx = 0, n_tx = 0;
unsigned int credits_pend[SGE_CMDQ_N] = { 0, 0 };
struct respQ_e *e = &Q->entries[Q->cidx];
prefetch(e);
t1_write_reg_4(adapter, A_PL_CAUSE, F_PL_INTR_SGE_DATA);
while (e->GenerationBit == Q->genbit) {
if (--credits < SGE_RESPQ_REPLENISH_THRES) {
u32 n = Q->entries_n - credits - 1;
t1_write_reg_4(adapter, A_SG_RSPQUEUECREDIT, n);
credits += n;
}
if (likely(e->DataValid)) {
if (!e->Sop || !e->Eop)
BUG();
t1_sge_rx(sge, &sge->freelQ[e->FreelistQid],
e->BufferLength, e->Offload);
tot_rxpayload += e->BufferLength;
++n_rx;
}
flags |= e->Qsleeping;
credits_pend[0] += e->Cmdq0CreditReturn;
credits_pend[1] += e->Cmdq1CreditReturn;
#ifdef CONFIG_SMP
/*
* If enough cmdQ0 buffers have finished DMAing free them so
* anyone that may be waiting for their release can continue.
* We do this only on MP systems to allow other CPUs to proceed
* promptly. UP systems can wait for the free_cmdQ_buffers()
* calls after this loop as the sole CPU is currently busy in
* this loop.
*/
if (unlikely(credits_pend[0] > SGE_FREEL_REFILL_THRESH)) {
free_cmdQ_buffers(sge, &sge->cmdQ[0], credits_pend[0],
&tot_txpayload);
n_tx += credits_pend[0];
credits_pend[0] = 0;
}
#endif
ret++;
e++;
if (unlikely(++Q->cidx == Q->entries_n)) {
Q->cidx = 0;
Q->genbit ^= 1;
e = Q->entries;
}
}
Q->credits = credits;
t1_write_reg_4(adapter, A_SG_SLEEPING, Q->cidx);
if (credits_pend[0])
free_cmdQ_buffers(sge, &sge->cmdQ[0], credits_pend[0], &tot_txpayload);
if (credits_pend[1])
free_cmdQ_buffers(sge, &sge->cmdQ[1], credits_pend[1], &tot_txpayload);
/* Do any coalescing and interrupt latency timer adjustments */
if (adapter->params.sge.coalesce_enable) {
unsigned int avg_txpayload = 0, avg_rxpayload = 0;
n_tx += credits_pend[0] + credits_pend[1];
/*
* Choose larger avg. payload size to increase
* throughput and reduce [CPU util., intr/s.]
*
* Throughput behavior favored in mixed-mode.
*/
if (n_tx)
avg_txpayload = tot_txpayload/n_tx;
if (n_rx)
avg_rxpayload = tot_rxpayload/n_rx;
if (n_tx && avg_txpayload > avg_rxpayload){
update_intr_timer(sge, avg_txpayload);
} else if (n_rx) {
update_intr_timer(sge, avg_rxpayload);
}
}
if (flags & F_CMDQ0_ENABLE) {
struct cmdQ *cmdQ = &sge->cmdQ[0];
atomic_set(&cmdQ->asleep, 1);
if (atomic_read(&cmdQ->pio_pidx) != cmdQ->pidx) {
doorbell_pio(sge, F_CMDQ0_ENABLE);
atomic_set(&cmdQ->pio_pidx, cmdQ->pidx);
}
}
if (unlikely(flags & (F_FL0_ENABLE | F_FL1_ENABLE)))
freelQs_empty(sge);
netdev = adapter->port[0].dev;
if (unlikely(netif_queue_stopped(netdev) && netif_carrier_ok(netdev) &&
enough_free_Tx_descs(sge, 0) &&
enough_free_Tx_descs(sge, 1))) {
netif_wake_queue(netdev);
}
if (unlikely(!ret))
ret = t1_slow_intr_handler(adapter);
return IRQ_RETVAL(ret != 0);
}
/*
* Enqueues the sk_buff onto the cmdQ[qid] and has hardware fetch it.
*
* The code figures out how many entries the sk_buff will require in the
* cmdQ and updates the cmdQ data structure with the state once the enqueue
* has complete. Then, it doesn't access the global structure anymore, but
* uses the corresponding fields on the stack. In conjuction with a spinlock
* around that code, we can make the function reentrant without holding the
* lock when we actually enqueue (which might be expensive, especially on
* architectures with IO MMUs).
*/
static unsigned int t1_sge_tx(struct sk_buff *skb, struct adapter *adapter,
unsigned int qid)
{
struct sge *sge = adapter->sge;
struct cmdQ *Q = &sge->cmdQ[qid];
struct cmdQ_e *e;
struct cmdQ_ce *ce;
dma_addr_t mapping;
unsigned int credits, pidx, genbit;
unsigned int count = 1 + skb_shinfo(skb)->nr_frags;
/*
* Coming from the timer
*/
if ((skb == sge->pskb)) {
/*
* Quit if any cmdQ activities
*/
if (!spin_trylock(&Q->Qlock))
return 0;
if (atomic_read(&Q->credits) != Q->entries_n) {
spin_unlock(&Q->Qlock);
return 0;
}
}
else
spin_lock(&Q->Qlock);
genbit = Q->genbit;
pidx = Q->pidx;
credits = atomic_read(&Q->credits);
credits -= count;
atomic_sub(count, &Q->credits);
Q->pidx += count;
if (Q->pidx >= Q->entries_n) {
Q->pidx -= Q->entries_n;
Q->genbit ^= 1;
}
if (unlikely(credits < (MAX_SKB_FRAGS + 1))) {
sge->intr_cnt.cmdQ_full[qid]++;
netif_stop_queue(adapter->port[0].dev);
}
spin_unlock(&Q->Qlock);
mapping = pci_map_single(adapter->pdev, skb->data,
skb->len - skb->data_len, PCI_DMA_TODEVICE);
ce = &Q->centries[pidx];
ce->skb = NULL;
pci_unmap_addr_set(ce, dma_addr, mapping);
pci_unmap_len_set(ce, dma_len, skb->len - skb->data_len);
ce->single = 1;
e = &Q->entries[pidx];
e->Sop = 1;
e->DataValid = 1;
e->BufferLength = skb->len - skb->data_len;
e->AddrHigh = (u64)mapping >> 32;
e->AddrLow = (u32)mapping;
if (--count > 0) {
unsigned int i;
e->Eop = 0;
wmb();
e->GenerationBit = e->GenerationBit2 = genbit;
for (i = 0; i < count; i++) {
skb_frag_t *frag = &skb_shinfo(skb)->frags[i];
ce++; e++;
if (++pidx == Q->entries_n) {
pidx = 0;
genbit ^= 1;
ce = Q->centries;
e = Q->entries;
}
mapping = pci_map_page(adapter->pdev, frag->page,
frag->page_offset,
frag->size,
PCI_DMA_TODEVICE);
ce->skb = NULL;
pci_unmap_addr_set(ce, dma_addr, mapping);
pci_unmap_len_set(ce, dma_len, frag->size);
ce->single = 0;
e->Sop = 0;
e->DataValid = 1;
e->BufferLength = frag->size;
e->AddrHigh = (u64)mapping >> 32;
e->AddrLow = (u32)mapping;
if (i < count - 1) {
e->Eop = 0;
wmb();
e->GenerationBit = e->GenerationBit2 = genbit;
}
}
}
if (skb != sge->pskb)
ce->skb = skb;
e->Eop = 1;
wmb();
e->GenerationBit = e->GenerationBit2 = genbit;
/*
* We always ring the doorbell for cmdQ1. For cmdQ0, we only ring
* the doorbell if the Q is asleep. There is a natural race, where
* the hardware is going to sleep just after we checked, however,
* then the interrupt handler will detect the outstanding TX packet
* and ring the doorbell for us.
*/
if (qid) {
doorbell_pio(sge, F_CMDQ1_ENABLE);
} else if (atomic_read(&Q->asleep)) {
atomic_set(&Q->asleep, 0);
doorbell_pio(sge, F_CMDQ0_ENABLE);
atomic_set(&Q->pio_pidx, Q->pidx);
}
return 0;
}
#define MK_ETH_TYPE_MSS(type, mss) (((mss) & 0x3FFF) | ((type) << 14))
/*
* Adds the CPL header to the sk_buff and passes it to t1_sge_tx.
*/
int t1_start_xmit(struct sk_buff *skb, struct net_device *dev)
{
struct adapter *adapter = dev->priv;
struct cpl_tx_pkt *cpl;
struct ethhdr *eth;
size_t max_len;
/*
* We are using a non-standard hard_header_len and some kernel
* components, such as pktgen, do not handle it right. Complain
* when this happens but try to fix things up.
*/
if (unlikely(skb_headroom(skb) < dev->hard_header_len - ETH_HLEN)) {
struct sk_buff *orig_skb = skb;
if (net_ratelimit())
printk(KERN_ERR
"%s: Tx packet has inadequate headroom\n",
dev->name);
skb = skb_realloc_headroom(skb, sizeof(struct cpl_tx_pkt_lso));
dev_kfree_skb_any(orig_skb);
if (!skb)
return -ENOMEM;
}
if (skb_shinfo(skb)->tso_size) {
int eth_type;
struct cpl_tx_pkt_lso *hdr;
eth_type = skb->nh.raw - skb->data == ETH_HLEN ?
CPL_ETH_II : CPL_ETH_II_VLAN;
hdr = (struct cpl_tx_pkt_lso *)skb_push(skb, sizeof(*hdr));
hdr->opcode = CPL_TX_PKT_LSO;
hdr->ip_csum_dis = hdr->l4_csum_dis = 0;
hdr->ip_hdr_words = skb->nh.iph->ihl;
hdr->tcp_hdr_words = skb->h.th->doff;
hdr->eth_type_mss = htons(MK_ETH_TYPE_MSS(eth_type,
skb_shinfo(skb)->tso_size));
hdr->len = htonl(skb->len - sizeof(*hdr));
cpl = (struct cpl_tx_pkt *)hdr;
} else
{
/*
* An Ethernet packet must have at least space for
* the DIX Ethernet header and be no greater than
* the device set MTU. Otherwise trash the packet.
*/
if (skb->len < ETH_HLEN)
goto t1_start_xmit_fail2;
eth = (struct ethhdr *)skb->data;
if (eth->h_proto == htons(ETH_P_8021Q))
max_len = dev->mtu + VLAN_ETH_HLEN;
else
max_len = dev->mtu + ETH_HLEN;
if (skb->len > max_len)
goto t1_start_xmit_fail2;
if (!(adapter->flags & UDP_CSUM_CAPABLE) &&
skb->ip_summed == CHECKSUM_HW &&
skb->nh.iph->protocol == IPPROTO_UDP &&
skb_checksum_help(skb, 0))
goto t1_start_xmit_fail3;
if (!adapter->sge->pskb) {
if (skb->protocol == htons(ETH_P_ARP) &&
skb->nh.arph->ar_op == htons(ARPOP_REQUEST))
adapter->sge->pskb = skb;
}
cpl = (struct cpl_tx_pkt *)skb_push(skb, sizeof(*cpl));
cpl->opcode = CPL_TX_PKT;
cpl->ip_csum_dis = 1; /* SW calculates IP csum */
cpl->l4_csum_dis = skb->ip_summed == CHECKSUM_HW ? 0 : 1;
/* the length field isn't used so don't bother setting it */
}
cpl->iff = dev->if_port;
#if defined(CONFIG_VLAN_8021Q) || defined(CONFIG_VLAN_8021Q_MODULE)
if (adapter->vlan_grp && vlan_tx_tag_present(skb)) {
cpl->vlan_valid = 1;
cpl->vlan = htons(vlan_tx_tag_get(skb));
} else
#endif
cpl->vlan_valid = 0;
dev->trans_start = jiffies;
return t1_sge_tx(skb, adapter, 0);
t1_start_xmit_fail3:
printk(KERN_INFO "%s: Unable to complete checksum\n", dev->name);
goto t1_start_xmit_fail1;
t1_start_xmit_fail2:
printk(KERN_INFO "%s: Invalid packet length %d, dropping\n",
dev->name, skb->len);
t1_start_xmit_fail1:
dev_kfree_skb_any(skb);
return 0;
}
void t1_sge_set_ptimeout(adapter_t *adapter, u32 val)
{
struct sge *sge = adapter->sge;
if (is_T2(adapter))
sge->ptimeout = max((u32)((HZ * val) / 1000), (u32)1);
}
u32 t1_sge_get_ptimeout(adapter_t *adapter)
{
struct sge *sge = adapter->sge;
return (is_T2(adapter) ? ((sge->ptimeout * 1000) / HZ) : 0);
}
/*****************************************************************************
* *
* File: sge.h *
* $Revision: 1.7 $ *
* $Date: 2005/03/23 07:15:59 $ *
* Description: *
* part of the Chelsio 10Gb Ethernet Driver. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License, version 2, as *
* published by the Free Software Foundation. *
* *
* You should have received a copy of the GNU General Public License along *
* with this program; if not, write to the Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
* *
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED *
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF *
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. *
* *
* http://www.chelsio.com *
* *
* Copyright (c) 2003 - 2005 Chelsio Communications, Inc. *
* All rights reserved. *
* *
* Maintainers: maintainers@chelsio.com *
* *
* Authors: Dimitrios Michailidis <dm@chelsio.com> *
* Tina Yang <tainay@chelsio.com> *
* Felix Marti <felix@chelsio.com> *
* Scott Bardone <sbardone@chelsio.com> *
* Kurt Ottaway <kottaway@chelsio.com> *
* Frank DiMambro <frank@chelsio.com> *
* *
* History: *
* *
****************************************************************************/
#ifndef _CHELSIO_LINUX_SGE_H_
#define _CHELSIO_LINUX_SGE_H_
#include <linux/types.h>
#include <linux/interrupt.h>
#include <asm/byteorder.h>
struct sge_intr_counts {
unsigned int respQ_empty; /* # times respQ empty */
unsigned int respQ_overflow; /* # respQ overflow (fatal) */
unsigned int freelistQ_empty; /* # times freelist empty */
unsigned int pkt_too_big; /* packet too large (fatal) */
unsigned int pkt_mismatch;
unsigned int cmdQ_full[2]; /* not HW interrupt, host cmdQ[] full */
};
struct sk_buff;
struct net_device;
struct cxgbdev;
struct adapter;
struct sge_params;
struct sge;
struct sge *t1_sge_create(struct adapter *, struct sge_params *);
int t1_sge_configure(struct sge *, struct sge_params *);
int t1_sge_set_coalesce_params(struct sge *, struct sge_params *);
void t1_sge_destroy(struct sge *);
irqreturn_t t1_interrupt(int, void *, struct pt_regs *);
int t1_start_xmit(struct sk_buff *skb, struct net_device *dev);
void t1_set_vlan_accel(struct adapter *adapter, int on_off);
void t1_sge_start(struct sge *);
void t1_sge_stop(struct sge *);
int t1_sge_intr_error_handler(struct sge *);
void t1_sge_intr_enable(struct sge *);
void t1_sge_intr_disable(struct sge *);
void t1_sge_intr_clear(struct sge *);
void t1_sge_set_ptimeout(adapter_t *adapter, u32 val);
u32 t1_sge_get_ptimeout(adapter_t *adapter);
#endif /* _CHELSIO_LINUX_SGE_H_ */
/*****************************************************************************
* *
* File: subr.c *
* $Revision: 1.12 $ *
* $Date: 2005/03/23 07:41:27 $ *
* Description: *
* Various subroutines (intr,pio,etc.) used by Chelsio 10G Ethernet driver. *
* part of the Chelsio 10Gb Ethernet Driver. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License, version 2, as *
* published by the Free Software Foundation. *
* *
* You should have received a copy of the GNU General Public License along *
* with this program; if not, write to the Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
* *
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED *
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF *
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. *
* *
* http://www.chelsio.com *
* *
* Copyright (c) 2003 - 2005 Chelsio Communications, Inc. *
* All rights reserved. *
* *
* Maintainers: maintainers@chelsio.com *
* *
* Authors: Dimitrios Michailidis <dm@chelsio.com> *
* Tina Yang <tainay@chelsio.com> *
* Felix Marti <felix@chelsio.com> *
* Scott Bardone <sbardone@chelsio.com> *
* Kurt Ottaway <kottaway@chelsio.com> *
* Frank DiMambro <frank@chelsio.com> *
* *
* History: *
* *
****************************************************************************/
#include "common.h"
#include "elmer0.h"
#include "regs.h"
#include "gmac.h"
#include "cphy.h"
#include "sge.h"
#include "tp.h"
#include "espi.h"
/**
* t1_wait_op_done - wait until an operation is completed
* @adapter: the adapter performing the operation
* @reg: the register to check for completion
* @mask: a single-bit field within @reg that indicates completion
* @polarity: the value of the field when the operation is completed
* @attempts: number of check iterations
* @delay: delay in usecs between iterations
*
* Wait until an operation is completed by checking a bit in a register
* up to @attempts times. Returns %0 if the operation completes and %1
* otherwise.
*/
static int t1_wait_op_done(adapter_t *adapter, int reg, u32 mask, int polarity,
int attempts, int delay)
{
while (1) {
u32 val = t1_read_reg_4(adapter, reg) & mask;
if (!!val == polarity)
return 0;
if (--attempts == 0)
return 1;
if (delay)
udelay(delay);
}
}
#define TPI_ATTEMPTS 50
/*
* Write a register over the TPI interface (unlocked and locked versions).
*/
static int __t1_tpi_write(adapter_t *adapter, u32 addr, u32 value)
{
int tpi_busy;
t1_write_reg_4(adapter, A_TPI_ADDR, addr);
t1_write_reg_4(adapter, A_TPI_WR_DATA, value);
t1_write_reg_4(adapter, A_TPI_CSR, F_TPIWR);
tpi_busy = t1_wait_op_done(adapter, A_TPI_CSR, F_TPIRDY, 1,
TPI_ATTEMPTS, 3);
if (tpi_busy)
CH_ALERT("%s: TPI write to 0x%x failed\n",
adapter->name, addr);
return tpi_busy;
}
int t1_tpi_write(adapter_t *adapter, u32 addr, u32 value)
{
int ret;
TPI_LOCK(adapter);
ret = __t1_tpi_write(adapter, addr, value);
TPI_UNLOCK(adapter);
return ret;
}
/*
* Read a register over the TPI interface (unlocked and locked versions).
*/
static int __t1_tpi_read(adapter_t *adapter, u32 addr, u32 *valp)
{
int tpi_busy;
t1_write_reg_4(adapter, A_TPI_ADDR, addr);
t1_write_reg_4(adapter, A_TPI_CSR, 0);
tpi_busy = t1_wait_op_done(adapter, A_TPI_CSR, F_TPIRDY, 1,
TPI_ATTEMPTS, 3);
if (tpi_busy)
CH_ALERT("%s: TPI read from 0x%x failed\n",
adapter->name, addr);
else
*valp = t1_read_reg_4(adapter, A_TPI_RD_DATA);
return tpi_busy;
}
int t1_tpi_read(adapter_t *adapter, u32 addr, u32 *valp)
{
int ret;
TPI_LOCK(adapter);
ret = __t1_tpi_read(adapter, addr, valp);
TPI_UNLOCK(adapter);
return ret;
}
/*
* Set a TPI parameter.
*/
static void t1_tpi_par(adapter_t *adapter, u32 value)
{
t1_write_reg_4(adapter, A_TPI_PAR, V_TPIPAR(value));
}
/*
* Called when a port's link settings change to propagate the new values to the
* associated PHY and MAC. After performing the common tasks it invokes an
* OS-specific handler.
*/
/* static */ void link_changed(adapter_t *adapter, int port_id)
{
int link_ok, speed, duplex, fc;
struct cphy *phy = adapter->port[port_id].phy;
struct link_config *lc = &adapter->port[port_id].link_config;
phy->ops->get_link_status(phy, &link_ok, &speed, &duplex, &fc);
lc->speed = speed < 0 ? SPEED_INVALID : speed;
lc->duplex = duplex < 0 ? DUPLEX_INVALID : duplex;
if (!(lc->requested_fc & PAUSE_AUTONEG))
fc = lc->requested_fc & (PAUSE_RX | PAUSE_TX);
if (link_ok && speed >= 0 && lc->autoneg == AUTONEG_ENABLE) {
/* Set MAC speed, duplex, and flow control to match PHY. */
struct cmac *mac = adapter->port[port_id].mac;
mac->ops->set_speed_duplex_fc(mac, speed, duplex, fc);
lc->fc = (unsigned char)fc;
}
t1_link_changed(adapter, port_id, link_ok, speed, duplex, fc);
}
static int t1_pci_intr_handler(adapter_t *adapter)
{
u32 pcix_cause;
pci_read_config_dword(adapter->pdev, A_PCICFG_INTR_CAUSE, &pcix_cause);
if (pcix_cause) {
pci_write_config_dword(adapter->pdev, A_PCICFG_INTR_CAUSE,
pcix_cause);
t1_fatal_err(adapter); /* PCI errors are fatal */
}
return 0;
}
/*
* Wait until Elmer's MI1 interface is ready for new operations.
*/
static int mi1_wait_until_ready(adapter_t *adapter, int mi1_reg)
{
int attempts = 100, busy;
do {
u32 val;
__t1_tpi_read(adapter, mi1_reg, &val);
busy = val & F_MI1_OP_BUSY;
if (busy)
udelay(10);
} while (busy && --attempts);
if (busy)
CH_ALERT("%s: MDIO operation timed out\n",
adapter->name);
return busy;
}
/*
* MI1 MDIO initialization.
*/
static void mi1_mdio_init(adapter_t *adapter, const struct board_info *bi)
{
u32 clkdiv = bi->clock_elmer0 / (2 * bi->mdio_mdc) - 1;
u32 val = F_MI1_PREAMBLE_ENABLE | V_MI1_MDI_INVERT(bi->mdio_mdiinv) |
V_MI1_MDI_ENABLE(bi->mdio_mdien) | V_MI1_CLK_DIV(clkdiv);
if (!(bi->caps & SUPPORTED_10000baseT_Full))
val |= V_MI1_SOF(1);
t1_tpi_write(adapter, A_ELMER0_PORT0_MI1_CFG, val);
}
static int mi1_mdio_ext_read(adapter_t *adapter, int phy_addr, int mmd_addr,
int reg_addr, unsigned int *valp)
{
u32 addr = V_MI1_REG_ADDR(mmd_addr) | V_MI1_PHY_ADDR(phy_addr);
TPI_LOCK(adapter);
/* Write the address we want. */
__t1_tpi_write(adapter, A_ELMER0_PORT0_MI1_ADDR, addr);
__t1_tpi_write(adapter, A_ELMER0_PORT0_MI1_DATA, reg_addr);
__t1_tpi_write(adapter, A_ELMER0_PORT0_MI1_OP,
MI1_OP_INDIRECT_ADDRESS);
mi1_wait_until_ready(adapter, A_ELMER0_PORT0_MI1_OP);
/* Write the operation we want. */
__t1_tpi_write(adapter, A_ELMER0_PORT0_MI1_OP, MI1_OP_INDIRECT_READ);
mi1_wait_until_ready(adapter, A_ELMER0_PORT0_MI1_OP);
/* Read the data. */
__t1_tpi_read(adapter, A_ELMER0_PORT0_MI1_DATA, valp);
TPI_UNLOCK(adapter);
return 0;
}
static int mi1_mdio_ext_write(adapter_t *adapter, int phy_addr, int mmd_addr,
int reg_addr, unsigned int val)
{
u32 addr = V_MI1_REG_ADDR(mmd_addr) | V_MI1_PHY_ADDR(phy_addr);
TPI_LOCK(adapter);
/* Write the address we want. */
__t1_tpi_write(adapter, A_ELMER0_PORT0_MI1_ADDR, addr);
__t1_tpi_write(adapter, A_ELMER0_PORT0_MI1_DATA, reg_addr);
__t1_tpi_write(adapter, A_ELMER0_PORT0_MI1_OP,
MI1_OP_INDIRECT_ADDRESS);
mi1_wait_until_ready(adapter, A_ELMER0_PORT0_MI1_OP);
/* Write the data. */
__t1_tpi_write(adapter, A_ELMER0_PORT0_MI1_DATA, val);
__t1_tpi_write(adapter, A_ELMER0_PORT0_MI1_OP, MI1_OP_INDIRECT_WRITE);
mi1_wait_until_ready(adapter, A_ELMER0_PORT0_MI1_OP);
TPI_UNLOCK(adapter);
return 0;
}
static struct mdio_ops mi1_mdio_ext_ops = {
mi1_mdio_init,
mi1_mdio_ext_read,
mi1_mdio_ext_write
};
enum {
CH_BRD_N110_1F,
CH_BRD_N210_1F,
CH_BRD_T210_1F,
};
static struct board_info t1_board[] = {
{ CHBT_BOARD_N110, 1/*ports#*/,
SUPPORTED_10000baseT_Full | SUPPORTED_FIBRE /*caps*/, CHBT_TERM_T1,
CHBT_MAC_PM3393, CHBT_PHY_88X2010,
125000000/*clk-core*/, 0/*clk-mc3*/, 0/*clk-mc4*/,
1/*espi-ports*/, 0/*clk-cspi*/, 44/*clk-elmer0*/, 0/*mdien*/,
0/*mdiinv*/, 1/*mdc*/, 0/*phybaseaddr*/, &t1_pm3393_ops,
&t1_mv88x201x_ops, &mi1_mdio_ext_ops,
"Chelsio N110 1x10GBaseX NIC" },
{ CHBT_BOARD_N210, 1/*ports#*/,
SUPPORTED_10000baseT_Full | SUPPORTED_FIBRE /*caps*/, CHBT_TERM_T2,
CHBT_MAC_PM3393, CHBT_PHY_88X2010,
125000000/*clk-core*/, 0/*clk-mc3*/, 0/*clk-mc4*/,
1/*espi-ports*/, 0/*clk-cspi*/, 44/*clk-elmer0*/, 0/*mdien*/,
0/*mdiinv*/, 1/*mdc*/, 0/*phybaseaddr*/, &t1_pm3393_ops,
&t1_mv88x201x_ops, &mi1_mdio_ext_ops,
"Chelsio N210 1x10GBaseX NIC" },
};
struct pci_device_id t1_pci_tbl[] = {
CH_DEVICE(7, 0, CH_BRD_N110_1F),
CH_DEVICE(10, 1, CH_BRD_N210_1F),
{ 0, }
};
/*
* Return the board_info structure with a given index. Out-of-range indices
* return NULL.
*/
const struct board_info *t1_get_board_info(unsigned int board_id)
{
return board_id < DIMOF(t1_board) ? &t1_board[board_id] : NULL;
}
struct chelsio_vpd_t {
u32 format_version;
u8 serial_number[16];
u8 mac_base_address[6];
u8 pad[2]; /* make multiple-of-4 size requirement explicit */
};
#define EEPROMSIZE (8 * 1024)
#define EEPROM_MAX_POLL 4
/*
* Read SEEPROM. A zero is written to the flag register when the addres is
* written to the Control register. The hardware device will set the flag to a
* one when 4B have been transferred to the Data register.
*/
int t1_seeprom_read(adapter_t *adapter, u32 addr, u32 *data)
{
int i = EEPROM_MAX_POLL;
u16 val;
if (addr >= EEPROMSIZE || (addr & 3))
return -EINVAL;
pci_write_config_word(adapter->pdev, A_PCICFG_VPD_ADDR, (u16)addr);
do {
udelay(50);
pci_read_config_word(adapter->pdev, A_PCICFG_VPD_ADDR, &val);
} while (!(val & F_VPD_OP_FLAG) && --i);
if (!(val & F_VPD_OP_FLAG)) {
CH_ERR("%s: reading EEPROM address 0x%x failed\n",
adapter->name, addr);
return -EIO;
}
pci_read_config_dword(adapter->pdev, A_PCICFG_VPD_DATA, data);
*data = le32_to_cpu(*data);
return 0;
}
static int t1_eeprom_vpd_get(adapter_t *adapter, struct chelsio_vpd_t *vpd)
{
int addr, ret = 0;
for (addr = 0; !ret && addr < sizeof(*vpd); addr += sizeof(u32))
ret = t1_seeprom_read(adapter, addr,
(u32 *)((u8 *)vpd + addr));
return ret;
}
/*
* Read a port's MAC address from the VPD ROM.
*/
static int vpd_macaddress_get(adapter_t *adapter, int index, u8 mac_addr[])
{
struct chelsio_vpd_t vpd;
if (t1_eeprom_vpd_get(adapter, &vpd))
return 1;
memcpy(mac_addr, vpd.mac_base_address, 5);
mac_addr[5] = vpd.mac_base_address[5] + index;
return 0;
}
/*
* Set up the MAC/PHY according to the requested link settings.
*
* If the PHY can auto-negotiate first decide what to advertise, then
* enable/disable auto-negotiation as desired and reset.
*
* If the PHY does not auto-negotiate we just reset it.
*
* If auto-negotiation is off set the MAC to the proper speed/duplex/FC,
* otherwise do it later based on the outcome of auto-negotiation.
*/
int t1_link_start(struct cphy *phy, struct cmac *mac, struct link_config *lc)
{
unsigned int fc = lc->requested_fc & (PAUSE_RX | PAUSE_TX);
if (lc->supported & SUPPORTED_Autoneg) {
lc->advertising &= ~(ADVERTISED_ASYM_PAUSE | ADVERTISED_PAUSE);
if (fc) {
lc->advertising |= ADVERTISED_ASYM_PAUSE;
if (fc == (PAUSE_RX | PAUSE_TX))
lc->advertising |= ADVERTISED_PAUSE;
}
phy->ops->advertise(phy, lc->advertising);
if (lc->autoneg == AUTONEG_DISABLE) {
lc->speed = lc->requested_speed;
lc->duplex = lc->requested_duplex;
lc->fc = (unsigned char)fc;
mac->ops->set_speed_duplex_fc(mac, lc->speed,
lc->duplex, fc);
/* Also disables autoneg */
phy->ops->set_speed_duplex(phy, lc->speed, lc->duplex);
phy->ops->reset(phy, 0);
} else
phy->ops->autoneg_enable(phy); /* also resets PHY */
} else {
mac->ops->set_speed_duplex_fc(mac, -1, -1, fc);
lc->fc = (unsigned char)fc;
phy->ops->reset(phy, 0);
}
return 0;
}
/*
* External interrupt handler for boards using elmer0.
*/
int elmer0_ext_intr_handler(adapter_t *adapter)
{
struct cphy *phy;
int phy_cause;
u32 cause;
t1_tpi_read(adapter, A_ELMER0_INT_CAUSE, &cause);
switch (board_info(adapter)->board) {
case CHBT_BOARD_CHT210:
case CHBT_BOARD_N210:
case CHBT_BOARD_N110:
if (cause & ELMER0_GP_BIT6) { /* Marvell 88x2010 interrupt */
phy = adapter->port[0].phy;
phy_cause = phy->ops->interrupt_handler(phy);
if (phy_cause & cphy_cause_link_change)
link_changed(adapter, 0);
}
break;
case CHBT_BOARD_8000:
case CHBT_BOARD_CHT110:
CH_DBG(adapter, INTR, "External interrupt cause 0x%x\n",
cause);
if (cause & ELMER0_GP_BIT1) { /* PMC3393 INTB */
struct cmac *mac = adapter->port[0].mac;
mac->ops->interrupt_handler(mac);
}
if (cause & ELMER0_GP_BIT5) { /* XPAK MOD_DETECT */
u32 mod_detect;
t1_tpi_read(adapter, A_ELMER0_GPI_STAT, &mod_detect);
CH_MSG(adapter, INFO, LINK, "XPAK %s\n",
mod_detect ? "removed" : "inserted");
}
break;
}
t1_tpi_write(adapter, A_ELMER0_INT_CAUSE, cause);
return 0;
}
/* Enables all interrupts. */
void t1_interrupts_enable(adapter_t *adapter)
{
unsigned int i;
adapter->slow_intr_mask = F_PL_INTR_SGE_ERR | F_PL_INTR_TP;
t1_sge_intr_enable(adapter->sge);
t1_tp_intr_enable(adapter->tp);
if (adapter->espi) {
adapter->slow_intr_mask |= F_PL_INTR_ESPI;
t1_espi_intr_enable(adapter->espi);
}
/* Enable MAC/PHY interrupts for each port. */
for_each_port(adapter, i) {
adapter->port[i].mac->ops->interrupt_enable(adapter->port[i].mac);
adapter->port[i].phy->ops->interrupt_enable(adapter->port[i].phy);
}
/* Enable PCIX & external chip interrupts on ASIC boards. */
if (t1_is_asic(adapter)) {
u32 pl_intr = t1_read_reg_4(adapter, A_PL_ENABLE);
/* PCI-X interrupts */
pci_write_config_dword(adapter->pdev, A_PCICFG_INTR_ENABLE,
0xffffffff);
adapter->slow_intr_mask |= F_PL_INTR_EXT | F_PL_INTR_PCIX;
pl_intr |= F_PL_INTR_EXT | F_PL_INTR_PCIX;
t1_write_reg_4(adapter, A_PL_ENABLE, pl_intr);
}
}
/* Disables all interrupts. */
void t1_interrupts_disable(adapter_t* adapter)
{
unsigned int i;
t1_sge_intr_disable(adapter->sge);
t1_tp_intr_disable(adapter->tp);
if (adapter->espi)
t1_espi_intr_disable(adapter->espi);
/* Disable MAC/PHY interrupts for each port. */
for_each_port(adapter, i) {
adapter->port[i].mac->ops->interrupt_disable(adapter->port[i].mac);
adapter->port[i].phy->ops->interrupt_disable(adapter->port[i].phy);
}
/* Disable PCIX & external chip interrupts. */
if (t1_is_asic(adapter))
t1_write_reg_4(adapter, A_PL_ENABLE, 0);
/* PCI-X interrupts */
pci_write_config_dword(adapter->pdev, A_PCICFG_INTR_ENABLE, 0);
adapter->slow_intr_mask = 0;
}
/* Clears all interrupts */
void t1_interrupts_clear(adapter_t* adapter)
{
unsigned int i;
t1_sge_intr_clear(adapter->sge);
t1_tp_intr_clear(adapter->tp);
if (adapter->espi)
t1_espi_intr_clear(adapter->espi);
/* Clear MAC/PHY interrupts for each port. */
for_each_port(adapter, i) {
adapter->port[i].mac->ops->interrupt_clear(adapter->port[i].mac);
adapter->port[i].phy->ops->interrupt_clear(adapter->port[i].phy);
}
/* Enable interrupts for external devices. */
if (t1_is_asic(adapter)) {
u32 pl_intr = t1_read_reg_4(adapter, A_PL_CAUSE);
t1_write_reg_4(adapter, A_PL_CAUSE,
pl_intr | F_PL_INTR_EXT | F_PL_INTR_PCIX);
}
/* PCI-X interrupts */
pci_write_config_dword(adapter->pdev, A_PCICFG_INTR_CAUSE, 0xffffffff);
}
/*
* Slow path interrupt handler for ASICs.
*/
static int asic_slow_intr(adapter_t *adapter)
{
u32 cause = t1_read_reg_4(adapter, A_PL_CAUSE);
cause &= adapter->slow_intr_mask;
if (!cause)
return 0;
if (cause & F_PL_INTR_SGE_ERR)
t1_sge_intr_error_handler(adapter->sge);
if (cause & F_PL_INTR_TP)
t1_tp_intr_handler(adapter->tp);
if (cause & F_PL_INTR_ESPI)
t1_espi_intr_handler(adapter->espi);
if (cause & F_PL_INTR_PCIX)
t1_pci_intr_handler(adapter);
if (cause & F_PL_INTR_EXT)
t1_elmer0_ext_intr(adapter);
/* Clear the interrupts just processed. */
t1_write_reg_4(adapter, A_PL_CAUSE, cause);
(void)t1_read_reg_4(adapter, A_PL_CAUSE); /* flush writes */
return 1;
}
int t1_slow_intr_handler(adapter_t *adapter)
{
return asic_slow_intr(adapter);
}
/* Power sequencing is a work-around for Intel's XPAKs. */
static void power_sequence_xpak(adapter_t* adapter)
{
u32 mod_detect;
u32 gpo;
/* Check for XPAK */
t1_tpi_read(adapter, A_ELMER0_GPI_STAT, &mod_detect);
if (!(ELMER0_GP_BIT5 & mod_detect)) {
/* XPAK is present */
t1_tpi_read(adapter, A_ELMER0_GPO, &gpo);
gpo |= ELMER0_GP_BIT18;
t1_tpi_write(adapter, A_ELMER0_GPO, gpo);
}
}
int __devinit t1_get_board_rev(adapter_t *adapter, const struct board_info *bi,
struct adapter_params *p)
{
p->chip_version = bi->chip_term;
p->is_asic = (p->chip_version != CHBT_TERM_FPGA);
if (p->chip_version == CHBT_TERM_T1 ||
p->chip_version == CHBT_TERM_T2 ||
p->chip_version == CHBT_TERM_FPGA) {
u32 val = t1_read_reg_4(adapter, A_TP_PC_CONFIG);
val = G_TP_PC_REV(val);
if (val == 2)
p->chip_revision = TERM_T1B;
else if (val == 3)
p->chip_revision = TERM_T2;
else
return -1;
} else
return -1;
return 0;
}
/*
* Enable board components other than the Chelsio chip, such as external MAC
* and PHY.
*/
static int board_init(adapter_t *adapter, const struct board_info *bi)
{
switch (bi->board) {
case CHBT_BOARD_8000:
case CHBT_BOARD_N110:
case CHBT_BOARD_N210:
case CHBT_BOARD_CHT210:
case CHBT_BOARD_COUGAR:
t1_tpi_par(adapter, 0xf);
t1_tpi_write(adapter, A_ELMER0_GPO, 0x800);
break;
case CHBT_BOARD_CHT110:
t1_tpi_par(adapter, 0xf);
t1_tpi_write(adapter, A_ELMER0_GPO, 0x1800);
/* TBD XXX Might not need. This fixes a problem
* described in the Intel SR XPAK errata.
*/
power_sequence_xpak(adapter);
break;
}
return 0;
}
/*
* Initialize and configure the Terminator HW modules. Note that external
* MAC and PHYs are initialized separately.
*/
int t1_init_hw_modules(adapter_t *adapter)
{
int err = -EIO;
const struct board_info *bi = board_info(adapter);
if (!adapter->mc4) {
u32 val = t1_read_reg_4(adapter, A_MC4_CFG);
t1_write_reg_4(adapter, A_MC4_CFG, val | F_READY | F_MC4_SLOW);
t1_write_reg_4(adapter, A_MC5_CONFIG,
F_M_BUS_ENABLE | F_TCAM_RESET);
}
if (adapter->espi && t1_espi_init(adapter->espi, bi->chip_mac,
bi->espi_nports))
goto out_err;
if (t1_tp_reset(adapter->tp, &adapter->params.tp, bi->clock_core))
goto out_err;
err = t1_sge_configure(adapter->sge, &adapter->params.sge);
if (err)
goto out_err;
err = 0;
out_err:
return err;
}
/*
* Determine a card's PCI mode.
*/
static void __devinit get_pci_mode(adapter_t *adapter, struct pci_params *p)
{
static unsigned short speed_map[] = { 33, 66, 100, 133 };
u32 pci_mode;
pci_read_config_dword(adapter->pdev, A_PCICFG_MODE, &pci_mode);
p->speed = speed_map[G_PCI_MODE_CLK(pci_mode)];
p->width = (pci_mode & F_PCI_MODE_64BIT) ? 64 : 32;
p->is_pcix = (pci_mode & F_PCI_MODE_PCIX) != 0;
}
/*
* Release the structures holding the SW per-Terminator-HW-module state.
*/
void t1_free_sw_modules(adapter_t *adapter)
{
unsigned int i;
for_each_port(adapter, i) {
struct cmac *mac = adapter->port[i].mac;
struct cphy *phy = adapter->port[i].phy;
if (mac)
mac->ops->destroy(mac);
if (phy)
phy->ops->destroy(phy);
}
if (adapter->sge)
t1_sge_destroy(adapter->sge);
if (adapter->tp)
t1_tp_destroy(adapter->tp);
if (adapter->espi)
t1_espi_destroy(adapter->espi);
}
static void __devinit init_link_config(struct link_config *lc,
const struct board_info *bi)
{
lc->supported = bi->caps;
lc->requested_speed = lc->speed = SPEED_INVALID;
lc->requested_duplex = lc->duplex = DUPLEX_INVALID;
lc->requested_fc = lc->fc = PAUSE_RX | PAUSE_TX;
if (lc->supported & SUPPORTED_Autoneg) {
lc->advertising = lc->supported;
lc->autoneg = AUTONEG_ENABLE;
lc->requested_fc |= PAUSE_AUTONEG;
} else {
lc->advertising = 0;
lc->autoneg = AUTONEG_DISABLE;
}
}
/*
* Allocate and initialize the data structures that hold the SW state of
* the Terminator HW modules.
*/
int __devinit t1_init_sw_modules(adapter_t *adapter,
const struct board_info *bi)
{
unsigned int i;
adapter->params.brd_info = bi;
adapter->params.nports = bi->port_number;
adapter->params.stats_update_period = bi->gmac->stats_update_period;
adapter->sge = t1_sge_create(adapter, &adapter->params.sge);
if (!adapter->sge) {
CH_ERR("%s: SGE initialization failed\n",
adapter->name);
goto error;
}
if (bi->espi_nports && !(adapter->espi = t1_espi_create(adapter))) {
CH_ERR("%s: ESPI initialization failed\n",
adapter->name);
goto error;
}
adapter->tp = t1_tp_create(adapter, &adapter->params.tp);
if (!adapter->tp) {
CH_ERR("%s: TP initialization failed\n",
adapter->name);
goto error;
}
board_init(adapter, bi);
bi->mdio_ops->init(adapter, bi);
if (bi->gphy->reset)
bi->gphy->reset(adapter);
if (bi->gmac->reset)
bi->gmac->reset(adapter);
for_each_port(adapter, i) {
u8 hw_addr[6];
struct cmac *mac;
int phy_addr = bi->mdio_phybaseaddr + i;
adapter->port[i].phy = bi->gphy->create(adapter, phy_addr,
bi->mdio_ops);
if (!adapter->port[i].phy) {
CH_ERR("%s: PHY %d initialization failed\n",
adapter->name, i);
goto error;
}
adapter->port[i].mac = mac = bi->gmac->create(adapter, i);
if (!mac) {
CH_ERR("%s: MAC %d initialization failed\n",
adapter->name, i);
goto error;
}
/*
* Get the port's MAC addresses either from the EEPROM if one
* exists or the one hardcoded in the MAC.
*/
if (!t1_is_asic(adapter) || bi->chip_mac == CHBT_MAC_DUMMY)
mac->ops->macaddress_get(mac, hw_addr);
else if (vpd_macaddress_get(adapter, i, hw_addr)) {
CH_ERR("%s: could not read MAC address from VPD ROM\n",
port_name(adapter, i));
goto error;
}
t1_set_hw_addr(adapter, i, hw_addr);
init_link_config(&adapter->port[i].link_config, bi);
}
get_pci_mode(adapter, &adapter->params.pci);
t1_interrupts_clear(adapter);
return 0;
error:
t1_free_sw_modules(adapter);
return -1;
}
/*****************************************************************************
* *
* File: suni1x10gexp_regs.h *
* $Revision: 1.4 $ *
* $Date: 2005/03/23 07:15:59 $ *
* Description: *
* PMC/SIERRA (pm3393) MAC-PHY functionality. *
* part of the Chelsio 10Gb Ethernet Driver. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License, version 2, as *
* published by the Free Software Foundation. *
* *
* You should have received a copy of the GNU General Public License along *
* with this program; if not, write to the Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
* *
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED *
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF *
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. *
* *
* http://www.chelsio.com *
* *
* Copyright (c) 2003 - 2005 Chelsio Communications, Inc. *
* All rights reserved. *
* *
* Maintainers: maintainers@chelsio.com *
* *
* Authors: Dimitrios Michailidis <dm@chelsio.com> *
* Tina Yang <tainay@chelsio.com> *
* Felix Marti <felix@chelsio.com> *
* Scott Bardone <sbardone@chelsio.com> *
* Kurt Ottaway <kottaway@chelsio.com> *
* Frank DiMambro <frank@chelsio.com> *
* *
* History: *
* *
****************************************************************************/
#ifndef _SUNI1x10GEXP_REGS_H
#define _SUNI1x10GEXP_REGS_H
/******************************************************************************/
/** S/UNI-1x10GE-XP REGISTER ADDRESS MAP **/
/******************************************************************************/
/* Refer to the Register Bit Masks bellow for the naming of each register and */
/* to the S/UNI-1x10GE-XP Data Sheet for the signification of each bit */
/******************************************************************************/
#define SUNI1x10GEXP_REG_DEVICE_STATUS 0x0004
#define SUNI1x10GEXP_REG_MASTER_INTERRUPT_STATUS 0x000D
#define SUNI1x10GEXP_REG_GLOBAL_INTERRUPT_ENABLE 0x000E
#define SUNI1x10GEXP_REG_SERDES_3125_INTERRUPT_ENABLE 0x0102
#define SUNI1x10GEXP_REG_SERDES_3125_INTERRUPT_STATUS 0x0104
#define SUNI1x10GEXP_REG_RXXG_CONFIG_1 0x2040
#define SUNI1x10GEXP_REG_RXXG_CONFIG_3 0x2042
#define SUNI1x10GEXP_REG_RXXG_INTERRUPT 0x2043
#define SUNI1x10GEXP_REG_RXXG_MAX_FRAME_LENGTH 0x2045
#define SUNI1x10GEXP_REG_RXXG_SA_15_0 0x2046
#define SUNI1x10GEXP_REG_RXXG_SA_31_16 0x2047
#define SUNI1x10GEXP_REG_RXXG_SA_47_32 0x2048
#define SUNI1x10GEXP_REG_RXXG_EXACT_MATCH_ADDR_1_LOW 0x204D
#define SUNI1x10GEXP_REG_RXXG_EXACT_MATCH_ADDR_1_MID 0x204E
#define SUNI1x10GEXP_REG_RXXG_EXACT_MATCH_ADDR_1_HIGH 0x204F
#define SUNI1x10GEXP_REG_RXXG_MULTICAST_HASH_LOW 0x206A
#define SUNI1x10GEXP_REG_RXXG_MULTICAST_HASH_MIDLOW 0x206B
#define SUNI1x10GEXP_REG_RXXG_MULTICAST_HASH_MIDHIGH 0x206C
#define SUNI1x10GEXP_REG_RXXG_MULTICAST_HASH_HIGH 0x206D
#define SUNI1x10GEXP_REG_RXXG_ADDRESS_FILTER_CONTROL_0 0x206E
#define SUNI1x10GEXP_REG_RXXG_ADDRESS_FILTER_CONTROL_2 0x2070
#define SUNI1x10GEXP_REG_XRF_INTERRUPT_ENABLE 0x2088
#define SUNI1x10GEXP_REG_XRF_INTERRUPT_STATUS 0x2089
#define SUNI1x10GEXP_REG_XRF_DIAG_INTERRUPT_ENABLE 0x208B
#define SUNI1x10GEXP_REG_XRF_DIAG_INTERRUPT_STATUS 0x208C
#define SUNI1x10GEXP_REG_RXOAM_INTERRUPT_ENABLE 0x20C7
#define SUNI1x10GEXP_REG_RXOAM_INTERRUPT_STATUS 0x20C8
#define SUNI1x10GEXP_REG_MSTAT_CONTROL 0x2100
#define SUNI1x10GEXP_REG_MSTAT_COUNTER_ROLLOVER_0 0x2101
#define SUNI1x10GEXP_REG_MSTAT_COUNTER_ROLLOVER_1 0x2102
#define SUNI1x10GEXP_REG_MSTAT_COUNTER_ROLLOVER_2 0x2103
#define SUNI1x10GEXP_REG_MSTAT_COUNTER_ROLLOVER_3 0x2104
#define SUNI1x10GEXP_REG_MSTAT_INTERRUPT_MASK_0 0x2105
#define SUNI1x10GEXP_REG_MSTAT_INTERRUPT_MASK_1 0x2106
#define SUNI1x10GEXP_REG_MSTAT_INTERRUPT_MASK_2 0x2107
#define SUNI1x10GEXP_REG_MSTAT_INTERRUPT_MASK_3 0x2108
#define SUNI1x10GEXP_REG_MSTAT_COUNTER_0_LOW 0x2110
#define SUNI1x10GEXP_REG_MSTAT_COUNTER_1_LOW 0x2114
#define SUNI1x10GEXP_REG_MSTAT_COUNTER_4_LOW 0x2120
#define SUNI1x10GEXP_REG_MSTAT_COUNTER_5_LOW 0x2124
#define SUNI1x10GEXP_REG_MSTAT_COUNTER_6_LOW 0x2128
#define SUNI1x10GEXP_REG_MSTAT_COUNTER_8_LOW 0x2130
#define SUNI1x10GEXP_REG_MSTAT_COUNTER_10_LOW 0x2138
#define SUNI1x10GEXP_REG_MSTAT_COUNTER_11_LOW 0x213C
#define SUNI1x10GEXP_REG_MSTAT_COUNTER_12_LOW 0x2140
#define SUNI1x10GEXP_REG_MSTAT_COUNTER_13_LOW 0x2144
#define SUNI1x10GEXP_REG_MSTAT_COUNTER_15_LOW 0x214C
#define SUNI1x10GEXP_REG_MSTAT_COUNTER_16_LOW 0x2150
#define SUNI1x10GEXP_REG_MSTAT_COUNTER_17_LOW 0x2154
#define SUNI1x10GEXP_REG_MSTAT_COUNTER_18_LOW 0x2158
#define SUNI1x10GEXP_REG_MSTAT_COUNTER_33_LOW 0x2194
#define SUNI1x10GEXP_REG_MSTAT_COUNTER_35_LOW 0x219C
#define SUNI1x10GEXP_REG_MSTAT_COUNTER_36_LOW 0x21A0
#define SUNI1x10GEXP_REG_MSTAT_COUNTER_38_LOW 0x21A8
#define SUNI1x10GEXP_REG_MSTAT_COUNTER_40_LOW 0x21B0
#define SUNI1x10GEXP_REG_MSTAT_COUNTER_42_LOW 0x21B8
#define SUNI1x10GEXP_REG_MSTAT_COUNTER_43_LOW 0x21BC
#define SUNI1x10GEXP_REG_IFLX_FIFO_OVERFLOW_ENABLE 0x2209
#define SUNI1x10GEXP_REG_IFLX_FIFO_OVERFLOW_INTERRUPT 0x220A
#define SUNI1x10GEXP_REG_PL4ODP_INTERRUPT_MASK 0x2282
#define SUNI1x10GEXP_REG_PL4ODP_INTERRUPT 0x2283
#define SUNI1x10GEXP_REG_PL4IO_LOCK_DETECT_STATUS 0x2300
#define SUNI1x10GEXP_REG_PL4IO_LOCK_DETECT_CHANGE 0x2301
#define SUNI1x10GEXP_REG_PL4IO_LOCK_DETECT_MASK 0x2302
#define SUNI1x10GEXP_REG_TXXG_CONFIG_1 0x3040
#define SUNI1x10GEXP_REG_TXXG_CONFIG_3 0x3042
#define SUNI1x10GEXP_REG_TXXG_INTERRUPT 0x3043
#define SUNI1x10GEXP_REG_TXXG_MAX_FRAME_SIZE 0x3045
#define SUNI1x10GEXP_REG_TXXG_SA_15_0 0x3047
#define SUNI1x10GEXP_REG_TXXG_SA_31_16 0x3048
#define SUNI1x10GEXP_REG_TXXG_SA_47_32 0x3049
#define SUNI1x10GEXP_REG_XTEF_INTERRUPT_STATUS 0x3084
#define SUNI1x10GEXP_REG_XTEF_INTERRUPT_ENABLE 0x3085
#define SUNI1x10GEXP_REG_TXOAM_INTERRUPT_ENABLE 0x30C6
#define SUNI1x10GEXP_REG_TXOAM_INTERRUPT_STATUS 0x30C7
#define SUNI1x10GEXP_REG_EFLX_FIFO_OVERFLOW_ERROR_ENABLE 0x320C
#define SUNI1x10GEXP_REG_EFLX_FIFO_OVERFLOW_ERROR_INDICATION 0x320D
#define SUNI1x10GEXP_REG_PL4IDU_INTERRUPT_MASK 0x3282
#define SUNI1x10GEXP_REG_PL4IDU_INTERRUPT 0x3283
/******************************************************************************/
/* -- End register offset definitions -- */
/******************************************************************************/
/******************************************************************************/
/** SUNI-1x10GE-XP REGISTER BIT MASKS **/
/******************************************************************************/
/*----------------------------------------------------------------------------
* Register 0x0004: S/UNI-1x10GE-XP Device Status
* Bit 9 TOP_SXRA_EXPIRED
* Bit 8 TOP_MDIO_BUSY
* Bit 7 TOP_DTRB
* Bit 6 TOP_EXPIRED
* Bit 5 TOP_PAUSED
* Bit 4 TOP_PL4_ID_DOOL
* Bit 3 TOP_PL4_IS_DOOL
* Bit 2 TOP_PL4_ID_ROOL
* Bit 1 TOP_PL4_IS_ROOL
* Bit 0 TOP_PL4_OUT_ROOL
*----------------------------------------------------------------------------*/
#define SUNI1x10GEXP_BITMSK_TOP_SXRA_EXPIRED 0x0200
#define SUNI1x10GEXP_BITMSK_TOP_EXPIRED 0x0040
#define SUNI1x10GEXP_BITMSK_TOP_PL4_ID_DOOL 0x0010
#define SUNI1x10GEXP_BITMSK_TOP_PL4_IS_DOOL 0x0008
#define SUNI1x10GEXP_BITMSK_TOP_PL4_ID_ROOL 0x0004
#define SUNI1x10GEXP_BITMSK_TOP_PL4_IS_ROOL 0x0002
#define SUNI1x10GEXP_BITMSK_TOP_PL4_OUT_ROOL 0x0001
/*----------------------------------------------------------------------------
* Register 0x000E:PM3393 Global interrupt enable
* Bit 15 TOP_INTE
*----------------------------------------------------------------------------*/
#define SUNI1x10GEXP_BITMSK_TOP_INTE 0x8000
/*----------------------------------------------------------------------------
* Register 0x2040: RXXG Configuration 1
* Bit 15 RXXG_RXEN
* Bit 14 RXXG_ROCF
* Bit 13 RXXG_PAD_STRIP
* Bit 10 RXXG_PUREP
* Bit 9 RXXG_LONGP
* Bit 8 RXXG_PARF
* Bit 7 RXXG_FLCHK
* Bit 5 RXXG_PASS_CTRL
* Bit 3 RXXG_CRC_STRIP
* Bit 2-0 RXXG_MIFG
*----------------------------------------------------------------------------*/
#define SUNI1x10GEXP_BITMSK_RXXG_RXEN 0x8000
#define SUNI1x10GEXP_BITMSK_RXXG_PUREP 0x0400
#define SUNI1x10GEXP_BITMSK_RXXG_FLCHK 0x0080
#define SUNI1x10GEXP_BITMSK_RXXG_CRC_STRIP 0x0008
/*----------------------------------------------------------------------------
* Register 0x2070: RXXG Address Filter Control 2
* Bit 1 RXXG_PMODE
* Bit 0 RXXG_MHASH_EN
*----------------------------------------------------------------------------*/
#define SUNI1x10GEXP_BITMSK_RXXG_PMODE 0x0002
#define SUNI1x10GEXP_BITMSK_RXXG_MHASH_EN 0x0001
/*----------------------------------------------------------------------------
* Register 0x2100: MSTAT Control
* Bit 2 MSTAT_WRITE
* Bit 1 MSTAT_CLEAR
* Bit 0 MSTAT_SNAP
*----------------------------------------------------------------------------*/
#define SUNI1x10GEXP_BITMSK_MSTAT_CLEAR 0x0002
#define SUNI1x10GEXP_BITMSK_MSTAT_SNAP 0x0001
/*----------------------------------------------------------------------------
* Register 0x3040: TXXG Configuration Register 1
* Bit 15 TXXG_TXEN0
* Bit 13 TXXG_HOSTPAUSE
* Bit 12-7 TXXG_IPGT
* Bit 5 TXXG_32BIT_ALIGN
* Bit 4 TXXG_CRCEN
* Bit 3 TXXG_FCTX
* Bit 2 TXXG_FCRX
* Bit 1 TXXG_PADEN
* Bit 0 TXXG_SPRE
*----------------------------------------------------------------------------*/
#define SUNI1x10GEXP_BITMSK_TXXG_TXEN0 0x8000
#define SUNI1x10GEXP_BITOFF_TXXG_IPGT 7
#define SUNI1x10GEXP_BITMSK_TXXG_32BIT_ALIGN 0x0020
#define SUNI1x10GEXP_BITMSK_TXXG_CRCEN 0x0010
#define SUNI1x10GEXP_BITMSK_TXXG_FCTX 0x0008
#define SUNI1x10GEXP_BITMSK_TXXG_FCRX 0x0004
#define SUNI1x10GEXP_BITMSK_TXXG_PADEN 0x0002
#endif /* _SUNI1x10GEXP_REGS_H */
/*****************************************************************************
* *
* File: tp.c *
* $Revision: 1.6 $ *
* $Date: 2005/03/23 07:15:59 $ *
* Description: *
* Core ASIC Management. *
* part of the Chelsio 10Gb Ethernet Driver. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License, version 2, as *
* published by the Free Software Foundation. *
* *
* You should have received a copy of the GNU General Public License along *
* with this program; if not, write to the Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
* *
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED *
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF *
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. *
* *
* http://www.chelsio.com *
* *
* Copyright (c) 2003 - 2005 Chelsio Communications, Inc. *
* All rights reserved. *
* *
* Maintainers: maintainers@chelsio.com *
* *
* Authors: Dimitrios Michailidis <dm@chelsio.com> *
* Tina Yang <tainay@chelsio.com> *
* Felix Marti <felix@chelsio.com> *
* Scott Bardone <sbardone@chelsio.com> *
* Kurt Ottaway <kottaway@chelsio.com> *
* Frank DiMambro <frank@chelsio.com> *
* *
* History: *
* *
****************************************************************************/
#include "common.h"
#include "regs.h"
#include "tp.h"
struct petp {
adapter_t *adapter;
};
/* Pause deadlock avoidance parameters */
#define DROP_MSEC 16
#define DROP_PKTS_CNT 1
static void tp_init(adapter_t *ap, const struct tp_params *p,
unsigned int tp_clk)
{
if (t1_is_asic(ap)) {
u32 val;
val = F_TP_IN_CSPI_CPL | F_TP_IN_CSPI_CHECK_IP_CSUM |
F_TP_IN_CSPI_CHECK_TCP_CSUM | F_TP_IN_ESPI_ETHERNET;
if (!p->pm_size)
val |= F_OFFLOAD_DISABLE;
else
val |= F_TP_IN_ESPI_CHECK_IP_CSUM |
F_TP_IN_ESPI_CHECK_TCP_CSUM;
t1_write_reg_4(ap, A_TP_IN_CONFIG, val);
t1_write_reg_4(ap, A_TP_OUT_CONFIG, F_TP_OUT_CSPI_CPL |
F_TP_OUT_ESPI_ETHERNET |
F_TP_OUT_ESPI_GENERATE_IP_CSUM |
F_TP_OUT_ESPI_GENERATE_TCP_CSUM);
t1_write_reg_4(ap, A_TP_GLOBAL_CONFIG, V_IP_TTL(64) |
F_PATH_MTU /* IP DF bit */ |
V_5TUPLE_LOOKUP(p->use_5tuple_mode) |
V_SYN_COOKIE_PARAMETER(29));
/*
* Enable pause frame deadlock prevention.
*/
if (is_T2(ap)) {
u32 drop_ticks = DROP_MSEC * (tp_clk / 1000);
t1_write_reg_4(ap, A_TP_TX_DROP_CONFIG,
F_ENABLE_TX_DROP | F_ENABLE_TX_ERROR |
V_DROP_TICKS_CNT(drop_ticks) |
V_NUM_PKTS_DROPPED(DROP_PKTS_CNT));
}
}
}
void t1_tp_destroy(struct petp *tp)
{
kfree(tp);
}
struct petp * __devinit t1_tp_create(adapter_t *adapter, struct tp_params *p)
{
struct petp *tp = kmalloc(sizeof(*tp), GFP_KERNEL);
if (!tp)
return NULL;
memset(tp, 0, sizeof(*tp));
tp->adapter = adapter;
return tp;
}
void t1_tp_intr_enable(struct petp *tp)
{
u32 tp_intr = t1_read_reg_4(tp->adapter, A_PL_ENABLE);
{
/* We don't use any TP interrupts */
t1_write_reg_4(tp->adapter, A_TP_INT_ENABLE, 0);
t1_write_reg_4(tp->adapter, A_PL_ENABLE,
tp_intr | F_PL_INTR_TP);
}
}
void t1_tp_intr_disable(struct petp *tp)
{
u32 tp_intr = t1_read_reg_4(tp->adapter, A_PL_ENABLE);
{
t1_write_reg_4(tp->adapter, A_TP_INT_ENABLE, 0);
t1_write_reg_4(tp->adapter, A_PL_ENABLE,
tp_intr & ~F_PL_INTR_TP);
}
}
void t1_tp_intr_clear(struct petp *tp)
{
t1_write_reg_4(tp->adapter, A_TP_INT_CAUSE, 0xffffffff);
t1_write_reg_4(tp->adapter, A_PL_CAUSE, F_PL_INTR_TP);
}
int t1_tp_intr_handler(struct petp *tp)
{
u32 cause;
cause = t1_read_reg_4(tp->adapter, A_TP_INT_CAUSE);
t1_write_reg_4(tp->adapter, A_TP_INT_CAUSE, cause);
return 0;
}
static void set_csum_offload(struct petp *tp, u32 csum_bit, int enable)
{
u32 val = t1_read_reg_4(tp->adapter, A_TP_GLOBAL_CONFIG);
if (enable)
val |= csum_bit;
else
val &= ~csum_bit;
t1_write_reg_4(tp->adapter, A_TP_GLOBAL_CONFIG, val);
}
void t1_tp_set_ip_checksum_offload(struct petp *tp, int enable)
{
set_csum_offload(tp, F_IP_CSUM, enable);
}
void t1_tp_set_udp_checksum_offload(struct petp *tp, int enable)
{
set_csum_offload(tp, F_UDP_CSUM, enable);
}
void t1_tp_set_tcp_checksum_offload(struct petp *tp, int enable)
{
set_csum_offload(tp, F_TCP_CSUM, enable);
}
/*
* Initialize TP state. tp_params contains initial settings for some TP
* parameters, particularly the one-time PM and CM settings.
*/
int t1_tp_reset(struct petp *tp, struct tp_params *p, unsigned int tp_clk)
{
int busy = 0;
adapter_t *adapter = tp->adapter;
tp_init(adapter, p, tp_clk);
if (!busy)
t1_write_reg_4(adapter, A_TP_RESET, F_TP_RESET);
else
CH_ERR("%s: TP initialization timed out\n",
adapter->name);
return busy;
}
/*****************************************************************************
* *
* File: tp.h *
* $Revision: 1.3 $ *
* $Date: 2005/03/23 07:15:59 $ *
* Description: *
* part of the Chelsio 10Gb Ethernet Driver. *
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License, version 2, as *
* published by the Free Software Foundation. *
* *
* You should have received a copy of the GNU General Public License along *
* with this program; if not, write to the Free Software Foundation, Inc., *
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. *
* *
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED *
* WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF *
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. *
* *
* http://www.chelsio.com *
* *
* Copyright (c) 2003 - 2005 Chelsio Communications, Inc. *
* All rights reserved. *
* *
* Maintainers: maintainers@chelsio.com *
* *
* Authors: Dimitrios Michailidis <dm@chelsio.com> *
* Tina Yang <tainay@chelsio.com> *
* Felix Marti <felix@chelsio.com> *
* Scott Bardone <sbardone@chelsio.com> *
* Kurt Ottaway <kottaway@chelsio.com> *
* Frank DiMambro <frank@chelsio.com> *
* *
* History: *
* *
****************************************************************************/
#ifndef CHELSIO_TP_H
#define CHELSIO_TP_H
#include "common.h"
#define TP_MAX_RX_COALESCING_SIZE 16224U
struct tp_mib_statistics {
/* IP */
u32 ipInReceive_hi;
u32 ipInReceive_lo;
u32 ipInHdrErrors_hi;
u32 ipInHdrErrors_lo;
u32 ipInAddrErrors_hi;
u32 ipInAddrErrors_lo;
u32 ipInUnknownProtos_hi;
u32 ipInUnknownProtos_lo;
u32 ipInDiscards_hi;
u32 ipInDiscards_lo;
u32 ipInDelivers_hi;
u32 ipInDelivers_lo;
u32 ipOutRequests_hi;
u32 ipOutRequests_lo;
u32 ipOutDiscards_hi;
u32 ipOutDiscards_lo;
u32 ipOutNoRoutes_hi;
u32 ipOutNoRoutes_lo;
u32 ipReasmTimeout;
u32 ipReasmReqds;
u32 ipReasmOKs;
u32 ipReasmFails;
u32 reserved[8];
/* TCP */
u32 tcpActiveOpens;
u32 tcpPassiveOpens;
u32 tcpAttemptFails;
u32 tcpEstabResets;
u32 tcpOutRsts;
u32 tcpCurrEstab;
u32 tcpInSegs_hi;
u32 tcpInSegs_lo;
u32 tcpOutSegs_hi;
u32 tcpOutSegs_lo;
u32 tcpRetransSeg_hi;
u32 tcpRetransSeg_lo;
u32 tcpInErrs_hi;
u32 tcpInErrs_lo;
u32 tcpRtoMin;
u32 tcpRtoMax;
};
struct petp;
struct tp_params;
struct petp *t1_tp_create(adapter_t *adapter, struct tp_params *p);
void t1_tp_destroy(struct petp *tp);
void t1_tp_intr_disable(struct petp *tp);
void t1_tp_intr_enable(struct petp *tp);
void t1_tp_intr_clear(struct petp *tp);
int t1_tp_intr_handler(struct petp *tp);
void t1_tp_get_mib_statistics(adapter_t *adap, struct tp_mib_statistics *tps);
void t1_tp_set_udp_checksum_offload(struct petp *tp, int enable);
void t1_tp_set_tcp_checksum_offload(struct petp *tp, int enable);
void t1_tp_set_ip_checksum_offload(struct petp *tp, int enable);
int t1_tp_set_coalescing_size(struct petp *tp, unsigned int size);
int t1_tp_reset(struct petp *tp, struct tp_params *p, unsigned int tp_clk);
#endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment