diff options
author | Christoph Lameter <christoph@graphe.net> | 2005-03-30 13:34:31 -0800 |
---|---|---|
committer | Jeff Garzik <jgarzik@pobox.com> | 2005-05-15 19:15:02 -0400 |
commit | 8199d3a79c224bbe5943fa08684e1f93a17881b0 (patch) | |
tree | 77726ddade7ca4282bc12315abcb01fdf241be74 | |
parent | 88d7bd8cb9eb8d64bf7997600b0d64f7834047c5 (diff) | |
download | lwn-8199d3a79c224bbe5943fa08684e1f93a17881b0.tar.gz lwn-8199d3a79c224bbe5943fa08684e1f93a17881b0.zip |
[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: Tina Yang <tinay@chelsio.com>
Signed-off-by: Scott Bardone <sbardone@chelsio.com>
Signed-off-by: Christoph 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: Scott Bardone <sbardone@chelsio.com>
Signed-off-by: Christoph Lameter <christoph@lameter.com>
Signed-off-by: Andrew Morton <akpm@osdl.org>
diff -puN /dev/null Documentation/networking/cxgb.txt
-rw-r--r-- | Documentation/networking/cxgb.txt | 322 | ||||
-rw-r--r-- | drivers/net/Kconfig | 19 | ||||
-rw-r--r-- | drivers/net/Makefile | 1 | ||||
-rw-r--r-- | drivers/net/chelsio/Makefile | 12 | ||||
-rw-r--r-- | drivers/net/chelsio/ch_ethtool.h | 102 | ||||
-rw-r--r-- | drivers/net/chelsio/common.h | 269 | ||||
-rw-r--r-- | drivers/net/chelsio/cphy.h | 150 | ||||
-rw-r--r-- | drivers/net/chelsio/cpl5_cmd.h | 145 | ||||
-rw-r--r-- | drivers/net/chelsio/cxgb2.c | 1231 | ||||
-rw-r--r-- | drivers/net/chelsio/cxgb2.h | 122 | ||||
-rw-r--r-- | drivers/net/chelsio/elmer0.h | 157 | ||||
-rw-r--r-- | drivers/net/chelsio/espi.c | 386 | ||||
-rw-r--r-- | drivers/net/chelsio/espi.h | 67 | ||||
-rw-r--r-- | drivers/net/chelsio/gmac.h | 133 | ||||
-rw-r--r-- | drivers/net/chelsio/mv88x201x.c | 258 | ||||
-rw-r--r-- | drivers/net/chelsio/osdep.h | 169 | ||||
-rw-r--r-- | drivers/net/chelsio/pm3393.c | 831 | ||||
-rw-r--r-- | drivers/net/chelsio/regs.h | 453 | ||||
-rw-r--r-- | drivers/net/chelsio/sge.c | 1451 | ||||
-rw-r--r-- | drivers/net/chelsio/sge.h | 79 | ||||
-rw-r--r-- | drivers/net/chelsio/subr.c | 831 | ||||
-rw-r--r-- | drivers/net/chelsio/suni1x10gexp_regs.h | 221 | ||||
-rw-r--r-- | drivers/net/chelsio/tp.c | 188 | ||||
-rw-r--r-- | drivers/net/chelsio/tp.h | 110 |
24 files changed, 7707 insertions, 0 deletions
diff --git a/Documentation/networking/cxgb.txt b/Documentation/networking/cxgb.txt new file mode 100644 index 000000000000..9f2eb646c6f5 --- /dev/null +++ b/Documentation/networking/cxgb.txt @@ -0,0 +1,322 @@ + 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. + +=============================================================================== diff --git a/drivers/net/Kconfig b/drivers/net/Kconfig index 3a0a55b62aaf..8a7928f1d579 100644 --- a/drivers/net/Kconfig +++ b/drivers/net/Kconfig @@ -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 diff --git a/drivers/net/Makefile b/drivers/net/Makefile index 6202b10dbb4d..1992166ffba9 100644 --- a/drivers/net/Makefile +++ b/drivers/net/Makefile @@ -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 diff --git a/drivers/net/chelsio/Makefile b/drivers/net/chelsio/Makefile new file mode 100644 index 000000000000..ff8c11b3a4e1 --- /dev/null +++ b/drivers/net/chelsio/Makefile @@ -0,0 +1,12 @@ +# +# 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 + + diff --git a/drivers/net/chelsio/ch_ethtool.h b/drivers/net/chelsio/ch_ethtool.h new file mode 100644 index 000000000000..c523d24836b5 --- /dev/null +++ b/drivers/net/chelsio/ch_ethtool.h @@ -0,0 +1,102 @@ +/***************************************************************************** + * * + * 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 diff --git a/drivers/net/chelsio/common.h b/drivers/net/chelsio/common.h new file mode 100644 index 000000000000..017684ff48dc --- /dev/null +++ b/drivers/net/chelsio/common.h @@ -0,0 +1,269 @@ +/***************************************************************************** + * * + * 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 + diff --git a/drivers/net/chelsio/cphy.h b/drivers/net/chelsio/cphy.h new file mode 100644 index 000000000000..1bc2248264c0 --- /dev/null +++ b/drivers/net/chelsio/cphy.h @@ -0,0 +1,150 @@ +/***************************************************************************** + * * + * 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 diff --git a/drivers/net/chelsio/cpl5_cmd.h b/drivers/net/chelsio/cpl5_cmd.h new file mode 100644 index 000000000000..45e9248979f1 --- /dev/null +++ b/drivers/net/chelsio/cpl5_cmd.h @@ -0,0 +1,145 @@ +/***************************************************************************** + * * + * 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 diff --git a/drivers/net/chelsio/cxgb2.c b/drivers/net/chelsio/cxgb2.c new file mode 100644 index 000000000000..48c4d5acfcd1 --- /dev/null +++ b/drivers/net/chelsio/cxgb2.c @@ -0,0 +1,1231 @@ +/***************************************************************************** + * * + * 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); + diff --git a/drivers/net/chelsio/cxgb2.h b/drivers/net/chelsio/cxgb2.h new file mode 100644 index 000000000000..6ac326afcf01 --- /dev/null +++ b/drivers/net/chelsio/cxgb2.h @@ -0,0 +1,122 @@ +/***************************************************************************** + * * + * 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 diff --git a/drivers/net/chelsio/elmer0.h b/drivers/net/chelsio/elmer0.h new file mode 100644 index 000000000000..08f148643e7f --- /dev/null +++ b/drivers/net/chelsio/elmer0.h @@ -0,0 +1,157 @@ +/***************************************************************************** + * * + * 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 diff --git a/drivers/net/chelsio/espi.c b/drivers/net/chelsio/espi.c new file mode 100644 index 000000000000..7ec2dc7bafac --- /dev/null +++ b/drivers/net/chelsio/espi.c @@ -0,0 +1,386 @@ +/***************************************************************************** + * * + * 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; +} diff --git a/drivers/net/chelsio/espi.h b/drivers/net/chelsio/espi.h new file mode 100644 index 000000000000..0f84e8b6399f --- /dev/null +++ b/drivers/net/chelsio/espi.h @@ -0,0 +1,67 @@ +/***************************************************************************** + * * + * 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 diff --git a/drivers/net/chelsio/gmac.h b/drivers/net/chelsio/gmac.h new file mode 100644 index 000000000000..24501e2232cc --- /dev/null +++ b/drivers/net/chelsio/gmac.h @@ -0,0 +1,133 @@ +/***************************************************************************** + * * + * 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 diff --git a/drivers/net/chelsio/mv88x201x.c b/drivers/net/chelsio/mv88x201x.c new file mode 100644 index 000000000000..f54133af1bce --- /dev/null +++ b/drivers/net/chelsio/mv88x201x.c @@ -0,0 +1,258 @@ +/***************************************************************************** + * * + * 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 +}; diff --git a/drivers/net/chelsio/osdep.h b/drivers/net/chelsio/osdep.h new file mode 100644 index 000000000000..095cb474434f --- /dev/null +++ b/drivers/net/chelsio/osdep.h @@ -0,0 +1,169 @@ +/***************************************************************************** + * * + * 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 diff --git a/drivers/net/chelsio/pm3393.c b/drivers/net/chelsio/pm3393.c new file mode 100644 index 000000000000..17bd20f60d99 --- /dev/null +++ b/drivers/net/chelsio/pm3393.c @@ -0,0 +1,831 @@ +/***************************************************************************** + * * + * 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 +}; diff --git a/drivers/net/chelsio/regs.h b/drivers/net/chelsio/regs.h new file mode 100644 index 000000000000..5a70803eb1b6 --- /dev/null +++ b/drivers/net/chelsio/regs.h @@ -0,0 +1,453 @@ +/***************************************************************************** + * * + * 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) + diff --git a/drivers/net/chelsio/sge.c b/drivers/net/chelsio/sge.c new file mode 100644 index 000000000000..bcf8b1e939b0 --- /dev/null +++ b/drivers/net/chelsio/sge.c @@ -0,0 +1,1451 @@ +/***************************************************************************** + * * + * 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); +} + diff --git a/drivers/net/chelsio/sge.h b/drivers/net/chelsio/sge.h new file mode 100644 index 000000000000..140f896def60 --- /dev/null +++ b/drivers/net/chelsio/sge.h @@ -0,0 +1,79 @@ +/***************************************************************************** + * * + * 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_ */ diff --git a/drivers/net/chelsio/subr.c b/drivers/net/chelsio/subr.c new file mode 100644 index 000000000000..a90a3f95fcac --- /dev/null +++ b/drivers/net/chelsio/subr.c @@ -0,0 +1,831 @@ +/***************************************************************************** + * * + * 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; +} diff --git a/drivers/net/chelsio/suni1x10gexp_regs.h b/drivers/net/chelsio/suni1x10gexp_regs.h new file mode 100644 index 000000000000..98352bdda89b --- /dev/null +++ b/drivers/net/chelsio/suni1x10gexp_regs.h @@ -0,0 +1,221 @@ +/***************************************************************************** + * * + * 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 */ + diff --git a/drivers/net/chelsio/tp.c b/drivers/net/chelsio/tp.c new file mode 100644 index 000000000000..9ad5c539fd28 --- /dev/null +++ b/drivers/net/chelsio/tp.c @@ -0,0 +1,188 @@ +/***************************************************************************** + * * + * 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; +} diff --git a/drivers/net/chelsio/tp.h b/drivers/net/chelsio/tp.h new file mode 100644 index 000000000000..2ebc5c0d62e7 --- /dev/null +++ b/drivers/net/chelsio/tp.h @@ -0,0 +1,110 @@ +/***************************************************************************** + * * + * 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 |