S: San Jose, California 95129
S: USA
+N: Andrea Arcangeli
+E: arcangeli@mbox.queen.it
+W: http://www.cs.unibo.it/~arcangel/
+P: 1024/CB4660B9 CC A0 71 81 F4 A0 63 AC C0 4B 81 1D 8C 15 C8 E5
+D: Fixed a 2.0.33 mm bug that corrupts memory in linux/mm/vmalloc.c
+S: Via Ciaclini 26
+S: Imola 40026
+S: Italy
+
N: Derek Atkins
E: warlord@MIT.EDU
D: Linux-AFS Port, random kernel hacker,
S: Notre Dame, Indiana
S: USA
+N: James Banks
+E: james.banks@caldera.com
+D: TLAN network driver
+S: Caldera, Inc.
+S: 633 South 550 East
+S: Provo, UT 84606
+S: USA
+
N: Peter Bauer
E: 100136.3530@compuserve.com
D: Driver for depca-ethernet-board
Multiple-Ethernet-mini-HOWTO, available from
sunsite.unc.edu:/pub/Linux/docs/HOWTO/mini.
+TI ThunderLAN support (EXPERIMENTAL)
+CONFIG_TLAN
+ If you have a TLAN based network card which is supported by this
+ driver, say Y and read the Ethernet-HOWTO. Devices currently supported
+ are the Compaq Netelligent 10, Netelligent 10/100, and Internal
+ NetFlex 3. This driver is also available as a module. Please email
+ feedback to james.banks@caldera.com.
+
Zenith Z-Note support
CONFIG_ZNET
The Zenith Z-Note notebook computer has a built-in network
--- /dev/null
+1. Q. Ifconfig reports "10Mbs Ethernet" for my 10/100Mbs card. How do
+ I get my card to run in 100Mbs mode?
+
+ A. The "10Mbs Ethernet" is poorly named. What this is really saying
+ is that this device is some kind of Ethernet device (ie, uses
+ ethernet style frames at the link layer). Recent versions of
+ ifconfig report this as simply "Ethernet".
+
+ The TLAN driver is designed to autodetect 10Mbs vs. 100Mbs
+ connections, and choose the higher speed. The most sure way
+ to determine what speed you are running at is to look at the
+ 100Mbs LED on the card, if your device has one.
+
+
+2. Q. My network card is using the same interrupt as my SCSI card.
+
+ A. Some Compaqs set all PCI devices to the same interrupt by default.
+ You can either change the interrupt used by one of the device
+ with your Compaq configuration utility, or you can have the TLAN
+ driver use the same type of interrupt handler:
+
+ a. For module based use, add 'sa_int=1' to the command line, eg.:
+ insmod tlan.o sa_int=1
+
+ b. For compiled in kernel, set the 0x2 bit in the third part
+ for the boot parameter for that device, eg.:
+ ether=0,0,0x2,0,eth0
+
--- /dev/null
+Caldera TLAN driver for Linux, version 0.42
+README
+
+
+I. Supported Devices.
+
+ Only PCI devices will work with this driver.
+
+ Supported:
+ Vendor ID Device ID Name
+ 0e11 ae32 Compaq Netelligent 10/100 TX
+ 0e11 ae34 Compaq Netelligent 10 T
+ 0e11 ae35 Compaq Integrated NetFlex 3/P
+ 0e11 ae43 Compaq ProLiant Integrated Netelligent 10/100 TX
+ 0e11 ae40 Compaq Dual Port Netelligent 10/100 TX
+ 0e11 b011 Compaq Deskpro 4000 5233MMX
+ 0e11 f130 Compaq NetFlex 3/P
+ 0e11 f150 Compaq NetFlex 3/P
+ 108d 0014 Olicom OC-2326
+
+ Caveats:
+
+ I don't believe 100BaseTX daughterboards will work. I am interested
+ in any reports.
+
+
+II. Building the Driver.
+
+ The TLAN driver may be compiled into the kernel, or it may be compiled
+ as a module separately, or in the kernel. A patch is included for
+ 2.0.29 (which also works for 2.0.30, 2.0.31, and 2.0.32).
+
+ To compile it as part of the kernel:
+ 1. Download and untar the TLAN driver package.
+ 2. If your kernel is 2.1.45 or later, you do not need to patch the
+ kernel sources. Copy the tlan.c and tlan.h to drivers/net in
+ the kernel source tree.
+ 3. Otherwise, apply the appropriate patch for your kernel. For
+ example:
+
+ cd /usr/src/linux
+ patch -p1 < kernel.2.0.29
+
+ 4. Copy the files tlan.c and tlan.h from the TLAN package to the
+ directory drivers/net in the Linux kernel source tree.
+ 5. Configure your kernel for the TLAN driver. Answer 'Y' when
+ prompted to ask about experimental code (the first question).
+ Then answer 'Y' when prompted if to include TI ThunderLAN
+ support. If you want the driver compiled as a module, answer 'M'
+ instead of 'Y'.
+ 6. Make the kernel and, if necessary, the modules.
+
+ To compile the TLAN driver independently:
+ 1. Download and untar the TLAN driver package.
+ 2. Change to the tlan directory.
+ 3. If you are NOT using a versioned kernel (ie, want an non-
+ versioned module), edit the Makefile, and comment out the
+ line:
+ MODVERSIONS = -DMODVERSIONS
+ 4. Run 'make'.
+
+
+III. Driver Options
+ 1. You can append debug=x to the end of the insmod line to get
+ debug messages, where x is a bit field where the bits mean
+ the following:
+
+ 0x01 Turn on general debugging messages.
+ 0x02 Turn on receive debugging messages.
+ 0x04 Turn on transmit debugging messages.
+ 0x08 Turn on list debugging messsages.
+
+ 2. You can append aui=1 to the end of the insmod line to cause
+ the adapter to use the AUI interface instead of the 10 Base T
+ interface. This is also what to do if you want to use the BNC
+ connector on a TLAN based device. (Setting this option on a
+ device that does not have an AUI/BNC connector will probably
+ cause it to not function correctly.)
+
+ 3. If the driver is built into the kernel, you can use the 3rd
+ and 4th parameters to set aui and debug respectively. For
+ example:
+
+ ether=0,0,0x1,0x7,eth0
+
+ This sets aui to 0x1 and debug to 0x7, assuming eth0 is a
+ supported TLAN device.
+
+ The bits in the third byte are assigned as follows:
+
+ 0x01 = aui
+ 0x02 = use SA_INTERRUPT flag when reserving the irq.
+
+
+IV. Things to try if you have problems.
+ 1. Make sure your card's PCI id is among those listed in
+ section I, above.
+ 1. Make sure routing is correct.
+ 2. If you are using a 2.1.x kernel, try to duplicate the
+ problem on a 2.0.x (preferably 2.0.29 or 2.0.30) kernel.
+ 3. Set debug to 7, either in tlan.c or through insmod as in
+ section III.1 above.
+ 4. Make sure klog is running so the kernel messages are
+ being recorded somewhere.
+ 5. Run the following sequence of programs in order (you
+ may want to do this within an xterm, as background
+ traffic may cause a lot of TLAN RECEIVED: messages
+ on the console):
+
+ ifconfig eth0 your.ip.address netmask your.net.mask up
+ route add -net local.net.address eth0
+ ifconfig
+ ping some.computer.on.local.net
+ ifconfig eth0 down
+
+ 6. Mail the log of what occurred to me. Also include the
+ kernel version and what media/connector type (eg,
+ 10 BaseT/RJ45, 100 BaseTX/RJ45, Thinnet/BNC, etc).
+
+
+
+Please e-mail me with any comments, successes, or failures. Thanks.
+
+There is also a tlan mailing list which you can join by sending "subscribe tlan"
+in the body of an email to majordomo@vuser.vu.union.edu. I will announce new
+releases of the TLAN driver there.
+
+James
+james.banks@caldera.com
+
L: linux-net@vger.rutgers.edu
S: Maintained
+TLAN NETWORK DRIVER
+P: James Banks
+M: james.banks@caldera.com
+L: linux-net@vger.rutgers.edu
+S: Supported
+
DIGI RIGHTSWITCH NETWORK DRIVER
P: Rick Richardson
M: rick@dgii.com
}
#else
-int tc59x_probe(struct device *dev)
+int isa515_probe(struct device *dev)
{
int cards_found = 0;
--- /dev/null
+TLan Device Driver change log.
+
+0.42 - Coverted tranceiver from misused per-tranceiver functions
+ to a timer-oriented path that covers four possible classes
+ of tranceivers:
+ 1. Unmanaged
+ 2. Manual configuration
+ 3. Autonegotiation w/ manual configuration
+ 4. Autonegotiation w/ auto configuration
+ - Added ability to force speed and duplex settings.
+ - Made speed, duplex, sa_int, etc, to be set per adapter with
+ ether= command.
+ - Added support for Olicom OC-2326
+
+0.41 - Added non-bounce buffer paths. Added TLan_FreeLists to
+ dispose of unused sk_buff's at device close time.
+ - Discovered inlined functions aren't being inlined, or at
+ least take up more space than macros would.
+
+0.40 - Refined polarity checking to handle case when polarity
+ changes to normal from abnormal.
+ - Cleaned up TLan_Probe routine.
+ - Added an option for the SA_INTERRUPT flag to be set.
+ - Created FAQ.
+ - Removed all C++ style comments.
+ - Added error message if devices busmastering is inactive.
+ Also will now skip device.
+ - Put cli and sti back into TLan_HandleInterrupt. It makes
+ me feel better.
+ - Moved the code that checks for boot parameter options to
+ tlan_probe.
+
+0.39 - Minor cosmetic cleanups (especially variable declarations).
+ - Changes low level TLAN functions to use dev structures instead
+ individual data elements.
+ - Changed low level TLAN functions not to play with sti and cli
+ if in an interrupt routine.
+ - Removed cli and sti from TLan_HandleInterrupt.
+
+0.38 - Added code to isolate the external PHY if the internal PHY is
+ being used for AUI/BNC connectivity. Also set the aui and
+ debug variables from mem_start and mem_end if the driver is
+ built into the kernel.
+
+0.37 - Added TLAN_PHY_ACTIVITY flag for Unmanaged PHY.
+ - If aui is selected and the card is not a 0xF130 (unmanaged phy),
+ select the builtin PHY and put it in AUI mode. I don't know if
+ this will work, but it is my best guess as to how AUI/BNC
+ functionality is being provided for the external managed PHYs,
+ which don't support AUI/BNC.
+ - If aui is set and the card is a 0xF130, set the MTXD1 bit in
+ the ACOMMIT register, to select AUI mode on the unmanaged PHY.
+ I don't know if this is necessary.
+
+0.36 - Changed AN_ADV register to not advertise full duplex modes.
+ 100Mbs should work now on 0xAE32.
+ - Fixed a small bug where heartbeat and PHY interrupts were
+ always being enabled.
+ - Force the the driver into Unmanaged PHY mode for 0xF130 devices,
+ even if a managed (ie, the built-in one) PHY is detected.
+ - Moved the PHY initialization to after the onboard PHY is enabled,
+ if selected.
+
+0.35 - Added entry for Level One LXT970 PHY.
+ - Commented out instruction to set phyOnline to 1 in DP Check.
+
+0.34 - Revised Reset routine.
+ - Added support for 0xF130 device.
+ - Added entry for NS DP83840A Rev 0 PHY.
+
+0.33 - Major Reformatting of comments to fit in 80 columns.
+ - Changed tabs to 8 spaces, reformatted accordingly.
+ - Added code to check and change polarity.
+ - Added caveats to README.
+ - Small fix to Makefile to make unversioned modules correctly.
+ - Redid PhySelect to list all phy ids then choose one.
+
+0.32 - Put in another change for BNC stuff that I mistakenly
+ omitted in 0.31.
+
+0.31 - Completed BNC changes (at least one person had it working).
+ - Added another device id.
+ - Changed debugging messages to hopefully be more useful.
+
+0.30 - Added PCI device IDs provided by Don Dupuis of Compaq.
+ - Turned of MINTEN when using 10/100 PHY.
+ - Added ability to select AUI at insmod time. What will this do
+ for BNC, if anything?
+ - Tweaked with names.
+
+0.29 Fixed problem with older TLAN chips (pre-PG3.0 didn't have
+ EOC bit in lists nor INTDIS register). Added support for NetFlex
+ card.
+
+0.28 Changed a lot of commented out printfs to debugging statements.
+ Added detail to product names. Added 2nd reset to open command.
+ Don't know why, just have to, at least for Netelligent 10 card.
+
+0.27 Created a tlan_probe function for compiled-in-kernel use.
+ Also added debugging macro, which could be reviewed. Is there
+ a kernel variable which stores desired verbosity/debug level?
+
+0.26 Change Invalid interrupt handler to return 0, not print out debug
+ message. This was causing lots of messages to be printed out with
+ shared interrupts.
+
+0.25
+ Added IDs for integrated NetFlex-3 controller. Not sure if it is even
+ TLAN yet, but we'll see.
+
+0.24
+ Increased second delay in internal PHY check routine, as driver wasn't
+ starting on the first insmod.
+
+0.23
+ Added documentation to function preambles.
+ Removed some commented out printk's.
+ Tested Rx and Tx busy frequency with current queue sizes (no busies).
+
+0.22
+ Cleared tbusy flag at adapter check.
+ Removed some commented out printk's.
+ Added TLan_SetMulticastList routine.
+
+0.21
+ Got all virt_to_bus calls in place for 2.1 kernels.
+
+0.20
+ Rewrote buffer/list subsystem to use bounce buffers as DMA
+ doesn't work over 16 Meg. Why? This is PCI not ISA.
+ Fixed a problem with buffers stalling due to a race condition
+ in the kernel.
+
tristate 'RTL 8139 support' CONFIG_RTL8139
tristate 'SMC EtherPower II support' CONFIG_SMC_EPIC
if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
+ tristate 'TI ThunderLAN support (EXPERIMENTAL)' CONFIG_TLAN
bool 'Zenith Z-Note support (EXPERIMENTAL)' CONFIG_ZNET
fi
fi
endif
endif
+ifeq ($(CONFIG_TLAN),y)
+L_OBJS += tlan.o
+else
+ ifeq ($(CONFIG_TLAN),m)
+ M_OBJS += tlan.o
+ endif
+endif
+
ifeq ($(CONFIG_ZNET),y)
L_OBJS += znet.o
endif
extern int yellowfin_probe(struct device *);
extern int epic100_probe(struct device *);
extern int rtl8139_probe(struct device *);
+extern int tlan_probe(struct device *);
+extern int isa515_probe(struct device *);
+extern int pcnet32_probe(struct device *);
+extern int lance_probe(struct device *);
/* Detachable devices ("pocket adaptors") */
extern int atp_init(struct device *);
extern int de600_probe(struct device *);
#endif
#ifdef CONFIG_SUNLANCE
&& sparc_lance_probe(dev)
+#endif
+#ifdef CONFIG_TLAN
+ && tlan_probe(dev)
+#endif
+#ifdef CONFIG_LANCE32
+ && pcnet32_probe(dev)
+#endif
+#ifdef CONFIG_CORKSCREW
+ && isa515_probe(dev)
+#endif
+#ifdef CONFIG_LANCE
+ && lance_probe(dev)
#endif
&& 1 ) {
return 1; /* -ENODEV or -EAGAIN would be more accurate. */
-/* drivers/net/eepro100.c: An Intel i82557 ethernet driver for linux. */
-/*
- NOTICE: this version tested with kernels 1.3.72 and later only!
- Written 1996-1998 by Donald Becker.
+/* at1700.c: A network device driver for the Allied Telesis AT1700.
+
+ Written 1993-98 by Donald Becker.
+
+ Copyright 1993 United States Government as represented by the
+ Director, National Security Agency.
This software may be used and distributed according to the terms
of the GNU Public License, incorporated herein by reference.
- This driver is for the Intel EtherExpress Pro 100B boards.
- It should work with other i82557 boards (if any others exist).
- To use a built-in driver, install as drivers/net/eepro100.c.
- To use as a module, use the compile-command at the end of the file.
-
- The author may be reached as becker@CESDIS.usra.edu, or C/O
+ The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O
Center of Excellence in Space Data and Information Sciences
- Code 930.5, NASA Goddard Space Flight Center, Greenbelt MD 20771
- For updates see
- <base href="http://cesdis.gsfc.nasa.gov/linux/drivers/eepro100.html">
-*/
+ Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
-static const char *version =
-"eepro100.c:v0.99A 2/7/98 Donald Becker linux-eepro100@cesdis.gsfc.nasa.gov\n";
-
-/* A few user-configurable values that apply to all boards.
- First set are undocumented and spelled per Intel recommendations. */
+ This is a device driver for the Allied Telesis AT1700, which is a
+ straight-forward Fujitsu MB86965 implementation.
-static int congenb = 0; /* Enable congestion control in the DP83840. */
-static int txfifo = 8; /* Tx FIFO threshold in 4 byte units, 0-15 */
-static int rxfifo = 8; /* Rx FIFO threshold, default 32 bytes. */
-/* Tx/Rx DMA burst length, 0-127, 0 == no preemption, tx==128 -> disabled. */
-static int txdmacount = 128;
-static int rxdmacount = 0;
+ Sources:
+ The Fujitsu MB86965 datasheet.
-/* Set the copy breakpoint for the copy-only-tiny-buffer Rx method.
- Lower values use more memory, but are faster. */
-static int rx_copybreak = 200;
+ After the initial version of this driver was written Gerry Sawkins of
+ ATI provided their EEPROM configuration code header file.
+ Thanks to NIIBE Yutaka <gniibe@mri.co.jp> for bug fixes.
-/* Maximum events (Rx packets, etc.) to handle at each interrupt. */
-static int max_interrupt_work = 20;
+ Bugs:
+ The MB86965 has a design flaw that makes all probes unreliable. Not
+ only is it difficult to detect, it also moves around in I/O space in
+ response to inb()s from other device probes!
+*/
-/* Maximum number of multicast addresses to filter (vs. rx-all-multicast) */
-static int multicast_filter_limit = 64;
+static const char *version =
+ "at1700.c:v1.13 1/31/98 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
-#include <linux/config.h>
-#ifdef MODULE
-#ifdef MODVERSIONS
-#include <linux/modversions.h>
-#endif
#include <linux/module.h>
-#else
-#define MOD_INC_USE_COUNT
-#define MOD_DEC_USE_COUNT
-#endif
-#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/sched.h>
-#include <linux/string.h>
-#include <linux/timer.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
#include <linux/ptrace.h>
-#include <linux/errno.h>
#include <linux/ioport.h>
+#include <linux/in.h>
#include <linux/malloc.h>
-#include <linux/interrupt.h>
-#include <linux/pci.h>
-#include <linux/bios32.h>
-#include <asm/processor.h> /* Processor type for cache alignment. */
+#include <linux/string.h>
+#include <asm/system.h>
#include <asm/bitops.h>
#include <asm/io.h>
#include <asm/dma.h>
+#include <linux/errno.h>
#include <linux/netdevice.h>
#include <linux/etherdevice.h>
#include <linux/skbuff.h>
-#include <linux/delay.h>
-
-/* Unused in the 2.0.* version, but retained for documentation. */
-#if LINUX_VERSION_CODE > 0x20118
-MODULE_AUTHOR("Donald Becker <becker@cesdis.gsfc.nasa.gov>");
-MODULE_DESCRIPTION("Intel i82557/i82558 EtherExpressPro driver");
-MODULE_PARM(debug, "i");
-MODULE_PARM(options, "1-" __MODULE_STRING(8) "i");
-MODULE_PARM(full_duplex, "1-" __MODULE_STRING(8) "i");
-MODULE_PARM(congenb, "i");
-MODULE_PARM(txfifo, "i");
-MODULE_PARM(rxfifo, "i");
-MODULE_PARM(txdmacount, "i");
-MODULE_PARM(rxdmacount, "i");
-MODULE_PARM(rx_copybreak, "i");
-MODULE_PARM(max_interrupt_work, "i");
-MODULE_PARM(multicast_filter_limit, "i");
-#endif
-
-#define RUN_AT(x) (jiffies + (x))
-#if (LINUX_VERSION_CODE < 0x20123)
-#define test_and_set_bit(val, addr) set_bit(val, addr)
-#endif
-
-/* The total I/O port extent of the board.
- The registers beyond 0x18 only exist on the i82558. */
-#define SPEEDO3_TOTAL_SIZE 0x20
+/* Tunable parameters. */
-int speedo_debug = 1;
-
-/*
- Theory of Operation
-
-I. Board Compatibility
-
-This device driver is designed for the Intel i82557 "Speedo3" chip, Intel's
-single-chip fast ethernet controller for PCI, as used on the Intel
-EtherExpress Pro 100 adapter.
-
-II. Board-specific settings
-
-PCI bus devices are configured by the system at boot time, so no jumpers
-need to be set on the board. The system BIOS should be set to assign the
-PCI INTA signal to an otherwise unused system IRQ line. While it's
-possible to share PCI interrupt lines, it negatively impacts performance and
-only recent kernels support it.
-
-III. Driver operation
-
-IIIA. General
-The Speedo3 is very similar to other Intel network chips, that is to say
-"apparently designed on a different planet". This chips retains the complex
-Rx and Tx descriptors and multiple buffers pointers as previous chips, but
-also has simplified Tx and Rx buffer modes. This driver uses the "flexible"
-Tx mode, but in a simplified lower-overhead manner: it associates only a
-single buffer descriptor with each frame descriptor.
-
-Despite the extra space overhead in each receive skbuff, the driver must use
-the simplified Rx buffer mode to assure that only a single data buffer is
-associated with each RxFD. The driver implements this by reserving space
-for the Rx descriptor at the head of each Rx skbuff
-
-The Speedo-3 has receive and command unit base addresses that are added to
-almost all descriptor pointers. The driver sets these to zero, so that all
-pointer fields are absolute addresses.
-
-The System Control Block (SCB) of some previous Intel chips exists on the
-chip in both PCI I/O and memory space. This driver uses the I/O space
-registers, but might switch to memory mapped mode to better support non-x86
-processors.
-
-IIIB. Transmit structure
-
-The driver must use the complex Tx command+descriptor mode in order to
-have a indirect pointer to the skbuff data section. Each Tx command block
-(TxCB) is associated with a single, immediately appended Tx buffer descriptor
-(TxBD). A fixed ring of these TxCB+TxBD pairs are kept as part of the
-speedo_private data structure for each adapter instance.
-
-This ring structure is used for all normal transmit packets, but the
-transmit packet descriptors aren't long enough for most non-Tx commands such
-as CmdConfigure. This is complicated by the possibility that the chip has
-already loaded the link address in the previous descriptor. So for these
-commands we convert the next free descriptor on the ring to a NoOp, and point
-that descriptor's link to the complex command.
-
-An additional complexity of these non-transmit commands are that they may be
-added asynchronous to the normal transmit queue, so we disable interrupts
-whenever the Tx descriptor ring is manipulated.
-
-A notable aspect of the these special configure commands is that they do
-work with the normal Tx ring entry scavenge method. The Tx ring scavenge
-is done at interrupt time using the 'dirty_tx' index, and checking for the
-command-complete bit. While the setup frames may have the NoOp command on the
-Tx ring marked as complete, but not have completed the setup command, this
-is not a problem. The tx_ring entry can be still safely reused, as the
-tx_skbuff[] entry is always empty for config_cmd and mc_setup frames.
-
-Commands may have bits set e.g. CmdSuspend in the command word to either
-suspend or stop the transmit/command unit. This driver always flags the last
-command with CmdSuspend, erases the CmdSuspend in the previous command, and
-then issues a CU_RESUME.
-Note: Watch out for the potential race condition here: imagine
- erasing the previous suspend
- the chip processes the previous command
- the chip processes the final command, and suspends
- doing the CU_RESUME
- the chip processes the next-yet-valid post-final-command.
-So blindly sending a CU_RESUME is only safe if we do it immediately after
-after erasing the previous CmdSuspend, without the possibility of an
-intervening delay. Thus the resume command is always within the
-interrupts-disabled region. This is a timing dependence, but handling this
-condition in a timing-independent way would considerably complicate the code.
-
-Note: In previous generation Intel chips, restarting the command unit was a
-notoriously slow process. This is presumably no longer true.
-
-IIIC. Receive structure
-
-Because of the bus-master support on the Speedo3 this driver uses the new
-SKBUFF_RX_COPYBREAK scheme, rather than a fixed intermediate receive buffer.
-This scheme allocates full-sized skbuffs as receive buffers. The value
-SKBUFF_RX_COPYBREAK is used as the copying breakpoint: it is chosen to
-trade-off the memory wasted by passing the full-sized skbuff to the queue
-layer for all frames vs. the copying cost of copying a frame to a
-correctly-sized skbuff.
-
-For small frames the copying cost is negligible (esp. considering that we
-are pre-loading the cache with immediately useful header information), so we
-allocate a new, minimally-sized skbuff. For large frames the copying cost
-is non-trivial, and the larger copy might flush the cache of useful data, so
-we pass up the skbuff the packet was received into.
-
-IIID. Synchronization
-The driver runs as two independent, single-threaded flows of control. One
-is the send-packet routine, which enforces single-threaded use by the
-dev->tbusy flag. The other thread is the interrupt handler, which is single
-threaded by the hardware and other software.
-
-The send packet thread has partial control over the Tx ring and 'dev->tbusy'
-flag. It sets the tbusy flag whenever it's queuing a Tx packet. If the next
-queue slot is empty, it clears the tbusy flag when finished otherwise it sets
-the 'sp->tx_full' flag.
-
-The interrupt handler has exclusive control over the Rx ring and records stats
-from the Tx ring. (The Tx-done interrupt can't be selectively turned off, so
-we can't avoid the interrupt overhead by having the Tx routine reap the Tx
-stats.) After reaping the stats, it marks the queue entry as empty by setting
-the 'base' to zero. Iff the 'sp->tx_full' flag is set, it clears both the
-tx_full and tbusy flags.
-
-IV. Notes
-
-Thanks to Steve Williams of Intel for arranging the non-disclosure agreement
-that stated that I could disclose the information. But I still resent
-having to sign an Intel NDA when I'm helping Intel sell their own product!
+/* When to switch from the 64-entry multicast filter to Rx-all-multicast. */
+#define MC_FILTERBREAK 64
-*/
+/* This unusual address order is used to verify the CONFIG register. */
+static int at1700_probe_list[] =
+{0x260, 0x280, 0x2a0, 0x240, 0x340, 0x320, 0x380, 0x300, 0};
-/* A few values that may be tweaked. */
-/* The ring sizes should be a power of two for efficiency. */
-#define TX_RING_SIZE 16 /* Effectively 2 entries fewer. */
-#define RX_RING_SIZE 16
-/* Size of an pre-allocated Rx buffer: <Ethernet MTU> + slack.*/
-#define PKT_BUF_SZ 1536
-/* Time in jiffies before concluding the transmitter is hung. */
-#define TX_TIMEOUT ((800*HZ)/1000)
+/* use 0 for production, 1 for verification, >2 for debug */
+#ifndef NET_DEBUG
+#define NET_DEBUG 1
+#endif
+static unsigned int net_debug = NET_DEBUG;
-/* How to wait for the command unit to accept a command.
- Typically this takes 0 ticks. */
-static inline void wait_for_cmd_done(int cmd_ioaddr)
-{
- short wait = 100;
- do ;
- while(inb(cmd_ioaddr) && --wait >= 0);
-}
+typedef unsigned char uchar;
-/* Operational parameter that usually are not changed. */
+/* Information that need to be kept for each board. */
+struct net_local {
+ struct enet_statistics stats;
+ unsigned char mc_filter[8];
+ uint tx_started:1; /* Packets are on the Tx queue. */
+ uchar tx_queue; /* Number of packet on the Tx queue. */
+ ushort tx_queue_len; /* Current length of the Tx queue. */
+};
-/* The rest of these values should never change. */
-/* Offsets to the various registers.
- All accesses need not be longword aligned. */
-enum speedo_offsets {
- SCBStatus = 0, SCBCmd = 2, /* Rx/Command Unit command and status. */
- SCBPointer = 4, /* General purpose pointer. */
- SCBPort = 8, /* Misc. commands and operands. */
- SCBflash = 12, SCBeeprom = 14, /* EEPROM and flash memory control. */
- SCBCtrlMDI = 16, /* MDI interface control. */
- SCBEarlyRx = 20, /* Early receive byte count. */
-};
-/* Commands that can be put in a command list entry. */
-enum commands {
- CmdNOp = 0, CmdIASetup = 1, CmdConfigure = 2, CmdMulticastList = 3,
- CmdTx = 4, CmdTDR = 5, CmdDump = 6, CmdDiagnose = 7,
- CmdSuspend = 0x4000, /* Suspend after completion. */
- CmdIntr = 0x2000, /* Interrupt after completion. */
- CmdTxFlex = 0x0008, /* Use "Flexible mode" for CmdTx command. */
-};
+/* Offsets from the base address. */
+#define STATUS 0
+#define TX_STATUS 0
+#define RX_STATUS 1
+#define TX_INTR 2 /* Bit-mapped interrupt enable registers. */
+#define RX_INTR 3
+#define TX_MODE 4
+#define RX_MODE 5
+#define CONFIG_0 6 /* Misc. configuration settings. */
+#define CONFIG_1 7
+/* Run-time register bank 2 definitions. */
+#define DATAPORT 8 /* Word-wide DMA or programmed-I/O dataport. */
+#define TX_START 10
+#define MODE13 13
+#define EEPROM_Ctrl 16
+#define EEPROM_Data 17
+#define IOCONFIG 19
+#define RESET 31 /* Write to reset some parts of the chip. */
+#define AT1700_IO_EXTENT 32
-/* The SCB accepts the following controls for the Tx and Rx units: */
-#define CU_START 0x0010
-#define CU_RESUME 0x0020
-#define CU_STATSADDR 0x0040
-#define CU_SHOWSTATS 0x0050 /* Dump statistics counters. */
-#define CU_CMD_BASE 0x0060 /* Base address to add to add CU commands. */
-#define CU_DUMPSTATS 0x0070 /* Dump then reset stats counters. */
-
-#define RX_START 0x0001
-#define RX_RESUME 0x0002
-#define RX_ABORT 0x0004
-#define RX_ADDR_LOAD 0x0006
-#define RX_RESUMENR 0x0007
-#define INT_MASK 0x0100
-#define DRVR_INT 0x0200 /* Driver generated interrupt. */
-
-/* The Speedo3 Rx and Tx frame/buffer descriptors. */
-struct descriptor { /* A generic descriptor. */
- s16 status; /* Offset 0. */
- s16 command; /* Offset 2. */
- u32 link; /* struct descriptor * */
- unsigned char params[0];
-};
+/* EEPROM_Ctrl bits. */
+#define EE_SHIFT_CLK 0x40 /* EEPROM shift clock, in reg. 16. */
+#define EE_CS 0x20 /* EEPROM chip select, in reg. 16. */
+#define EE_DATA_WRITE 0x80 /* EEPROM chip data in, in reg. 17. */
+#define EE_DATA_READ 0x80 /* EEPROM chip data out, in reg. 17. */
-/* The Speedo3 Rx and Tx buffer descriptors. */
-struct RxFD { /* Receive frame descriptor. */
- s32 status;
- u32 link; /* struct RxFD * */
- u32 rx_buf_addr; /* void * */
- u16 count;
- u16 size;
-};
+/* Delay between EEPROM clock transitions. */
+#define eeprom_delay() do { int _i = 40; while (--_i > 0) { __SLOW_DOWN_IO; }} while (0)
-/* Elements of the RxFD.status word. */
-#define RX_COMPLETE 0x8000
-
-struct TxFD { /* Transmit frame descriptor set. */
- s32 status;
- u32 link; /* void * */
- u32 tx_desc_addr; /* Always points to the tx_buf_addr element. */
- s32 count; /* # of TBD (=1), Tx start thresh., etc. */
- /* This constitutes a single "TBD" entry -- we only use one. */
- u32 tx_buf_addr; /* void *, frame to be transmitted. */
- s32 tx_buf_size; /* Length of Tx frame. */
-};
+/* The EEPROM commands include the alway-set leading bit. */
+#define EE_WRITE_CMD (5 << 6)
+#define EE_READ_CMD (6 << 6)
+#define EE_ERASE_CMD (7 << 6)
-/* Elements of the dump_statistics block. This block must be lword aligned. */
-struct speedo_stats {
- u32 tx_good_frames;
- u32 tx_coll16_errs;
- u32 tx_late_colls;
- u32 tx_underruns;
- u32 tx_lost_carrier;
- u32 tx_deferred;
- u32 tx_one_colls;
- u32 tx_multi_colls;
- u32 tx_total_colls;
- u32 rx_good_frames;
- u32 rx_crc_errs;
- u32 rx_align_errs;
- u32 rx_resource_errs;
- u32 rx_overrun_errs;
- u32 rx_colls_errs;
- u32 rx_runt_errs;
- u32 done_marker;
-};
-struct speedo_private {
- char devname[8]; /* Used only for kernel debugging. */
- const char *product_name;
- struct device *next_module;
- struct TxFD tx_ring[TX_RING_SIZE]; /* Commands (usually CmdTxPacket). */
- /* The saved address of a sent-in-place packet/buffer, for skfree(). */
- struct sk_buff* tx_skbuff[TX_RING_SIZE];
- struct descriptor *last_cmd; /* Last command sent. */
- /* Rx descriptor ring & addresses of receive-in-place skbuffs. */
- struct RxFD *rx_ringp[RX_RING_SIZE];
- struct sk_buff* rx_skbuff[RX_RING_SIZE];
- struct RxFD *last_rxf; /* Last command sent. */
- struct enet_statistics stats;
- struct speedo_stats lstats;
- struct timer_list timer; /* Media selection timer. */
- long last_rx_time; /* Last Rx, in jiffies, to handle Rx hang. */
- unsigned int cur_rx, cur_tx; /* The next free ring entry */
- unsigned int dirty_rx, dirty_tx; /* The ring entries to be free()ed. */
- struct descriptor config_cmd; /* A configure command, with header... */
- u8 config_cmd_data[22]; /* .. and setup parameters. */
- int mc_setup_frm_len; /* The length of an allocated.. */
- struct descriptor *mc_setup_frm; /* ..multicast setup frame. */
- int in_interrupt; /* Word-aligned dev->interrupt */
- char rx_mode; /* Current PROMISC/ALLMULTI setting. */
- unsigned int tx_full:1; /* The Tx queue is full. */
- unsigned int full_duplex:1; /* Full-duplex operation requested. */
- unsigned int default_port:1; /* Last dev->if_port value. */
- unsigned int rx_bug:1; /* Work around receiver hang errata. */
- unsigned int rx_bug10:1; /* Receiver might hang at 10mbps. */
- unsigned int rx_bug100:1; /* Receiver might hang at 100mbps. */
- unsigned short phy[2]; /* PHY media interfaces available. */
-};
+/* Index to functions, as function prototypes. */
-/* The parameters for a CmdConfigure operation.
- There are so many options that it would be difficult to document each bit.
- We mostly use the default or recommended settings. */
-const char basic_config_cmd[22] = {
- 22, 0x08, 0, 0, 0, 0x80, 0x32, 0x03, 1, /* 1=Use MII 0=Use AUI */
- 0, 0x2E, 0, 0x60, 0,
- 0xf2, 0x48, 0, 0x40, 0xf2, 0x80, /* 0x40=Force full-duplex */
- 0x3f, 0x05, };
-
-/* PHY media interface chips. */
-static const char *phys[] = {
- "None", "i82553-A/B", "i82553-C", "i82503",
- "DP83840", "80c240", "80c24", "i82555",
- "unknown-8", "unknown-9", "DP83840A", "unknown-11",
- "unknown-12", "unknown-13", "unknown-14", "unknown-15", };
-enum phy_chips { NonSuchPhy=0, I82553AB, I82553C, I82503, DP83840, S80C240,
- S80C24, I82555, DP83840A=10, };
-static const char is_mii[] = { 0, 1, 1, 0, 1, 1, 0, 1 };
-
-static void speedo_found1(struct device *dev, int ioaddr, int irq,
- int card_idx);
+extern int at1700_probe(struct device *dev);
+static int at1700_probe1(struct device *dev, int ioaddr);
static int read_eeprom(int ioaddr, int location);
-static int mdio_read(int ioaddr, int phy_id, int location);
-static int mdio_write(int ioaddr, int phy_id, int location, int value);
-static int speedo_open(struct device *dev);
-static void speedo_timer(unsigned long data);
-static void speedo_init_rx_ring(struct device *dev);
-static int speedo_start_xmit(struct sk_buff *skb, struct device *dev);
-static int speedo_rx(struct device *dev);
-static void speedo_interrupt(int irq, void *dev_instance, struct pt_regs *regs);
-static int speedo_close(struct device *dev);
-static struct enet_statistics *speedo_get_stats(struct device *dev);
-static int speedo_ioctl(struct device *dev, struct ifreq *rq, int cmd);
+static int net_open(struct device *dev);
+static int net_send_packet(struct sk_buff *skb, struct device *dev);
+static void net_interrupt(int irq, void *dev_id, struct pt_regs *regs);
+static void net_rx(struct device *dev);
+static int net_close(struct device *dev);
+static struct enet_statistics *net_get_stats(struct device *dev);
static void set_rx_mode(struct device *dev);
\f
-
-/* The parameters that may be passed in... */
-/* 'options' is used to pass a transceiver override or full-duplex flag
- e.g. "options=16" for FD, "options=32" for 100mbps-only. */
-static int full_duplex[] = {-1, -1, -1, -1, -1, -1, -1, -1};
-static int options[] = {-1, -1, -1, -1, -1, -1, -1, -1};
-#ifdef MODULE
-static int debug = -1; /* The debug level */
-#endif
-
-/* A list of all installed Speedo devices, for removing the driver module. */
-static struct device *root_speedo_dev = NULL;
-
-int eepro100_init(struct device *dev)
+/* Check for a network adaptor of this type, and return '0' iff one exists.
+ If dev->base_addr == 0, probe all likely locations.
+ If dev->base_addr == 1, always return failure.
+ If dev->base_addr == 2, allocate space for the device and return success
+ (detachable devices only).
+ */
+#ifdef HAVE_DEVLIST
+/* Support for a alternate probe manager, which will eliminate the
+ boilerplate below. */
+struct netdev_entry at1700_drv =
+{"at1700", at1700_probe1, AT1700_IO_EXTENT, at1700_probe_list};
+#else
+int
+at1700_probe(struct device *dev)
{
- int cards_found = 0;
-
- if (pcibios_present()) {
- static int pci_index = 0;
- for (; pci_index < 8; pci_index++) {
- unsigned char pci_bus, pci_device_fn, pci_irq_line, pci_latency;
- int pci_ioaddr;
-
- unsigned short pci_command, new_command;
-
- if (pcibios_find_device(PCI_VENDOR_ID_INTEL,
- PCI_DEVICE_ID_INTEL_82557,
- pci_index, &pci_bus,
- &pci_device_fn))
- break;
- pcibios_read_config_byte(pci_bus, pci_device_fn,
- PCI_INTERRUPT_LINE, &pci_irq_line);
- /* Note: BASE_ADDRESS_0 is for memory-mapping the registers. */
- pcibios_read_config_dword(pci_bus, pci_device_fn,
- PCI_BASE_ADDRESS_1, &pci_ioaddr);
- /* Remove I/O space marker in bit 0. */
- pci_ioaddr &= ~3;
- if (speedo_debug > 2)
- printk("Found Intel i82557 PCI Speedo at I/O %#x, IRQ %d.\n",
- (int)pci_ioaddr, pci_irq_line);
-
- /* Get and check the bus-master and latency values. */
- pcibios_read_config_word(pci_bus, pci_device_fn,
- PCI_COMMAND, &pci_command);
- new_command = pci_command | PCI_COMMAND_MASTER|PCI_COMMAND_IO;
- if (pci_command != new_command) {
- printk(KERN_INFO " The PCI BIOS has not enabled this"
- " device! Updating PCI command %4.4x->%4.4x.\n",
- pci_command, new_command);
- pcibios_write_config_word(pci_bus, pci_device_fn,
- PCI_COMMAND, new_command);
- }
- pcibios_read_config_byte(pci_bus, pci_device_fn,
- PCI_LATENCY_TIMER, &pci_latency);
- if (pci_latency < 32) {
- printk(" PCI latency timer (CFLT) is unreasonably low at %d."
- " Setting to 32 clocks.\n", pci_latency);
- pcibios_write_config_byte(pci_bus, pci_device_fn,
- PCI_LATENCY_TIMER, 32);
- } else if (speedo_debug > 1)
- printk(" PCI latency timer (CFLT) is %#x.\n", pci_latency);
-
- speedo_found1(dev, pci_ioaddr, pci_irq_line, cards_found);
- dev = NULL;
- cards_found++;
- }
+ int i;
+ int base_addr = dev ? dev->base_addr : 0;
+
+ if (base_addr > 0x1ff) /* Check a single specified location. */
+ return at1700_probe1(dev, base_addr);
+ else if (base_addr != 0) /* Don't probe at all. */
+ return ENXIO;
+
+ for (i = 0; at1700_probe_list[i]; i++) {
+ int ioaddr = at1700_probe_list[i];
+ if (check_region(ioaddr, AT1700_IO_EXTENT))
+ continue;
+ if (at1700_probe1(dev, ioaddr) == 0)
+ return 0;
}
- return cards_found;
+ return ENODEV;
}
+#endif
-static void speedo_found1(struct device *dev, int ioaddr, int irq,
- int card_idx)
-{
- static int did_version = 0; /* Already printed version info. */
- struct speedo_private *sp;
- char *product;
- int i, option;
- u16 eeprom[0x40];
-
- if (speedo_debug > 0 && did_version++ == 0)
- printk(version);
+/* The Fujitsu datasheet suggests that the NIC be probed for by checking its
+ "signature", the default bit pattern after a reset. This *doesn't* work --
+ there is no way to reset the bus interface without a complete power-cycle!
- dev = init_etherdev(dev, sizeof(struct speedo_private));
+ It turns out that ATI came to the same conclusion I did: the only thing
+ that can be done is checking a few bits and then diving right into an
+ EEPROM read. */
- if (dev->mem_start > 0)
- option = dev->mem_start;
- else if (card_idx >= 0 && options[card_idx] >= 0)
- option = options[card_idx];
- else
- option = 0;
+int at1700_probe1(struct device *dev, int ioaddr)
+{
+ char irqmap[8] = {3, 4, 5, 9, 10, 11, 14, 15};
+ unsigned int i, irq;
- /* Read the station address EEPROM before doing the reset.
- Perhaps this should even be done before accepting the device,
- then we wouldn't have a device name with which to report the error. */
- {
- u16 sum = 0;
- int j;
- for (j = 0, i = 0; i < 0x40; i++) {
- u16 value = read_eeprom(ioaddr, i);
- eeprom[i] = value;
- sum += value;
- if (i < 3) {
- dev->dev_addr[j++] = value;
- dev->dev_addr[j++] = value >> 8;
- }
- }
- if (sum != 0xBABA)
- printk(KERN_WARNING "%s: Invalid EEPROM checksum %#4.4x, "
- "check settings before activating this device!\n",
- dev->name, sum);
- /* Don't unregister_netdev(dev); as the EEPro may actually be
- usable, especially if the MAC address is set later. */
+ /* Resetting the chip doesn't reset the ISA interface, so don't bother.
+ That means we have to be careful with the register values we probe for.
+ */
+#ifdef notdef
+ printk("at1700 probe at %#x, eeprom is %4.4x %4.4x %4.4x ctrl %4.4x.\n",
+ ioaddr, read_eeprom(ioaddr, 4), read_eeprom(ioaddr, 5),
+ read_eeprom(ioaddr, 6), inw(ioaddr + EEPROM_Ctrl));
+#endif
+ if (at1700_probe_list[inb(ioaddr + IOCONFIG) & 0x07] != ioaddr
+ || read_eeprom(ioaddr, 4) != 0x0000
+ || (read_eeprom(ioaddr, 5) & 0xff00) != 0xF400)
+ return -ENODEV;
+
+ /* Reset the internal state machines. */
+ outb(0, ioaddr + RESET);
+
+ irq = irqmap[(read_eeprom(ioaddr, 12)&0x04)
+ | (read_eeprom(ioaddr, 0)>>14)];
+
+ /* Snarf the interrupt vector now. */
+ if (request_irq(irq, &net_interrupt, 0, "at1700", NULL)) {
+ printk ("AT1700 found at %#3x, but it's unusable due to a conflict on"
+ "IRQ %d.\n", ioaddr, irq);
+ return EAGAIN;
}
- /* Reset the chip: stop Tx and Rx processes and clear counters.
- This takes less than 10usec and will easily finish before the next
- action. */
- outl(0, ioaddr + SCBPort);
+ /* Allocate a new 'dev' if needed. */
+ if (dev == NULL)
+ dev = init_etherdev(0, sizeof(struct net_local));
+
+ /* Grab the region so that we can find another board if the IRQ request
+ fails. */
+ request_region(ioaddr, AT1700_IO_EXTENT, "at1700");
- if (eeprom[3] & 0x0100)
- product = "OEM i82557/i82558 10/100 Ethernet";
- else
- product = "Intel EtherExpress Pro 10/100";
+ printk("%s: AT1700 found at %#3x, IRQ %d, address ", dev->name,
+ ioaddr, irq);
- printk(KERN_INFO "%s: %s at %#3x, ", dev->name, product, ioaddr);
+ dev->base_addr = ioaddr;
+ dev->irq = irq;
+ irq2dev_map[irq] = dev;
- for (i = 0; i < 5; i++)
- printk("%2.2X:", dev->dev_addr[i]);
- printk("%2.2X, IRQ %d.\n", dev->dev_addr[i], irq);
+ for(i = 0; i < 3; i++) {
+ unsigned short eeprom_val = read_eeprom(ioaddr, 4+i);
+ printk("%04x", eeprom_val);
+ ((unsigned short *)dev->dev_addr)[i] = ntohs(eeprom_val);
+ }
-#ifndef kernel_bloat
- /* OK, this is pure kernel bloat. I don't like it when other drivers
- waste non-pageable kernel space to emit similar messages, but I need
- them for bug reports. */
+ /* The EEPROM word 12 bit 0x0400 means use regular 100 ohm 10baseT signals,
+ rather than 150 ohm shielded twisted pair compensation.
+ 0x0000 == auto-sense the interface
+ 0x0800 == use TP interface
+ 0x1800 == use coax interface
+ */
{
- const char *connectors[] = {" RJ45", " BNC", " AUI", " MII"};
- /* The self-test results must be paragraph aligned. */
- s32 str[6], *volatile self_test_results;
- int boguscnt = 16000; /* Timeout for set-test. */
- if (eeprom[3] & 0x03)
- printk(KERN_INFO " Receiver lock-up bug exists -- enabling"
- " work-around.\n");
- printk(KERN_INFO " Board assembly %4.4x%2.2x-%3.3d, Physical"
- " connectors present:",
- eeprom[8], eeprom[9]>>8, eeprom[9] & 0xff);
- for (i = 0; i < 4; i++)
- if (eeprom[5] & (1<<i))
- printk(connectors[i]);
- printk("\n"KERN_INFO" Primary interface chip %s PHY #%d.\n",
- phys[(eeprom[6]>>8)&15], eeprom[6] & 0x1f);
- if (eeprom[7] & 0x0700)
- printk(KERN_INFO " Secondary interface chip %s.\n",
- phys[(eeprom[7]>>8)&7]);
- if (((eeprom[6]>>8) & 0x3f) == DP83840
- || ((eeprom[6]>>8) & 0x3f) == DP83840A) {
- int mdi_reg23 = mdio_read(ioaddr, eeprom[6] & 0x1f, 23) | 0x0422;
- if (congenb)
- mdi_reg23 |= 0x0100;
- printk(KERN_INFO" DP83840 specific setup, setting register 23 to %4.4x.\n",
- mdi_reg23);
- mdio_write(ioaddr, eeprom[6] & 0x1f, 23, mdi_reg23);
- }
- if ((option >= 0) && (option & 0x70)) {
- printk(KERN_INFO " Forcing %dMbs %s-duplex operation.\n",
- (option & 0x20 ? 100 : 10),
- (option & 0x10 ? "full" : "half"));
- mdio_write(ioaddr, eeprom[6] & 0x1f, 0,
- ((option & 0x20) ? 0x2000 : 0) | /* 100mbps? */
- ((option & 0x10) ? 0x0100 : 0)); /* Full duplex? */
- }
+ const char *porttype[] = {"auto-sense", "10baseT", "auto-sense", "10base2"};
+ ushort setup_value = read_eeprom(ioaddr, 12);
- /* Perform a system self-test. */
- self_test_results = (s32*) ((((long) str) + 15) & ~0xf);
- self_test_results[0] = 0;
- self_test_results[1] = -1;
- outl(virt_to_bus(self_test_results) | 1, ioaddr + SCBPort);
- do {
- udelay(10);
- } while (self_test_results[1] == -1 && --boguscnt >= 0);
-
- if (boguscnt < 0) { /* Test optimized out. */
- printk(KERN_ERR "Self test failed, status %8.8x:\n"
- KERN_ERR " Failure to initialize the i82557.\n"
- KERN_ERR " Verify that the card is a bus-master"
- " capable slot.\n",
- self_test_results[1]);
- } else
- printk(KERN_INFO " General self-test: %s.\n"
- KERN_INFO " Serial sub-system self-test: %s.\n"
- KERN_INFO " Internal registers self-test: %s.\n"
- KERN_INFO " ROM checksum self-test: %s (%#8.8x).\n",
- self_test_results[1] & 0x1000 ? "failed" : "passed",
- self_test_results[1] & 0x0020 ? "failed" : "passed",
- self_test_results[1] & 0x0008 ? "failed" : "passed",
- self_test_results[1] & 0x0004 ? "failed" : "passed",
- self_test_results[0]);
+ dev->if_port = setup_value >> 8;
+ printk(" %s interface.\n", porttype[(dev->if_port>>3) & 3]);
}
-#endif /* kernel_bloat */
- outl(0, ioaddr + SCBPort);
+ /* Set the station address in bank zero. */
+ outb(0xe0, ioaddr + 7);
+ for (i = 0; i < 6; i++)
+ outb(dev->dev_addr[i], ioaddr + 8 + i);
- /* We do a request_region() only to register /proc/ioports info. */
- request_region(ioaddr, SPEEDO3_TOTAL_SIZE, "Intel Speedo3 Ethernet");
+ /* Switch to bank 1 and set the multicast table to accept none. */
+ outb(0xe4, ioaddr + 7);
+ for (i = 0; i < 8; i++)
+ outb(0x00, ioaddr + 8 + i);
- dev->base_addr = ioaddr;
- dev->irq = irq;
+ /* Set the configuration register 0 to 32K 100ns. byte-wide memory, 16 bit
+ bus access, two 4K Tx queues, and disabled Tx and Rx. */
+ outb(0xda, ioaddr + CONFIG_0);
- if (dev->priv == NULL)
- dev->priv = kmalloc(sizeof(*sp), GFP_KERNEL);
- sp = dev->priv;
- memset(sp, 0, sizeof(*sp));
- sp->next_module = root_speedo_dev;
- root_speedo_dev = dev;
-
- sp->full_duplex = option >= 0 && (option & 0x10) ? 1 : 0;
- if (card_idx >= 0) {
- if (full_duplex[card_idx] >= 0)
- sp->full_duplex = full_duplex[card_idx];
- }
- sp->default_port = option >= 0 ? (option & 0x0f) : 0;
+ /* Switch to bank 2 and lock our I/O address. */
+ outb(0xe8, ioaddr + 7);
+ outb(dev->if_port, MODE13);
- sp->phy[0] = eeprom[6];
- sp->phy[1] = eeprom[7];
- sp->rx_bug = (eeprom[3] & 0x03) == 3 ? 0 : 1;
+ /* Power-down the chip. Aren't we green! */
+ outb(0x00, ioaddr + CONFIG_1);
- if (sp->rx_bug)
- printk(KERN_INFO " Receiver lock-up workaround activated.\n");
+ if (net_debug)
+ printk(version);
+
+ /* Initialize the device structure. */
+ dev->priv = kmalloc(sizeof(struct net_local), GFP_KERNEL);
+ if (dev->priv == NULL)
+ return -ENOMEM;
+ memset(dev->priv, 0, sizeof(struct net_local));
- /* The Speedo-specific entries in the device structure. */
- dev->open = &speedo_open;
- dev->hard_start_xmit = &speedo_start_xmit;
- dev->stop = &speedo_close;
- dev->get_stats = &speedo_get_stats;
+ dev->open = net_open;
+ dev->stop = net_close;
+ dev->hard_start_xmit = net_send_packet;
+ dev->get_stats = net_get_stats;
dev->set_multicast_list = &set_rx_mode;
- dev->do_ioctl = &speedo_ioctl;
- return;
+ /* Fill in the fields of 'dev' with ethernet-generic values. */
+
+ ether_setup(dev);
+ return 0;
}
-\f
-/* Serial EEPROM section.
- A "bit" grungy, but we work our way through bit-by-bit :->. */
-/* EEPROM_Ctrl bits. */
-#define EE_SHIFT_CLK 0x01 /* EEPROM shift clock. */
-#define EE_CS 0x02 /* EEPROM chip select. */
-#define EE_DATA_WRITE 0x04 /* EEPROM chip data in. */
-#define EE_WRITE_0 0x01
-#define EE_WRITE_1 0x05
-#define EE_DATA_READ 0x08 /* EEPROM chip data out. */
-#define EE_ENB (0x4800 | EE_CS)
-
-/* Delay between EEPROM clock transitions.
- This will actually work with no delay on 33Mhz PCI. */
-#define eeprom_delay(nanosec) udelay(1);
-
-/* The EEPROM commands include the alway-set leading bit. */
-#define EE_WRITE_CMD (5 << 6)
-#define EE_READ_CMD (6 << 6)
-#define EE_ERASE_CMD (7 << 6)
static int read_eeprom(int ioaddr, int location)
{
int i;
unsigned short retval = 0;
- int ee_addr = ioaddr + SCBeeprom;
+ int ee_addr = ioaddr + EEPROM_Ctrl;
+ int ee_daddr = ioaddr + EEPROM_Data;
int read_cmd = location | EE_READ_CMD;
-
- outw(EE_ENB & ~EE_CS, ee_addr);
- outw(EE_ENB, ee_addr);
-
+ short ctrl_val = EE_CS;
+
+ outb(ctrl_val, ee_addr);
+
/* Shift the read command bits out. */
- for (i = 10; i >= 0; i--) {
+ for (i = 9; i >= 0; i--) {
short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0;
- outw(EE_ENB | dataval, ee_addr);
- eeprom_delay(100);
- outw(EE_ENB | dataval | EE_SHIFT_CLK, ee_addr);
- eeprom_delay(150);
+ outb(dataval, ee_daddr);
+ outb(EE_CS | EE_SHIFT_CLK, ee_addr); /* EEPROM clock tick. */
+ eeprom_delay();
+ outb(EE_CS, ee_addr); /* Finish EEPROM a clock tick. */
+ eeprom_delay();
}
- outw(EE_ENB, ee_addr);
-
- for (i = 15; i >= 0; i--) {
- outw(EE_ENB | EE_SHIFT_CLK, ee_addr);
- eeprom_delay(100);
- retval = (retval << 1) | ((inw(ee_addr) & EE_DATA_READ) ? 1 : 0);
- outw(EE_ENB, ee_addr);
- eeprom_delay(100);
+ outb(EE_CS, ee_addr);
+
+ for (i = 16; i > 0; i--) {
+ outb(EE_CS | EE_SHIFT_CLK, ee_addr);
+ eeprom_delay();
+ retval = (retval << 1) | ((inb(ee_daddr) & EE_DATA_READ) ? 1 : 0);
+ outb(EE_CS, ee_addr);
+ eeprom_delay();
}
/* Terminate the EEPROM access. */
- outw(EE_ENB & ~EE_CS, ee_addr);
+ ctrl_val &= ~EE_CS;
+ outb(ctrl_val | EE_SHIFT_CLK, ee_addr);
+ eeprom_delay();
+ outb(ctrl_val, ee_addr);
+ eeprom_delay();
return retval;
}
-static int mdio_read(int ioaddr, int phy_id, int location)
-{
- int val, boguscnt = 64*10; /* <64 usec. to complete, typ 27 ticks */
- outl(0x08000000 | (location<<16) | (phy_id<<21), ioaddr + SCBCtrlMDI);
- do {
- val = inl(ioaddr + SCBCtrlMDI);
- if (--boguscnt < 0) {
- printk(KERN_ERR " mdio_read() timed out with val = %8.8x.\n", val);
- }
- } while (! (val & 0x10000000));
- return val & 0xffff;
-}
-
-static int mdio_write(int ioaddr, int phy_id, int location, int value)
-{
- int val, boguscnt = 64*10; /* <64 usec. to complete, typ 27 ticks */
- outl(0x04000000 | (location<<16) | (phy_id<<21) | value,
- ioaddr + SCBCtrlMDI);
- do {
- val = inl(ioaddr + SCBCtrlMDI);
- if (--boguscnt < 0) {
- printk(KERN_ERR" mdio_write() timed out with val = %8.8x.\n", val);
- }
- } while (! (val & 0x10000000));
- return val & 0xffff;
-}
-
\f
-static int
-speedo_open(struct device *dev)
+
+static int net_open(struct device *dev)
{
- struct speedo_private *sp = (struct speedo_private *)dev->priv;
+ struct net_local *lp = (struct net_local *)dev->priv;
int ioaddr = dev->base_addr;
+ int i;
-#ifdef notdef
- /* We could reset the chip, but should not need to. */
- outl(0, ioaddr + SCBPort);
- udelay(10);
-#endif
-
- if (request_irq(dev->irq, &speedo_interrupt, SA_SHIRQ,
- "Intel EtherExpress Pro 10/100 Ethernet", dev)) {
- return -EAGAIN;
- }
- if (speedo_debug > 1)
- printk(KERN_DEBUG "%s: speedo_open() irq %d.\n", dev->name, dev->irq);
-
- MOD_INC_USE_COUNT;
+ /* Powerup the chip, initialize config register 1, and select bank 0. */
+ outb(0xe0, ioaddr + CONFIG_1);
- /* Load the statistics block address. */
- wait_for_cmd_done(ioaddr + SCBCmd);
- outl(virt_to_bus(&sp->lstats), ioaddr + SCBPointer);
- outw(INT_MASK | CU_STATSADDR, ioaddr + SCBCmd);
- sp->lstats.done_marker = 0;
+ /* Set the station address in bank zero. */
+ for (i = 0; i < 6; i++)
+ outb(dev->dev_addr[i], ioaddr + 8 + i);
- speedo_init_rx_ring(dev);
- wait_for_cmd_done(ioaddr + SCBCmd);
- outl(0, ioaddr + SCBPointer);
- outw(INT_MASK | RX_ADDR_LOAD, ioaddr + SCBCmd);
+ /* Switch to bank 1 and set the multicast table to accept none. */
+ outb(0xe4, ioaddr + CONFIG_1);
+ for (i = 0; i < 8; i++)
+ outb(0x00, ioaddr + 8 + i);
- /* Todo: verify that we must wait for previous command completion. */
- wait_for_cmd_done(ioaddr + SCBCmd);
- outl(virt_to_bus(sp->rx_ringp[0]), ioaddr + SCBPointer);
- outw(INT_MASK | RX_START, ioaddr + SCBCmd);
+ /* Set the configuration register 0 to 32K 100ns. byte-wide memory, 16 bit
+ bus access, and two 4K Tx queues. */
+ outb(0xda, ioaddr + CONFIG_0);
- /* Fill the first command with our physical address. */
- {
- u16 *eaddrs = (u16 *)dev->dev_addr;
- u16 *setup_frm = (u16 *)&(sp->tx_ring[0].tx_desc_addr);
-
- /* Avoid a bug(?!) here by marking the command already completed. */
- sp->tx_ring[0].status = ((CmdSuspend | CmdIASetup) << 16) | 0xa000;
- sp->tx_ring[0].link = virt_to_bus(&(sp->tx_ring[1]));
- *setup_frm++ = eaddrs[0];
- *setup_frm++ = eaddrs[1];
- *setup_frm++ = eaddrs[2];
- }
- sp->last_cmd = (struct descriptor *)&sp->tx_ring[0];
- sp->cur_tx = 1;
- sp->dirty_tx = 0;
- sp->tx_full = 0;
+ /* Same config 0, except enable the Rx and Tx. */
+ outb(0x5a, ioaddr + CONFIG_0);
+ /* Switch to register bank 2 for the run-time registers. */
+ outb(0xe8, ioaddr + CONFIG_1);
- wait_for_cmd_done(ioaddr + SCBCmd);
- outl(0, ioaddr + SCBPointer);
- outw(INT_MASK | CU_CMD_BASE, ioaddr + SCBCmd);
+ lp->tx_started = 0;
+ lp->tx_queue = 0;
+ lp->tx_queue_len = 0;
- dev->if_port = sp->default_port;
+ /* Turn on Rx interrupts, leave Tx interrupts off until packet Tx. */
+ outb(0x00, ioaddr + TX_INTR);
+ outb(0x81, ioaddr + RX_INTR);
- sp->in_interrupt = 0;
dev->tbusy = 0;
dev->interrupt = 0;
dev->start = 1;
- /* Start the chip's Tx process and unmask interrupts. */
- /* Todo: verify that we must wait for previous command completion. */
- wait_for_cmd_done(ioaddr + SCBCmd);
- outl(virt_to_bus(&sp->tx_ring[0]), ioaddr + SCBPointer);
- outw(CU_START, ioaddr + SCBCmd);
-
- /* Setup the chip and configure the multicast list. */
- sp->mc_setup_frm = NULL;
- sp->mc_setup_frm_len = 0;
- sp->rx_mode = -1; /* Invalid -> always reset the mode. */
- set_rx_mode(dev);
-
- if (speedo_debug > 2) {
- printk(KERN_DEBUG "%s: Done speedo_open(), status %8.8x.\n",
- dev->name, inw(ioaddr + SCBStatus));
- }
- /* Set the timer. The timer serves a dual purpose:
- 1) to monitor the media interface (e.g. link beat) and perhaps switch
- to an alternate media type
- 2) to monitor Rx activity, and restart the Rx process if the receiver
- hangs. */
- init_timer(&sp->timer);
- sp->timer.expires = RUN_AT((24*HZ)/10); /* 2.4 sec. */
- sp->timer.data = (unsigned long)dev;
- sp->timer.function = &speedo_timer; /* timer handler */
- add_timer(&sp->timer);
-
- wait_for_cmd_done(ioaddr + SCBCmd);
- outw(CU_DUMPSTATS, ioaddr + SCBCmd);
- return 0;
-}
-
-/* Media monitoring and control. */
-static void speedo_timer(unsigned long data)
-{
- struct device *dev = (struct device *)data;
- struct speedo_private *sp = (struct speedo_private *)dev->priv;
- int tickssofar = jiffies - sp->last_rx_time;
-
- if (speedo_debug > 3) {
- int ioaddr = dev->base_addr;
- printk(KERN_DEBUG "%s: Media selection tick, status %4.4x.\n",
- dev->name, inw(ioaddr + SCBStatus));
- }
- if (sp->rx_bug) {
- if (tickssofar > 2*HZ || sp->rx_mode < 0) {
- /* We haven't received a packet in a Long Time. We might have been
- bitten by the receiver hang bug. This can be cleared by sending
- a set multicast list command. */
- set_rx_mode(dev);
- }
- /* We must continue to monitor the media. */
- sp->timer.expires = RUN_AT(2*HZ); /* 2.0 sec. */
- add_timer(&sp->timer);
- }
-}
-
-/* Initialize the Rx and Tx rings, along with various 'dev' bits. */
-static void
-speedo_init_rx_ring(struct device *dev)
-{
- struct speedo_private *sp = (struct speedo_private *)dev->priv;
- struct RxFD *rxf, *last_rxf = NULL;
- int i;
-
- sp->cur_rx = 0;
- sp->dirty_rx = RX_RING_SIZE - 1;
-
- for (i = 0; i < RX_RING_SIZE; i++) {
- struct sk_buff *skb;
- skb = alloc_skb(PKT_BUF_SZ, GFP_ATOMIC);
- sp->rx_skbuff[i] = skb;
- if (skb == NULL)
- break; /* Bad news! */
- skb->dev = dev; /* Mark as being used by this device. */
-
- rxf = (struct RxFD *)skb->tail;
- skb_reserve(skb, sizeof(struct RxFD));
- sp->rx_ringp[i] = rxf;
- if (last_rxf)
- last_rxf->link = virt_to_bus(rxf);
- last_rxf = rxf;
- rxf->status = 0x00000001; /* '1' is flag value only. */
- rxf->link = 0; /* None yet. */
- /* This field unused by i82557, we use it as a consistency check. */
- rxf->rx_buf_addr = virt_to_bus(skb->tail);
-
- rxf->count = 0;
- rxf->size = PKT_BUF_SZ;
- }
- /* Mark the last entry as end-of-list. */
- last_rxf->status = 0xC0000002; /* '2' is flag value only. */
- sp->last_rxf = last_rxf;
-}
-
-static void speedo_tx_timeout(struct device *dev)
-{
- struct speedo_private *sp = (struct speedo_private *)dev->priv;
- int ioaddr = dev->base_addr;
-
- printk(KERN_WARNING "%s: Transmit timed out: status %4.4x "
- "command %4.4x.\n",
- dev->name, inw(ioaddr + SCBStatus), inw(ioaddr + SCBCmd));
+ MOD_INC_USE_COUNT;
- if ((inw(ioaddr + SCBStatus) & 0x00C0) != 0x0080) {
- printk(KERN_WARNING "%s: Trying to restart the transmitter...\n",
- dev->name);
- outl(virt_to_bus(&sp->tx_ring[sp->dirty_tx % TX_RING_SIZE]),
- ioaddr + SCBPointer);
- outw(CU_START, ioaddr + SCBCmd);
- } else {
- outw(DRVR_INT, ioaddr + SCBCmd);
- }
- /* Reset the MII transceiver, suggested by Fred Young @ scalable.com. */
- if ((sp->phy[0] & 0x8000) == 0) {
- int phy_addr = sp->phy[0] & 0x1f;
- mdio_write(ioaddr, phy_addr, 0, 0x0400);
- mdio_write(ioaddr, phy_addr, 1, 0x0000);
- mdio_write(ioaddr, phy_addr, 4, 0x0000);
- mdio_write(ioaddr, phy_addr, 0, 0x8000);
- }
- sp->stats.tx_errors++;
- dev->trans_start = jiffies;
- return;
+ return 0;
}
static int
-speedo_start_xmit(struct sk_buff *skb, struct device *dev)
+net_send_packet(struct sk_buff *skb, struct device *dev)
{
- struct speedo_private *sp = (struct speedo_private *)dev->priv;
+ struct net_local *lp = (struct net_local *)dev->priv;
int ioaddr = dev->base_addr;
- int entry;
- /* Block a timer-based transmit from overlapping. This could better be
- done with atomic_swap(1, dev->tbusy), but set_bit() works as well.
- If this ever occurs the queue layer is doing something evil! */
- if (test_and_set_bit(0, (void*)&dev->tbusy) != 0) {
+ if (dev->tbusy) {
+ /* If we get here, some higher level has decided we are broken.
+ There should really be a "kick me" function call instead. */
int tickssofar = jiffies - dev->trans_start;
- if (tickssofar < TX_TIMEOUT - 2)
+ if (tickssofar < 10)
return 1;
- if (tickssofar < TX_TIMEOUT) {
- /* Reap sent packets from the full Tx queue. */
- outw(DRVR_INT, ioaddr + SCBCmd);
- return 1;
- }
- speedo_tx_timeout(dev);
- return 0;
+ printk("%s: transmit timed out with status %04x, %s?\n", dev->name,
+ inw(ioaddr + STATUS), inb(ioaddr + TX_STATUS) & 0x80
+ ? "IRQ conflict" : "network cable problem");
+ printk("%s: timeout registers: %04x %04x %04x %04x %04x %04x %04x %04x.\n",
+ dev->name, inw(ioaddr + 0), inw(ioaddr + 2), inw(ioaddr + 4),
+ inw(ioaddr + 6), inw(ioaddr + 8), inw(ioaddr + 10),
+ inw(ioaddr + 12), inw(ioaddr + 14));
+ lp->stats.tx_errors++;
+ /* ToDo: We should try to restart the adaptor... */
+ outw(0xffff, ioaddr + 24);
+ outw(0xffff, ioaddr + TX_STATUS);
+ outw(0xe85a, ioaddr + CONFIG_0);
+ outw(0x8100, ioaddr + TX_INTR);
+ dev->tbusy=0;
+ dev->trans_start = jiffies;
+ lp->tx_started = 0;
+ lp->tx_queue = 0;
+ lp->tx_queue_len = 0;
}
- /* Caution: the write order is important here, set the base address
- with the "ownership" bits last. */
-
- { /* Prevent interrupts from changing the Tx ring from underneath us. */
- unsigned long flags;
-
- save_flags(flags);
- cli();
- /* Calculate the Tx descriptor entry. */
- entry = sp->cur_tx++ % TX_RING_SIZE;
-
- sp->tx_skbuff[entry] = skb;
- /* Todo: be a little more clever about setting the interrupt bit. */
- sp->tx_ring[entry].status =
- (CmdSuspend | CmdTx | CmdTxFlex) << 16;
- sp->tx_ring[entry].link =
- virt_to_bus(&sp->tx_ring[sp->cur_tx % TX_RING_SIZE]);
- sp->tx_ring[entry].tx_desc_addr =
- virt_to_bus(&sp->tx_ring[entry].tx_buf_addr);
- /* The data region is always in one buffer descriptor, Tx FIFO
- threshold of 256. */
- sp->tx_ring[entry].count = 0x01208000;
- sp->tx_ring[entry].tx_buf_addr = virt_to_bus(skb->data);
- sp->tx_ring[entry].tx_buf_size = skb->len;
- /* Todo: perhaps leave the interrupt bit set if the Tx queue is more
- than half full. Argument against: we should be receiving packets
- and scavenging the queue. Argument for: if so, it shouldn't
- matter. */
- sp->last_cmd->command &= ~(CmdSuspend | CmdIntr);
- sp->last_cmd = (struct descriptor *)&sp->tx_ring[entry];
- /* Trigger the command unit resume. */
- wait_for_cmd_done(ioaddr + SCBCmd);
- outw(CU_RESUME, ioaddr + SCBCmd);
- restore_flags(flags);
+ /* If some higher layer thinks we've missed an tx-done interrupt
+ we are passed NULL. Caution: dev_tint() handles the cli()/sti()
+ itself. */
+ if (skb == NULL) {
+ dev_tint(dev);
+ return 0;
}
- /* Leave room for set_rx_mode() to fill two entries. */
- if (sp->cur_tx - sp->dirty_tx > TX_RING_SIZE - 3)
- sp->tx_full = 1;
- else
- dev->tbusy = 0;
-
- dev->trans_start = jiffies;
+ /* Block a timer-based transmit from overlapping. This could better be
+ done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */
+ if (set_bit(0, (void*)&dev->tbusy) != 0)
+ printk("%s: Transmitter access conflict.\n", dev->name);
+ else {
+ short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
+ unsigned char *buf = skb->data;
+
+ /* Turn off the possible Tx interrupts. */
+ outb(0x00, ioaddr + TX_INTR);
+
+ outw(length, ioaddr + DATAPORT);
+ outsw(ioaddr + DATAPORT, buf, (length + 1) >> 1);
+
+ lp->tx_queue++;
+ lp->tx_queue_len += length + 2;
+
+ if (lp->tx_started == 0) {
+ /* If the Tx is idle, always trigger a transmit. */
+ outb(0x80 | lp->tx_queue, ioaddr + TX_START);
+ lp->tx_queue = 0;
+ lp->tx_queue_len = 0;
+ dev->trans_start = jiffies;
+ lp->tx_started = 1;
+ dev->tbusy = 0;
+ } else if (lp->tx_queue_len < 4096 - 1502)
+ /* Yes, there is room for one more packet. */
+ dev->tbusy = 0;
+
+ /* Turn on Tx interrupts back on. */
+ outb(0x82, ioaddr + TX_INTR);
+ }
+ dev_kfree_skb (skb, FREE_WRITE);
return 0;
}
-
-/* The interrupt handler does all of the Rx thread work and cleans up
- after the Tx thread. */
-static void speedo_interrupt(int irq, void *dev_instance, struct pt_regs *regs)
+\f
+/* The typical workload of the driver:
+ Handle the network interface interrupts. */
+static void
+net_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
- struct device *dev = (struct device *)dev_instance;
- struct speedo_private *sp;
- int ioaddr, boguscnt = max_interrupt_work;
- unsigned short status;
+ struct device *dev = (struct device *)(irq2dev_map[irq]);
+ struct net_local *lp;
+ int ioaddr, status;
-#ifndef final_version
if (dev == NULL) {
- printk(KERN_ERR "speedo_interrupt(): irq %d for unknown device.\n", irq);
+ printk ("at1700_interrupt(): irq %d for unknown device.\n", irq);
return;
}
-#endif
+ dev->interrupt = 1;
ioaddr = dev->base_addr;
- sp = (struct speedo_private *)dev->priv;
-#ifndef final_version
- /* A lock to prevent simultaneous entry on SMP machines. */
- if (test_and_set_bit(0, (void*)&sp->in_interrupt)) {
- printk(KERN_ERR"%s: SMP simultaneous entry of an interrupt handler.\n",
- dev->name);
- return;
+ lp = (struct net_local *)dev->priv;
+ status = inw(ioaddr + TX_STATUS);
+ outw(status, ioaddr + TX_STATUS);
+
+ if (net_debug > 4)
+ printk("%s: Interrupt with status %04x.\n", dev->name, status);
+ if (status & 0xff00
+ || (inb(ioaddr + RX_MODE) & 0x40) == 0) { /* Got a packet(s). */
+ net_rx(dev);
}
- dev->interrupt = 1;
-#endif
-
- do {
- status = inw(ioaddr + SCBStatus);
- /* Acknowledge all of the current interrupt sources ASAP. */
- outw(status & 0xfc00, ioaddr + SCBStatus);
-
- if (speedo_debug > 4)
- printk(KERN_DEBUG "%s: interrupt status=%#4.4x.\n",
- dev->name, status);
-
- if ((status & 0xfc00) == 0)
- break;
-
- if (status & 0x4000) /* Packet received. */
- speedo_rx(dev);
-
- if (status & 0x1000) {
- if ((status & 0x003c) == 0x0028) /* No more Rx buffers. */
- outw(RX_RESUMENR, ioaddr + SCBCmd);
- else if ((status & 0x003c) == 0x0008) { /* No resources (why?!) */
- /* No idea of what went wrong. Restart the receiver. */
- outl(virt_to_bus(sp->rx_ringp[sp->cur_rx % RX_RING_SIZE]),
- ioaddr + SCBPointer);
- outw(RX_START, ioaddr + SCBCmd);
- }
- sp->stats.rx_errors++;
- }
-
- /* User interrupt, Command/Tx unit interrupt or CU not active. */
- if (status & 0xA400) {
- unsigned int dirty_tx = sp->dirty_tx;
-
- while (sp->cur_tx - dirty_tx > 0) {
- int entry = dirty_tx % TX_RING_SIZE;
- int status = sp->tx_ring[entry].status;
-
- if (speedo_debug > 5)
- printk(KERN_DEBUG " scavenge candidate %d status %4.4x.\n",
- entry, status);
- if ((status & 0x8000) == 0)
- break; /* It still hasn't been processed. */
- /* Free the original skb. */
- if (sp->tx_skbuff[entry]) {
- sp->stats.tx_packets++; /* Count only user packets. */
- dev_kfree_skb(sp->tx_skbuff[entry], FREE_WRITE);
- sp->tx_skbuff[entry] = 0;
- }
- dirty_tx++;
- }
-
-#ifndef final_version
- if (sp->cur_tx - dirty_tx > TX_RING_SIZE) {
- printk(KERN_ERR "out-of-sync dirty pointer, %d vs. %d,"
- " full=%d.\n",
- dirty_tx, sp->cur_tx, sp->tx_full);
- dirty_tx += TX_RING_SIZE;
- }
-#endif
-
- if (sp->tx_full && dev->tbusy
- && dirty_tx > sp->cur_tx - TX_RING_SIZE + 2) {
- /* The ring is no longer full, clear tbusy. */
- sp->tx_full = 0;
+ if (status & 0x00ff) {
+ if (status & 0x80) {
+ lp->stats.tx_packets++;
+ if (lp->tx_queue) {
+ outb(0x80 | lp->tx_queue, ioaddr + TX_START);
+ lp->tx_queue = 0;
+ lp->tx_queue_len = 0;
+ dev->trans_start = jiffies;
dev->tbusy = 0;
- mark_bh(NET_BH);
+ mark_bh(NET_BH); /* Inform upper layers. */
+ } else {
+ lp->tx_started = 0;
+ /* Turn on Tx interrupts off. */
+ outb(0x00, ioaddr + TX_INTR);
+ dev->tbusy = 0;
+ mark_bh(NET_BH); /* Inform upper layers. */
}
-
- sp->dirty_tx = dirty_tx;
- }
-
- if (--boguscnt < 0) {
- printk(KERN_ERR "%s: Too much work at interrupt, status=0x%4.4x.\n",
- dev->name, status);
- /* Clear all interrupt sources. */
- outl(0xfc00, ioaddr + SCBStatus);
- break;
}
- } while (1);
-
- if (speedo_debug > 3)
- printk(KERN_DEBUG "%s: exiting interrupt, status=%#4.4x.\n",
- dev->name, inw(ioaddr + SCBStatus));
+ }
dev->interrupt = 0;
- sp->in_interrupt = 0;
return;
}
-static int
-speedo_rx(struct device *dev)
+/* We have a good packet(s), get it/them out of the buffers. */
+static void
+net_rx(struct device *dev)
{
- struct speedo_private *sp = (struct speedo_private *)dev->priv;
- int entry = sp->cur_rx % RX_RING_SIZE;
- int status;
-
- if (speedo_debug > 4)
- printk(KERN_DEBUG " In speedo_rx().\n");
- /* If we own the next entry, it's a new packet. Send it up. */
- while ((status = sp->rx_ringp[entry]->status) & RX_COMPLETE) {
-
- if (speedo_debug > 4)
- printk(KERN_DEBUG " speedo_rx() status %8.8x len %d.\n", status,
- sp->rx_ringp[entry]->count & 0x3fff);
- if (status & 0x0200) {
- printk(KERN_ERR "%s: Ethernet frame overran the Rx buffer, "
- "status %8.8x!\n", dev->name, status);
- } else if ( ! (status & 0x2000)) {
- /* There was a fatal error. This *should* be impossible. */
- sp->stats.rx_errors++;
- printk(KERN_ERR "%s: Anomalous event in speedo_rx(), status %8.8x.\n",
- dev->name, status);
+ struct net_local *lp = (struct net_local *)dev->priv;
+ int ioaddr = dev->base_addr;
+ int boguscount = 5;
+
+ while ((inb(ioaddr + RX_MODE) & 0x40) == 0) {
+ ushort status = inw(ioaddr + DATAPORT);
+ ushort pkt_len = inw(ioaddr + DATAPORT);
+
+ if (net_debug > 4)
+ printk("%s: Rxing packet mode %02x status %04x.\n",
+ dev->name, inb(ioaddr + RX_MODE), status);
+#ifndef final_version
+ if (status == 0) {
+ outb(0x05, ioaddr + 14);
+ break;
+ }
+#endif
+
+ if ((status & 0xF0) != 0x20) { /* There was an error. */
+ lp->stats.rx_errors++;
+ if (status & 0x08) lp->stats.rx_length_errors++;
+ if (status & 0x04) lp->stats.rx_frame_errors++;
+ if (status & 0x02) lp->stats.rx_crc_errors++;
+ if (status & 0x01) lp->stats.rx_over_errors++;
} else {
- /* Malloc up new buffer, compatible with net-2e. */
- int pkt_len = sp->rx_ringp[entry]->count & 0x3fff;
+ /* Malloc up new buffer. */
struct sk_buff *skb;
- int rx_in_place = 0;
-
- /* Check if the packet is long enough to just accept without
- copying to a properly sized skbuff. */
- if (pkt_len > rx_copybreak) {
- struct sk_buff *newskb;
- char *temp;
-
- /* Pass up the skb already on the Rx ring. */
- skb = sp->rx_skbuff[entry];
- temp = skb_put(skb, pkt_len);
- if (bus_to_virt(sp->rx_ringp[entry]->rx_buf_addr) != temp)
- printk(KERN_ERR "%s: Warning -- the skbuff addresses do not match"
- " in speedo_rx: %8.8x vs. %p / %p.\n", dev->name,
- sp->rx_ringp[entry]->rx_buf_addr, skb->head, temp);
- /* Get a fresh skbuff to replace the filled one. */
- newskb = dev_alloc_skb(PKT_BUF_SZ + sizeof(struct RxFD));
-
- if (newskb) {
- struct RxFD *rxf;
- rx_in_place = 1;
- sp->rx_skbuff[entry] = newskb;
- newskb->dev = dev;
- rxf = sp->rx_ringp[entry] = (struct RxFD *)newskb->tail;
- skb_reserve(newskb, sizeof(struct RxFD));
- /* Unused by i82557, consistency check only. */
- rxf->rx_buf_addr = virt_to_bus(newskb->tail);
- rxf->status = 0x00000001;
- } else /* No memory, drop the packet. */
- skb = 0;
- } else
- skb = dev_alloc_skb(pkt_len + 2);
+
+ if (pkt_len > 1550) {
+ printk("%s: The AT1700 claimed a very large packet, size %d.\n",
+ dev->name, pkt_len);
+ /* Prime the FIFO and then flush the packet. */
+ inw(ioaddr + DATAPORT); inw(ioaddr + DATAPORT);
+ outb(0x05, ioaddr + 14);
+ lp->stats.rx_errors++;
+ break;
+ }
+ skb = dev_alloc_skb(pkt_len+3);
if (skb == NULL) {
- int i;
- printk(KERN_ERR "%s: Memory squeeze, deferring packet.\n", dev->name);
- /* Check that at least two ring entries are free.
- If not, free one and mark stats->rx_dropped++. */
- /* ToDo: This is not correct!!!! We should count the number
- of linked-in Rx buffer to very that we have at least two
- remaining. */
- for (i = 0; i < RX_RING_SIZE; i++)
- if (! ((sp->rx_ringp[(entry+i) % RX_RING_SIZE]->status)
- & RX_COMPLETE))
- break;
-
- if (i > RX_RING_SIZE -2) {
- sp->stats.rx_dropped++;
- sp->rx_ringp[entry]->status = 0;
- sp->cur_rx++;
- }
+ printk("%s: Memory squeeze, dropping packet (len %d).\n",
+ dev->name, pkt_len);
+ /* Prime the FIFO and then flush the packet. */
+ inw(ioaddr + DATAPORT); inw(ioaddr + DATAPORT);
+ outb(0x05, ioaddr + 14);
+ lp->stats.rx_dropped++;
break;
}
skb->dev = dev;
- if (! rx_in_place) {
- skb_reserve(skb, 2); /* 16 byte align the data fields */
-#if defined(__i386) && notyet
- /* Packet is in one chunk -- we can copy + cksum. */
- eth_io_copy_and_sum(skb, bus_to_virt(sp->rx_ringp[entry]->rx_buf_addr),
- pkt_len, 0);
-#else
- memcpy(skb_put(skb, pkt_len),
- bus_to_virt(sp->rx_ringp[entry]->rx_buf_addr), pkt_len);
-#endif
- }
- skb->protocol = eth_type_trans(skb, dev);
+ skb_reserve(skb,2);
+
+ insw(ioaddr + DATAPORT, skb_put(skb,pkt_len), (pkt_len + 1) >> 1);
+ skb->protocol=eth_type_trans(skb, dev);
netif_rx(skb);
- sp->stats.rx_packets++;
+ lp->stats.rx_packets++;
}
+ if (--boguscount <= 0)
+ break;
+ }
- /* ToDo: This is better than before, but should be checked. */
- {
- struct RxFD *rxf = sp->rx_ringp[entry];
- rxf->status = 0xC0000003; /* '3' for verification only */
- rxf->link = 0; /* None yet. */
- rxf->count = 0;
- rxf->size = PKT_BUF_SZ;
- sp->last_rxf->link = virt_to_bus(rxf);
- sp->last_rxf->status &= ~0xC0000000;
- sp->last_rxf = rxf;
- entry = (++sp->cur_rx) % RX_RING_SIZE;
+ /* If any worth-while packets have been received, dev_rint()
+ has done a mark_bh(NET_BH) for us and will work on them
+ when we get to the bottom-half routine. */
+ {
+ int i;
+ for (i = 0; i < 20; i++) {
+ if ((inb(ioaddr + RX_MODE) & 0x40) == 0x40)
+ break;
+ inw(ioaddr + DATAPORT); /* dummy status read */
+ outb(0x05, ioaddr + 14);
}
- }
- sp->last_rx_time = jiffies;
- return 0;
+ if (net_debug > 5)
+ printk("%s: Exint Rx packet with mode %02x after %d ticks.\n",
+ dev->name, inb(ioaddr + RX_MODE), i);
+ }
+ return;
}
-static int
-speedo_close(struct device *dev)
+/* The inverse routine to net_open(). */
+static int net_close(struct device *dev)
{
int ioaddr = dev->base_addr;
- struct speedo_private *sp = (struct speedo_private *)dev->priv;
- int i;
- dev->start = 0;
dev->tbusy = 1;
+ dev->start = 0;
- if (speedo_debug > 1)
- printk(KERN_DEBUG "%s: Shutting down ethercard, status was %4.4x.\n",
- dev->name, inw(ioaddr + SCBStatus));
-
- /* Shut off the media monitoring timer. */
- del_timer(&sp->timer);
-
- /* Disable interrupts, and stop the chip's Rx process. */
- outw(INT_MASK, ioaddr + SCBCmd);
- outw(INT_MASK | RX_ABORT, ioaddr + SCBCmd);
+ /* Set configuration register 0 to disable Tx and Rx. */
+ outb(0xda, ioaddr + CONFIG_0);
- free_irq(dev->irq, dev);
+ /* Update the statistics -- ToDo. */
- /* Free all the skbuffs in the Rx and Tx queues. */
- for (i = 0; i < RX_RING_SIZE; i++) {
- struct sk_buff *skb = sp->rx_skbuff[i];
- sp->rx_skbuff[i] = 0;
- /* Clear the Rx descriptors. */
- if (skb)
- dev_kfree_skb(skb, FREE_WRITE);
- }
+ /* Power-down the chip. Green, green, green! */
+ outb(0x00, ioaddr + CONFIG_1);
- for (i = 0; i < TX_RING_SIZE; i++) {
- struct sk_buff *skb = sp->tx_skbuff[i];
- sp->tx_skbuff[i] = 0;
- /* Clear the Tx descriptors. */
- if (skb)
- dev_kfree_skb(skb, FREE_WRITE);
- }
- if (sp->mc_setup_frm) {
- kfree(sp->mc_setup_frm);
- sp->mc_setup_frm_len = 0;
- }
-
- /* Print a few items for debugging. */
- if (speedo_debug > 3) {
- int phy_num = sp->phy[0] & 0x1f;
- printk(KERN_DEBUG "%s:Printing Rx ring (next to receive into %d).\n",
- dev->name, sp->cur_rx);
-
- for (i = 0; i < RX_RING_SIZE; i++)
- printk(KERN_DEBUG " Rx ring entry %d %8.8x.\n",
- i, (int)sp->rx_ringp[i]->status);
-
- for (i = 0; i < 5; i++)
- printk(KERN_DEBUG " PHY index %d register %d is %4.4x.\n",
- phy_num, i, mdio_read(ioaddr, phy_num, i));
- for (i = 21; i < 26; i++)
- printk(KERN_DEBUG " PHY index %d register %d is %4.4x.\n",
- phy_num, i, mdio_read(ioaddr, phy_num, i));
- }
MOD_DEC_USE_COUNT;
return 0;
}
-/* The Speedo-3 has an especially awkward and unusable method of getting
- statistics out of the chip. It takes an unpredictable length of time
- for the dump-stats command to complete. To avoid a busy-wait loop we
- update the stats with the previous dump results, and then trigger a
- new dump.
-
- These problems are mitigated by the current /proc implementation, which
- calls this routine first to judge the output length, and then to emit the
- output.
-
- Oh, and incoming frames are dropped while executing dump-stats!
- */
+/* Get the current statistics.
+ This may be called with the card open or closed.
+ There are no on-chip counters, so this function is trivial.
+*/
static struct enet_statistics *
-speedo_get_stats(struct device *dev)
+net_get_stats(struct device *dev)
{
- struct speedo_private *sp = (struct speedo_private *)dev->priv;
- int ioaddr = dev->base_addr;
-
- if (sp->lstats.done_marker == 0xA007) { /* Previous dump finished */
- sp->stats.tx_aborted_errors += sp->lstats.tx_coll16_errs;
- sp->stats.tx_window_errors += sp->lstats.tx_late_colls;
- sp->stats.tx_fifo_errors += sp->lstats.tx_underruns;
- sp->stats.tx_fifo_errors += sp->lstats.tx_lost_carrier;
- /*sp->stats.tx_deferred += sp->lstats.tx_deferred;*/
- sp->stats.collisions += sp->lstats.tx_total_colls;
- sp->stats.rx_crc_errors += sp->lstats.rx_crc_errs;
- sp->stats.rx_frame_errors += sp->lstats.rx_align_errs;
- sp->stats.rx_over_errors += sp->lstats.rx_resource_errs;
- sp->stats.rx_fifo_errors += sp->lstats.rx_overrun_errs;
- sp->stats.rx_length_errors += sp->lstats.rx_runt_errs;
- sp->lstats.done_marker = 0x0000;
- if (dev->start) {
- wait_for_cmd_done(ioaddr + SCBCmd);
- outw(CU_DUMPSTATS, ioaddr + SCBCmd);
- }
- }
- return &sp->stats;
+ struct net_local *lp = (struct net_local *)dev->priv;
+ return &lp->stats;
}
-static int speedo_ioctl(struct device *dev, struct ifreq *rq, int cmd)
+/*
+ Set the multicast/promiscuous mode for this adaptor.
+*/
+
+/* The little-endian AUTODIN II ethernet CRC calculation.
+ N.B. Do not use for bulk data, use a table-based routine instead.
+ This is common code and should be moved to net/core/crc.c */
+static unsigned const ethernet_polynomial_le = 0xedb88320U;
+static inline unsigned ether_crc_le(int length, unsigned char *data)
{
- struct speedo_private *sp = (struct speedo_private *)dev->priv;
- int ioaddr = dev->base_addr;
- u16 *data = (u16 *)&rq->ifr_data;
- int phy = sp->phy[0] & 0x1f;
-
- switch(cmd) {
- case SIOCDEVPRIVATE: /* Get the address of the PHY in use. */
- data[0] = phy;
- case SIOCDEVPRIVATE+1: /* Read the specified MII register. */
- data[3] = mdio_read(ioaddr, data[0], data[1]);
- return 0;
- case SIOCDEVPRIVATE+2: /* Write the specified MII register */
- if (!suser())
- return -EPERM;
- mdio_write(ioaddr, data[0], data[1], data[2]);
- return 0;
- default:
- return -EOPNOTSUPP;
+ unsigned int crc = 0xffffffff; /* Initial value. */
+ while(--length >= 0) {
+ unsigned char current_octet = *data++;
+ int bit;
+ for (bit = 8; --bit >= 0; current_octet >>= 1) {
+ if ((crc ^ current_octet) & 1) {
+ crc >>= 1;
+ crc ^= ethernet_polynomial_le;
+ } else
+ crc >>= 1;
+ }
}
+ return crc;
}
-/* Set or clear the multicast filter for this adaptor.
- This is very ugly with Intel chips -- we usually have to execute an
- entire configuration command, plus process a multicast command.
- This is complicated. We must put a large configuration command and
- an arbitrarily-sized multicast command in the transmit list.
- To minimize the disruption -- the previous command might have already
- loaded the link -- we convert the current command block, normally a Tx
- command, into a no-op and link it to the new command.
-*/
static void
set_rx_mode(struct device *dev)
{
- struct speedo_private *sp = (struct speedo_private *)dev->priv;
int ioaddr = dev->base_addr;
- char new_rx_mode;
- unsigned long flags;
- int entry, i;
-
- if (dev->flags & IFF_PROMISC) { /* Set promiscuous. */
- new_rx_mode = 3;
- } else if ((dev->flags & IFF_ALLMULTI) ||
- dev->mc_count > multicast_filter_limit) {
- new_rx_mode = 1;
- } else
- new_rx_mode = 0;
-
- if (sp->cur_tx - sp->dirty_tx >= TX_RING_SIZE - 1) {
- /* The Tx ring is full -- don't add anything! Presumably the new mode
- is in config_cmd_data and will be added anyway. */
- sp->rx_mode = -1;
- return;
- }
-
- if (new_rx_mode != sp->rx_mode) {
- /* We must change the configuration. Construct a CmdConfig frame. */
- memcpy(sp->config_cmd_data, basic_config_cmd,sizeof(basic_config_cmd));
- sp->config_cmd_data[1] = (txfifo << 4) | rxfifo;
- sp->config_cmd_data[4] = rxdmacount;
- sp->config_cmd_data[5] = txdmacount + 0x80;
- sp->config_cmd_data[15] = (new_rx_mode & 2) ? 0x49 : 0x48;
- sp->config_cmd_data[19] = sp->full_duplex ? 0xC0 : 0x80;
- sp->config_cmd_data[21] = (new_rx_mode & 1) ? 0x0D : 0x05;
- if (sp->phy[0] & 0x8000) { /* Use the AUI port instead. */
- sp->config_cmd_data[15] |= 0x80;
- sp->config_cmd_data[8] = 0;
- }
- save_flags(flags);
- cli();
- /* Fill the "real" tx_ring frame with a no-op and point it to us. */
- entry = sp->cur_tx++ % TX_RING_SIZE;
- sp->tx_skbuff[entry] = 0; /* Nothing to free. */
- sp->tx_ring[entry].status = CmdNOp << 16;
- sp->tx_ring[entry].link = virt_to_bus(&sp->config_cmd);
- sp->config_cmd.status = 0;
- sp->config_cmd.command = CmdSuspend | CmdConfigure;
- sp->config_cmd.link =
- virt_to_bus(&(sp->tx_ring[sp->cur_tx % TX_RING_SIZE]));
- sp->last_cmd->command &= ~CmdSuspend;
- /* Immediately trigger the command unit resume. */
- wait_for_cmd_done(ioaddr + SCBCmd);
- outw(CU_RESUME, ioaddr + SCBCmd);
- sp->last_cmd = &sp->config_cmd;
- restore_flags(flags);
- if (speedo_debug > 5) {
- int i;
- printk(KERN_DEBUG " CmdConfig frame in entry %d.\n", entry);
- for(i = 0; i < 32; i++)
- printk(" %2.2x", ((unsigned char *)&sp->config_cmd)[i]);
- printk(".\n");
- }
- }
-
- if (new_rx_mode == 0 && dev->mc_count < 3) {
- /* The simple case of 0-2 multicast list entries occurs often, and
- fits within one tx_ring[] entry. */
- u16 *setup_params, *eaddrs;
- struct dev_mc_list *mclist;
-
- save_flags(flags);
- cli();
- entry = sp->cur_tx++ % TX_RING_SIZE;
- sp->tx_skbuff[entry] = 0;
- sp->tx_ring[entry].status = (CmdSuspend | CmdMulticastList) << 16;
- sp->tx_ring[entry].link =
- virt_to_bus(&sp->tx_ring[sp->cur_tx % TX_RING_SIZE]);
- sp->tx_ring[entry].tx_desc_addr = 0; /* Really MC list count. */
- setup_params = (u16 *)&sp->tx_ring[entry].tx_desc_addr;
- *setup_params++ = dev->mc_count*6;
- /* Fill in the multicast addresses. */
- for (i = 0, mclist = dev->mc_list; i < dev->mc_count;
- i++, mclist = mclist->next) {
- eaddrs = (u16 *)mclist->dmi_addr;
- *setup_params++ = *eaddrs++;
- *setup_params++ = *eaddrs++;
- *setup_params++ = *eaddrs++;
- }
+ struct net_local *lp = (struct net_local *)dev->priv;
+ unsigned char mc_filter[8]; /* Multicast hash filter */
+ long flags;
+ int i;
- sp->last_cmd->command &= ~CmdSuspend;
- /* Immediately trigger the command unit resume. */
- wait_for_cmd_done(ioaddr + SCBCmd);
- outw(CU_RESUME, ioaddr + SCBCmd);
- sp->last_cmd = (struct descriptor *)&sp->tx_ring[entry];
- restore_flags(flags);
- } else if (new_rx_mode == 0) {
- /* This does not work correctly, but why not? */
+ if (dev->flags & IFF_PROMISC) {
+ /* Unconditionally log net taps. */
+ printk("%s: Promiscuous mode enabled.\n", dev->name);
+ memset(mc_filter, 0xff, sizeof(mc_filter));
+ outb(3, ioaddr + RX_MODE); /* Enable promiscuous mode */
+ } else if (dev->mc_count > MC_FILTERBREAK
+ || (dev->flags & IFF_ALLMULTI)) {
+ /* Too many to filter perfectly -- accept all multicasts. */
+ memset(mc_filter, 0xff, sizeof(mc_filter));
+ outb(2, ioaddr + RX_MODE); /* Use normal mode. */
+ } else if (dev->mc_count == 0) {
+ memset(mc_filter, 0x00, sizeof(mc_filter));
+ outb(1, ioaddr + RX_MODE); /* Ignore almost all multicasts. */
+ } else {
struct dev_mc_list *mclist;
- u16 *eaddrs;
- struct descriptor *mc_setup_frm = sp->mc_setup_frm;
- u16 *setup_params;
int i;
- if (sp->mc_setup_frm_len < 10 + dev->mc_count*6
- || sp->mc_setup_frm == NULL) {
- /* Allocate a new frame, 10bytes + addrs, with a few
- extra entries for growth. */
- if (sp->mc_setup_frm)
- kfree(sp->mc_setup_frm);
- sp->mc_setup_frm_len = 10 + dev->mc_count*6 + 24;
- sp->mc_setup_frm = kmalloc(sp->mc_setup_frm_len, GFP_ATOMIC);
- if (sp->mc_setup_frm == NULL) {
- printk(KERN_ERR "%s: Failed to allocate a setup frame.\n", dev->name);
- sp->rx_mode = -1; /* We failed, try again. */
- return;
- }
- }
- mc_setup_frm = sp->mc_setup_frm;
- /* Construct the new setup frame. */
- if (speedo_debug > 1)
- printk(KERN_DEBUG "%s: Constructing a setup frame at %p, "
- "%d bytes.\n",
- dev->name, sp->mc_setup_frm, sp->mc_setup_frm_len);
- mc_setup_frm->status = 0;
- mc_setup_frm->command = CmdSuspend | CmdIntr | CmdMulticastList;
- /* Link set below. */
- setup_params = (u16 *)mc_setup_frm->params;
- *setup_params++ = dev->mc_count*6;
- /* Fill in the multicast addresses. */
- for (i = 0, mclist = dev->mc_list; i < dev->mc_count;
- i++, mclist = mclist->next) {
- eaddrs = (u16 *)mclist->dmi_addr;
- *setup_params++ = *eaddrs++;
- *setup_params++ = *eaddrs++;
- *setup_params++ = *eaddrs++;
- }
-
- /* Disable interrupts while playing with the Tx Cmd list. */
- save_flags(flags);
- cli();
- entry = sp->cur_tx++ % TX_RING_SIZE;
-
- if (speedo_debug > 5)
- printk(" CmdMCSetup frame length %d in entry %d.\n",
- dev->mc_count, entry);
-
- /* Change the command to a NoOp, pointing to the CmdMulti command. */
- sp->tx_skbuff[entry] = 0;
- sp->tx_ring[entry].status = CmdNOp << 16;
- sp->tx_ring[entry].link = virt_to_bus(mc_setup_frm);
-
- /* Set the link in the setup frame. */
- mc_setup_frm->link =
- virt_to_bus(&(sp->tx_ring[sp->cur_tx % TX_RING_SIZE]));
-
- sp->last_cmd->command &= ~CmdSuspend;
- /* Immediately trigger the command unit resume. */
- wait_for_cmd_done(ioaddr + SCBCmd);
- outw(CU_RESUME, ioaddr + SCBCmd);
- sp->last_cmd = mc_setup_frm;
- restore_flags(flags);
- if (speedo_debug > 1)
- printk(KERN_DEBUG "%s: Last command at %p is %4.4x.\n",
- dev->name, sp->last_cmd, sp->last_cmd->command);
+ memset(mc_filter, 0, sizeof(mc_filter));
+ for (i = 0, mclist = dev->mc_list; mclist && i < dev->mc_count;
+ i++, mclist = mclist->next)
+ set_bit(ether_crc_le(ETH_ALEN, mclist->dmi_addr) & 0x3f,
+ mc_filter);
}
- sp->rx_mode = new_rx_mode;
+ save_flags(flags);
+ cli();
+ if (memcmp(mc_filter, lp->mc_filter, sizeof(mc_filter))) {
+ int saved_bank = inb(ioaddr + CONFIG_1);
+ /* Switch to bank 1 and set the multicast table. */
+ outb(0xe4, ioaddr + CONFIG_1);
+ for (i = 0; i < 8; i++)
+ outb(mc_filter[i], ioaddr + 8 + i);
+ memcpy(lp->mc_filter, mc_filter, sizeof(mc_filter));
+ outb(saved_bank, ioaddr + CONFIG_1);
+ }
+ restore_flags(flags);
+ return;
}
-\f
-#ifdef MODULE
-int
-init_module(void)
-{
- int cards_found;
+#ifdef MODULE
+static char devicename[9] = { 0, };
+static struct device dev_at1700 = {
+ devicename, /* device name is inserted by linux/drivers/net/net_init.c */
+ 0, 0, 0, 0,
+ 0, 0,
+ 0, 0, 0, NULL, at1700_probe };
- if (debug >= 0)
- speedo_debug = debug;
- if (speedo_debug)
- printk(KERN_INFO "%s", version);
+static int io = 0x260;
+static int irq = 0;
- root_speedo_dev = NULL;
- cards_found = eepro100_init(NULL);
- return cards_found ? 0 : -ENODEV;
+int init_module(void)
+{
+ if (io == 0)
+ printk("at1700: You should not use auto-probing with insmod!\n");
+ dev_at1700.base_addr = io;
+ dev_at1700.irq = irq;
+ if (register_netdev(&dev_at1700) != 0) {
+ printk("at1700: register_netdev() returned non-zero.\n");
+ return -EIO;
+ }
+ return 0;
}
void
cleanup_module(void)
{
- struct device *next_dev;
-
- /* No need to check MOD_IN_USE, as sys_delete_module() checks. */
- while (root_speedo_dev) {
- next_dev = ((struct speedo_private *)root_speedo_dev->priv)->next_module;
- unregister_netdev(root_speedo_dev);
- release_region(root_speedo_dev->base_addr, SPEEDO3_TOTAL_SIZE);
- kfree(root_speedo_dev);
- root_speedo_dev = next_dev;
- }
-}
-#else /* not MODULE */
-int eepro100_probe(struct device *dev)
-{
- int cards_found = 0;
-
- cards_found = eepro100_init(dev);
-
- if (speedo_debug > 0 && cards_found)
- printk(version);
-
- return cards_found ? 0 : -ENODEV;
+ unregister_netdev(&dev_at1700);
+ kfree(dev_at1700.priv);
+ dev_at1700.priv = NULL;
+
+ /* If we don't do this, we can't re-insmod it later. */
+ free_irq(dev_at1700.irq, NULL);
+ irq2dev_map[dev_at1700.irq] = NULL;
+ release_region(dev_at1700.base_addr, AT1700_IO_EXTENT);
}
-#endif /* MODULE */
+#endif /* MODULE */
\f
/*
* Local variables:
- * compile-command: "gcc -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -c eepro100.c `[ -f /usr/include/linux/modversions.h ] && echo -DMODVERSIONS`"
- * c-indent-level: 4
- * c-basic-offset: 4
+ * compile-command: "gcc -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c at1700.c"
+ * alt-compile-command: "gcc -DMODVERSIONS -DMODULE -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -c at1700.c"
* tab-width: 4
+ * c-basic-offset: 4
+ * c-indent-level: 4
* End:
*/
--- /dev/null
+/********************************************************************
+ *
+ * Linux ThunderLAN Driver
+ *
+ * tlan.c
+ * by James Banks, james.banks@caldera.com
+ *
+ * (C) 1997-1998 Caldera, Inc.
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU Public License, incorporated herein by reference.
+ *
+ ** This file is best viewed/edited with columns>=132.
+ *
+ ** Useful (if not required) reading:
+ *
+ * Texas Instruments, ThunderLAN Programmer's Guide,
+ * TI Literature Number SPWU013A
+ * available in PDF format from www.ti.com
+ * Level One, LXT901 and LXT970 Data Sheets
+ * available in PDF format from www.level1.com
+ * National Semiconductor, DP83840A Data Sheet
+ * available in PDF format from www.national.com
+ * Microchip Technology, 24C01A/02A/04A Data Sheet
+ * available in PDF format from www.microchip.com
+ *
+ ********************************************************************/
+
+
+#include <linux/module.h>
+
+#include "tlan.h"
+
+#include <linux/bios32.h>
+#include <linux/ioport.h>
+#include <linux/pci.h>
+#include <linux/etherdevice.h>
+#include <linux/delay.h>
+
+
+
+typedef u32 (TLanIntVectorFunc)( struct device *, u16 );
+
+
+#ifdef MODULE
+
+static struct device *TLanDevices = NULL;
+static int TLanDevicesInstalled = 0;
+
+#endif
+
+
+static int debug = 0;
+static int aui = 0;
+static int sa_int = 0;
+static int bbuf = 0;
+static int duplex = 0;
+static int speed = 0;
+static u8 *TLanPadBuffer;
+static char TLanSignature[] = "TLAN";
+static int TLanVersionMajor = 0;
+static int TLanVersionMinor = 42;
+
+
+static TLanAdapterEntry TLanAdapterList[] = {
+ { PCI_VENDOR_ID_COMPAQ,
+ PCI_DEVICE_ID_NETELLIGENT_10,
+ "Compaq Netelligent 10",
+ TLAN_ADAPTER_ACTIVITY_LED
+ },
+ { PCI_VENDOR_ID_COMPAQ,
+ PCI_DEVICE_ID_NETELLIGENT_10_100,
+ "Compaq Netelligent 10/100",
+ TLAN_ADAPTER_ACTIVITY_LED
+ },
+ { PCI_VENDOR_ID_COMPAQ,
+ PCI_DEVICE_ID_NETFLEX_3P_INTEGRATED,
+ "Compaq Integrated NetFlex-3/P",
+ TLAN_ADAPTER_NONE
+ },
+ { PCI_VENDOR_ID_COMPAQ,
+ PCI_DEVICE_ID_NETFLEX_3P,
+ "Compaq NetFlex-3/P",
+ TLAN_ADAPTER_UNMANAGED_PHY | TLAN_ADAPTER_BIT_RATE_PHY
+ },
+ { PCI_VENDOR_ID_COMPAQ,
+ PCI_DEVICE_ID_NETFLEX_3P_BNC,
+ "Compaq NetFlex-3/P",
+ TLAN_ADAPTER_NONE
+ },
+ { PCI_VENDOR_ID_COMPAQ,
+ PCI_DEVICE_ID_NETELLIGENT_10_100_PROLIANT,
+ "Compaq ProLiant Netelligent 10/100",
+ TLAN_ADAPTER_NONE
+ },
+ { PCI_VENDOR_ID_COMPAQ,
+ PCI_DEVICE_ID_NETELLIGENT_10_100_DUAL,
+ "Compaq Dual Port Netelligent 10/100",
+ TLAN_ADAPTER_NONE
+ },
+ { PCI_VENDOR_ID_COMPAQ,
+ PCI_DEVICE_ID_DESKPRO_4000_5233MMX,
+ "Compaq Integrated Netelligent 10/100",
+ TLAN_ADAPTER_NONE
+ },
+ { PCI_VENDOR_ID_OLICOM,
+ PCI_DEVICE_ID_OC_2326,
+ "Olicom OC-2326",
+ TLAN_ADAPTER_USE_INTERN_10
+ },
+ { 0,
+ 0,
+ NULL,
+ 0
+ } /* End of List */
+};
+
+
+static int TLan_PciProbe( u8 *, u8 *, u8 *, u8 *, u32 *, u32 * );
+static int TLan_Init( struct device * );
+static int TLan_Open(struct device *dev);
+static int TLan_StartTx(struct sk_buff *, struct device *);
+static void TLan_HandleInterrupt(int, void *, struct pt_regs *);
+static int TLan_Close(struct device *);
+static struct net_device_stats *TLan_GetStats( struct device * );
+static void TLan_SetMulticastList( struct device * );
+
+static u32 TLan_HandleInvalid( struct device *, u16 );
+static u32 TLan_HandleTxEOF( struct device *, u16 );
+static u32 TLan_HandleStatOverflow( struct device *, u16 );
+static u32 TLan_HandleRxEOF( struct device *, u16 );
+static u32 TLan_HandleDummy( struct device *, u16 );
+static u32 TLan_HandleTxEOC( struct device *, u16 );
+static u32 TLan_HandleStatusCheck( struct device *, u16 );
+static u32 TLan_HandleRxEOC( struct device *, u16 );
+
+static void TLan_Timer( unsigned long );
+
+static void TLan_ResetLists( struct device * );
+static void TLan_FreeLists( struct device * );
+static void TLan_PrintDio( u16 );
+static void TLan_PrintList( TLanList *, char *, int );
+static void TLan_ReadAndClearStats( struct device *, int );
+static void TLan_ResetAdapter( struct device * );
+static void TLan_FinishReset( struct device * );
+static void TLan_SetMac( struct device *, int areg, char *mac );
+
+static void TLan_PhyPrint( struct device * );
+static void TLan_PhyDetect( struct device * );
+static void TLan_PhyPowerDown( struct device * );
+static void TLan_PhyPowerUp( struct device * );
+static void TLan_PhyReset( struct device * );
+static void TLan_PhyStartLink( struct device * );
+static void TLan_PhyFinishAutoNeg( struct device * );
+/*
+static int TLan_PhyNop( struct device * );
+static int TLan_PhyInternalCheck( struct device * );
+static int TLan_PhyInternalService( struct device * );
+static int TLan_PhyDp83840aCheck( struct device * );
+*/
+
+static int TLan_MiiReadReg( struct device *, u16, u16, u16 * );
+static void TLan_MiiSendData( u16, u32, unsigned );
+static void TLan_MiiSync( u16 );
+static void TLan_MiiWriteReg( struct device *, u16, u16, u16 );
+
+static void TLan_EeSendStart( u16 );
+static int TLan_EeSendByte( u16, u8, int );
+static void TLan_EeReceiveByte( u16, u8 *, int );
+static int TLan_EeReadByte( struct device *, u8, u8 * );
+
+
+static TLanIntVectorFunc *TLanIntVector[TLAN_INT_NUMBER_OF_INTS] = {
+ TLan_HandleInvalid,
+ TLan_HandleTxEOF,
+ TLan_HandleStatOverflow,
+ TLan_HandleRxEOF,
+ TLan_HandleDummy,
+ TLan_HandleTxEOC,
+ TLan_HandleStatusCheck,
+ TLan_HandleRxEOC
+};
+
+static inline void
+TLan_SetTimer( struct device *dev, u32 ticks, u32 type )
+{
+ TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+
+ cli();
+ if ( priv->timer.function != NULL ) {
+ return;
+ }
+ priv->timer.function = &TLan_Timer;
+ sti();
+
+ priv->timer.data = (unsigned long) dev;
+ priv->timer.expires = jiffies + ticks;
+ priv->timerSetAt = jiffies;
+ priv->timerType = type;
+ add_timer( &priv->timer );
+
+} /* TLan_SetTimer */
+
+
+/*****************************************************************************
+******************************************************************************
+
+ ThunderLAN Driver Primary Functions
+
+ These functions are more or less common to all Linux network drivers.
+
+******************************************************************************
+*****************************************************************************/
+
+
+#ifdef MODULE
+
+ /***************************************************************
+ * init_module
+ *
+ * Returns:
+ * 0 if module installed ok, non-zero if not.
+ * Parms:
+ * None
+ *
+ * This function begins the setup of the driver creating a
+ * pad buffer, finding all TLAN devices (matching
+ * TLanAdapterList entries), and creating and initializing a
+ * device structure for each adapter.
+ *
+ **************************************************************/
+
+extern int init_module(void)
+{
+ TLanPrivateInfo *priv;
+ u8 bus;
+ struct device *dev;
+ size_t dev_size;
+ u8 dfn;
+ u32 index;
+ int failed;
+ int found;
+ u32 io_base;
+ u8 irq;
+ u8 rev;
+
+ printk( "TLAN driver, v%d.%d, (C) 1997-8 Caldera, Inc.\n",
+ TLanVersionMajor,
+ TLanVersionMinor
+ );
+ TLanPadBuffer = (u8 *) kmalloc( TLAN_MIN_FRAME_SIZE,
+ ( GFP_KERNEL | GFP_DMA )
+ );
+ if ( TLanPadBuffer == NULL ) {
+ printk( "TLAN: Could not allocate memory for pad buffer.\n" );
+ return -ENOMEM;
+ }
+
+ memset( TLanPadBuffer, 0, TLAN_MIN_FRAME_SIZE );
+
+ dev_size = sizeof(struct device) + sizeof(TLanPrivateInfo);
+
+ while ( ( found = TLan_PciProbe( &bus, &dfn, &irq, &rev, &io_base, &index ) ) ) {
+ dev = (struct device *) kmalloc( dev_size, GFP_KERNEL );
+ if ( dev == NULL ) {
+ printk( "TLAN: Could not allocate memory for device.\n" );
+ continue;
+ }
+ memset( dev, 0, dev_size );
+
+ dev->priv = priv = ( (void *) dev ) + sizeof(struct device);
+ dev->name = priv->devName;
+ strcpy( priv->devName, " " );
+ dev->base_addr = io_base;
+ dev->irq = irq;
+ dev->init = TLan_Init;
+
+ priv->adapter = &TLanAdapterList[index];
+ priv->adapterRev = rev;
+ priv->aui = aui;
+ priv->duplex = duplex;
+ priv->speed = speed;
+ priv->sa_int = sa_int;
+ priv->debug = debug;
+
+ ether_setup( dev );
+
+ failed = register_netdev( dev );
+
+ if ( failed ) {
+ printk( "TLAN: Could not register device.\n" );
+ kfree( dev );
+ } else {
+ priv->nextDevice = TLanDevices;
+ TLanDevices = dev;
+ TLanDevicesInstalled++;
+ printk("TLAN: %s irq=%2d io=%04x, %s, Rev. %d\n",
+ dev->name,
+ (int) dev->irq,
+ (int) dev->base_addr,
+ priv->adapter->deviceLabel,
+ priv->adapterRev );
+ }
+ }
+
+ /* printk( "TLAN: Found %d device(s).\n", TLanDevicesInstalled ); */
+
+ return ( ( TLanDevicesInstalled >= 0 ) ? 0 : -ENODEV );
+
+} /* init_module */
+
+
+
+
+ /***************************************************************
+ * cleanup_module
+ *
+ * Returns:
+ * Nothing
+ * Parms:
+ * None
+ *
+ * Goes through the TLanDevices list and frees the device
+ * structs and memory associated with each device (lists
+ * and buffers). It also ureserves the IO port regions
+ * associated with this device.
+ *
+ **************************************************************/
+
+extern void cleanup_module(void)
+{
+ struct device *dev;
+ TLanPrivateInfo *priv;
+
+ while ( TLanDevicesInstalled ) {
+ dev = TLanDevices;
+ priv = (TLanPrivateInfo *) dev->priv;
+ if ( priv->dmaStorage ) {
+ kfree( priv->dmaStorage );
+ }
+ release_region( dev->base_addr, 0x10 );
+ unregister_netdev( dev );
+ TLanDevices = priv->nextDevice;
+ kfree( dev );
+ TLanDevicesInstalled--;
+ }
+ kfree( TLanPadBuffer );
+
+} /* cleanup_module */
+
+
+#else /* MODULE */
+
+
+
+
+ /***************************************************************
+ * tlan_probe
+ *
+ * Returns:
+ * 0 on success, error code on error
+ * Parms:
+ * dev device struct to use if adapter is
+ * found.
+ *
+ * The name is lower case to fit in with all the rest of
+ * the netcard_probe names. This function looks for a/
+ * another TLan based adapter, setting it up with the
+ * provided device struct if one is found.
+ *
+ **************************************************************/
+
+extern int tlan_probe( struct device *dev )
+{
+ TLanPrivateInfo *priv;
+ static int pad_allocated = 0;
+ int found;
+ u8 bus, dfn, irq, rev;
+ u32 io_base, index;
+
+ found = TLan_PciProbe( &bus, &dfn, &irq, &rev, &io_base, &index );
+
+ if ( ! found ) {
+ return -ENODEV;
+ }
+
+ dev->priv = kmalloc( sizeof(TLanPrivateInfo), GFP_KERNEL );
+
+ if ( dev->priv == NULL ) {
+ printk( "TLAN: Could not allocate memory for device.\n" );
+ return -ENOMEM;
+ }
+
+ memset( dev->priv, 0, sizeof(TLanPrivateInfo) );
+
+ if ( ! pad_allocated ) {
+ TLanPadBuffer = (u8 *) kmalloc( TLAN_MIN_FRAME_SIZE,
+// ( GFP_KERNEL | GFP_DMA )
+ ( GFP_KERNEL )
+ );
+ if ( TLanPadBuffer == NULL ) {
+ printk( "TLAN: Could not allocate memory for padding.\n" );
+ kfree( dev->priv );
+ return -ENOMEM;
+ } else {
+ pad_allocated = 1;
+ memset( TLanPadBuffer, 0, TLAN_MIN_FRAME_SIZE );
+ }
+ }
+
+ priv = (TLanPrivateInfo *) dev->priv;
+
+ dev->name = priv->devName;
+ strcpy( priv->devName, " " );
+
+ dev = init_etherdev( dev, sizeof(TLanPrivateInfo) );
+
+ dev->base_addr = io_base;
+ dev->irq = irq;
+
+
+ priv->adapter = &TLanAdapterList[index];
+ priv->adapterRev = rev;
+ priv->aui = dev->mem_start & 0x01;
+ priv->duplex = ( ( dev->mem_start & 0x0C ) == 0x0C ) ? 0 : dev->mem_start & 0x0C >> 2;
+ priv->speed = ( ( dev->mem_start & 0x30 ) == 0x30 ) ? 0 : dev->mem_start & 0x30 >> 4;
+ priv->sa_int = dev->mem_start & 0x02;
+ priv->debug = dev->mem_end;
+
+
+ printk("TLAN %d.%d: %s irq=%2d io=%04x, %s, Rev. %d\n",
+ TLanVersionMajor,
+ TLanVersionMinor,
+ dev->name,
+ (int) irq,
+ io_base,
+ priv->adapter->deviceLabel,
+ priv->adapterRev );
+
+ TLan_Init( dev );
+
+ return 0;
+
+} /* tlan_probe */
+
+
+#endif /* MODULE */
+
+
+
+
+ /***************************************************************
+ * TLan_PciProbe
+ *
+ * Returns:
+ * 1 if another TLAN card was found, 0 if not.
+ * Parms:
+ * pci_bus The PCI bus the card was found
+ * on.
+ * pci_dfn The PCI whatever the card was
+ * found at.
+ * pci_irq The IRQ of the found adapter.
+ * pci_rev The revision of the adapter.
+ * pci_io_base The first IO port used by the
+ * adapter.
+ * dl_ix The index in the device list
+ * of the adapter.
+ *
+ * This function searches for an adapter with PCI vendor
+ * and device IDs matching those in the TLanAdapterList.
+ * The function 'remembers' the last device it found,
+ * and so finds a new device (if anymore are to be found)
+ * each time the function is called. It then looks up
+ * pertinent PCI info and returns it to the caller.
+ *
+ **************************************************************/
+
+int TLan_PciProbe( u8 *pci_bus, u8 *pci_dfn, u8 *pci_irq, u8 *pci_rev, u32 *pci_io_base, u32 *dl_ix )
+{
+ static int dl_index = 0;
+ static int pci_index = 0;
+
+ int not_found;
+ u8 pci_latency;
+ u16 pci_command;
+ int reg;
+
+
+ if ( ! pcibios_present() ) {
+ printk( "TLAN: PCI Bios not present.\n" );
+ return 0;
+ }
+
+ for (; TLanAdapterList[dl_index].vendorId != 0; dl_index++) {
+
+ not_found = pcibios_find_device(
+ TLanAdapterList[dl_index].vendorId,
+ TLanAdapterList[dl_index].deviceId,
+ pci_index,
+ pci_bus,
+ pci_dfn
+ );
+
+ if ( ! not_found ) {
+
+ TLAN_DBG(
+ TLAN_DEBUG_GNRL,
+ "TLAN: found: Vendor Id = 0x%hx, Device Id = 0x%hx\n",
+ TLanAdapterList[dl_index].vendorId,
+ TLanAdapterList[dl_index].deviceId
+ );
+
+ pcibios_read_config_byte ( *pci_bus, *pci_dfn, PCI_REVISION_ID, pci_rev);
+ pcibios_read_config_byte ( *pci_bus, *pci_dfn, PCI_INTERRUPT_LINE, pci_irq);
+ pcibios_read_config_word ( *pci_bus, *pci_dfn, PCI_COMMAND, &pci_command);
+ pcibios_read_config_dword( *pci_bus, *pci_dfn, PCI_BASE_ADDRESS_0, pci_io_base);
+ pcibios_read_config_byte ( *pci_bus, *pci_dfn, PCI_LATENCY_TIMER, &pci_latency);
+
+ if (pci_latency < 0x10) {
+ pcibios_write_config_byte( *pci_bus, *pci_dfn, PCI_LATENCY_TIMER, 0xff);
+ TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN: Setting latency timer to max.\n");
+ }
+
+ for ( reg = PCI_BASE_ADDRESS_0; reg <= PCI_BASE_ADDRESS_5; reg +=4 ) {
+ pcibios_read_config_dword( *pci_bus, *pci_dfn, reg, pci_io_base);
+ if ((pci_command & PCI_COMMAND_IO) && (*pci_io_base & 0x3)) {
+ *pci_io_base &= PCI_BASE_ADDRESS_IO_MASK;
+ TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN: IO mapping is available at %x.\n", *pci_io_base);
+ break;
+ } else {
+ *pci_io_base = 0;
+ }
+ }
+
+ if ( *pci_io_base == 0 )
+ printk("TLAN: IO mapping not available, ignoring device.\n");
+
+ if ( ! ( pci_command & PCI_COMMAND_MASTER ) ) {
+ pcibios_write_config_word ( *pci_bus, *pci_dfn, PCI_COMMAND, pci_command | PCI_COMMAND_MASTER );
+ printk( "TLAN: Attempting to activate busmastering.\n" );
+ printk( "TLAN: You may need to set busmastering to on in the CMOS\n" );
+ printk( "TLAN: before this card will work.\n" );
+ *pci_io_base = 0;
+ }
+
+ pci_index++;
+
+ if ( *pci_io_base ) {
+ *dl_ix = dl_index;
+ return 1;
+ }
+
+ } else {
+ pci_index = 0;
+ }
+ }
+
+ return 0;
+
+} /* TLan_PciProbe */
+
+
+
+
+ /***************************************************************
+ * TLan_Init
+ *
+ * Returns:
+ * 0 on success, error code otherwise.
+ * Parms:
+ * dev The structure of the device to be
+ * init'ed.
+ *
+ * This function completes the initialization of the
+ * device structure and driver. It reserves the IO
+ * addresses, allocates memory for the lists and bounce
+ * buffers, retrieves the MAC address from the eeprom
+ * and assignes the device's methods.
+ *
+ **************************************************************/
+
+int TLan_Init( struct device *dev )
+{
+ int dma_size;
+ int err;
+ int i;
+ TLanPrivateInfo *priv;
+
+ priv = (TLanPrivateInfo *) dev->priv;
+
+ err = check_region( dev->base_addr, 0x10 );
+ if ( err ) {
+ printk( "TLAN: %s: Io port region 0x%lx size 0x%x in use.\n",
+ dev->name,
+ dev->base_addr,
+ 0x10 );
+ return -EIO;
+ }
+ request_region( dev->base_addr, 0x10, TLanSignature );
+
+ if ( bbuf ) {
+ dma_size = ( TLAN_NUM_RX_LISTS + TLAN_NUM_TX_LISTS )
+ * ( sizeof(TLanList) + TLAN_MAX_FRAME_SIZE );
+ } else {
+ dma_size = ( TLAN_NUM_RX_LISTS + TLAN_NUM_TX_LISTS )
+ * ( sizeof(TLanList) );
+ }
+
+ priv->dmaStorage = kmalloc( dma_size, GFP_KERNEL | GFP_DMA );
+ if ( priv->dmaStorage == NULL ) {
+ printk( "TLAN: Could not allocate lists and buffers for %s.\n",
+ dev->name );
+ return -ENOMEM;
+ }
+ memset( priv->dmaStorage, 0, dma_size );
+ priv->rxList = (TLanList *)
+ ( ( ( (u32) priv->dmaStorage ) + 7 ) & 0xFFFFFFF8 );
+ priv->txList = priv->rxList + TLAN_NUM_RX_LISTS;
+
+ if ( bbuf ) {
+ priv->rxBuffer = (u8 *) ( priv->txList + TLAN_NUM_TX_LISTS );
+ priv->txBuffer = priv->rxBuffer
+ + ( TLAN_NUM_RX_LISTS * TLAN_MAX_FRAME_SIZE );
+ }
+
+ err = 0;
+ for ( i = 0; i < 6 ; i++ )
+ err |= TLan_EeReadByte( dev,
+ (u8) 0x83 + i,
+ (u8 *) &dev->dev_addr[i] );
+ if ( err ) {
+ printk( "TLAN: %s: Error reading MAC from eeprom: %d\n",
+ dev->name,
+ err );
+ }
+
+ dev->addr_len = 6;
+
+ dev->open = &TLan_Open;
+ dev->hard_start_xmit = &TLan_StartTx;
+ dev->stop = &TLan_Close;
+ dev->get_stats = &TLan_GetStats;
+ dev->set_multicast_list = &TLan_SetMulticastList;
+
+
+ return 0;
+
+} /* TLan_Init */
+
+
+
+
+ /***************************************************************
+ * TLan_Open
+ *
+ * Returns:
+ * 0 on success, error code otherwise.
+ * Parms:
+ * dev Structure of device to be opened.
+ *
+ * This routine puts the driver and TLAN adapter in a
+ * state where it is ready to send and receive packets.
+ * It allocates the IRQ, resets and brings the adapter
+ * out of reset, and allows interrupts. It also delays
+ * the startup for autonegotiation or sends a Rx GO
+ * command to the adapter, as appropriate.
+ *
+ **************************************************************/
+
+int TLan_Open( struct device *dev )
+{
+ TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+ int err;
+
+ priv->tlanRev = TLan_DioRead8( dev->base_addr, TLAN_DEF_REVISION );
+ if ( priv->sa_int ) {
+ TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN: Using SA_INTERRUPT\n" );
+ err = request_irq( dev->irq, TLan_HandleInterrupt, SA_SHIRQ | SA_INTERRUPT, TLanSignature, dev );
+ } else {
+ err = request_irq( dev->irq, TLan_HandleInterrupt, SA_SHIRQ, TLanSignature, dev );
+ }
+ if ( err ) {
+ printk( "TLAN: Cannot open %s because IRQ %d is already in use.\n", dev->name, dev->irq );
+ return -EAGAIN;
+ }
+
+ MOD_INC_USE_COUNT;
+
+ dev->tbusy = 0;
+ dev->interrupt = 0;
+ dev->start = 1;
+
+ /* NOTE: It might not be necessary to read the stats before a
+ reset if you don't care what the values are.
+ */
+ TLan_ResetLists( dev );
+ TLan_ReadAndClearStats( dev, TLAN_IGNORE );
+ TLan_ResetAdapter( dev );
+
+ TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN: %s: Opened. TLAN Chip Rev: %x\n", dev->name, priv->tlanRev );
+
+ return 0;
+
+} /* TLan_Open */
+
+
+
+
+ /***************************************************************
+ * TLan_StartTx
+ *
+ * Returns:
+ * 0 on success, non-zero on failure.
+ * Parms:
+ * skb A pointer to the sk_buff containing the
+ * frame to be sent.
+ * dev The device to send the data on.
+ *
+ * This function adds a frame to the Tx list to be sent
+ * ASAP. First it verifies that the adapter is ready and
+ * there is room in the queue. Then it sets up the next
+ * available list, copies the frame to the corresponding
+ * buffer. If the adapter Tx channel is idle, it gives
+ * the adapter a Tx Go command on the list, otherwise it
+ * sets the forward address of the previous list to point
+ * to this one. Then it frees the sk_buff.
+ *
+ **************************************************************/
+
+int TLan_StartTx( struct sk_buff *skb, struct device *dev )
+{
+ TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+ TLanList *tail_list;
+ u8 *tail_buffer;
+ int pad;
+
+ if ( ! priv->phyOnline ) {
+ TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT: %s PHY is not ready\n", dev->name );
+ dev_kfree_skb( skb, FREE_WRITE );
+ return 0;
+ }
+
+ tail_list = priv->txList + priv->txTail;
+
+ if ( tail_list->cStat != TLAN_CSTAT_UNUSED ) {
+ TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT: %s is busy (Head=%d Tail=%d)\n", dev->name, priv->txHead, priv->txTail );
+ dev->tbusy = 1;
+ priv->txBusyCount++;
+ return 1;
+ }
+
+ tail_list->forward = 0;
+
+ if ( bbuf ) {
+ tail_buffer = priv->txBuffer + ( priv->txTail * TLAN_MAX_FRAME_SIZE );
+ memcpy( tail_buffer, skb->data, skb->len );
+ } else {
+ tail_list->buffer[0].address = virt_to_bus( skb->data );
+ tail_list->buffer[9].address = (u32) skb;
+ }
+
+ pad = TLAN_MIN_FRAME_SIZE - skb->len;
+
+ if ( pad > 0 ) {
+ tail_list->frameSize = (u16) skb->len + pad;
+ tail_list->buffer[0].count = (u32) skb->len;
+ tail_list->buffer[1].count = TLAN_LAST_BUFFER | (u32) pad;
+ tail_list->buffer[1].address = virt_to_bus( TLanPadBuffer );
+ } else {
+ tail_list->frameSize = (u16) skb->len;
+ tail_list->buffer[0].count = TLAN_LAST_BUFFER | (u32) skb->len;
+ tail_list->buffer[1].count = 0;
+ tail_list->buffer[1].address = 0;
+ }
+
+ cli();
+ tail_list->cStat = TLAN_CSTAT_READY;
+ if ( ! priv->txInProgress ) {
+ priv->txInProgress = 1;
+ outw( 0x4, dev->base_addr + TLAN_HOST_INT );
+ TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT: Starting TX on buffer %d\n", priv->txTail );
+ outl( virt_to_bus( tail_list ), dev->base_addr + TLAN_CH_PARM );
+ outl( TLAN_HC_GO | TLAN_HC_ACK, dev->base_addr + TLAN_HOST_CMD );
+ } else {
+ TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT: Adding buffer %d to TX channel\n", priv->txTail );
+ if ( priv->txTail == 0 ) {
+ ( priv->txList + ( TLAN_NUM_TX_LISTS - 1 ) )->forward = virt_to_bus( tail_list );
+ } else {
+ ( priv->txList + ( priv->txTail - 1 ) )->forward = virt_to_bus( tail_list );
+ }
+ }
+ sti();
+
+ CIRC_INC( priv->txTail, TLAN_NUM_TX_LISTS );
+
+ if ( bbuf ) {
+ dev_kfree_skb( skb, FREE_WRITE );
+ }
+
+ dev->trans_start = jiffies;
+ return 0;
+
+} /* TLan_StartTx */
+
+
+
+
+ /***************************************************************
+ * TLan_HandleInterrupt
+ *
+ * Returns:
+ * Nothing
+ * Parms:
+ * irq The line on which the interrupt
+ * occurred.
+ * dev_id A pointer to the device assigned to
+ * this irq line.
+ * regs ???
+ *
+ * This function handles an interrupt generated by its
+ * assigned TLAN adapter. The function deactivates
+ * interrupts on its adapter, records the type of
+ * interrupt, executes the appropriate subhandler, and
+ * acknowdges the interrupt to the adapter (thus
+ * re-enabling adapter interrupts.
+ *
+ **************************************************************/
+
+void TLan_HandleInterrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+ u32 ack;
+ struct device *dev;
+ u32 host_cmd;
+ u16 host_int;
+ int type;
+
+ dev = (struct device *) dev_id;
+
+ cli();
+ if ( dev->interrupt ) {
+ printk( "TLAN: Re-entering interrupt handler for %s: %d.\n" , dev->name, dev->interrupt );
+ }
+ dev->interrupt++;
+
+ host_int = inw( dev->base_addr + TLAN_HOST_INT );
+ outw( host_int, dev->base_addr + TLAN_HOST_INT );
+
+ type = ( host_int & TLAN_HI_IT_MASK ) >> 2;
+
+ ack = TLanIntVector[type]( dev, host_int );
+
+ if ( ack ) {
+ host_cmd = TLAN_HC_ACK | ack | ( type << 18 );
+ outl( host_cmd, dev->base_addr + TLAN_HOST_CMD );
+ }
+
+ dev->interrupt--;
+ sti();
+
+} /* TLan_HandleInterrupts */
+
+
+
+
+ /***************************************************************
+ * TLan_Close
+ *
+ * Returns:
+ * An error code.
+ * Parms:
+ * dev The device structure of the device to
+ * close.
+ *
+ * This function shuts down the adapter. It records any
+ * stats, puts the adapter into reset state, deactivates
+ * its time as needed, and frees the irq it is using.
+ *
+ **************************************************************/
+
+int TLan_Close(struct device *dev)
+{
+ TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+
+ dev->start = 0;
+ dev->tbusy = 1;
+
+ TLan_ReadAndClearStats( dev, TLAN_RECORD );
+ outl( TLAN_HC_AD_RST, dev->base_addr + TLAN_HOST_CMD );
+ if ( priv->timer.function != NULL )
+ del_timer( &priv->timer );
+ free_irq( dev->irq, dev );
+ TLan_FreeLists( dev );
+ TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN: Device %s closed.\n", dev->name );
+
+ MOD_DEC_USE_COUNT;
+
+ return 0;
+
+} /* TLan_Close */
+
+
+
+
+ /***************************************************************
+ * TLan_GetStats
+ *
+ * Returns:
+ * A pointer to the device's statistics structure.
+ * Parms:
+ * dev The device structure to return the
+ * stats for.
+ *
+ * This function updates the devices statistics by reading
+ * the TLAN chip's onboard registers. Then it returns the
+ * address of the statistics structure.
+ *
+ **************************************************************/
+
+struct net_device_stats *TLan_GetStats( struct device *dev )
+{
+ TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+ int i;
+
+ /* Should only read stats if open ? */
+ TLan_ReadAndClearStats( dev, TLAN_RECORD );
+
+ TLAN_DBG( TLAN_DEBUG_RX, "TLAN RECEIVE: %s EOC count = %d\n", dev->name, priv->rxEocCount );
+ TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT: %s Busy count = %d\n", dev->name, priv->txBusyCount );
+ if ( debug & TLAN_DEBUG_GNRL ) {
+ TLan_PrintDio( dev->base_addr );
+ TLan_PhyPrint( dev );
+ }
+ if ( debug & TLAN_DEBUG_LIST ) {
+ for ( i = 0; i < TLAN_NUM_RX_LISTS; i++ )
+ TLan_PrintList( priv->rxList + i, "RX", i );
+ for ( i = 0; i < TLAN_NUM_TX_LISTS; i++ )
+ TLan_PrintList( priv->txList + i, "TX", i );
+ }
+
+ return ( &( (TLanPrivateInfo *) dev->priv )->stats );
+
+} /* TLan_GetStats */
+
+
+
+
+ /***************************************************************
+ * TLan_SetMulticastList
+ *
+ * Returns:
+ * Nothing
+ * Parms:
+ * dev The device structure to set the
+ * multicast list for.
+ *
+ * This function sets the TLAN adaptor to various receive
+ * modes. If the IFF_PROMISC flag is set, promiscuous
+ * mode is acitviated. Otherwise, promiscuous mode is
+ * turned off. If the IFF_ALLMULTI flag is set, then
+ * the hash table is set to receive all group addresses.
+ * Otherwise, the first three multicast addresses are
+ * stored in AREG_1-3, and the rest are selected via the
+ * hash table, as necessary.
+ *
+ **************************************************************/
+
+void TLan_SetMulticastList( struct device *dev )
+{
+ struct dev_mc_list *dmi = dev->mc_list;
+ u32 hash1 = 0;
+ u32 hash2 = 0;
+ int i;
+ u32 offset;
+ u8 tmp;
+
+ if ( dev->flags & IFF_PROMISC ) {
+ tmp = TLan_DioRead8( dev->base_addr, TLAN_NET_CMD );
+ TLan_DioWrite8( dev->base_addr, TLAN_NET_CMD, tmp | TLAN_NET_CMD_CAF );
+ } else {
+ tmp = TLan_DioRead8( dev->base_addr, TLAN_NET_CMD );
+ TLan_DioWrite8( dev->base_addr, TLAN_NET_CMD, tmp & ~TLAN_NET_CMD_CAF );
+ if ( dev->flags & IFF_ALLMULTI ) {
+ for ( i = 0; i < 3; i++ )
+ TLan_SetMac( dev, i + 1, NULL );
+ TLan_DioWrite32( dev->base_addr, TLAN_HASH_1, 0xFFFFFFFF );
+ TLan_DioWrite32( dev->base_addr, TLAN_HASH_2, 0xFFFFFFFF );
+ } else {
+ for ( i = 0; i < dev->mc_count; i++ ) {
+ if ( i < 3 ) {
+ TLan_SetMac( dev, i + 1, (char *) &dmi->dmi_addr );
+ } else {
+ offset = TLan_HashFunc( (u8 *) &dmi->dmi_addr );
+ if ( offset < 32 )
+ hash1 |= ( 1 << offset );
+ else
+ hash2 |= ( 1 << ( offset - 32 ) );
+ }
+ dmi = dmi->next;
+ }
+ for ( ; i < 3; i++ )
+ TLan_SetMac( dev, i + 1, NULL );
+ TLan_DioWrite32( dev->base_addr, TLAN_HASH_1, hash1 );
+ TLan_DioWrite32( dev->base_addr, TLAN_HASH_2, hash2 );
+ }
+ }
+
+} /* TLan_SetMulticastList */
+
+
+
+/*****************************************************************************
+******************************************************************************
+
+ ThunderLAN Driver Interrupt Vectors and Table
+
+ Please see Chap. 4, "Interrupt Handling" of the "ThunderLAN
+ Programmer's Guide" for more informations on handling interrupts
+ generated by TLAN based adapters.
+
+******************************************************************************
+*****************************************************************************/
+
+
+ /***************************************************************
+ * TLan_HandleInvalid
+ *
+ * Returns:
+ * 0
+ * Parms:
+ * dev Device assigned the IRQ that was
+ * raised.
+ * host_int The contents of the HOST_INT
+ * port.
+ *
+ * This function handles invalid interrupts. This should
+ * never happen unless some other adapter is trying to use
+ * the IRQ line assigned to the device.
+ *
+ **************************************************************/
+
+u32 TLan_HandleInvalid( struct device *dev, u16 host_int )
+{
+ host_int = 0;
+ /* printk( "TLAN: Invalid interrupt on %s.\n", dev->name ); */
+ return 0;
+
+} /* TLan_HandleInvalid */
+
+
+
+
+ /***************************************************************
+ * TLan_HandleTxEOF
+ *
+ * Returns:
+ * 1
+ * Parms:
+ * dev Device assigned the IRQ that was
+ * raised.
+ * host_int The contents of the HOST_INT
+ * port.
+ *
+ * This function handles Tx EOF interrupts which are raised
+ * by the adapter when it has completed sending the
+ * contents of a buffer. If detemines which list/buffer
+ * was completed and resets it. If the buffer was the last
+ * in the channel (EOC), then the function checks to see if
+ * another buffer is ready to send, and if so, sends a Tx
+ * Go command. Finally, the driver activates/continues the
+ * activity LED.
+ *
+ **************************************************************/
+
+u32 TLan_HandleTxEOF( struct device *dev, u16 host_int )
+{
+ TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+ int eoc = 0;
+ TLanList *head_list;
+ u32 ack = 1;
+
+ TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT: Handling TX EOF (Head=%d Tail=%d)\n", priv->txHead, priv->txTail );
+ host_int = 0;
+ head_list = priv->txList + priv->txHead;
+
+ if ( ! bbuf ) {
+ dev_kfree_skb( (struct sk_buff *) head_list->buffer[9].address, FREE_WRITE );
+ head_list->buffer[9].address = 0;
+ }
+
+ if ( head_list->cStat & TLAN_CSTAT_EOC )
+ eoc = 1;
+ if ( ! head_list->cStat & TLAN_CSTAT_FRM_CMP ) {
+ printk( "TLAN: Received interrupt for uncompleted TX frame.\n" );
+ }
+
+#if LINUX_KERNEL_VERSION > 0x20100
+ priv->stats->tx_bytes += head_list->frameSize;
+#endif
+
+ head_list->cStat = TLAN_CSTAT_UNUSED;
+ dev->tbusy = 0;
+ CIRC_INC( priv->txHead, TLAN_NUM_TX_LISTS );
+ if ( eoc ) {
+ TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT: Handling TX EOC (Head=%d Tail=%d)\n", priv->txHead, priv->txTail );
+ head_list = priv->txList + priv->txHead;
+ if ( ( head_list->cStat & TLAN_CSTAT_READY ) == TLAN_CSTAT_READY ) {
+ outl( virt_to_bus( head_list ), dev->base_addr + TLAN_CH_PARM );
+ ack |= TLAN_HC_GO;
+ } else {
+ priv->txInProgress = 0;
+ }
+ }
+
+ if ( priv->adapter->flags & TLAN_ADAPTER_ACTIVITY_LED ) {
+ TLan_DioWrite8( dev->base_addr, TLAN_LED_REG, TLAN_LED_LINK | TLAN_LED_ACT );
+ if ( priv->timer.function == NULL ) {
+ TLan_SetTimer( dev, TLAN_TIMER_ACT_DELAY, TLAN_TIMER_ACTIVITY );
+ } else if ( priv->timerType == TLAN_TIMER_ACTIVITY ) {
+ priv->timerSetAt = jiffies;
+ }
+ }
+
+ return ack;
+
+} /* TLan_HandleTxEOF */
+
+
+
+
+ /***************************************************************
+ * TLan_HandleStatOverflow
+ *
+ * Returns:
+ * 1
+ * Parms:
+ * dev Device assigned the IRQ that was
+ * raised.
+ * host_int The contents of the HOST_INT
+ * port.
+ *
+ * This function handles the Statistics Overflow interrupt
+ * which means that one or more of the TLAN statistics
+ * registers has reached 1/2 capacity and needs to be read.
+ *
+ **************************************************************/
+
+u32 TLan_HandleStatOverflow( struct device *dev, u16 host_int )
+{
+ host_int = 0;
+ TLan_ReadAndClearStats( dev, TLAN_RECORD );
+
+ return 1;
+
+} /* TLan_HandleStatOverflow */
+
+
+
+
+ /***************************************************************
+ * TLan_HandleRxEOF
+ *
+ * Returns:
+ * 1
+ * Parms:
+ * dev Device assigned the IRQ that was
+ * raised.
+ * host_int The contents of the HOST_INT
+ * port.
+ *
+ * This function handles the Rx EOF interrupt which
+ * indicates a frame has been received by the adapter from
+ * the net and the frame has been transferred to memory.
+ * The function determines the bounce buffer the frame has
+ * been loaded into, creates a new sk_buff big enough to
+ * hold the frame, and sends it to protocol stack. It
+ * then resets the used buffer and appends it to the end
+ * of the list. If the frame was the last in the Rx
+ * channel (EOC), the function restarts the receive channel
+ * by sending an Rx Go command to the adapter. Then it
+ * activates/continues the the activity LED.
+ *
+ **************************************************************/
+
+u32 TLan_HandleRxEOF( struct device *dev, u16 host_int )
+{
+ TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+ u32 ack = 1;
+ int eoc = 0;
+ u8 *head_buffer;
+ TLanList *head_list;
+ struct sk_buff *skb;
+ TLanList *tail_list;
+ void *t;
+
+ TLAN_DBG( TLAN_DEBUG_RX, "TLAN RECEIVE: Handling RX EOF (Head=%d Tail=%d)\n", priv->rxHead, priv->rxTail );
+ host_int = 0;
+ head_list = priv->rxList + priv->rxHead;
+ tail_list = priv->rxList + priv->rxTail;
+
+ if ( head_list->cStat & TLAN_CSTAT_EOC ) {
+ eoc = 1;
+ }
+
+ if ( ! head_list->cStat & TLAN_CSTAT_FRM_CMP ) {
+ printk( "TLAN: Received interrupt for uncompleted RX frame.\n" );
+ } else if ( bbuf ) {
+ skb = dev_alloc_skb( head_list->frameSize + 7 );
+ if ( skb == NULL ) {
+ printk( "TLAN: Couldn't allocate memory for received data.\n" );
+ } else {
+ head_buffer = priv->rxBuffer + ( priv->rxHead * TLAN_MAX_FRAME_SIZE );
+ skb->dev = dev;
+ skb_reserve( skb, 2 );
+ t = (void *) skb_put( skb, head_list->frameSize );
+
+#if LINUX_KERNEL_VERSION > 0x20100
+ priv->stats->rx_bytes += head_list->frameSize;
+#endif
+
+ memcpy( t, head_buffer, head_list->frameSize );
+ skb->protocol = eth_type_trans( skb, dev );
+ netif_rx( skb );
+ }
+ } else {
+ skb = (struct sk_buff *) head_list->buffer[9].address;
+ head_list->buffer[9].address = 0;
+ skb_trim( skb, head_list->frameSize );
+
+#if LINUX_KERNEL_VERSION > 0x20100
+ priv->stats->rx_bytes += head_list->frameSize;
+#endif
+
+ skb->protocol = eth_type_trans( skb, dev );
+ netif_rx( skb );
+
+ skb = dev_alloc_skb( TLAN_MAX_FRAME_SIZE + 7 );
+ if ( skb == NULL ) {
+ printk( "TLAN: Couldn't allocate memory for received data.\n" );
+ /* If this ever happened it would be a problem */
+ } else {
+ skb->dev = dev;
+ skb_reserve( skb, 2 );
+ t = (void *) skb_put( skb, TLAN_MAX_FRAME_SIZE );
+ head_list->buffer[0].address = virt_to_bus( t );
+ head_list->buffer[9].address = (u32) skb;
+ }
+ }
+
+ head_list->forward = 0;
+ head_list->frameSize = TLAN_MAX_FRAME_SIZE;
+ head_list->buffer[0].count = TLAN_MAX_FRAME_SIZE | TLAN_LAST_BUFFER;
+ tail_list->forward = virt_to_bus( head_list );
+
+ CIRC_INC( priv->rxHead, TLAN_NUM_RX_LISTS );
+ CIRC_INC( priv->rxTail, TLAN_NUM_RX_LISTS );
+
+ if ( eoc ) {
+ TLAN_DBG( TLAN_DEBUG_RX, "TLAN RECEIVE: Handling RX EOC (Head=%d Tail=%d)\n", priv->rxHead, priv->rxTail );
+ head_list = priv->rxList + priv->rxHead;
+ outl( virt_to_bus( head_list ), dev->base_addr + TLAN_CH_PARM );
+ ack |= TLAN_HC_GO | TLAN_HC_RT;
+ priv->rxEocCount++;
+ }
+
+ if ( priv->adapter->flags & TLAN_ADAPTER_ACTIVITY_LED ) {
+ TLan_DioWrite8( dev->base_addr, TLAN_LED_REG, TLAN_LED_LINK | TLAN_LED_ACT );
+ if ( priv->timer.function == NULL ) {
+ TLan_SetTimer( dev, TLAN_TIMER_ACT_DELAY, TLAN_TIMER_ACTIVITY );
+ } else if ( priv->timerType == TLAN_TIMER_ACTIVITY ) {
+ priv->timerSetAt = jiffies;
+ }
+ }
+
+ dev->last_rx = jiffies;
+
+ return ack;
+
+} /* TLan_HandleRxEOF */
+
+
+
+
+ /***************************************************************
+ * TLan_HandleDummy
+ *
+ * Returns:
+ * 1
+ * Parms:
+ * dev Device assigned the IRQ that was
+ * raised.
+ * host_int The contents of the HOST_INT
+ * port.
+ *
+ * This function handles the Dummy interrupt, which is
+ * raised whenever a test interrupt is generated by setting
+ * the Req_Int bit of HOST_CMD to 1.
+ *
+ **************************************************************/
+
+u32 TLan_HandleDummy( struct device *dev, u16 host_int )
+{
+ host_int = 0;
+ printk( "TLAN: Test interrupt on %s.\n", dev->name );
+ return 1;
+
+} /* TLan_HandleDummy */
+
+
+
+
+ /***************************************************************
+ * TLan_HandleTxEOC
+ *
+ * Returns:
+ * 1
+ * Parms:
+ * dev Device assigned the IRQ that was
+ * raised.
+ * host_int The contents of the HOST_INT
+ * port.
+ *
+ * This driver is structured to determine EOC occurances by
+ * reading the CSTAT member of the list structure. Tx EOC
+ * interrupts are disabled via the DIO INTDIS register.
+ * However, TLAN chips before revision 3.0 didn't have this
+ * functionality, so process EOC events if this is the
+ * case.
+ *
+ **************************************************************/
+
+u32 TLan_HandleTxEOC( struct device *dev, u16 host_int )
+{
+ TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+ TLanList *head_list;
+ u32 ack = 1;
+
+ host_int = 0;
+ if ( priv->tlanRev < 0x30 ) {
+ TLAN_DBG( TLAN_DEBUG_TX, "TLAN TRANSMIT: Handling TX EOC (Head=%d Tail=%d) -- IRQ\n", priv->txHead, priv->txTail );
+ head_list = priv->txList + priv->txHead;
+ if ( ( head_list->cStat & TLAN_CSTAT_READY ) == TLAN_CSTAT_READY ) {
+ outl( virt_to_bus( head_list ), dev->base_addr + TLAN_CH_PARM );
+ ack |= TLAN_HC_GO;
+ } else {
+ priv->txInProgress = 0;
+ }
+ }
+
+ return ack;
+
+} /* TLan_HandleTxEOC */
+
+
+
+
+ /***************************************************************
+ * TLan_HandleStatusCheck
+ *
+ * Returns:
+ * 0 if Adapter check, 1 if Network Status check.
+ * Parms:
+ * dev Device assigned the IRQ that was
+ * raised.
+ * host_int The contents of the HOST_INT
+ * port.
+ *
+ * This function handles Adapter Check/Network Status
+ * interrupts generated by the adapter. It checks the
+ * vector in the HOST_INT register to determine if it is
+ * an Adapter Check interrupt. If so, it resets the
+ * adapter. Otherwise it clears the status registers
+ * and services the PHY.
+ *
+ **************************************************************/
+
+u32 TLan_HandleStatusCheck( struct device *dev, u16 host_int )
+{
+ TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+ u32 ack;
+ u32 error;
+ u8 net_sts;
+ u32 phy;
+ u16 tlphy_ctl;
+ u16 tlphy_sts;
+
+ ack = 1;
+ if ( host_int & TLAN_HI_IV_MASK ) {
+ error = inl( dev->base_addr + TLAN_CH_PARM );
+ printk( "TLAN: %s: Adaptor Error = 0x%x\n", dev->name, error );
+ TLan_ReadAndClearStats( dev, TLAN_RECORD );
+ outl( TLAN_HC_AD_RST, dev->base_addr + TLAN_HOST_CMD );
+ TLan_FreeLists( dev );
+ TLan_ResetLists( dev );
+ TLan_ResetAdapter( dev );
+ dev->tbusy = 0;
+ ack = 0;
+ } else {
+ TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN: %s: Status Check\n", dev->name );
+ phy = priv->phy[priv->phyNum];
+
+ net_sts = TLan_DioRead8( dev->base_addr, TLAN_NET_STS );
+ if ( net_sts ) {
+ TLan_DioWrite8( dev->base_addr, TLAN_NET_STS, net_sts );
+ TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN: %s: Net_Sts = %x\n", dev->name, (unsigned) net_sts );
+ }
+ if ( ( net_sts & TLAN_NET_STS_MIRQ ) && ( priv->phyNum == 0 ) ) {
+ TLan_MiiReadReg( dev, phy, TLAN_TLPHY_STS, &tlphy_sts );
+ TLan_MiiReadReg( dev, phy, TLAN_TLPHY_CTL, &tlphy_ctl );
+ if ( ! ( tlphy_sts & TLAN_TS_POLOK ) && ! ( tlphy_ctl & TLAN_TC_SWAPOL ) ) {
+ tlphy_ctl |= TLAN_TC_SWAPOL;
+ TLan_MiiWriteReg( dev, phy, TLAN_TLPHY_CTL, tlphy_ctl);
+ } else if ( ( tlphy_sts & TLAN_TS_POLOK ) && ( tlphy_ctl & TLAN_TC_SWAPOL ) ) {
+ tlphy_ctl &= ~TLAN_TC_SWAPOL;
+ TLan_MiiWriteReg( dev, phy, TLAN_TLPHY_CTL, tlphy_ctl);
+ }
+
+ if (debug) {
+ TLan_PhyPrint( dev );
+ }
+ }
+ udelay(10000000);
+ }
+
+ return ack;
+
+} /* TLan_HandleStatusCheck */
+
+
+
+
+ /***************************************************************
+ * TLan_HandleRxEOC
+ *
+ * Returns:
+ * 1
+ * Parms:
+ * dev Device assigned the IRQ that was
+ * raised.
+ * host_int The contents of the HOST_INT
+ * port.
+ *
+ * This driver is structured to determine EOC occurances by
+ * reading the CSTAT member of the list structure. Rx EOC
+ * interrupts are disabled via the DIO INTDIS register.
+ * However, TLAN chips before revision 3.0 didn't have this
+ * CSTAT member or a INTDIS register, so if this chip is
+ * pre-3.0, process EOC interrupts normally.
+ *
+ **************************************************************/
+
+u32 TLan_HandleRxEOC( struct device *dev, u16 host_int )
+{
+ TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+ TLanList *head_list;
+ u32 ack = 1;
+
+ host_int = 0;
+ if ( priv->tlanRev < 0x30 ) {
+ TLAN_DBG( TLAN_DEBUG_RX, "TLAN RECEIVE: Handling RX EOC (Head=%d Tail=%d) -- IRQ\n", priv->rxHead, priv->rxTail );
+ head_list = priv->rxList + priv->rxHead;
+ outl( virt_to_bus( head_list ), dev->base_addr + TLAN_CH_PARM );
+ ack |= TLAN_HC_GO | TLAN_HC_RT;
+ priv->rxEocCount++;
+ }
+
+ return ack;
+
+} /* TLan_HandleRxEOC */
+
+
+
+
+/*****************************************************************************
+******************************************************************************
+
+ ThunderLAN Driver Timer Function
+
+******************************************************************************
+*****************************************************************************/
+
+
+ /***************************************************************
+ * TLan_Timer
+ *
+ * Returns:
+ * Nothing
+ * Parms:
+ * data A value given to add timer when
+ * add_timer was called.
+ *
+ * This function handles timed functionality for the
+ * TLAN driver. The two current timer uses are for
+ * delaying for autonegotionation and driving the ACT LED.
+ * - Autonegotiation requires being allowed about
+ * 2 1/2 seconds before attempting to transmit a
+ * packet. It would be a very bad thing to hang
+ * the kernel this long, so the driver doesn't
+ * allow transmission 'til after this time, for
+ * certain PHYs. It would be much nicer if all
+ * PHYs were interrupt-capable like the internal
+ * PHY.
+ * - The ACT LED, which shows adapter activity, is
+ * driven by the driver, and so must be left on
+ * for a short period to power up the LED so it
+ * can be seen. This delay can be changed by
+ * changing the TLAN_TIMER_ACT_DELAY in tlan.h,
+ * if desired. 10 jiffies produces a slightly
+ * sluggish response.
+ *
+ **************************************************************/
+
+void TLan_Timer( unsigned long data )
+{
+ struct device *dev = (struct device *) data;
+ TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+ u32 elapsed;
+
+ priv->timer.function = NULL;
+
+ switch ( priv->timerType ) {
+ case TLAN_TIMER_PHY_PDOWN:
+ TLan_PhyPowerDown( dev );
+ break;
+ case TLAN_TIMER_PHY_PUP:
+ TLan_PhyPowerUp( dev );
+ break;
+ case TLAN_TIMER_PHY_RESET:
+ TLan_PhyReset( dev );
+ break;
+ case TLAN_TIMER_PHY_START_LINK:
+ TLan_PhyStartLink( dev );
+ break;
+ case TLAN_TIMER_PHY_FINISH_AN:
+ TLan_PhyFinishAutoNeg( dev );
+ break;
+ case TLAN_TIMER_FINISH_RESET:
+ TLan_FinishReset( dev );
+ break;
+ case TLAN_TIMER_ACTIVITY:
+ cli();
+ if ( priv->timer.function == NULL ) {
+ elapsed = jiffies - priv->timerSetAt;
+ if ( elapsed >= TLAN_TIMER_ACT_DELAY ) {
+ TLan_DioWrite8( dev->base_addr, TLAN_LED_REG, TLAN_LED_LINK );
+ } else {
+ priv->timer.function = &TLan_Timer;
+ priv->timer.expires = priv->timerSetAt + TLAN_TIMER_ACT_DELAY;
+ sti();
+ add_timer( &priv->timer );
+ }
+ }
+ sti();
+ break;
+ default:
+ break;
+ }
+
+} /* TLan_Timer */
+
+
+
+
+/*****************************************************************************
+******************************************************************************
+
+ ThunderLAN Driver Adapter Related Routines
+
+******************************************************************************
+*****************************************************************************/
+
+
+ /***************************************************************
+ * TLan_ResetLists
+ *
+ * Returns:
+ * Nothing
+ * Parms:
+ * dev The device structure with the list
+ * stuctures to be reset.
+ *
+ * This routine sets the variables associated with managing
+ * the TLAN lists to their initial values.
+ *
+ **************************************************************/
+
+void TLan_ResetLists( struct device *dev )
+{
+ TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+ int i;
+ TLanList *list;
+ struct sk_buff *skb;
+ void *t = NULL;
+
+ priv->txHead = 0;
+ priv->txTail = 0;
+ for ( i = 0; i < TLAN_NUM_TX_LISTS; i++ ) {
+ list = priv->txList + i;
+ list->cStat = TLAN_CSTAT_UNUSED;
+ if ( bbuf ) {
+ list->buffer[0].address = virt_to_bus( priv->txBuffer + ( i * TLAN_MAX_FRAME_SIZE ) );
+ } else {
+ list->buffer[0].address = 0;
+ }
+ list->buffer[2].count = 0;
+ list->buffer[2].address = 0;
+ }
+
+ priv->rxHead = 0;
+ priv->rxTail = TLAN_NUM_RX_LISTS - 1;
+ for ( i = 0; i < TLAN_NUM_RX_LISTS; i++ ) {
+ list = priv->rxList + i;
+ list->cStat = TLAN_CSTAT_READY;
+ list->frameSize = TLAN_MAX_FRAME_SIZE;
+ list->buffer[0].count = TLAN_MAX_FRAME_SIZE | TLAN_LAST_BUFFER;
+ if ( bbuf ) {
+ list->buffer[0].address = virt_to_bus( priv->rxBuffer + ( i * TLAN_MAX_FRAME_SIZE ) );
+ } else {
+ skb = dev_alloc_skb( TLAN_MAX_FRAME_SIZE + 7 );
+ if ( skb == NULL ) {
+ printk( "TLAN: Couldn't allocate memory for received data.\n" );
+ /* If this ever happened it would be a problem */
+ } else {
+ skb->dev = dev;
+ skb_reserve( skb, 2 );
+ t = (void *) skb_put( skb, TLAN_MAX_FRAME_SIZE );
+ }
+ list->buffer[0].address = virt_to_bus( t );
+ list->buffer[9].address = (u32) skb;
+ }
+ list->buffer[1].count = 0;
+ list->buffer[1].address = 0;
+ if ( i < TLAN_NUM_RX_LISTS - 1 )
+ list->forward = virt_to_bus( list + 1 );
+ else
+ list->forward = 0;
+ }
+
+} /* TLan_ResetLists */
+
+
+void TLan_FreeLists( struct device *dev )
+{
+ TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+ int i;
+ TLanList *list;
+ struct sk_buff *skb;
+
+ if ( ! bbuf ) {
+ for ( i = 0; i < TLAN_NUM_TX_LISTS; i++ ) {
+ list = priv->txList + i;
+ skb = (struct sk_buff *) list->buffer[9].address;
+ if ( skb ) {
+ dev_kfree_skb( skb, FREE_WRITE );
+ list->buffer[9].address = 0;
+ }
+ }
+
+ for ( i = 0; i < TLAN_NUM_RX_LISTS; i++ ) {
+ list = priv->rxList + i;
+ skb = (struct sk_buff *) list->buffer[9].address;
+ if ( skb ) {
+ dev_kfree_skb( skb, FREE_READ );
+ list->buffer[9].address = 0;
+ }
+ }
+ }
+
+} /* TLan_FreeLists */
+
+
+
+
+ /***************************************************************
+ * TLan_PrintDio
+ *
+ * Returns:
+ * Nothing
+ * Parms:
+ * io_base Base IO port of the device of
+ * which to print DIO registers.
+ *
+ * This function prints out all the the internal (DIO)
+ * registers of a TLAN chip.
+ *
+ **************************************************************/
+
+void TLan_PrintDio( u16 io_base )
+{
+ u32 data0, data1;
+ int i;
+
+ printk( "TLAN: Contents of internal registers for io base 0x%04hx.\n", io_base );
+ printk( "TLAN: Off. +0 +4\n" );
+ for ( i = 0; i < 0x4C; i+= 8 ) {
+ data0 = TLan_DioRead32( io_base, i );
+ data1 = TLan_DioRead32( io_base, i + 0x4 );
+ printk( "TLAN: 0x%02x 0x%08x 0x%08x\n", i, data0, data1 );
+ }
+
+} /* TLan_PrintDio */
+
+
+
+
+ /***************************************************************
+ * TLan_PrintList
+ *
+ * Returns:
+ * Nothing
+ * Parms:
+ * list A pointer to the TLanList structure to
+ * be printed.
+ * type A string to designate type of list,
+ * "Rx" or "Tx".
+ * num The index of the list.
+ *
+ * This function prints out the contents of the list
+ * pointed to by the list parameter.
+ *
+ **************************************************************/
+
+void TLan_PrintList( TLanList *list, char *type, int num)
+{
+ int i;
+
+ printk( "TLAN: %s List %d at 0x%08x\n", type, num, (u32) list );
+ printk( "TLAN: Forward = 0x%08x\n", list->forward );
+ printk( "TLAN: CSTAT = 0x%04hx\n", list->cStat );
+ printk( "TLAN: Frame Size = 0x%04hx\n", list->frameSize );
+ /* for ( i = 0; i < 10; i++ ) { */
+ for ( i = 0; i < 2; i++ ) {
+ printk( "TLAN: Buffer[%d].count, addr = 0x%08x, 0x%08x\n", i, list->buffer[i].count, list->buffer[i].address );
+ }
+
+} /* TLan_PrintList */
+
+
+
+
+ /***************************************************************
+ * TLan_ReadAndClearStats
+ *
+ * Returns:
+ * Nothing
+ * Parms:
+ * dev Pointer to device structure of adapter
+ * to which to read stats.
+ * record Flag indicating whether to add
+ *
+ * This functions reads all the internal status registers
+ * of the TLAN chip, which clears them as a side effect.
+ * It then either adds the values to the device's status
+ * struct, or discards them, depending on whether record
+ * is TLAN_RECORD (!=0) or TLAN_IGNORE (==0).
+ *
+ **************************************************************/
+
+void TLan_ReadAndClearStats( struct device *dev, int record )
+{
+ TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+ u32 tx_good, tx_under;
+ u32 rx_good, rx_over;
+ u32 def_tx, crc, code;
+ u32 multi_col, single_col;
+ u32 excess_col, late_col, loss;
+
+ outw( TLAN_GOOD_TX_FRMS, dev->base_addr + TLAN_DIO_ADR );
+ tx_good = inb( dev->base_addr + TLAN_DIO_DATA );
+ tx_good += inb( dev->base_addr + TLAN_DIO_DATA + 1 ) << 8;
+ tx_good += inb( dev->base_addr + TLAN_DIO_DATA + 2 ) << 16;
+ tx_under = inb( dev->base_addr + TLAN_DIO_DATA + 3 );
+
+ outw( TLAN_GOOD_RX_FRMS, dev->base_addr + TLAN_DIO_ADR );
+ rx_good = inb( dev->base_addr + TLAN_DIO_DATA );
+ rx_good += inb( dev->base_addr + TLAN_DIO_DATA + 1 ) << 8;
+ rx_good += inb( dev->base_addr + TLAN_DIO_DATA + 2 ) << 16;
+ rx_over = inb( dev->base_addr + TLAN_DIO_DATA + 3 );
+
+ outw( TLAN_DEFERRED_TX, dev->base_addr + TLAN_DIO_ADR );
+ def_tx = inb( dev->base_addr + TLAN_DIO_DATA );
+ def_tx += inb( dev->base_addr + TLAN_DIO_DATA + 1 ) << 8;
+ crc = inb( dev->base_addr + TLAN_DIO_DATA + 2 );
+ code = inb( dev->base_addr + TLAN_DIO_DATA + 3 );
+
+ outw( TLAN_MULTICOL_FRMS, dev->base_addr + TLAN_DIO_ADR );
+ multi_col = inb( dev->base_addr + TLAN_DIO_DATA );
+ multi_col += inb( dev->base_addr + TLAN_DIO_DATA + 1 ) << 8;
+ single_col = inb( dev->base_addr + TLAN_DIO_DATA + 2 );
+ single_col += inb( dev->base_addr + TLAN_DIO_DATA + 3 ) << 8;
+
+ outw( TLAN_EXCESSCOL_FRMS, dev->base_addr + TLAN_DIO_ADR );
+ excess_col = inb( dev->base_addr + TLAN_DIO_DATA );
+ late_col = inb( dev->base_addr + TLAN_DIO_DATA + 1 );
+ loss = inb( dev->base_addr + TLAN_DIO_DATA + 2 );
+
+ if ( record ) {
+ priv->stats.rx_packets += rx_good;
+ priv->stats.rx_errors += rx_over + crc + code;
+ priv->stats.tx_packets += tx_good;
+ priv->stats.tx_errors += tx_under + loss;
+ priv->stats.collisions += multi_col + single_col + excess_col + late_col;
+
+ priv->stats.rx_over_errors += rx_over;
+ priv->stats.rx_crc_errors += crc;
+ priv->stats.rx_frame_errors += code;
+
+ priv->stats.tx_aborted_errors += tx_under;
+ priv->stats.tx_carrier_errors += loss;
+ }
+
+} /* TLan_ReadAndClearStats */
+
+
+
+
+ /***************************************************************
+ * TLan_Reset
+ *
+ * Returns:
+ * 0
+ * Parms:
+ * dev Pointer to device structure of adapter
+ * to be reset.
+ *
+ * This function resets the adapter and it's physical
+ * device. See Chap. 3, pp. 9-10 of the "ThunderLAN
+ * Programmer's Guide" for details. The routine tries to
+ * implement what is detailed there, though adjustments
+ * have been made.
+ *
+ **************************************************************/
+
+void
+TLan_ResetAdapter( struct device *dev )
+{
+ TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+ int i;
+ u32 addr;
+ u32 data;
+ u8 data8;
+
+ priv->tlanFullDuplex = FALSE;
+/* 1. Assert reset bit. */
+
+ data = inl(dev->base_addr + TLAN_HOST_CMD);
+ data |= TLAN_HC_AD_RST;
+ outl(data, dev->base_addr + TLAN_HOST_CMD);
+
+ udelay(1000);
+
+/* 2. Turn off interrupts. ( Probably isn't necessary ) */
+
+ data = inl(dev->base_addr + TLAN_HOST_CMD);
+ data |= TLAN_HC_INT_OFF;
+ outl(data, dev->base_addr + TLAN_HOST_CMD);
+
+/* 3. Clear AREGs and HASHs. */
+
+ for ( i = TLAN_AREG_0; i <= TLAN_HASH_2; i += 4 ) {
+ TLan_DioWrite32( dev->base_addr, (u16) i, 0 );
+ }
+
+/* 4. Setup NetConfig register. */
+
+ data = TLAN_NET_CFG_1FRAG | TLAN_NET_CFG_1CHAN | TLAN_NET_CFG_PHY_EN;
+ TLan_DioWrite16( dev->base_addr, TLAN_NET_CONFIG, (u16) data );
+
+/* 5. Load Ld_Tmr and Ld_Thr in HOST_CMD. */
+
+ outl( TLAN_HC_LD_TMR | 0x0, dev->base_addr + TLAN_HOST_CMD );
+ outl( TLAN_HC_LD_THR | 0x1, dev->base_addr + TLAN_HOST_CMD );
+
+/* 6. Unreset the MII by setting NMRST (in NetSio) to 1. */
+
+ outw( TLAN_NET_SIO, dev->base_addr + TLAN_DIO_ADR );
+ addr = dev->base_addr + TLAN_DIO_DATA + TLAN_NET_SIO;
+ TLan_SetBit( TLAN_NET_SIO_NMRST, addr );
+
+/* 7. Setup the remaining registers. */
+
+ if ( priv->tlanRev >= 0x30 ) {
+ data8 = TLAN_ID_TX_EOC | TLAN_ID_RX_EOC;
+ TLan_DioWrite8( dev->base_addr, TLAN_INT_DIS, data8 );
+ }
+ TLan_PhyDetect( dev );
+ data = TLAN_NET_CFG_1FRAG | TLAN_NET_CFG_1CHAN;
+ if ( priv->adapter->flags & TLAN_ADAPTER_BIT_RATE_PHY ) {
+ data |= TLAN_NET_CFG_BIT;
+ if ( priv->aui == 1 ) {
+ TLan_DioWrite8( dev->base_addr, TLAN_ACOMMIT, 0x0a );
+ } else if ( priv->duplex == TLAN_DUPLEX_FULL ) {
+ TLan_DioWrite8( dev->base_addr, TLAN_ACOMMIT, 0x00 );
+ priv->tlanFullDuplex = TRUE;
+ } else {
+ TLan_DioWrite8( dev->base_addr, TLAN_ACOMMIT, 0x08 );
+ }
+ }
+ if ( priv->phyNum == 0 ) {
+ data |= TLAN_NET_CFG_PHY_EN;
+ }
+ TLan_DioWrite16( dev->base_addr, TLAN_NET_CONFIG, (u16) data );
+
+ if ( priv->adapter->flags & TLAN_ADAPTER_UNMANAGED_PHY ) {
+ TLan_FinishReset( dev );
+ } else {
+ TLan_PhyPowerDown( dev );
+ }
+
+} /* TLan_ResetAdapter */
+
+
+
+
+void
+TLan_FinishReset( struct device *dev )
+{
+ TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+ u8 data;
+ u32 phy;
+ u8 sio;
+ u16 status;
+ u16 tlphy_ctl;
+
+ phy = priv->phy[priv->phyNum];
+
+ data = TLAN_NET_CMD_NRESET | TLAN_NET_CMD_NWRAP;
+ if ( priv->tlanFullDuplex ) {
+ data |= TLAN_NET_CMD_DUPLEX;
+ }
+ TLan_DioWrite8( dev->base_addr, TLAN_NET_CMD, data );
+ data = TLAN_NET_MASK_MASK4 | TLAN_NET_MASK_MASK5;
+ if ( priv->phyNum == 0 ) {
+ data |= TLAN_NET_MASK_MASK7;
+ }
+ TLan_DioWrite8( dev->base_addr, TLAN_NET_MASK, data );
+ TLan_DioWrite16( dev->base_addr, TLAN_MAX_RX, TLAN_MAX_FRAME_SIZE );
+
+ if ( ( priv->adapter->flags & TLAN_ADAPTER_UNMANAGED_PHY ) || ( priv->aui ) ) {
+ status = MII_GS_LINK;
+ printk( "TLAN: %s: Link forced.\n", dev->name );
+ } else {
+ TLan_MiiReadReg( dev, phy, MII_GEN_STS, &status );
+ udelay( 1000 );
+ TLan_MiiReadReg( dev, phy, MII_GEN_STS, &status );
+ if ( status & MII_GS_LINK ) {
+ printk( "TLAN: %s: Link active.\n", dev->name );
+ TLan_DioWrite8( dev->base_addr, TLAN_LED_REG, TLAN_LED_LINK );
+ }
+ }
+
+ if ( priv->phyNum == 0 ) {
+ TLan_MiiReadReg( dev, phy, TLAN_TLPHY_CTL, &tlphy_ctl );
+ tlphy_ctl |= TLAN_TC_INTEN;
+ TLan_MiiWriteReg( dev, phy, TLAN_TLPHY_CTL, tlphy_ctl );
+ sio = TLan_DioRead8( dev->base_addr, TLAN_NET_SIO );
+ sio |= TLAN_NET_SIO_MINTEN;
+ TLan_DioWrite8( dev->base_addr, TLAN_NET_SIO, sio );
+ }
+
+ if ( status & MII_GS_LINK ) {
+ TLan_SetMac( dev, 0, dev->dev_addr );
+ priv->phyOnline = 1;
+ outb( ( TLAN_HC_INT_ON >> 8 ), dev->base_addr + TLAN_HOST_CMD + 1 );
+ if ( debug >= 1 ) {
+ outb( ( TLAN_HC_REQ_INT >> 8 ), dev->base_addr + TLAN_HOST_CMD + 1 );
+ }
+ outl( virt_to_bus( priv->rxList ), dev->base_addr + TLAN_CH_PARM );
+ outl( TLAN_HC_GO | TLAN_HC_RT, dev->base_addr + TLAN_HOST_CMD );
+ } else {
+ printk( "TLAN: %s: Link inactive, will retry in 10 secs...\n", dev->name );
+ TLan_SetTimer( dev, 1000, TLAN_TIMER_FINISH_RESET );
+ return;
+ }
+
+} /* TLan_FinishReset */
+
+
+
+
+ /***************************************************************
+ * TLan_SetMac
+ *
+ * Returns:
+ * Nothing
+ * Parms:
+ * dev Pointer to device structure of adapter
+ * on which to change the AREG.
+ * areg The AREG to set the address in (0 - 3).
+ * mac A pointer to an array of chars. Each
+ * element stores one byte of the address.
+ * IE, it isn't in ascii.
+ *
+ * This function transfers a MAC address to one of the
+ * TLAN AREGs (address registers). The TLAN chip locks
+ * the register on writing to offset 0 and unlocks the
+ * register after writing to offset 5. If NULL is passed
+ * in mac, then the AREG is filled with 0's.
+ *
+ **************************************************************/
+
+void TLan_SetMac( struct device *dev, int areg, char *mac )
+{
+ int i;
+
+ areg *= 6;
+
+ if ( mac != NULL ) {
+ for ( i = 0; i < 6; i++ )
+ TLan_DioWrite8( dev->base_addr, TLAN_AREG_0 + areg + i, mac[i] );
+ } else {
+ for ( i = 0; i < 6; i++ )
+ TLan_DioWrite8( dev->base_addr, TLAN_AREG_0 + areg + i, 0 );
+ }
+
+} /* TLan_SetMac */
+
+
+
+
+/*****************************************************************************
+******************************************************************************
+
+ ThunderLAN Driver PHY Layer Routines
+
+******************************************************************************
+*****************************************************************************/
+
+
+
+ /*********************************************************************
+ * TLan_PhyPrint
+ *
+ * Returns:
+ * Nothing
+ * Parms:
+ * dev A pointer to the device structure of the
+ * TLAN device having the PHYs to be detailed.
+ *
+ * This function prints the registers a PHY (aka tranceiver).
+ *
+ ********************************************************************/
+
+void TLan_PhyPrint( struct device *dev )
+{
+ TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+ u16 i, data0, data1, data2, data3, phy;
+
+ phy = priv->phy[priv->phyNum];
+
+ if ( priv->adapter->flags & TLAN_ADAPTER_UNMANAGED_PHY ) {
+ printk( "TLAN: Device %s, Unmanaged PHY.\n", dev->name );
+ } else if ( phy <= TLAN_PHY_MAX_ADDR ) {
+ printk( "TLAN: Device %s, PHY 0x%02x.\n", dev->name, phy );
+ printk( "TLAN: Off. +0 +1 +2 +3 \n" );
+ for ( i = 0; i < 0x20; i+= 4 ) {
+ printk( "TLAN: 0x%02x", i );
+ TLan_MiiReadReg( dev, phy, i, &data0 );
+ printk( " 0x%04hx", data0 );
+ TLan_MiiReadReg( dev, phy, i + 1, &data1 );
+ printk( " 0x%04hx", data1 );
+ TLan_MiiReadReg( dev, phy, i + 2, &data2 );
+ printk( " 0x%04hx", data2 );
+ TLan_MiiReadReg( dev, phy, i + 3, &data3 );
+ printk( " 0x%04hx\n", data3 );
+ }
+ } else {
+ printk( "TLAN: Device %s, Invalid PHY.\n", dev->name );
+ }
+
+} /* TLan_PhyPrint */
+
+
+
+
+ /*********************************************************************
+ * TLan_PhyDetect
+ *
+ * Returns:
+ * Nothing
+ * Parms:
+ * dev A pointer to the device structure of the adapter
+ * for which the PHY needs determined.
+ *
+ * So far I've found that adapters which have external PHYs
+ * may also use the internal PHY for part of the functionality.
+ * (eg, AUI/Thinnet). This function finds out if this TLAN
+ * chip has an internal PHY, and then finds the first external
+ * PHY (starting from address 0) if it exists).
+ *
+ ********************************************************************/
+
+void TLan_PhyDetect( struct device *dev )
+{
+ TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+ u16 control;
+ u16 hi;
+ u16 lo;
+ u32 phy;
+
+ if ( priv->adapter->flags & TLAN_ADAPTER_UNMANAGED_PHY ) {
+ priv->phyNum = 0xFFFF;
+ return;
+ }
+
+ TLan_MiiReadReg( dev, TLAN_PHY_MAX_ADDR, MII_GEN_ID_HI, &hi );
+
+ if ( hi != 0xFFFF ) {
+ priv->phy[0] = TLAN_PHY_MAX_ADDR;
+ } else {
+ priv->phy[0] = TLAN_PHY_NONE;
+ }
+
+ priv->phy[1] = TLAN_PHY_NONE;
+ for ( phy = 0; phy <= TLAN_PHY_MAX_ADDR; phy++ ) {
+ TLan_MiiReadReg( dev, phy, MII_GEN_CTL, &control );
+ TLan_MiiReadReg( dev, phy, MII_GEN_ID_HI, &hi );
+ TLan_MiiReadReg( dev, phy, MII_GEN_ID_LO, &lo );
+ if ( ( control != 0xFFFF ) || ( hi != 0xFFFF ) || ( lo != 0xFFFF ) ) {
+ TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN: PHY found at %02x %04x %04x %04x\n", phy, control, hi, lo );
+ if ( ( priv->phy[1] == TLAN_PHY_NONE ) && ( phy != TLAN_PHY_MAX_ADDR ) ) {
+ priv->phy[1] = phy;
+ }
+ }
+ }
+
+ if ( priv->phy[1] != TLAN_PHY_NONE ) {
+ priv->phyNum = 1;
+ } else if ( priv->phy[0] != TLAN_PHY_NONE ) {
+ priv->phyNum = 0;
+ } else {
+ printk( "TLAN: Cannot initialize device, no PHY was found!\n" );
+ }
+
+} /* TLan_PhyDetect */
+
+
+
+
+void TLan_PhyPowerDown( struct device *dev )
+{
+ TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+ u16 value;
+
+ TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN: %s: Powering down PHY(s).\n", dev->name );
+ value = MII_GC_PDOWN | MII_GC_LOOPBK | MII_GC_ISOLATE;
+ TLan_MiiSync( dev->base_addr );
+ TLan_MiiWriteReg( dev, priv->phy[priv->phyNum], MII_GEN_CTL, value );
+ if ( ( priv->phyNum == 0 ) && ( priv->phy[1] != TLAN_PHY_NONE ) && ( ! ( priv->adapter->flags & TLAN_ADAPTER_USE_INTERN_10 ) ) ) {
+ TLan_MiiSync( dev->base_addr );
+ TLan_MiiWriteReg( dev, priv->phy[1], MII_GEN_CTL, value );
+ }
+
+ /* Wait for 5 jiffies (50 ms) and powerup
+ * This is abitrary. It is intended to make sure the
+ * tranceiver settles.
+ */
+ TLan_SetTimer( dev, 5, TLAN_TIMER_PHY_PUP );
+
+} /* TLan_PhyPowerDown */
+
+
+
+
+void TLan_PhyPowerUp( struct device *dev )
+{
+ TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+ u16 value;
+
+ TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN: %s: Powering up PHY.\n", dev->name );
+ TLan_MiiSync( dev->base_addr );
+ value = MII_GC_LOOPBK;
+ TLan_MiiWriteReg( dev, priv->phy[priv->phyNum], MII_GEN_CTL, value );
+
+ /* Wait for 50 jiffies (500 ms) and reset the
+ * tranceiver. The TLAN docs say both 50 ms and
+ * 500 ms, so do the longer, just in case
+ */
+ TLan_SetTimer( dev, 50, TLAN_TIMER_PHY_RESET );
+
+} /* TLan_PhyPowerUp */
+
+
+
+
+void TLan_PhyReset( struct device *dev )
+{
+ TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+ u16 phy;
+ u16 value;
+
+ phy = priv->phy[priv->phyNum];
+
+ TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN: %s: Reseting PHY.\n", dev->name );
+ TLan_MiiSync( dev->base_addr );
+ value = MII_GC_LOOPBK | MII_GC_RESET;
+ TLan_MiiWriteReg( dev, phy, MII_GEN_CTL, value );
+ TLan_MiiReadReg( dev, phy, MII_GEN_CTL, &value );
+ while ( value & MII_GC_RESET ) {
+ TLan_MiiReadReg( dev, phy, MII_GEN_CTL, &value );
+ }
+ TLan_MiiWriteReg( dev, phy, MII_GEN_CTL, 0 );
+
+ /* Wait for 50 jiffies (500 ms) and initialize.
+ * I don't remember why I wait this long.
+ */
+ TLan_SetTimer( dev, 50, TLAN_TIMER_PHY_START_LINK );
+
+} /* TLan_PhyReset */
+
+
+
+
+void TLan_PhyStartLink( struct device *dev )
+{
+ TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+ u16 ability;
+ u16 control;
+ u16 data;
+ u16 phy;
+ u16 status;
+ u16 tctl;
+
+ phy = priv->phy[priv->phyNum];
+
+ TLAN_DBG( TLAN_DEBUG_GNRL, "TLAN: %s: Trying to activate link.\n", dev->name );
+ TLan_MiiReadReg( dev, phy, MII_GEN_STS, &status );
+ if ( ( status & MII_GS_AUTONEG ) &&
+ ( priv->duplex == TLAN_DUPLEX_DEFAULT ) &&
+ ( priv->speed == TLAN_SPEED_DEFAULT ) &&
+ ( ! priv->aui ) ) {
+ ability = status >> 11;
+
+ if ( priv->speed == TLAN_SPEED_10 ) {
+ ability &= 0x0003;
+ } else if ( priv->speed == TLAN_SPEED_100 ) {
+ ability &= 0x001C;
+ }
+
+ if ( priv->duplex == TLAN_DUPLEX_FULL ) {
+ ability &= 0x000A;
+ } else if ( priv->duplex == TLAN_DUPLEX_HALF ) {
+ ability &= 0x0005;
+ }
+
+ TLan_MiiWriteReg( dev, phy, MII_AN_ADV, ( ability << 5 ) | 1 );
+ TLan_MiiWriteReg( dev, phy, MII_GEN_CTL, 0x1000 );
+ TLan_MiiWriteReg( dev, phy, MII_GEN_CTL, 0x1200 );
+
+ /* Wait for 400 jiffies (4 sec) for autonegotiation
+ * to complete. The max spec time is less than this
+ * but the card need additional time to start AN.
+ * .5 sec should be plenty extra.
+ */
+ printk( "TLAN: %s: Starting autonegotiation.\n", dev->name );
+ TLan_SetTimer( dev, 400, TLAN_TIMER_PHY_FINISH_AN );
+ return;
+ }
+
+ if ( ( priv->aui ) && ( priv->phyNum != 0 ) ) {
+ priv->phyNum = 0;
+ data = TLAN_NET_CFG_1FRAG | TLAN_NET_CFG_1CHAN | TLAN_NET_CFG_PHY_EN;
+ TLan_DioWrite16( dev->base_addr, TLAN_NET_CONFIG, data );
+ TLan_SetTimer( dev, 4, TLAN_TIMER_PHY_PDOWN );
+ return;
+ } else if ( priv->phyNum == 0 ) {
+ TLan_MiiReadReg( dev, phy, TLAN_TLPHY_CTL, &tctl );
+ if ( priv->aui ) {
+ tctl |= TLAN_TC_AUISEL;
+ } else {
+ tctl &= ~TLAN_TC_AUISEL;
+ control = 0;
+ if ( priv->duplex == TLAN_DUPLEX_FULL ) {
+ control |= MII_GC_DUPLEX;
+ priv->tlanFullDuplex = TRUE;
+ }
+ if ( priv->speed == TLAN_SPEED_100 ) {
+ control |= MII_GC_SPEEDSEL;
+ }
+ TLan_MiiWriteReg( dev, phy, MII_GEN_CTL, control );
+ }
+ TLan_MiiWriteReg( dev, phy, TLAN_TLPHY_CTL, tctl );
+ }
+
+ /* Wait for 100 jiffies (1 sec) to give the tranceiver time
+ * to establish link.
+ */
+ TLan_SetTimer( dev, 100, TLAN_TIMER_FINISH_RESET );
+
+} /* TLan_PhyStartLink */
+
+
+
+
+void TLan_PhyFinishAutoNeg( struct device *dev )
+{
+ TLanPrivateInfo *priv = (TLanPrivateInfo *) dev->priv;
+ u16 an_adv;
+ u16 an_lpa;
+ u16 data;
+ u16 mode;
+ u16 phy;
+ u16 status;
+
+ phy = priv->phy[priv->phyNum];
+
+ TLan_MiiReadReg( dev, phy, MII_GEN_STS, &status );
+ if ( ! ( status & MII_GS_AUTOCMPLT ) ) {
+ /* Wait for 800 jiffies (8 sec) to give the process
+ * more time. Perhaps we should fail after a while.
+ */
+ printk( "TLAN: Giving autonegotiation more time.\n" );
+ TLan_SetTimer( dev, 800, TLAN_TIMER_PHY_FINISH_AN );
+ return;
+ }
+
+ printk( "TLAN: %s: Autonegotiation complete.\n", dev->name );
+ TLan_MiiReadReg( dev, phy, MII_AN_ADV, &an_adv );
+ TLan_MiiReadReg( dev, phy, MII_AN_LPA, &an_lpa );
+ mode = an_adv & an_lpa & 0x03E0;
+ if ( mode & 0x0100 ) {
+ priv->tlanFullDuplex = TRUE;
+ } else if ( ! ( mode & 0x0080 ) && ( mode & 0x0040 ) ) {
+ priv->tlanFullDuplex = TRUE;
+ }
+
+ if ( ( ! ( mode & 0x0180 ) ) && ( priv->adapter->flags & TLAN_ADAPTER_USE_INTERN_10 ) && ( priv->phyNum != 0 ) ) {
+ priv->phyNum = 0;
+ data = TLAN_NET_CFG_1FRAG | TLAN_NET_CFG_1CHAN | TLAN_NET_CFG_PHY_EN;
+ TLan_DioWrite16( dev->base_addr, TLAN_NET_CONFIG, data );
+ TLan_SetTimer( dev, 40, TLAN_TIMER_PHY_PDOWN );
+ return;
+ }
+
+ if ( priv->phyNum == 0 ) {
+ if ( ( priv->duplex == TLAN_DUPLEX_FULL ) || ( an_adv & an_lpa & 0x0040 ) ) {
+ TLan_MiiWriteReg( dev, phy, MII_GEN_CTL, MII_GC_AUTOENB | MII_GC_DUPLEX );
+ }
+ }
+
+ /* Wait for 10 jiffies (100 ms). No reason in partiticular.
+ */
+ TLan_SetTimer( dev, 10, TLAN_TIMER_FINISH_RESET );
+
+} /* TLan_PhyFinishAutoNeg */
+
+
+
+
+/*****************************************************************************
+******************************************************************************
+
+ ThunderLAN Driver MII Routines
+
+ These routines are based on the information in Chap. 2 of the
+ "ThunderLAN Programmer's Guide", pp. 15-24.
+
+******************************************************************************
+*****************************************************************************/
+
+
+ /***************************************************************
+ * TLan_MiiReadReg
+ *
+ * Returns:
+ * 0 if ack received ok
+ * 1 otherwise.
+ *
+ * Parms:
+ * dev The device structure containing
+ * The io address and interrupt count
+ * for this device.
+ * phy The address of the PHY to be queried.
+ * reg The register whose contents are to be
+ * retreived.
+ * val A pointer to a variable to store the
+ * retrieved value.
+ *
+ * This function uses the TLAN's MII bus to retreive the contents
+ * of a given register on a PHY. It sends the appropriate info
+ * and then reads the 16-bit register value from the MII bus via
+ * the TLAN SIO register.
+ *
+ **************************************************************/
+
+int TLan_MiiReadReg( struct device *dev, u16 phy, u16 reg, u16 *val )
+{
+ u8 nack;
+ u16 sio, tmp;
+ u32 i;
+ int err;
+ int minten;
+
+ err = FALSE;
+ outw(TLAN_NET_SIO, dev->base_addr + TLAN_DIO_ADR);
+ sio = dev->base_addr + TLAN_DIO_DATA + TLAN_NET_SIO;
+
+ if ( dev->interrupt == 0 )
+ cli();
+ dev->interrupt++;
+
+ TLan_MiiSync(dev->base_addr);
+
+ minten = TLan_GetBit( TLAN_NET_SIO_MINTEN, sio );
+ if ( minten )
+ TLan_ClearBit(TLAN_NET_SIO_MINTEN, sio);
+
+ TLan_MiiSendData( dev->base_addr, 0x1, 2 ); /* Start ( 01b ) */
+ TLan_MiiSendData( dev->base_addr, 0x2, 2 ); /* Read ( 10b ) */
+ TLan_MiiSendData( dev->base_addr, phy, 5 ); /* Device # */
+ TLan_MiiSendData( dev->base_addr, reg, 5 ); /* Register # */
+
+
+ TLan_ClearBit(TLAN_NET_SIO_MTXEN, sio); /* Change direction */
+
+ TLan_ClearBit(TLAN_NET_SIO_MCLK, sio); /* Clock Idle bit */
+ TLan_SetBit(TLAN_NET_SIO_MCLK, sio);
+ TLan_ClearBit(TLAN_NET_SIO_MCLK, sio); /* Wait 300ns */
+
+ nack = TLan_GetBit(TLAN_NET_SIO_MDATA, sio); /* Check for ACK */
+ TLan_SetBit(TLAN_NET_SIO_MCLK, sio); /* Finish ACK */
+ if (nack) { /* No ACK, so fake it */
+ for (i = 0; i < 16; i++) {
+ TLan_ClearBit(TLAN_NET_SIO_MCLK, sio);
+ TLan_SetBit(TLAN_NET_SIO_MCLK, sio);
+ }
+ tmp = 0xffff;
+ err = TRUE;
+ } else { /* ACK, so read data */
+ for (tmp = 0, i = 0x8000; i; i >>= 1) {
+ TLan_ClearBit(TLAN_NET_SIO_MCLK, sio);
+ if (TLan_GetBit(TLAN_NET_SIO_MDATA, sio))
+ tmp |= i;
+ TLan_SetBit(TLAN_NET_SIO_MCLK, sio);
+ }
+ }
+
+
+ TLan_ClearBit(TLAN_NET_SIO_MCLK, sio); /* Idle cycle */
+ TLan_SetBit(TLAN_NET_SIO_MCLK, sio);
+
+ if ( minten )
+ TLan_SetBit(TLAN_NET_SIO_MINTEN, sio);
+
+ *val = tmp;
+
+ dev->interrupt--;
+ if ( dev->interrupt == 0 )
+ sti();
+
+ return err;
+
+} /* TLan_MiiReadReg */
+
+
+
+
+ /***************************************************************
+ * TLan_MiiSendData
+ *
+ * Returns:
+ * Nothing
+ * Parms:
+ * base_port The base IO port of the adapter in
+ * question.
+ * dev The address of the PHY to be queried.
+ * data The value to be placed on the MII bus.
+ * num_bits The number of bits in data that are to
+ * be placed on the MII bus.
+ *
+ * This function sends on sequence of bits on the MII
+ * configuration bus.
+ *
+ **************************************************************/
+
+void TLan_MiiSendData( u16 base_port, u32 data, unsigned num_bits )
+{
+ u16 sio;
+ u32 i;
+
+ if ( num_bits == 0 )
+ return;
+
+ outw( TLAN_NET_SIO, base_port + TLAN_DIO_ADR );
+ sio = base_port + TLAN_DIO_DATA + TLAN_NET_SIO;
+ TLan_SetBit( TLAN_NET_SIO_MTXEN, sio );
+
+ for ( i = ( 0x1 << ( num_bits - 1 ) ); i; i >>= 1 ) {
+ TLan_ClearBit( TLAN_NET_SIO_MCLK, sio );
+ TLan_GetBit( TLAN_NET_SIO_MCLK, sio );
+ if ( data & i )
+ TLan_SetBit( TLAN_NET_SIO_MDATA, sio );
+ else
+ TLan_ClearBit( TLAN_NET_SIO_MDATA, sio );
+ TLan_SetBit( TLAN_NET_SIO_MCLK, sio );
+ TLan_GetBit( TLAN_NET_SIO_MCLK, sio );
+ }
+
+} /* TLan_MiiSendData */
+
+
+
+
+ /***************************************************************
+ * TLan_MiiSync
+ *
+ * Returns:
+ * Nothing
+ * Parms:
+ * base_port The base IO port of the adapter in
+ * question.
+ *
+ * This functions syncs all PHYs in terms of the MII configuration
+ * bus.
+ *
+ **************************************************************/
+
+void TLan_MiiSync( u16 base_port )
+{
+ int i;
+ u16 sio;
+
+ outw( TLAN_NET_SIO, base_port + TLAN_DIO_ADR );
+ sio = base_port + TLAN_DIO_DATA + TLAN_NET_SIO;
+
+ TLan_ClearBit( TLAN_NET_SIO_MTXEN, sio );
+ for ( i = 0; i < 32; i++ ) {
+ TLan_ClearBit( TLAN_NET_SIO_MCLK, sio );
+ TLan_SetBit( TLAN_NET_SIO_MCLK, sio );
+ }
+
+} /* TLan_MiiSync */
+
+
+
+
+ /***************************************************************
+ * TLan_MiiWriteReg
+ *
+ * Returns:
+ * Nothing
+ * Parms:
+ * dev The device structure for the device
+ * to write to.
+ * phy The address of the PHY to be written to.
+ * reg The register whose contents are to be
+ * written.
+ * val The value to be written to the register.
+ *
+ * This function uses the TLAN's MII bus to write the contents of a
+ * given register on a PHY. It sends the appropriate info and then
+ * writes the 16-bit register value from the MII configuration bus
+ * via the TLAN SIO register.
+ *
+ **************************************************************/
+
+void TLan_MiiWriteReg( struct device *dev, u16 phy, u16 reg, u16 val )
+{
+ u16 sio;
+ int minten;
+
+ outw(TLAN_NET_SIO, dev->base_addr + TLAN_DIO_ADR);
+ sio = dev->base_addr + TLAN_DIO_DATA + TLAN_NET_SIO;
+
+ if ( dev->interrupt == 0 )
+ cli();
+ dev->interrupt++;
+
+ TLan_MiiSync( dev->base_addr );
+
+ minten = TLan_GetBit( TLAN_NET_SIO_MINTEN, sio );
+ if ( minten )
+ TLan_ClearBit( TLAN_NET_SIO_MINTEN, sio );
+
+ TLan_MiiSendData( dev->base_addr, 0x1, 2 ); /* Start ( 01b ) */
+ TLan_MiiSendData( dev->base_addr, 0x1, 2 ); /* Write ( 01b ) */
+ TLan_MiiSendData( dev->base_addr, phy, 5 ); /* Device # */
+ TLan_MiiSendData( dev->base_addr, reg, 5 ); /* Register # */
+
+ TLan_MiiSendData( dev->base_addr, 0x2, 2 ); /* Send ACK */
+ TLan_MiiSendData( dev->base_addr, val, 16 ); /* Send Data */
+
+ TLan_ClearBit( TLAN_NET_SIO_MCLK, sio ); /* Idle cycle */
+ TLan_SetBit( TLAN_NET_SIO_MCLK, sio );
+
+ if ( minten )
+ TLan_SetBit( TLAN_NET_SIO_MINTEN, sio );
+
+ dev->interrupt--;
+ if ( dev->interrupt == 0 )
+ sti();
+
+} /* TLan_MiiWriteReg */
+
+
+
+
+/*****************************************************************************
+******************************************************************************
+
+ ThunderLAN Driver Eeprom routines
+
+ The Compaq Netelligent 10 and 10/100 cards use a Microchip 24C02A
+ EEPROM. These functions are based on information in Microchip's
+ data sheet. I don't know how well this functions will work with
+ other EEPROMs.
+
+******************************************************************************
+*****************************************************************************/
+
+
+ /***************************************************************
+ * TLan_EeSendStart
+ *
+ * Returns:
+ * Nothing
+ * Parms:
+ * io_base The IO port base address for the
+ * TLAN device with the EEPROM to
+ * use.
+ *
+ * This function sends a start cycle to an EEPROM attached
+ * to a TLAN chip.
+ *
+ **************************************************************/
+
+void TLan_EeSendStart( u16 io_base )
+{
+ u16 sio;
+
+ outw( TLAN_NET_SIO, io_base + TLAN_DIO_ADR );
+ sio = io_base + TLAN_DIO_DATA + TLAN_NET_SIO;
+
+ TLan_SetBit( TLAN_NET_SIO_ECLOK, sio );
+ TLan_SetBit( TLAN_NET_SIO_EDATA, sio );
+ TLan_SetBit( TLAN_NET_SIO_ETXEN, sio );
+ TLan_ClearBit( TLAN_NET_SIO_EDATA, sio );
+ TLan_ClearBit( TLAN_NET_SIO_ECLOK, sio );
+
+} /* TLan_EeSendStart */
+
+
+
+
+ /***************************************************************
+ * TLan_EeSendByte
+ *
+ * Returns:
+ * If the correct ack was received, 0, otherwise 1
+ * Parms: io_base The IO port base address for the
+ * TLAN device with the EEPROM to
+ * use.
+ * data The 8 bits of information to
+ * send to the EEPROM.
+ * stop If TLAN_EEPROM_STOP is passed, a
+ * stop cycle is sent after the
+ * byte is sent after the ack is
+ * read.
+ *
+ * This function sends a byte on the serial EEPROM line,
+ * driving the clock to send each bit. The function then
+ * reverses transmission direction and reads an acknowledge
+ * bit.
+ *
+ **************************************************************/
+
+int TLan_EeSendByte( u16 io_base, u8 data, int stop )
+{
+ int err;
+ u8 place;
+ u16 sio;
+
+ outw( TLAN_NET_SIO, io_base + TLAN_DIO_ADR );
+ sio = io_base + TLAN_DIO_DATA + TLAN_NET_SIO;
+
+ /* Assume clock is low, tx is enabled; */
+ for ( place = 0x80; place != 0; place >>= 1 ) {
+ if ( place & data )
+ TLan_SetBit( TLAN_NET_SIO_EDATA, sio );
+ else
+ TLan_ClearBit( TLAN_NET_SIO_EDATA, sio );
+ TLan_SetBit( TLAN_NET_SIO_ECLOK, sio );
+ TLan_ClearBit( TLAN_NET_SIO_ECLOK, sio );
+ }
+ TLan_ClearBit( TLAN_NET_SIO_ETXEN, sio );
+ TLan_SetBit( TLAN_NET_SIO_ECLOK, sio );
+ err = TLan_GetBit( TLAN_NET_SIO_EDATA, sio );
+ TLan_ClearBit( TLAN_NET_SIO_ECLOK, sio );
+ TLan_SetBit( TLAN_NET_SIO_ETXEN, sio );
+
+ if ( ( ! err ) && stop ) {
+ TLan_ClearBit( TLAN_NET_SIO_EDATA, sio ); /* STOP, raise data while clock is high */
+ TLan_SetBit( TLAN_NET_SIO_ECLOK, sio );
+ TLan_SetBit( TLAN_NET_SIO_EDATA, sio );
+ }
+
+ return ( err );
+
+} /* TLan_EeSendByte */
+
+
+
+
+ /***************************************************************
+ * TLan_EeReceiveByte
+ *
+ * Returns:
+ * Nothing
+ * Parms:
+ * io_base The IO port base address for the
+ * TLAN device with the EEPROM to
+ * use.
+ * data An address to a char to hold the
+ * data sent from the EEPROM.
+ * stop If TLAN_EEPROM_STOP is passed, a
+ * stop cycle is sent after the
+ * byte is received, and no ack is
+ * sent.
+ *
+ * This function receives 8 bits of data from the EEPROM
+ * over the serial link. It then sends and ack bit, or no
+ * ack and a stop bit. This function is used to retrieve
+ * data after the address of a byte in the EEPROM has been
+ * sent.
+ *
+ **************************************************************/
+
+void TLan_EeReceiveByte( u16 io_base, u8 *data, int stop )
+{
+ u8 place;
+ u16 sio;
+
+ outw( TLAN_NET_SIO, io_base + TLAN_DIO_ADR );
+ sio = io_base + TLAN_DIO_DATA + TLAN_NET_SIO;
+ *data = 0;
+
+ /* Assume clock is low, tx is enabled; */
+ TLan_ClearBit( TLAN_NET_SIO_ETXEN, sio );
+ for ( place = 0x80; place; place >>= 1 ) {
+ TLan_SetBit( TLAN_NET_SIO_ECLOK, sio );
+ if ( TLan_GetBit( TLAN_NET_SIO_EDATA, sio ) )
+ *data |= place;
+ TLan_ClearBit( TLAN_NET_SIO_ECLOK, sio );
+ }
+
+ TLan_SetBit( TLAN_NET_SIO_ETXEN, sio );
+ if ( ! stop ) {
+ TLan_ClearBit( TLAN_NET_SIO_EDATA, sio ); /* Ack = 0 */
+ TLan_SetBit( TLAN_NET_SIO_ECLOK, sio );
+ TLan_ClearBit( TLAN_NET_SIO_ECLOK, sio );
+ } else {
+ TLan_SetBit( TLAN_NET_SIO_EDATA, sio ); /* No ack = 1 (?) */
+ TLan_SetBit( TLAN_NET_SIO_ECLOK, sio );
+ TLan_ClearBit( TLAN_NET_SIO_ECLOK, sio );
+ TLan_ClearBit( TLAN_NET_SIO_EDATA, sio ); /* STOP, raise data while clock is high */
+ TLan_SetBit( TLAN_NET_SIO_ECLOK, sio );
+ TLan_SetBit( TLAN_NET_SIO_EDATA, sio );
+ }
+
+} /* TLan_EeReceiveByte */
+
+
+
+
+ /***************************************************************
+ * TLan_EeReadByte
+ *
+ * Returns:
+ * No error = 0, else, the stage at which the error
+ * occured.
+ * Parms:
+ * io_base The IO port base address for the
+ * TLAN device with the EEPROM to
+ * use.
+ * ee_addr The address of the byte in the
+ * EEPROM whose contents are to be
+ * retrieved.
+ * data An address to a char to hold the
+ * data obtained from the EEPROM.
+ *
+ * This function reads a byte of information from an byte
+ * cell in the EEPROM.
+ *
+ **************************************************************/
+
+int TLan_EeReadByte( struct device *dev, u8 ee_addr, u8 *data )
+{
+ int err;
+
+ if ( dev->interrupt == 0 )
+ cli();
+ dev->interrupt++;
+
+ TLan_EeSendStart( dev->base_addr );
+ err = TLan_EeSendByte( dev->base_addr, 0xA0, TLAN_EEPROM_ACK );
+ if (err)
+ return 1;
+ err = TLan_EeSendByte( dev->base_addr, ee_addr, TLAN_EEPROM_ACK );
+ if (err)
+ return 2;
+ TLan_EeSendStart( dev->base_addr );
+ err = TLan_EeSendByte( dev->base_addr, 0xA1, TLAN_EEPROM_ACK );
+ if (err)
+ return 3;
+ TLan_EeReceiveByte( dev->base_addr, data, TLAN_EEPROM_STOP );
+
+ dev->interrupt--;
+ if ( dev->interrupt == 0 )
+ sti();
+
+ return 0;
+
+} /* TLan_EeReadByte */
+
+
+
+
+
--- /dev/null
+#ifndef TLAN_H
+#define TLAN_H
+/********************************************************************
+ *
+ * Linux ThunderLAN Driver
+ *
+ * tlan.h
+ * by James Banks, james.banks@caldera.com
+ *
+ * (C) 1997-1998 Caldera, Inc.
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU Public License, incorporated herein by reference.
+ *
+ ** This file is best viewed/edited with tabstop=4, colums>=132
+ *
+ ********************************************************************/
+
+
+#include <asm/io.h>
+#include <asm/types.h>
+#include <linux/netdevice.h>
+
+#if LINUX_VERSION_CODE <= 0x20100
+#define net_device_stats enet_statistics
+#endif
+
+
+
+
+ /*****************************************************************
+ * TLan Definitions
+ *
+ ****************************************************************/
+
+#define FALSE 0
+#define TRUE 1
+
+#define TLAN_MIN_FRAME_SIZE 64
+#define TLAN_MAX_FRAME_SIZE 1600
+
+#define TLAN_NUM_RX_LISTS 4
+#define TLAN_NUM_TX_LISTS 8
+
+#define TLAN_IGNORE 0
+#define TLAN_RECORD 1
+
+#define TLAN_DBG(lvl, format, args...) if (debug&lvl) printk( format, ##args );
+#define TLAN_DEBUG_GNRL 0x0001
+#define TLAN_DEBUG_TX 0x0002
+#define TLAN_DEBUG_RX 0x0004
+#define TLAN_DEBUG_LIST 0x0008
+
+
+
+
+ /*****************************************************************
+ * Device Identification Definitions
+ *
+ ****************************************************************/
+
+#define PCI_DEVICE_ID_NETELLIGENT_10 0xAE34
+#define PCI_DEVICE_ID_NETELLIGENT_10_100 0xAE32
+#define PCI_DEVICE_ID_NETFLEX_3P_INTEGRATED 0xAE35
+#define PCI_DEVICE_ID_NETFLEX_3P 0xF130
+#define PCI_DEVICE_ID_NETFLEX_3P_BNC 0xF150
+#define PCI_DEVICE_ID_NETELLIGENT_10_100_PROLIANT 0xAE43
+#define PCI_DEVICE_ID_NETELLIGENT_10_100_DUAL 0xAE40
+#define PCI_DEVICE_ID_DESKPRO_4000_5233MMX 0xB011
+#define PCI_DEVICE_ID_OC_2326 0x0014
+
+typedef struct tlan_adapter_entry {
+ u16 vendorId;
+ u16 deviceId;
+ char *deviceLabel;
+ u32 flags;
+} TLanAdapterEntry;
+
+#define TLAN_ADAPTER_NONE 0x00000000
+#define TLAN_ADAPTER_UNMANAGED_PHY 0x00000001
+#define TLAN_ADAPTER_BIT_RATE_PHY 0x00000002
+#define TLAN_ADAPTER_USE_INTERN_10 0x00000004
+#define TLAN_ADAPTER_ACTIVITY_LED 0x00000008
+
+#define TLAN_SPEED_DEFAULT 0
+#define TLAN_SPEED_10 10
+#define TLAN_SPEED_100 100
+
+#define TLAN_DUPLEX_DEFAULT 0
+#define TLAN_DUPLEX_HALF 1
+#define TLAN_DUPLEX_FULL 2
+
+
+
+
+ /*****************************************************************
+ * Rx/Tx List Definitions
+ *
+ ****************************************************************/
+
+#define TLAN_BUFFERS_PER_LIST 10
+#define TLAN_LAST_BUFFER 0x80000000
+#define TLAN_CSTAT_UNUSED 0x8000
+#define TLAN_CSTAT_FRM_CMP 0x4000
+#define TLAN_CSTAT_READY 0x3000
+#define TLAN_CSTAT_EOC 0x0800
+#define TLAN_CSTAT_RX_ERROR 0x0400
+#define TLAN_CSTAT_PASS_CRC 0x0200
+#define TLAN_CSTAT_DP_PR 0x0100
+
+
+typedef struct tlan_buffer_ref_tag {
+ u32 count;
+ u32 address;
+} TLanBufferRef;
+
+
+typedef struct tlan_list_tag {
+ u32 forward;
+ u16 cStat;
+ u16 frameSize;
+ TLanBufferRef buffer[TLAN_BUFFERS_PER_LIST];
+} TLanList;
+
+
+typedef u8 TLanBuffer[TLAN_MAX_FRAME_SIZE];
+
+
+
+
+ /*****************************************************************
+ * PHY definitions
+ *
+ ****************************************************************/
+
+#define TLAN_PHY_MAX_ADDR 0x1F
+#define TLAN_PHY_NONE 0x20
+
+
+
+
+ /*****************************************************************
+ * TLAN Private Information Structure
+ *
+ ****************************************************************/
+
+typedef struct tlan_private_tag {
+ struct device *nextDevice;
+ void *dmaStorage;
+ u8 *padBuffer;
+ TLanList *rxList;
+ u8 *rxBuffer;
+ u32 rxHead;
+ u32 rxTail;
+ u32 rxEocCount;
+ TLanList *txList;
+ u8 *txBuffer;
+ u32 txHead;
+ u32 txInProgress;
+ u32 txTail;
+ u32 txBusyCount;
+ u32 phyOnline;
+ u32 timerSetAt;
+ u32 timerType;
+ struct timer_list timer;
+ struct net_device_stats stats;
+ TLanAdapterEntry *adapter;
+ u32 adapterRev;
+ u32 aui;
+ u32 debug;
+ u32 duplex;
+ u32 phy[2];
+ u32 phyNum;
+ u32 sa_int;
+ u32 speed;
+ u8 tlanRev;
+ u8 tlanFullDuplex;
+ char devName[8];
+} TLanPrivateInfo;
+
+
+
+
+ /*****************************************************************
+ * TLan Driver Timer Definitions
+ *
+ ****************************************************************/
+
+#define TLAN_TIMER_LINK 1
+#define TLAN_TIMER_ACTIVITY 2
+#define TLAN_TIMER_PHY_PDOWN 3
+#define TLAN_TIMER_PHY_PUP 4
+#define TLAN_TIMER_PHY_RESET 5
+#define TLAN_TIMER_PHY_START_LINK 6
+#define TLAN_TIMER_PHY_FINISH_AN 7
+#define TLAN_TIMER_FINISH_RESET 8
+
+#define TLAN_TIMER_ACT_DELAY 10
+
+
+
+
+ /*****************************************************************
+ * TLan Driver Eeprom Definitions
+ *
+ ****************************************************************/
+
+#define TLAN_EEPROM_ACK 0
+#define TLAN_EEPROM_STOP 1
+
+
+
+
+ /*****************************************************************
+ * Host Register Offsets and Contents
+ *
+ ****************************************************************/
+
+#define TLAN_HOST_CMD 0x00
+#define TLAN_HC_GO 0x80000000
+#define TLAN_HC_STOP 0x40000000
+#define TLAN_HC_ACK 0x20000000
+#define TLAN_HC_CS_MASK 0x1FE00000
+#define TLAN_HC_EOC 0x00100000
+#define TLAN_HC_RT 0x00080000
+#define TLAN_HC_NES 0x00040000
+#define TLAN_HC_AD_RST 0x00008000
+#define TLAN_HC_LD_TMR 0x00004000
+#define TLAN_HC_LD_THR 0x00002000
+#define TLAN_HC_REQ_INT 0x00001000
+#define TLAN_HC_INT_OFF 0x00000800
+#define TLAN_HC_INT_ON 0x00000400
+#define TLAN_HC_AC_MASK 0x000000FF
+#define TLAN_CH_PARM 0x04
+#define TLAN_DIO_ADR 0x08
+#define TLAN_DA_ADR_INC 0x8000
+#define TLAN_DA_RAM_ADR 0x4000
+#define TLAN_HOST_INT 0x0A
+#define TLAN_HI_IV_MASK 0x1FE0
+#define TLAN_HI_IT_MASK 0x001C
+#define TLAN_DIO_DATA 0x0C
+
+
+/* ThunderLAN Internal Register DIO Offsets */
+
+#define TLAN_NET_CMD 0x00
+#define TLAN_NET_CMD_NRESET 0x80
+#define TLAN_NET_CMD_NWRAP 0x40
+#define TLAN_NET_CMD_CSF 0x20
+#define TLAN_NET_CMD_CAF 0x10
+#define TLAN_NET_CMD_NOBRX 0x08
+#define TLAN_NET_CMD_DUPLEX 0x04
+#define TLAN_NET_CMD_TRFRAM 0x02
+#define TLAN_NET_CMD_TXPACE 0x01
+#define TLAN_NET_SIO 0x01
+#define TLAN_NET_SIO_MINTEN 0x80
+#define TLAN_NET_SIO_ECLOK 0x40
+#define TLAN_NET_SIO_ETXEN 0x20
+#define TLAN_NET_SIO_EDATA 0x10
+#define TLAN_NET_SIO_NMRST 0x08
+#define TLAN_NET_SIO_MCLK 0x04
+#define TLAN_NET_SIO_MTXEN 0x02
+#define TLAN_NET_SIO_MDATA 0x01
+#define TLAN_NET_STS 0x02
+#define TLAN_NET_STS_MIRQ 0x80
+#define TLAN_NET_STS_HBEAT 0x40
+#define TLAN_NET_STS_TXSTOP 0x20
+#define TLAN_NET_STS_RXSTOP 0x10
+#define TLAN_NET_STS_RSRVD 0x0F
+#define TLAN_NET_MASK 0x03
+#define TLAN_NET_MASK_MASK7 0x80
+#define TLAN_NET_MASK_MASK6 0x40
+#define TLAN_NET_MASK_MASK5 0x20
+#define TLAN_NET_MASK_MASK4 0x10
+#define TLAN_NET_MASK_RSRVD 0x0F
+#define TLAN_NET_CONFIG 0x04
+#define TLAN_NET_CFG_RCLK 0x8000
+#define TLAN_NET_CFG_TCLK 0x4000
+#define TLAN_NET_CFG_BIT 0x2000
+#define TLAN_NET_CFG_RXCRC 0x1000
+#define TLAN_NET_CFG_PEF 0x0800
+#define TLAN_NET_CFG_1FRAG 0x0400
+#define TLAN_NET_CFG_1CHAN 0x0200
+#define TLAN_NET_CFG_MTEST 0x0100
+#define TLAN_NET_CFG_PHY_EN 0x0080
+#define TLAN_NET_CFG_MSMASK 0x007F
+#define TLAN_MAN_TEST 0x06
+#define TLAN_DEF_VENDOR_ID 0x08
+#define TLAN_DEF_DEVICE_ID 0x0A
+#define TLAN_DEF_REVISION 0x0C
+#define TLAN_DEF_SUBCLASS 0x0D
+#define TLAN_DEF_MIN_LAT 0x0E
+#define TLAN_DEF_MAX_LAT 0x0F
+#define TLAN_AREG_0 0x10
+#define TLAN_AREG_1 0x16
+#define TLAN_AREG_2 0x1C
+#define TLAN_AREG_3 0x22
+#define TLAN_HASH_1 0x28
+#define TLAN_HASH_2 0x2C
+#define TLAN_GOOD_TX_FRMS 0x30
+#define TLAN_TX_UNDERUNS 0x33
+#define TLAN_GOOD_RX_FRMS 0x34
+#define TLAN_RX_OVERRUNS 0x37
+#define TLAN_DEFERRED_TX 0x38
+#define TLAN_CRC_ERRORS 0x3A
+#define TLAN_CODE_ERRORS 0x3B
+#define TLAN_MULTICOL_FRMS 0x3C
+#define TLAN_SINGLECOL_FRMS 0x3E
+#define TLAN_EXCESSCOL_FRMS 0x40
+#define TLAN_LATE_COLS 0x41
+#define TLAN_CARRIER_LOSS 0x42
+#define TLAN_ACOMMIT 0x43
+#define TLAN_LED_REG 0x44
+#define TLAN_LED_ACT 0x10
+#define TLAN_LED_LINK 0x01
+#define TLAN_BSIZE_REG 0x45
+#define TLAN_MAX_RX 0x46
+#define TLAN_INT_DIS 0x48
+#define TLAN_ID_TX_EOC 0x04
+#define TLAN_ID_RX_EOF 0x02
+#define TLAN_ID_RX_EOC 0x01
+
+
+
+/* ThunderLAN Interrupt Codes */
+
+#define TLAN_INT_NUMBER_OF_INTS 8
+
+#define TLAN_INT_NONE 0x0000
+#define TLAN_INT_TX_EOF 0x0001
+#define TLAN_INT_STAT_OVERFLOW 0x0002
+#define TLAN_INT_RX_EOF 0x0003
+#define TLAN_INT_DUMMY 0x0004
+#define TLAN_INT_TX_EOC 0x0005
+#define TLAN_INT_STATUS_CHECK 0x0006
+#define TLAN_INT_RX_EOC 0x0007
+
+
+
+/* ThunderLAN MII Registers */
+
+/* Generic MII/PHY Registers */
+
+#define MII_GEN_CTL 0x00
+#define MII_GC_RESET 0x8000
+#define MII_GC_LOOPBK 0x4000
+#define MII_GC_SPEEDSEL 0x2000
+#define MII_GC_AUTOENB 0x1000
+#define MII_GC_PDOWN 0x0800
+#define MII_GC_ISOLATE 0x0400
+#define MII_GC_AUTORSRT 0x0200
+#define MII_GC_DUPLEX 0x0100
+#define MII_GC_COLTEST 0x0080
+#define MII_GC_RESERVED 0x007F
+#define MII_GEN_STS 0x01
+#define MII_GS_100BT4 0x8000
+#define MII_GS_100BTXFD 0x4000
+#define MII_GS_100BTXHD 0x2000
+#define MII_GS_10BTFD 0x1000
+#define MII_GS_10BTHD 0x0800
+#define MII_GS_RESERVED 0x07C0
+#define MII_GS_AUTOCMPLT 0x0020
+#define MII_GS_RFLT 0x0010
+#define MII_GS_AUTONEG 0x0008
+#define MII_GS_LINK 0x0004
+#define MII_GS_JABBER 0x0002
+#define MII_GS_EXTCAP 0x0001
+#define MII_GEN_ID_HI 0x02
+#define MII_GEN_ID_LO 0x03
+#define MII_GIL_OUI 0xFC00
+#define MII_GIL_MODEL 0x03F0
+#define MII_GIL_REVISION 0x000F
+#define MII_AN_ADV 0x04
+#define MII_AN_LPA 0x05
+#define MII_AN_EXP 0x06
+
+/* ThunderLAN Specific MII/PHY Registers */
+
+#define TLAN_TLPHY_ID 0x10
+#define TLAN_TLPHY_CTL 0x11
+#define TLAN_TC_IGLINK 0x8000
+#define TLAN_TC_SWAPOL 0x4000
+#define TLAN_TC_AUISEL 0x2000
+#define TLAN_TC_SQEEN 0x1000
+#define TLAN_TC_MTEST 0x0800
+#define TLAN_TC_RESERVED 0x07F8
+#define TLAN_TC_NFEW 0x0004
+#define TLAN_TC_INTEN 0x0002
+#define TLAN_TC_TINT 0x0001
+#define TLAN_TLPHY_STS 0x12
+#define TLAN_TS_MINT 0x8000
+#define TLAN_TS_PHOK 0x4000
+#define TLAN_TS_POLOK 0x2000
+#define TLAN_TS_TPENERGY 0x1000
+#define TLAN_TS_RESERVED 0x0FFF
+
+
+#define CIRC_INC( a, b ) if ( ++a >= b ) a = 0
+
+/* Routines to access internal registers. */
+
+inline u8 TLan_DioRead8(u16 base_addr, u16 internal_addr)
+{
+ outw(internal_addr, base_addr + TLAN_DIO_ADR);
+ return (inb((base_addr + TLAN_DIO_DATA) + (internal_addr & 0x3)));
+
+} /* TLan_DioRead8 */
+
+
+
+
+inline u16 TLan_DioRead16(u16 base_addr, u16 internal_addr)
+{
+ outw(internal_addr, base_addr + TLAN_DIO_ADR);
+ return (inw((base_addr + TLAN_DIO_DATA) + (internal_addr & 0x2)));
+
+} /* TLan_DioRead16 */
+
+
+
+
+inline u32 TLan_DioRead32(u16 base_addr, u16 internal_addr)
+{
+ outw(internal_addr, base_addr + TLAN_DIO_ADR);
+ return (inl(base_addr + TLAN_DIO_DATA));
+
+} /* TLan_DioRead32 */
+
+
+
+
+inline void TLan_DioWrite8(u16 base_addr, u16 internal_addr, u8 data)
+{
+ outw(internal_addr, base_addr + TLAN_DIO_ADR);
+ outb(data, base_addr + TLAN_DIO_DATA + (internal_addr & 0x3));
+
+}
+
+
+
+
+inline void TLan_DioWrite16(u16 base_addr, u16 internal_addr, u16 data)
+{
+ outw(internal_addr, base_addr + TLAN_DIO_ADR);
+ outw(data, base_addr + TLAN_DIO_DATA + (internal_addr & 0x2));
+
+}
+
+
+
+
+inline void TLan_DioWrite32(u16 base_addr, u16 internal_addr, u32 data)
+{
+ outw(internal_addr, base_addr + TLAN_DIO_ADR);
+ outl(data, base_addr + TLAN_DIO_DATA + (internal_addr & 0x2));
+
+}
+
+
+
+#if 0
+inline void TLan_ClearBit(u8 bit, u16 port)
+{
+ outb_p(inb_p(port) & ~bit, port);
+}
+
+
+
+
+inline int TLan_GetBit(u8 bit, u16 port)
+{
+ return ((int) (inb_p(port) & bit));
+}
+
+
+
+
+inline void TLan_SetBit(u8 bit, u16 port)
+{
+ outb_p(inb_p(port) | bit, port);
+}
+#endif
+
+#define TLan_ClearBit( bit, port ) outb_p(inb_p(port) & ~bit, port)
+#define TLan_GetBit( bit, port ) ((int) (inb_p(port) & bit))
+#define TLan_SetBit( bit, port ) outb_p(inb_p(port) | bit, port)
+
+
+inline u32 xor( u32 a, u32 b )
+{
+ return ( ( a && ! b ) || ( ! a && b ) );
+}
+#define XOR8( a, b, c, d, e, f, g, h ) xor( a, xor( b, xor( c, xor( d, xor( e, xor( f, xor( g, h ) ) ) ) ) ) )
+#define DA( a, bit ) ( ( (u8) a[bit/8] ) & ( (u8) ( 1 << bit%8 ) ) )
+
+inline u32 TLan_HashFunc( u8 *a )
+{
+ u32 hash;
+
+ hash = XOR8( DA(a,0), DA(a, 6), DA(a,12), DA(a,18), DA(a,24), DA(a,30), DA(a,36), DA(a,42) );
+ hash |= XOR8( DA(a,1), DA(a, 7), DA(a,13), DA(a,19), DA(a,25), DA(a,31), DA(a,37), DA(a,43) ) << 1;
+ hash |= XOR8( DA(a,2), DA(a, 8), DA(a,14), DA(a,20), DA(a,26), DA(a,32), DA(a,38), DA(a,44) ) << 2;
+ hash |= XOR8( DA(a,3), DA(a, 9), DA(a,15), DA(a,21), DA(a,27), DA(a,33), DA(a,39), DA(a,45) ) << 3;
+ hash |= XOR8( DA(a,4), DA(a,10), DA(a,16), DA(a,22), DA(a,28), DA(a,34), DA(a,40), DA(a,46) ) << 4;
+ hash |= XOR8( DA(a,5), DA(a,11), DA(a,17), DA(a,23), DA(a,29), DA(a,35), DA(a,41), DA(a,47) ) << 5;
+
+ return hash;
+
+}
+
+
+
+
+#endif
+Fri Jan 2 18:00 1998 Gerard Roudier (groudier@club-internet.fr)
+ * Revision 2.5f
+ - Use FAST-5 instead of SLOW for slow scsi devices according to
+ new SPI-2 draft.
+ - Make some changes in order to accomodate with 875 rev <= 3
+ device errata listing 397. Minor consequences are:
+ . Leave use of PCI Write and Invalidate under user control.
+ Now, by default the driver does not enable PCI MWI and option
+ 'specf:y' is required in order to enable this feature.
+ . Memory Read Line is not enabled for 875 and 875-like chips.
+ . Programmed burst length set to 64 DWORDS (instead of 128).
+ (Note: SYMBIOS uses 32 DWORDS for the SDMS BIOS)
+
+Sun Oct 26 12:00 1997 Gerard Roudier (groudier@club-internet.fr)
+ * revision 2.5e
+ - Add 'buschk' boot option.
+ This option enables checking of SCSI BUS data lines after SCSI
+ RESET (set by default). (Submitted by Richard Waltham).
+ - Update the README file.
+
+Sat Oct 4 18:00 1997 Gerard Roudier (groudier@club-internet.fr)
+ * revision 2.5d
+ - Dispatch CONDITION MET and RESERVATION CONFLICT scsi status
+ as OK driver status.
+ - Update the README file and the Symbios NVRAM format definition
+ with removable media flags values (available with SDMS 4.09).
+
+Sat Sep 20 21:00 1997 Gerard Roudier (groudier@club-internet.fr)
+ * revision 2.5c
+ - Several PCI configuration registers fix-ups for powerpc.
+ (Patch sent by Cort).
+
+Thu Aug 28 10:00 1997 Gerard Roudier (groudier@club-internet.fr)
+ * revision 2.5b
+ - Add 'ncr53c8xx' char pointer variable. This variable allows to
+ pass a boot command to the driver when it is loaded as a module.
+ Option separator is ' ' instead of ','. Example:
+ insmod <mod_path>/ncr53c8xx.o ncr53c8xx='verb:2 sync:0 specf:n'
+ - Always use 'driver_setup.settle_delay' for internal resets.
+ 2 seconds hardcoded is sometimes too short. Suggested by Richard W.
+ This delay may be shortenned in order to avoid spurious timeouts.
+ - Fix release module stuff that failed for more than 1 controller.
+ - For linux versions > 1.3.70, trust the 'dev_id' parameter passed
+ to the interrupt handler (dev_id = struct ncb *).
+ - Fix up in 'ncr_log_hard_error()' when the DSP points outside scripts.
+ Suggested by Stefan Esser.
+
+Tue Aug 23 23:43 1997 Gerard Roudier (groudier@club-internet.fr)
+ * revision 2.5a
+ - Update Configure.help for inclusion in linux-2.1.51/2/3
+ - Use BASE_2 address from PCI config space instead of some
+ IO register for getting the on-board SRAM bus address.
+ - Remove error testing of pcibios_read/write functions.
+ These functions are intended to be used for successfully
+ detected PCI devices. Expecting error condition from them
+ is nothing but paranoia.
+
+Thu Aug 21 23:00 1997 Gerard Roudier (groudier@club-internet.fr)
+ * revision 2.5
+ - 53C860 chip support fix.
+ - Move the 'host_status' to the last DWORD of the CCB header.
+ This header is copied back by the script processor. This
+ guarantees that the header is entirely copied back over
+ the PCI when the CPU completes a CCB.
+ - (re)read ISTAT prior to scanning CCBs for completion. This
+ ensure that any posted buffer are flushed prior CCBs scan.
+ - Support for BIG ENDIAN cpu. Added by Cort <cort@cs.nmt.edu>.
+ Initial patch did'nt support disconnections and tagged commands.
+ I've completed the patch and it seems that all is ok now.
+ Only some powerpc under 2.1.X is supported for the moment.
+ - Misc. trivial fixes and cleanups.
+
Sat July 26 18:00 1997 Gerard Roudier (groudier@club-internet.fr)
* revision 2.4
Several clean-ups:
21 Rue Carnot
95170 DEUIL LA BARRE - FRANCE
-19 June 1997
+2 January 1998
===============================================================================
1. Introduction
10.3 Advised boot setup commands
10.4 PCI configuration fix-up boot option
10.5 Serial NVRAM support boot option
+ 10.6 SCSI BUS checking boot option
11. Some constants and flags of the ncr53c8xx.h header file
12. Installation
12.1 Provided files
14. Known problems
14.1 Tagged commands with Iomega Jaz device
14.2 Device names change when another controller is added
+ 14.3 Using only 8 bit devices with a WIDE SCSI controller.
+ 14.4 Possible data corruption during a Memory Write and Invalidate
15. SCSI problem troubleshooting
16. Synchonous transfer negotiation tables
16.1 Synchronous timings for 53C875 and 53C860 Ultra-SCSI controllers
17.1 Features
17.2 Symbios NVRAM layout
17.3 Tekram NVRAM layout
+18. Support for Big Endian
+ 18.1 Big Endian CPU
+ 18.2 NCR chip in Big Endian mode of operations
===============================================================================
driver, configuration parameters and control commands available
through the proc SCSI file system read / write operations.
-This driver has been tested OK with linux/i386 and Linux/Alpha.
+This driver has been tested OK with linux/i386, Linux/Alpha and Linux/PPC.
Latest driver version and patches are available at:
CONFIG_SCSI_NCR53C8XX_IOMAPPED (default answer: n)
Answer "y" if you suspect your mother board to not allow memory mapped I/O.
- May slow down performance a little.
+ May slow down performance a little. This option is required by
+ Linux/PPC and is used no matter what you select here. Linux/PPC
+ suffers no performance loss with this option since all IO is memory
+ mapped anyway.
CONFIG_SCSI_NCR53C8XX_TAGGED_QUEUE (default answer: n)
Answer "y" if you are sure that all your SCSI devices that are able to
10.1 Syntax
-Setup commands can be passed to the driver at boot time.
+Setup commands can be passed to the driver either at boot time or as a
+string variable using 'insmod'.
+
A boot setup command for the ncr53c8xx driver begins with the driver name
"ncr53c8xx=". The kernel syntax parser then expects an optionnal list of
integers separated with comma followed by an optionnal list of comma-
- set synchronous negotiation speed to 10 Mega-transfers / second.
- set DEBUG_NEGO flag.
-For the moment, the integer list of arguments is disgarded by the driver.
+Since comma seems not to be allowed when defining a string variable using
+'insmod', the driver also accepts <space> as option separator.
+The following command will install driver module with the same options as
+above.
+
+insmod ncr53c8xx.o ncr53c8xx="tags:4 sync:10 debug:0x200"
+
+For the moment, the integer list of arguments is discarded by the driver.
It will be used in the future in order to allow a per controller setup.
Each string argument must be specified as "keyword:value". Only lower-case
Special features
Only apply to 810A, 825A, 860 and 875 controllers.
Have no effect with normal 810 and 825.
- specf:y enabled
- specf:n disabled
+ specf:y (or 1) enabled
+ specf:n (or 0) disabled
+ specf:3 enabled except Memory Write And Invalidate
+ The default driver setup is 'specf:3'. As a consequence, option 'specf:y'
+ must be specified in the boot setup command to enable Memory Write And
+ Invalidate.
Ultra SCSI support
Only apply to 860 and 875 controllers.
pcifix:<option bits>
Available option bits:
+ 0x0: No attempt to fix PCI configuration space registers values.
0x1: Set PCI cache-line size register if not set.
0x2: Set write and invalidate bit in PCI command register.
0x4: Increase if necessary PCI latency timer according to burst max.
nvram:n do not look for serial NVRAM
nvram:y test controllers for onboard serial NVRAM
+Check SCSI BUS
+ buschk:<option bits>
+
+ Available option bits:
+ 0x0: No check.
+ 0x1: Check and donnot attach the controller on error.
+ 0x2: Check and just warn on error.
+
Boot fail safe
safe:y load the following assumed fail safe initial setup
settle time 10 seconds settle:10
differential support from BIOS settings diff:1
irq mode from BIOS settings irqm:1
+ SCSI BUS check donnot attach on error buschk:1
10.3 Advised boot setup commands
If the driver has been configured with default options, the equivalent
boot setup is:
- ncr53c8xx=mpar:y,spar:y,disc:y,specf:y,fsn:n,ultra:y,fsn:n,revprob:n,verb:1\
+ ncr53c8xx=mpar:y,spar:y,disc:y,specf:3,fsn:n,ultra:y,fsn:n,revprob:n,verb:1\
tags:0,sync:50,debug:0,burst:7,led:0,wide:1,settle:2,diff:0,irqm:0
For an installation diskette or a safe but not fast system,
My personnal system works flawlessly with the following equivalent setup:
- ncr53c8xx=mpar:y,spar:y,disc:y,specf:y,fsn:n,ultra:y,fsn:n,revprob:n,verb:1\
+ ncr53c8xx=mpar:y,spar:y,disc:y,specf:1,fsn:n,ultra:y,fsn:n,revprob:n,verb:1\
tags:8,sync:12,debug:0,burst:7,led:1,wide:1,settle:2,diff:0,irqm:0
The driver prints its actual setup when verbosity level is 2. You can try
use its setup for synchronous and wide negotiations.
+10.6 SCSI BUS checking boot option.
+
+When this option is set to a non-zero value, the driver checks SCSI lines
+logic state, 100 micro-seconds after having asserted the SCSI RESET line.
+The driver just reads SCSI lines and checks all lines read FALSE except RESET.
+Since SCSI devices shall release the BUS at most 800 nano-seconds after SCSI
+RESET has been asserted, any signal to TRUE may indicate a SCSI BUS problem.
+Unfortunately, the following common SCSI BUS problems are not detected:
+- Only 1 terminator installed.
+- Misplaced terminators.
+- Bad quality terminators.
+On the other hand, either bad cabling, broken devices, not conformant
+devices, ... may cause a SCSI signal to be wrong when te driver reads it.
+
+
11. Some constants and flags of the ncr53c8xx.h header file
Some of these are defined from the configuration parameters. To
- Make appropriate changes in the fstab.
- Use the 'scsidev' tool from Eric Youngdale.
+14.3 Using only 8 bit devices with a WIDE SCSI controller.
+
+When only 8 bit NARROW devices are connected to a 16 bit WIDE SCSI controller,
+you must ensure that lines of the wide part of the SCSI BUS are pulled-up.
+This can be achieved by ENABLING the WIDE TERMINATOR portion of the SCSI
+controller card.
+The TYAN 1365 documentation revision 1.2 is not correct about such settings.
+(page 10, figure 3.3).
+
+14.4 Possible data corruption during a Memory Write and Invalidate
+
+This problem is described in SYMBIOS DEL 397, Part Number 69-039241, ITEM 4.
+
+In some complex situations, 53C875 chips revision <= 3 may start a PCI
+Write and Invalidate Command at a not cache-line-aligned 4 DWORDS boundary.
+This is only possible when Cache Line Size is 8 DWORDS or greater.
+Pentium systems use a 8 DWORDS cache line size and so are concerned by
+this chip bug, unlike i486 systems that use a 4 DWORDS cache line size.
+
+When this situation occurs, the chip may complete the Write and Invalidate
+command after having only filled part of the last cache line involved in
+the transfer, leaving to data corruption the remainder of this cache line.
+
+Not using Write And Invalidate obviously gets rid of this chip bug, and so
+it is now the default setting of the driver.
+However, for people like me who want to enable this feature, I have added
+part of a work-around suggested by SYMBIOS. This work-around resets the
+addressing logic when the DATA IN phase is entered and so prevents the bug
+from being triggered for the first SCSI MOVE of the phase. This work-around
+should be enough according to the following:
+
+The only driver internal data structure that is greater than 8 DWORDS and
+that is moved by the SCRIPTS processor is the 'CCB header' that contains
+the context of the SCSI transfer. This data structure is aligned on 8 DWORDS
+boundary (Pentium Cache Line Size), and so is immune to this chip bug, at
+least on Pentium systems.
+But the conditions of this bug can be met when a SCSI read command is
+performed using a buffer that is 4 DWORDS but not cache-line aligned.
+This cannot happen under Linux when scatter/gather lists are used since
+they only refer to system buffers that are well aligned. So, a work around
+may only be needed under Linux when a scatter/gather list is not used and
+when the SCSI DATA IN phase is reentered after a phase mismatch.
+
+
15. SCSI problem troubleshooting
Most SCSI problems are due to a non conformant SCSI bus or to buggy
controller set up
00 30 00 00 00 00 07 00 00 00 00 00 00 00 07 04 10 04 00 00
- | | |
- | | -- host ID
+ | | | |
+ | | | -- host ID
+ | | |
+ | | --Removable Media Support
+ | | 0x00 = none
+ | | 0x01 = Bootable Device
+ | | 0x02 = All with Media
+ | |
| --flag bits 2
| 0x00000001= scan order hi->low
| (default 0x00 - scan low->hi)
current set up for any of the controllers.
default set up is identical for 53c810a and 53c875 NVRAM
+(Removable Media added Symbios BIOS version 4.09)
-----------------------------------------------------------
boot configuration
0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000
0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0x0000 0xfbbc
-===============================================================================
-End of NCR53C8XX driver README file
+18. Support for Big Endian
+
+The PCI local bus has been primarily designed for x86 architecture.
+As a consequence, PCI devices generally expect DWORDS using little endian
+byte ordering.
+
+18.1 Big Endian CPU
+In order to support NCR chips on a Big Endian architecture the driver has to
+perform byte reordering each time it is needed. This feature has been
+added to the driver by Cort <cort@cs.nmt.edu> and is available in driver
+version 2.5 and later ones. For the moment Big Endian support has only
+been tested on Linux/PPC (PowerPC).
+18.2 NCR chip in Big Endian mode of operations
+
+It can be read in SYMBIOS documentation that some chips support a special
+Big Endian mode, on paper: 53C815, 53C825A, 53C875, 53C875N, 53C895.
+This mode of operations is not software-selectable, but needs pin named
+BigLit to be pulled-up. Using this mode, most of byte reorderings should
+be avoided when the driver is running on a Big Endian CPU.
+Driver version 2.5 is also, in theory, ready for this feature.
+
+===============================================================================
+End of NCR53C8XX driver README file
/*
* eata.c - Low-level driver for EATA/DMA SCSI host adapters.
*
+ * 4 Apr 1998 rev. 4.02 for linux 2.0.33 and 2.1.92
+ * io_port is now unsigned long.
+ *
+ * 17 Mar 1998 rev. 4.01 for linux 2.0.33 and 2.1.88
+ * Use new scsi error handling code (if linux version >= 2.1.88).
+ * Use new interrupt code.
+ *
+ *
* 12 Sep 1997 rev. 3.11 for linux 2.0.30 and 2.1.55
* Use of udelay inside the wait loops to avoid timeout
* problems with fast cpus.
* This driver is based on the CAM (Common Access Method Committee)
* EATA (Enhanced AT Bus Attachment) rev. 2.0A, using DMA protocol.
*
- * Copyright (C) 1994-1997 Dario Ballabio (dario@milano.europe.dg.com)
+ * Copyright (C) 1994-1998 Dario Ballabio (dario@milano.europe.dg.com)
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that redistributions of source
#undef DEBUG_LINKED_COMMANDS
#undef DEBUG_DETECT
#undef DEBUG_INTERRUPT
-#undef DEBUG_STATISTICS
#undef DEBUG_RESET
-#undef DEBUG_SMP
#define MAX_ISA 4
#define MAX_VESA 0
#define MAX_CHANNEL 4
#define MAX_LUN 32
#define MAX_TARGET 32
-#define MAX_IRQ 16
#define MAX_MAILBOXES 64
#define MAX_SGLIST 64
-#define MAX_LARGE_SGLIST 252
+#define MAX_LARGE_SGLIST 122
#define MAX_INTERNAL_RETRIES 64
#define MAX_CMD_PER_LUN 2
#define MAX_TAGGED_CMD_PER_LUN (MAX_MAILBOXES - MAX_CMD_PER_LUN)
-#define SKIP UINT_MAX
+#define SKIP ULONG_MAX
#define FALSE 0
#define TRUE 1
#define FREE 0
ulong data_len; /* Number of valid bytes after this field */
ulong sign; /* ASCII "EATA" signature */
unchar :4, /* unused low nibble */
- version:4; /* EATA version, should be 0x1 */
+ version:4; /* EATA version, should be 0x1 */
unchar ocsena:1, /* Overlap Command Support Enabled */
- tarsup:1, /* Target Mode Supported */
+ tarsup:1, /* Target Mode Supported */
trnxfr:1, /* Truncate Transfer Cmd NOT Necessary */
morsup:1, /* More Supported */
- dmasup:1, /* DMA Supported */
- drqvld:1, /* DRQ Index (DRQX) is valid */
- ata:1, /* This is an ATA device */
- haaval:1; /* Host Adapter Address Valid */
+ dmasup:1, /* DMA Supported */
+ drqvld:1, /* DRQ Index (DRQX) is valid */
+ ata:1, /* This is an ATA device */
+ haaval:1; /* Host Adapter Address Valid */
ushort cp_pad_len; /* Number of pad bytes after cp_len */
unchar host_addr[4]; /* Host Adapter SCSI ID for channels 3, 2, 1, 0 */
ulong cp_len; /* Number of valid bytes in cp */
ushort unused;
ushort scatt_size; /* Max number of entries in scatter/gather table */
unchar irq:4, /* Interrupt Request assigned to this controller */
- irq_tr:1, /* 0 for edge triggered, 1 for level triggered */
- second:1, /* 1 if this is a secondary (not primary) controller */
- drqx:2; /* DRQ Index (0=DMA0, 1=DMA7, 2=DMA6, 3=DMA5) */
+ irq_tr:1, /* 0 for edge triggered, 1 for level triggered */
+ second:1, /* 1 if this is a secondary (not primary) controller */
+ drqx:2; /* DRQ Index (0=DMA0, 1=DMA7, 2=DMA6, 3=DMA5) */
unchar sync; /* 1 if scsi target id 7...0 is running sync scsi */
/* Structure extension defined in EATA 2.0B */
unchar isaena:1, /* ISA i/o addressing is disabled/enabled */
- forcaddr:1, /* Port address has been forced */
+ forcaddr:1, /* Port address has been forced */
large_sg:1, /* 1 if large SG lists are supported */
res1:1,
- :4;
+ :4;
unchar max_id:5, /* Max SCSI target ID number */
- max_chan:3; /* Max SCSI channel number on this board */
+ max_chan:3; /* Max SCSI channel number on this board */
/* Structure extension defined in EATA 2.0C */
unchar max_lun; /* Max SCSI LUN number */
struct eata_config {
ushort len; /* Number of bytes following this field */
unchar edis:1, /* Disable EATA interface after config command */
- ocena:1, /* Overlapped Commands Enabled */
- mdpena:1, /* Transfer all Modified Data Pointer Messages */
- tarena:1, /* Target Mode Enabled for this controller */
- :4;
+ ocena:1, /* Overlapped Commands Enabled */
+ mdpena:1, /* Transfer all Modified Data Pointer Messages */
+ tarena:1, /* Target Mode Enabled for this controller */
+ :4;
unchar cpad[511];
};
/* Returned status packet structure */
struct mssp {
unchar adapter_status:7, /* State related to current command */
- eoc:1; /* End Of Command (1 = command completed) */
+ eoc:1; /* End Of Command (1 = command completed) */
unchar target_status; /* SCSI status received after data transfer */
unchar unused[2];
ulong inv_res_len; /* Number of bytes not transferred */
/* MailBox SCSI Command Packet */
struct mscp {
unchar sreset:1, /* SCSI Bus Reset Signal should be asserted */
- init:1, /* Re-initialize controller and self test */
- reqsen:1, /* Transfer Request Sense Data to addr using DMA */
- sg:1, /* Use Scatter/Gather */
- :1,
- interp:1, /* The controller interprets cp, not the target */
- dout:1, /* Direction of Transfer is Out (Host to Target) */
- din:1; /* Direction of Transfer is In (Target to Host) */
+ init:1, /* Re-initialize controller and self test */
+ reqsen:1, /* Transfer Request Sense Data to addr using DMA */
+ sg:1, /* Use Scatter/Gather */
+ :1,
+ interp:1, /* The controller interprets cp, not the target */
+ dout:1, /* Direction of Transfer is Out (Host to Target) */
+ din:1; /* Direction of Transfer is In (Target to Host) */
unchar sense_len; /* Request Sense Length */
unchar unused[3];
unchar fwnest:1, /* Send command to a component of an Array Group */
unchar target:5, /* SCSI target ID */
channel:3; /* SCSI channel number */
unchar lun:5, /* SCSI logical unit number */
- luntar:1, /* This cp is for Target (not LUN) */
- dispri:1, /* Disconnect Privilege granted */
- one:1; /* 1 */
+ luntar:1, /* This cp is for Target (not LUN) */
+ dispri:1, /* Disconnect Privilege granted */
+ one:1; /* 1 */
unchar mess[3]; /* Massage to/from Target */
unchar cdb[12]; /* Command Descriptor Block */
ulong data_len; /* If sg=0 Data Length, if sg=1 sglist length */
unsigned int cp_stat[MAX_MAILBOXES]; /* FREE, IN_USE, LOCKED, IN_RESET */
unsigned int last_cp_used; /* Index of last mailbox used */
unsigned int iocount; /* Total i/o done for this board */
- unsigned int multicount; /* Total ... in second ihdlr loop */
int board_number; /* Number of this board */
char board_name[16]; /* Name of this board */
char board_id[256]; /* data from INQUIRY on this board */
static struct Scsi_Host *sh[MAX_BOARDS + 1];
static const char *driver_name = "EATA";
-static unsigned int irqlist[MAX_IRQ], calls[MAX_IRQ];
+static char sha[MAX_BOARDS];
-static unsigned int io_port[] __initdata = {
+/* Initialize num_boards so that ihdlr can work while detect is in progress */
+static unsigned int num_boards = MAX_BOARDS;
+
+static unsigned long io_port[] __initdata = {
/* Space for MAX_INT_PARAM ports usable while loading as a module */
SKIP, SKIP, SKIP, SKIP, SKIP, SKIP, SKIP, SKIP,
return;
}
-static inline int wait_on_busy(unsigned int iobase, unsigned int loop) {
+static inline int wait_on_busy(unsigned long iobase, unsigned int loop) {
while (inb(iobase + REG_AUX_STATUS) & ABSY_ASSERTED) {
udelay(1L);
return FALSE;
}
-static inline int do_dma(unsigned int iobase, unsigned int addr, unchar cmd) {
+static inline int do_dma(unsigned long iobase, unsigned int addr, unchar cmd) {
if (wait_on_busy(iobase, (addr ? MAXLOOP * 100 : MAXLOOP))) return TRUE;
return FALSE;
}
-static inline int read_pio(unsigned int iobase, ushort *start, ushort *end) {
+static inline int read_pio(unsigned long iobase, ushort *start, ushort *end) {
unsigned int loop = MAXLOOP;
ushort *p;
while (!(inb(iobase + REG_STATUS) & DRQ_ASSERTED)) {
udelay(1L);
- if (--loop == 0) return TRUE;
+ if (--loop == 0) return TRUE;
}
loop = MAXLOOP;
}
__initfunc (static inline int port_detect \
- (unsigned int port_base, unsigned int j, Scsi_Host_Template *tpnt)) {
+ (unsigned long port_base, unsigned int j, Scsi_Host_Template *tpnt)) {
unsigned char irq, dma_channel, subversion, i;
unsigned char protocol_rev;
struct eata_info info;
sprintf(name, "%s%d", driver_name, j);
if(check_region(port_base, REGION_SIZE)) {
- printk("%s: address 0x%03x in use, skipping probe.\n", name, port_base);
+ printk("%s: address 0x%03lx in use, skipping probe.\n", name, port_base);
return FALSE;
}
if (DEV2H(info.data_len) < EATA_2_0A_SIZE) {
printk("%s: config structure size (%ld bytes) too short, detaching.\n",
- name, DEV2H(info.data_len));
+ name, DEV2H(info.data_len));
return FALSE;
}
else if (DEV2H(info.data_len) == EATA_2_0A_SIZE)
}
if (!info.haaval || info.ata) {
- printk("%s: address 0x%03x, unusable %s board (%d%d), detaching.\n",
+ printk("%s: address 0x%03lx, unusable %s board (%d%d), detaching.\n",
name, port_base, bus_type, info.haaval, info.ata);
return FALSE;
}
if (info.drqvld) {
if (subversion == ESA)
- printk("%s: warning, weird %s board using DMA.\n", name, bus_type);
+ printk("%s: warning, weird %s board using DMA.\n", name, bus_type);
subversion = ISA;
dma_channel = dma_channel_table[3 - info.drqx];
else {
if (subversion == ISA)
- printk("%s: warning, weird %s board not using DMA.\n", name, bus_type);
+ printk("%s: warning, weird %s board not using DMA.\n", name, bus_type);
subversion = ESA;
dma_channel = NO_DMA;
if (subversion == ESA && !info.irq_tr)
printk("%s: warning, LEVEL triggering is suggested for IRQ %u.\n",
- name, irq);
+ name, irq);
- /* Board detected, allocate its IRQ if not already done */
- if ((irq >= MAX_IRQ) || (!irqlist[irq] && request_irq(irq,
- eata2x_interrupt_handler, SA_INTERRUPT, driver_name, NULL))) {
+ /* Board detected, allocate its IRQ */
+ if (request_irq(irq, eata2x_interrupt_handler,
+ SA_INTERRUPT | ((subversion == ESA) ? SA_SHIRQ : 0),
+ driver_name, (void *) &sha[j])) {
printk("%s: unable to allocate IRQ %u, detaching.\n", name, irq);
return FALSE;
}
if (subversion == ISA && request_dma(dma_channel, driver_name)) {
printk("%s: unable to allocate DMA channel %u, detaching.\n",
- name, dma_channel);
- free_irq(irq, NULL);
+ name, dma_channel);
+ free_irq(irq, &sha[j]);
return FALSE;
}
if (sh[j] == NULL) {
printk("%s: unable to register host, detaching.\n", name);
- if (!irqlist[irq]) free_irq(irq, NULL);
+ free_irq(irq, &sha[j]);
if (subversion == ISA) free_dma(dma_channel);
HD(j)->subversion = subversion;
HD(j)->protocol_rev = protocol_rev;
HD(j)->board_number = j;
- irqlist[irq]++;
if (HD(j)->subversion == ESA)
sh[j]->unchecked_isa_dma = FALSE;
}
else tag_type = 'n';
- printk("%s: 2.0%c, %s 0x%03x, IRQ %u, %s, SG %d, MB %d, tc:%c, lc:%c, "\
- "mq:%d.\n", BN(j), HD(j)->protocol_rev, bus_type, sh[j]->io_port,
- sh[j]->irq, dma_name, sh[j]->sg_tablesize, sh[j]->can_queue,
- tag_type, YESNO(linked_comm), max_queue_depth);
+ printk("%s: 2.0%c, %s 0x%03lx, IRQ %u, %s, SG %d, MB %d, tc:%c, lc:%c, "\
+ "mq:%d.\n", BN(j), HD(j)->protocol_rev, bus_type,
+ (unsigned long)sh[j]->io_port, sh[j]->irq, dma_name,
+ sh[j]->sg_tablesize, sh[j]->can_queue, tag_type, YESNO(linked_comm),
+ max_queue_depth);
if (sh[j]->max_id > 8 || sh[j]->max_lun > 8)
printk("%s: wide SCSI support enabled, max_id %u, max_lun %u.\n",
}
#endif
- for (k = 0; k < MAX_IRQ; k++) {
- irqlist[k] = 0;
- calls[k] = 0;
- }
-
for (k = 0; k < MAX_BOARDS + 1; k++) sh[k] = NULL;
if (!setup_done) add_pci_ports();
}
if (j > 0)
- printk("EATA/DMA 2.0x: Copyright (C) 1994-1997 Dario Ballabio.\n");
+ printk("EATA/DMA 2.0x: Copyright (C) 1994-1998 Dario Ballabio.\n");
+ num_boards = j;
restore_flags(flags);
return j;
}
if (i >= sh[j]->can_queue) i = 0;
if (HD(j)->cp_stat[i] == FREE) {
- HD(j)->last_cp_used = i;
- break;
- }
+ HD(j)->last_cp_used = i;
+ break;
+ }
}
if (k == sh[j]->can_queue) {
printk("%s: qcomm, no free mailbox, resetting.\n", BN(j));
if (HD(j)->in_reset)
- printk("%s: qcomm, already in reset.\n", BN(j));
+ printk("%s: qcomm, already in reset.\n", BN(j));
else if (eata2x_reset(SCpnt, SCSI_RESET_SUGGEST_BUS_RESET)
== SCSI_RESET_SUCCESS)
- panic("%s: qcomm, SCSI_RESET_SUCCESS.\n", BN(j));
+ panic("%s: qcomm, SCSI_RESET_SUCCESS.\n", BN(j));
SCpnt->result = DID_BUS_BUSY << 16;
SCpnt->host_scribble = NULL;
printk("%s: qcomm, pid %ld, DID_BUS_BUSY, done.\n", BN(j), SCpnt->pid);
restore_flags(flags);
done(SCpnt);
- return 0;
+ return 1;
}
/* Set pointer to control packet structure */
SCpnt->host_scribble = (unsigned char *) &cpp->index;
if (do_trace) printk("%s: qcomm, mbox %d, target %d.%d:%d, pid %ld.\n",
- BN(j), i, SCpnt->channel, SCpnt->target,
+ BN(j), i, SCpnt->channel, SCpnt->target,
SCpnt->lun, SCpnt->pid);
for (k = 0; k < ARRAY_SIZE(data_out_cmds); k++)
if (SCpnt->cmnd[0] == data_out_cmds[k]) {
- cpp->dout = TRUE;
- break;
- }
+ cpp->dout = TRUE;
+ break;
+ }
if ((cpp->din = !cpp->dout))
for (k = 0; k < ARRAY_SIZE(data_none_cmds); k++)
if (SCpnt->cmnd[0] == data_none_cmds[k]) {
- cpp->din = FALSE;
- break;
- }
+ cpp->din = FALSE;
+ break;
+ }
cpp->reqsen = TRUE;
cpp->dispri = TRUE;
SCpnt->pid);
restore_flags(flags);
done(SCpnt);
- return 0;
+ return 1;
}
HD(j)->cp_stat[i] = IN_USE;
if (SCarg->host_scribble == NULL
|| SCarg->serial_number != SCarg->serial_number_at_timeout) {
printk("%s: abort, target %d.%d:%d, pid %ld inactive.\n",
- BN(j), SCarg->channel, SCarg->target, SCarg->lun, SCarg->pid);
+ BN(j), SCarg->channel, SCarg->target, SCarg->lun, SCarg->pid);
restore_flags(flags);
return SCSI_ABORT_NOT_RUNNING;
}
i = *(unsigned int *)SCarg->host_scribble;
printk("%s: abort, mbox %d, target %d.%d:%d, pid %ld.\n",
- BN(j), i, SCarg->channel, SCarg->target, SCarg->lun, SCarg->pid);
+ BN(j), i, SCarg->channel, SCarg->target, SCarg->lun, SCarg->pid);
if (i >= sh[j]->can_queue)
panic("%s: abort, invalid SCarg->host_scribble.\n", BN(j));
printk("%s: abort, mbox %d is in use.\n", BN(j), i);
if (SCarg != HD(j)->cp[i].SCpnt)
- panic("%s: abort, mbox %d, SCarg %p, cp SCpnt %p.\n",
- BN(j), i, SCarg, HD(j)->cp[i].SCpnt);
+ panic("%s: abort, mbox %d, SCarg %p, cp SCpnt %p.\n",
+ BN(j), i, SCarg, HD(j)->cp[i].SCpnt);
if (inb(sh[j]->io_port + REG_AUX_STATUS) & IRQ_ASSERTED)
printk("%s: abort, mbox %d, interrupt pending.\n", BN(j), i);
SCarg->host_scribble = NULL;
HD(j)->cp_stat[i] = FREE;
printk("%s, abort, mbox %d ready, DID_ABORT, pid %ld done.\n",
- BN(j), i, SCarg->pid);
+ BN(j), i, SCarg->pid);
SCarg->scsi_done(SCarg);
restore_flags(flags);
return SCSI_ABORT_SUCCESS;
cli();
j = ((struct hostdata *) SCarg->host->hostdata)->board_number;
printk("%s: reset, enter, target %d.%d:%d, pid %ld, reset_flags %u.\n",
- BN(j), SCarg->channel, SCarg->target, SCarg->lun, SCarg->pid,
+ BN(j), SCarg->channel, SCarg->target, SCarg->lun, SCarg->pid,
reset_flags);
if (SCarg->host_scribble == NULL)
if (HD(j)->cp_stat[i] == FREE) continue;
if (HD(j)->cp_stat[i] == LOCKED) {
- HD(j)->cp_stat[i] = FREE;
- printk("%s: reset, locked mbox %d forced free.\n", BN(j), i);
- continue;
- }
+ HD(j)->cp_stat[i] = FREE;
+ printk("%s: reset, locked mbox %d forced free.\n", BN(j), i);
+ continue;
+ }
if (!(SCpnt = HD(j)->cp[i].SCpnt))
- panic("%s: reset, mbox %d, SCpnt == NULL.\n", BN(j), i);
+ panic("%s: reset, mbox %d, SCpnt == NULL.\n", BN(j), i);
if (HD(j)->cp_stat[i] == READY || HD(j)->cp_stat[i] == ABORTING) {
HD(j)->cp_stat[i] = ABORTING;
}
if (SCpnt->host_scribble == NULL)
- panic("%s: reset, mbox %d, garbled SCpnt.\n", BN(j), i);
+ panic("%s: reset, mbox %d, garbled SCpnt.\n", BN(j), i);
if (*(unsigned int *)SCpnt->host_scribble != i)
- panic("%s: reset, mbox %d, index mismatch.\n", BN(j), i);
+ panic("%s: reset, mbox %d, index mismatch.\n", BN(j), i);
if (SCpnt->scsi_done == NULL)
- panic("%s: reset, mbox %d, SCpnt->scsi_done == NULL.\n", BN(j), i);
+ panic("%s: reset, mbox %d, SCpnt->scsi_done == NULL.\n", BN(j), i);
if (SCpnt == SCarg) arg_done = TRUE;
}
HD(j)->cp_stat[i] = LOCKED;
printk("%s, reset, mbox %d locked, DID_RESET, pid %ld done.\n",
- BN(j), i, SCpnt->pid);
+ BN(j), i, SCpnt->pid);
}
else if (HD(j)->cp_stat[i] == ABORTING) {
HD(j)->cp_stat[i] = FREE;
printk("%s, reset, mbox %d aborting, DID_RESET, pid %ld done.\n",
- BN(j), i, SCpnt->pid);
+ BN(j), i, SCpnt->pid);
}
else
}
-static void eata2x_interrupt_handler(int irq, void *dev_id,
+static void eata2x_interrupt_handler(int irq, void *shap,
struct pt_regs *regs) {
Scsi_Cmnd *SCpnt;
- unsigned long flags;
- unsigned int i, j, k, c, status, tstatus, loops, total_loops = 0, reg;
+ unsigned int i, j, k, c, status, tstatus, reg;
+ unsigned int n, n_ready, il[MAX_MAILBOXES];
struct mssp *spp;
struct mscp *cpp;
- save_flags(flags);
- cli();
+ /* Check if the interrupt must be processed by this handler */
+ if ((j = (unsigned int)((char *)shap - sha)) >= num_boards) return;
+
+ if (sh[j]->irq != irq)
+ panic("%s: ihdlr, irq %d, sh[j]->irq %d.\n", BN(j), irq, sh[j]->irq);
+
+ if (do_trace) printk("%s: ihdlr, enter, irq %d, count %d.\n", BN(j), irq,
+ HD(j)->iocount);
+
+ /* Check if this board need to be serviced */
+ if (!(inb(sh[j]->io_port + REG_AUX_STATUS) & IRQ_ASSERTED)) return;
- if (!irqlist[irq]) {
- printk("%s, ihdlr, irq %d, unexpected interrupt.\n", driver_name, irq);
- restore_flags(flags);
- return;
- }
+ n_ready = 0;
- if (do_trace) printk("%s: ihdlr, enter, irq %d, calls %d.\n",
- driver_name, irq, calls[irq]);
+ /* Find the mailboxes to be serviced on this board */
+ for (i = 0; i < sh[j]->can_queue; i++) {
+ spp = &HD(j)->sp[i];
- /* Service all the boards configured on this irq */
- for (j = 0; sh[j] != NULL; j++) {
+ /* Check if this mailbox has completed the operation */
+ if (spp->eoc == FALSE) continue;
- if (sh[j]->irq != irq) continue;
+ spp->eoc = FALSE;
+ il[n_ready++] = i;
+ }
- loops = 0;
+ /* Read the status register to clear the interrupt indication */
+ reg = inb(sh[j]->io_port + REG_STATUS);
- /* Loop until all interrupts for a board are serviced */
- while (inb(sh[j]->io_port + REG_AUX_STATUS) & IRQ_ASSERTED) {
- total_loops++;
- loops++;
+ /* Mailbox service loop */
+ for (n = 0; n < n_ready; n++) {
+ i = il[n];
+ spp = &HD(j)->sp[i];
- if (do_trace) printk("%s: ihdlr, start service, count %d.\n",
- BN(j), HD(j)->iocount);
-
- /* Read the status register to clear the interrupt indication */
- reg = inb(sh[j]->io_port + REG_STATUS);
-
- /* Service all mailboxes of this board */
- for (i = 0; i < sh[j]->can_queue; i++) {
- spp = &HD(j)->sp[i];
-
- /* Check if this mailbox has completed the operation */
- if (spp->eoc == FALSE) continue;
-
- spp->eoc = FALSE;
-
- if (HD(j)->cp_stat[i] == IGNORE) {
- HD(j)->cp_stat[i] = FREE;
- continue;
- }
- else if (HD(j)->cp_stat[i] == LOCKED) {
- HD(j)->cp_stat[i] = FREE;
- printk("%s: ihdlr, mbox %d unlocked, count %d.\n",
- BN(j), i, HD(j)->iocount);
- continue;
- }
- else if (HD(j)->cp_stat[i] == FREE) {
- printk("%s: ihdlr, mbox %d is free, count %d.\n",
- BN(j), i, HD(j)->iocount);
- continue;
- }
- else if (HD(j)->cp_stat[i] == IN_RESET)
- printk("%s: ihdlr, mbox %d is in reset.\n", BN(j), i);
- else if (HD(j)->cp_stat[i] != IN_USE)
- panic("%s: ihdlr, mbox %d, invalid cp_stat.\n", BN(j), i);
-
- HD(j)->cp_stat[i] = FREE;
- cpp = &HD(j)->cp[i];
- SCpnt = spp->SCpnt;
-
- if (SCpnt == NULL)
- panic("%s: ihdlr, mbox %d, SCpnt == NULL.\n", BN(j), i);
-
- if (SCpnt != cpp->SCpnt)
- panic("%s: ihdlr, mbox %d, sp SCpnt %p, cp SCpnt %p.\n",
- BN(j), i, SCpnt, cpp->SCpnt);
-
- if (SCpnt->host_scribble == NULL)
- panic("%s: ihdlr, mbox %d, pid %ld, SCpnt %p garbled.\n",
- BN(j), i, SCpnt->pid, SCpnt);
-
- if (*(unsigned int *)SCpnt->host_scribble != i)
- panic("%s: ihdlr, mbox %d, pid %ld, index mismatch %d,"\
- " irq %d.\n", BN(j), i, SCpnt->pid,
- *(unsigned int *)SCpnt->host_scribble, irq);
-
- if (linked_comm && SCpnt->device->queue_depth > 2
- && TLDEV(SCpnt->device->type))
- flush_dev(SCpnt->device, SCpnt->request.sector, j, TRUE);
+ if (HD(j)->cp_stat[i] == IGNORE) {
+ HD(j)->cp_stat[i] = FREE;
+ return;
+ }
+ else if (HD(j)->cp_stat[i] == LOCKED) {
+ HD(j)->cp_stat[i] = FREE;
+ printk("%s: ihdlr, mbox %d unlocked, count %d.\n", BN(j), i,
+ HD(j)->iocount);
+ return;
+ }
+ else if (HD(j)->cp_stat[i] == FREE) {
+ printk("%s: ihdlr, mbox %d is free, count %d.\n", BN(j), i,
+ HD(j)->iocount);
+ return;
+ }
+ else if (HD(j)->cp_stat[i] == IN_RESET)
+ printk("%s: ihdlr, mbox %d is in reset.\n", BN(j), i);
+ else if (HD(j)->cp_stat[i] != IN_USE)
+ panic("%s: ihdlr, mbox %d, invalid cp_stat.\n", BN(j), i);
- tstatus = status_byte(spp->target_status);
-
- switch (spp->adapter_status) {
- case ASOK: /* status OK */
-
- /* Forces a reset if a disk drive keeps returning BUSY */
- if (tstatus == BUSY && SCpnt->device->type != TYPE_TAPE)
- status = DID_ERROR << 16;
-
- /* If there was a bus reset, redo operation on each target */
- else if (tstatus != GOOD && SCpnt->device->type == TYPE_DISK
- && HD(j)->target_redo[SCpnt->target][SCpnt->channel])
- status = DID_BUS_BUSY << 16;
-
- /* Works around a flaw in scsi.c */
- else if (tstatus == CHECK_CONDITION
- && SCpnt->device->type == TYPE_DISK
- && (SCpnt->sense_buffer[2] & 0xf) == RECOVERED_ERROR)
- status = DID_BUS_BUSY << 16;
-
- else
- status = DID_OK << 16;
-
- if (tstatus == GOOD)
- HD(j)->target_redo[SCpnt->target][SCpnt->channel] = FALSE;
-
- if (spp->target_status && SCpnt->device->type == TYPE_DISK)
- printk("%s: ihdlr, target %d.%d:%d, pid %ld, "\
- "target_status 0x%x, sense key 0x%x.\n", BN(j),
- SCpnt->channel, SCpnt->target, SCpnt->lun,
- SCpnt->pid, spp->target_status,
- SCpnt->sense_buffer[2]);
-
- HD(j)->target_to[SCpnt->target][SCpnt->channel] = 0;
-
- if (HD(j)->last_retried_pid == SCpnt->pid) HD(j)->retries = 0;
+ HD(j)->cp_stat[i] = FREE;
+ cpp = &HD(j)->cp[i];
+ SCpnt = spp->SCpnt;
- break;
- case ASST: /* Selection Time Out */
- case 0x02: /* Command Time Out */
-
- if (HD(j)->target_to[SCpnt->target][SCpnt->channel] > 1)
- status = DID_ERROR << 16;
- else {
- status = DID_TIME_OUT << 16;
- HD(j)->target_to[SCpnt->target][SCpnt->channel]++;
- }
-
- break;
+ if(SCpnt == NULL) panic("%s: ihdlr, mbox %d, SCpnt == NULL.\n", BN(j), i);
- /* Perform a limited number of internal retries */
- case 0x03: /* SCSI Bus Reset Received */
- case 0x04: /* Initial Controller Power-up */
-
- for (c = 0; c <= sh[j]->max_channel; c++)
- for (k = 0; k < sh[j]->max_id; k++)
- HD(j)->target_redo[k][c] = TRUE;
-
- if (SCpnt->device->type != TYPE_TAPE
- && HD(j)->retries < MAX_INTERNAL_RETRIES) {
- status = DID_BUS_BUSY << 16;
- HD(j)->retries++;
- HD(j)->last_retried_pid = SCpnt->pid;
- }
- else
- status = DID_ERROR << 16;
-
- break;
- case 0x05: /* Unexpected Bus Phase */
- case 0x06: /* Unexpected Bus Free */
- case 0x07: /* Bus Parity Error */
- case 0x08: /* SCSI Hung */
- case 0x09: /* Unexpected Message Reject */
- case 0x0a: /* SCSI Bus Reset Stuck */
- case 0x0b: /* Auto Request-Sense Failed */
- case 0x0c: /* Controller Ram Parity Error */
- default:
- status = DID_ERROR << 16;
- break;
- }
-
- SCpnt->result = status | spp->target_status;
- HD(j)->iocount++;
+ if (SCpnt != cpp->SCpnt)
+ panic("%s: ihdlr, mbox %d, sp SCpnt %p, cp SCpnt %p.\n", BN(j), i,
+ SCpnt, cpp->SCpnt);
- if (loops > 1) HD(j)->multicount++;
+ if (SCpnt->host_scribble == NULL)
+ panic("%s: ihdlr, mbox %d, pid %ld, SCpnt %p garbled.\n", BN(j), i,
+ SCpnt->pid, SCpnt);
+
+ if (*(unsigned int *)SCpnt->host_scribble != i)
+ panic("%s: ihdlr, mbox %d, pid %ld, index mismatch %d, irq %d.\n",
+ BN(j), i, SCpnt->pid, *(unsigned int *)SCpnt->host_scribble, irq);
+
+ if (linked_comm && SCpnt->device->queue_depth > 2
+ && TLDEV(SCpnt->device->type))
+ flush_dev(SCpnt->device, SCpnt->request.sector, j, TRUE);
+
+ tstatus = status_byte(spp->target_status);
+
+ switch (spp->adapter_status) {
+ case ASOK: /* status OK */
+
+ /* Forces a reset if a disk drive keeps returning BUSY */
+ if (tstatus == BUSY && SCpnt->device->type != TYPE_TAPE)
+ status = DID_ERROR << 16;
+
+ /* If there was a bus reset, redo operation on each target */
+ else if (tstatus != GOOD && SCpnt->device->type == TYPE_DISK
+ && HD(j)->target_redo[SCpnt->target][SCpnt->channel])
+ status = DID_BUS_BUSY << 16;
+
+ /* Works around a flaw in scsi.c */
+ else if (tstatus == CHECK_CONDITION
+ && SCpnt->device->type == TYPE_DISK
+ && (SCpnt->sense_buffer[2] & 0xf) == RECOVERED_ERROR)
+ status = DID_BUS_BUSY << 16;
+
+ else
+ status = DID_OK << 16;
+
+ if (tstatus == GOOD)
+ HD(j)->target_redo[SCpnt->target][SCpnt->channel] = FALSE;
+
+ if (spp->target_status && SCpnt->device->type == TYPE_DISK)
+ printk("%s: ihdlr, target %d.%d:%d, pid %ld, "\
+ "target_status 0x%x, sense key 0x%x.\n", BN(j),
+ SCpnt->channel, SCpnt->target, SCpnt->lun,
+ SCpnt->pid, spp->target_status,
+ SCpnt->sense_buffer[2]);
+
+ HD(j)->target_to[SCpnt->target][SCpnt->channel] = 0;
+
+ if (HD(j)->last_retried_pid == SCpnt->pid) HD(j)->retries = 0;
+
+ break;
+ case ASST: /* Selection Time Out */
+ case 0x02: /* Command Time Out */
+
+ if (HD(j)->target_to[SCpnt->target][SCpnt->channel] > 1)
+ status = DID_ERROR << 16;
+ else {
+ status = DID_TIME_OUT << 16;
+ HD(j)->target_to[SCpnt->target][SCpnt->channel]++;
+ }
+
+ break;
+
+ /* Perform a limited number of internal retries */
+ case 0x03: /* SCSI Bus Reset Received */
+ case 0x04: /* Initial Controller Power-up */
+
+ for (c = 0; c <= sh[j]->max_channel; c++)
+ for (k = 0; k < sh[j]->max_id; k++)
+ HD(j)->target_redo[k][c] = TRUE;
+
+ if (SCpnt->device->type != TYPE_TAPE
+ && HD(j)->retries < MAX_INTERNAL_RETRIES) {
+ status = DID_BUS_BUSY << 16;
+ HD(j)->retries++;
+ HD(j)->last_retried_pid = SCpnt->pid;
+ }
+ else
+ status = DID_ERROR << 16;
+
+ break;
+ case 0x05: /* Unexpected Bus Phase */
+ case 0x06: /* Unexpected Bus Free */
+ case 0x07: /* Bus Parity Error */
+ case 0x08: /* SCSI Hung */
+ case 0x09: /* Unexpected Message Reject */
+ case 0x0a: /* SCSI Bus Reset Stuck */
+ case 0x0b: /* Auto Request-Sense Failed */
+ case 0x0c: /* Controller Ram Parity Error */
+ default:
+ status = DID_ERROR << 16;
+ break;
+ }
+
+ SCpnt->result = status | spp->target_status;
+ HD(j)->iocount++;
#if defined (DEBUG_INTERRUPT)
- if (SCpnt->result || do_trace)
+ if (SCpnt->result || do_trace)
#else
- if ((spp->adapter_status != ASOK && HD(j)->iocount > 1000) ||
- (spp->adapter_status != ASOK &&
- spp->adapter_status != ASST && HD(j)->iocount <= 1000) ||
- do_trace || msg_byte(spp->target_status))
+ if ((spp->adapter_status != ASOK && HD(j)->iocount > 1000) ||
+ (spp->adapter_status != ASOK &&
+ spp->adapter_status != ASST && HD(j)->iocount <= 1000) ||
+ do_trace || msg_byte(spp->target_status))
#endif
- printk("%s: ihdlr, mbox %2d, err 0x%x:%x,"\
- " target %d.%d:%d, pid %ld, reg 0x%x, count %d.\n",
- BN(j), i, spp->adapter_status, spp->target_status,
- SCpnt->channel, SCpnt->target, SCpnt->lun, SCpnt->pid,
- reg, HD(j)->iocount);
-
- /* Set the command state to inactive */
- SCpnt->host_scribble = NULL;
-
- restore_flags(flags);
- SCpnt->scsi_done(SCpnt);
- cli();
-
- } /* Mailbox loop */
+ printk("%s: ihdlr, mbox %2d, err 0x%x:%x,"\
+ " target %d.%d:%d, pid %ld, reg 0x%x, count %d.\n",
+ BN(j), i, spp->adapter_status, spp->target_status,
+ SCpnt->channel, SCpnt->target, SCpnt->lun, SCpnt->pid,
+ reg, HD(j)->iocount);
- } /* Multiple command loop */
-
- } /* Boards loop */
+ /* Set the command state to inactive */
+ SCpnt->host_scribble = NULL;
- calls[irq]++;
+ SCpnt->scsi_done(SCpnt);
-#if defined (DEBUG_SMP)
- if (total_loops == 0)
- printk("%s: ihdlr, irq %d, no command completed, calls %d.\n",
- driver_name, irq, calls[irq]);
-#endif
+ } /* Mailbox loop */
- if (do_trace) printk("%s: ihdlr, exit, irq %d, calls %d.\n",
- driver_name, irq, calls[irq]);
+ if (n_ready > 1)
+ printk("%s: ihdlr, multiple commands (%d) completed.\n", BN(j), n_ready);
-#if defined (DEBUG_STATISTICS)
- if ((calls[irq] % 100000) == 10000)
- for (j = 0; sh[j] != NULL; j++)
- printk("%s: ihdlr, calls %d, count %d, multi %d.\n", BN(j),
- calls[(sh[j]->irq)], HD(j)->iocount, HD(j)->multicount);
-#endif
+ if (do_trace) printk("%s: ihdlr, exit, irq %d, count %d.\n", BN(j), irq,
+ HD(j)->iocount);
- restore_flags(flags);
return;
}
for (i = 0; i < sh[j]->can_queue; i++)
if ((&HD(j)->cp[i])->sglist) kfree((&HD(j)->cp[i])->sglist);
- if (! --irqlist[sh[j]->irq]) free_irq(sh[j]->irq, NULL);
+ free_irq(sh[j]->irq, &sha[j]);
if (sh[j]->dma_channel != NO_DMA) free_dma(sh[j]->dma_channel);
/*
- * eata.h - used by the low-level driver for EATA/DMA SCSI host adapters.
+ * eata.h - used by the low-level driver for EATA/DMA SCSI host adapters.
*/
#ifndef _EATA_H
#define _EATA_H
int eata2x_abort(Scsi_Cmnd *);
int eata2x_reset(Scsi_Cmnd *, unsigned int);
-#define EATA_VERSION "3.11.00"
-
-
-#define EATA { \
- NULL, /* Ptr for modules */ \
- NULL, /* usage count for modules */ \
- NULL, \
- NULL, \
- "EATA/DMA 2.0x rev. " EATA_VERSION " ", \
- eata2x_detect, \
- eata2x_release, \
- NULL, \
- NULL, \
- eata2x_queuecommand, \
- eata2x_abort, \
- eata2x_reset, \
- NULL, \
- scsicam_bios_param, \
- 0, /* can_queue, reset by detect */ \
- 7, /* this_id, reset by detect */ \
- 0, /* sg_tablesize, reset by detect */ \
- 0, /* cmd_per_lun, reset by detect */ \
- 0, /* number of boards present */ \
- 1, /* unchecked isa dma, reset by detect */ \
- ENABLE_CLUSTERING \
- }
+#define EATA_VERSION "4.02.00"
+
+#define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s))
+
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,88)
+
+#define EATA { \
+ name: "EATA/DMA 2.0x rev. " EATA_VERSION " ", \
+ detect: eata2x_detect, \
+ release: eata2x_release, \
+ queuecommand: eata2x_queuecommand, \
+ abort: eata2x_abort, \
+ reset: eata2x_reset, \
+ bios_param: scsicam_bios_param, \
+ this_id: 7, \
+ unchecked_isa_dma: 1, \
+ use_clustering: ENABLE_CLUSTERING, \
+ use_new_eh_code: 1 /* Enable new error code */ \
+ }
+
+#else /* Use old scsi code */
+
+#define EATA { \
+ name: "EATA/DMA 2.0x rev. " EATA_VERSION " ", \
+ detect: eata2x_detect, \
+ release: eata2x_release, \
+ queuecommand: eata2x_queuecommand, \
+ abort: eata2x_abort, \
+ reset: eata2x_reset, \
+ bios_param: scsicam_bios_param, \
+ this_id: 7, \
+ unchecked_isa_dma: 1, \
+ use_clustering: ENABLE_CLUSTERING \
+ }
+
+#endif
+
#endif
** May 19 1997 by Richard Waltham <dormouse@farsrobt.demon.co.uk>:
** Support for NvRAM detection and reading.
**
+** August 18 1997 by Cort <cort@cs.nmt.edu>:
+** Support for Power/PC (Big Endian).
+**
*******************************************************************************
*/
/*
-** 21 August 1997, version 2.4a
+** 2 January 1998, version 2.5f
**
** Supported SCSI-II features:
** Synchronous negotiation
** architecture.
*/
+#ifndef NCR_IOMAPPED
__initfunc(
static vm_offset_t remap_pci_mem(u_long base, u_long size)
)
vfree((void *) (vaddr & PAGE_MASK));
#endif
}
+#endif /* !NCR_IOMAPPED */
#else /* linux-1.2.13 */
/*
** Head of list of NCR boards
**
-** Host is retrieved by its irq level.
-** If interrupts are shared, the internal host control block
-** address (struct ncb) is used as device id.
+** For kernel version < 1.3.70, host is retrieved by its irq level.
+** For later kernels, the internal host control block address
+** (struct ncb) is used as device id parameter of the irq stuff.
*/
static struct Scsi_Host *first_host = NULL;
unsigned master_parity : 1;
unsigned scsi_parity : 1;
unsigned disconnection : 1;
- unsigned special_features : 1;
+ unsigned special_features : 2;
unsigned ultra_scsi : 2;
unsigned force_sync_nego: 1;
unsigned reverse_probe: 1;
u_char settle_delay;
u_char diff_support;
u_char irqm;
+ u_char bus_check;
};
static struct ncr_driver_setup
#ifdef SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT
static struct ncr_driver_setup
driver_safe_setup __initdata = SCSI_NCR_DRIVER_SAFE_SETUP;
+#ifdef MODULE
+char *ncr53c8xx = 0; /* command line passed by insmod */
+#endif
#endif
/*
u_short flags1;
#define SYMBIOS_SCAN_HI_LO (1)
u_short word10; /* 0x00 */
- u_short word12; /* 0x00 */
+ u_short flags3; /* 0x00 */
+#define SYMBIOS_REMOVABLE_FLAGS (3) /* 0=none, 1=bootable, 2=all */
u_char host_id;
u_char byte15; /* 0x04 */
u_short word16; /* 0x0410 */
int bus;
u_char device_fn;
u_int base;
+ u_int base_2;
u_int io_port;
int irq;
/* port and reg fields to use INB, OUTB macros */
ncr_slot slot;
ncr_chip chip;
ncr_nvram *nvram;
- int attached;
+ int attach_done;
} ncr_device;
/*==========================================================
/*==========================================================
**
-** Access to the controller chip.
-**
-** If NCR_IOMAPPED is defined, only IO are used by the driver.
+** Big/Little endian support.
**
**==========================================================
*/
/*
-** IO mapped only input / ouput
+** If the NCR uses big endian addressing mode over the
+** PCI, actual io register addresses for byte and word
+** accesses must be changed according to lane routing.
+** Btw, ncr_offb() and ncr_offw() macros only apply to
+** constants and so donnot generate bloated code.
*/
-#define IOM_INB(r) inb (np->port + offsetof(struct ncr_reg, r))
-#define IOM_INB_OFF(o) inb (np->port + (o))
-#define IOM_INW(r) inw (np->port + offsetof(struct ncr_reg, r))
-#define IOM_INL(r) inl (np->port + offsetof(struct ncr_reg, r))
-#define IOM_INL_OFF(o) inl (np->port + (o))
+#if defined(SCSI_NCR_BIG_ENDIAN)
+
+#define ncr_offb(o) (((o)&~3)+((~((o)&3))&3))
+#define ncr_offw(o) (((o)&~3)+((~((o)&3))&2))
+
+#else
+
+#define ncr_offb(o) (o)
+#define ncr_offw(o) (o)
-#define IOM_OUTB(r, val) outb ((val), np->port+offsetof(struct ncr_reg,r))
-#define IOM_OUTW(r, val) outw ((val), np->port+offsetof(struct ncr_reg,r))
-#define IOM_OUTL(r, val) outl ((val), np->port+offsetof(struct ncr_reg,r))
-#define IOM_OUTL_OFF(o, val) outl ((val), np->port + (o))
+#endif
/*
-** MEMORY mapped IO input / output
+** If the CPU and the NCR use same endian-ness adressing,
+** no byte reordering is needed for script patching.
+** Macro cpu_to_scr() is to be used for script patching.
+** Macro scr_to_cpu() is to be used for getting a DWORD
+** from the script.
*/
-#define MMIO_INB(r) readb(&np->reg->r)
-#define MMIO_INB_OFF(o) readb((char *)np->reg + (o))
-#define MMIO_INW(r) readw(&np->reg->r)
-#define MMIO_INL(r) readl(&np->reg->r)
-#define MMIO_INL_OFF(o) readl((char *)np->reg + (o))
+#if defined(__BIG_ENDIAN) && !defined(SCSI_NCR_BIG_ENDIAN)
+
+#define cpu_to_scr(dw) cpu_to_le32(dw)
+#define scr_to_cpu(dw) le32_to_cpu(dw)
+
+#elif defined(__LITTLE_ENDIAN) && defined(SCSI_NCR_BIG_ENDIAN)
+
+#define cpu_to_scr(dw) cpu_to_be32(dw)
+#define scr_to_cpu(dw) be32_to_cpu(dw)
+
+#else
+
+#define cpu_to_scr(dw) (dw)
+#define scr_to_cpu(dw) (dw)
+
+#endif
-#define MMIO_OUTB(r, val) writeb((val), &np->reg->r)
-#define MMIO_OUTW(r, val) writew((val), &np->reg->r)
-#define MMIO_OUTL(r, val) writel((val), &np->reg->r)
-#define MMIO_OUTL_OFF(o, val) writel((val), (char *)np->reg + (o))
+/*==========================================================
+**
+** Access to the controller chip.
+**
+** If NCR_IOMAPPED is defined, only IO are used by the driver.
+**
+**==========================================================
+*/
/*
-** IO mapped input / output
+** If the CPU and the NCR use same endian-ness adressing,
+** no byte reordering is needed for accessing chip io
+** registers. Functions suffixed by '_raw' are assumed
+** to access the chip over the PCI without doing byte
+** reordering. Functions suffixed by '_l2b' are
+** assumed to perform little-endian to big-endian byte
+** reordering, those suffixed by '_b2l' blah, blah,
+** blah, ...
*/
#if defined(NCR_IOMAPPED)
-#define INB(r) IOM_INB(r)
-#define INB_OFF(o) IOM_INB_OFF(o)
-#define INW(r) IOM_INW(r)
-#define INL(r) IOM_INL(r)
-#define INL_OFF(o) IOM_INL_OFF(o)
+/*
+** IO mapped only input / ouput
+*/
+
+#define INB_OFF(o) inb (np->port + ncr_offb(o))
+#define OUTB_OFF(o, val) outb ((val), np->port + ncr_offb(o))
+
+#if defined(__BIG_ENDIAN) && !defined(SCSI_NCR_BIG_ENDIAN)
+
+#define INW_OFF(o) inw_l2b (np->port + ncr_offw(o))
+#define INL_OFF(o) inl_l2b (np->port + (o))
+
+#define OUTW_OFF(o, val) outw_b2l ((val), np->port + ncr_offw(o))
+#define OUTL_OFF(o, val) outl_b2l ((val), np->port + (o))
-#define OUTB(r, val) IOM_OUTB(r, val)
-#define OUTW(r, val) IOM_OUTW(r, val)
-#define OUTL(r, val) IOM_OUTL(r, val)
-#define OUTL_OFF(o, val) IOM_OUTL_OFF(o, val)
+#elif defined(__LITTLE_ENDIAN) && defined(SCSI_NCR_BIG_ENDIAN)
+
+#define INW_OFF(o) inw_b2l (np->port + ncr_offw(o))
+#define INL_OFF(o) inl_b2l (np->port + (o))
+
+#define OUTW_OFF(o, val) outw_l2b ((val), np->port + ncr_offw(o))
+#define OUTL_OFF(o, val) outl_l2b ((val), np->port + (o))
+
+#else
+
+#define INW_OFF(o) inw_raw (np->port + ncr_offw(o))
+#define INL_OFF(o) inl_raw (np->port + (o))
+
+#define OUTW_OFF(o, val) outw_raw ((val), np->port + ncr_offw(o))
+#define OUTL_OFF(o, val) outl_raw ((val), np->port + (o))
+
+#endif /* ENDIANs */
+
+#else /* defined NCR_IOMAPPED */
/*
-** MEMORY mapped only input / output
+** MEMORY mapped IO input / output
*/
+#define INB_OFF(o) readb((char *)np->reg + ncr_offb(o))
+#define OUTB_OFF(o, val) writeb((val), (char *)np->reg + ncr_offb(o))
+
+#if defined(__BIG_ENDIAN) && !defined(SCSI_NCR_BIG_ENDIAN)
+
+#define INW_OFF(o) readw_l2b((char *)np->reg + ncr_offw(o))
+#define INL_OFF(o) readl_l2b((char *)np->reg + (o))
+
+#define OUTW_OFF(o, val) writew_b2l((val), (char *)np->reg + ncr_offw(o))
+#define OUTL_OFF(o, val) writel_b2l((val), (char *)np->reg + (o))
+
+#elif defined(__LITTLE_ENDIAN) && defined(SCSI_NCR_BIG_ENDIAN)
+
+#define INW_OFF(o) readw_b2l((char *)np->reg + ncr_offw(o))
+#define INL_OFF(o) readl_b2l((char *)np->reg + (o))
+
+#define OUTW_OFF(o, val) writew_l2b((val), (char *)np->reg + ncr_offw(o))
+#define OUTL_OFF(o, val) writel_l2b((val), (char *)np->reg + (o))
+
#else
-#define INB(r) MMIO_INB(r)
-#define INB_OFF(o) MMIO_INB_OFF(o)
-#define INW(r) MMIO_INW(r)
-#define INL(r) MMIO_INL(r)
-#define INL_OFF(o) MMIO_INL_OFF(o)
+#define INW_OFF(o) readw_raw((char *)np->reg + ncr_offw(o))
+#define INL_OFF(o) readl_raw((char *)np->reg + (o))
-#define OUTB(r, val) MMIO_OUTB(r, val)
-#define OUTW(r, val) MMIO_OUTW(r, val)
-#define OUTL(r, val) MMIO_OUTL(r, val)
-#define OUTL_OFF(o, val) MMIO_OUTL_OFF(o, val)
+#define OUTW_OFF(o, val) writew_raw((val), (char *)np->reg + ncr_offw(o))
+#define OUTL_OFF(o, val) writel_raw((val), (char *)np->reg + (o))
#endif
+#endif /* defined NCR_IOMAPPED */
+
+#define INB(r) INB_OFF (offsetof(struct ncr_reg,r))
+#define INW(r) INW_OFF (offsetof(struct ncr_reg,r))
+#define INL(r) INL_OFF (offsetof(struct ncr_reg,r))
+
+#define OUTB(r, val) OUTB_OFF (offsetof(struct ncr_reg,r), (val))
+#define OUTW(r, val) OUTW_OFF (offsetof(struct ncr_reg,r), (val))
+#define OUTL(r, val) OUTL_OFF (offsetof(struct ncr_reg,r), (val))
+
/*
** Set bit field ON, OFF
*/
#define OUTONL(r, m) OUTL(r, INL(r) | (m))
#define OUTOFFL(r, m) OUTL(r, INL(r) & ~(m))
+
/*==========================================================
**
** Command control block states.
** status fields.
*/
- u_char status[8];
+ u_char scr_st[4]; /* script status */
+ u_char status[4]; /* host status. Must be the last */
+ /* DWORD of the CCB header */
};
/*
** The status bytes are used by the host and the script processor.
**
-** The first four byte are copied to the scratchb register
+** The byte corresponding to the host_status must be stored in the
+** last DWORD of the CCB header since it is used for command
+** completion (ncr_wakeup()). Doing so, we are sure that the header
+** has been entirely copied back to the CCB when the host_status is
+** seen complete by the CPU.
+**
+** The last four bytes (status[4]) are copied to the scratchb register
** (declared as scr0..scr3 in ncr_reg.h) just after the select/reselect,
** and copied back just after disconnecting.
** Inside the script the XX_REG are used.
**
-** The last four bytes are used inside the script by "COPY" commands.
+** The first four bytes (scr_st[4]) are used inside the script by
+** "COPY" commands.
** Because source and destination must have the same alignment
-** in a longword, the fields HAVE to be at the choosen offsets.
-** xerr_st (4) 0 (0x34) scratcha
-** sync_st (5) 1 (0x05) sxfer
-** wide_st (7) 3 (0x03) scntl3
+** in a DWORD, the fields HAVE to be at the choosen offsets.
+** xerr_st 0 (0x34) scratcha
+** sync_st 1 (0x05) sxfer
+** wide_st 3 (0x03) scntl3
*/
/*
-** First four bytes (script)
+** Last four bytes (script)
*/
#define QU_REG scr0
#define HS_REG scr1
#define PS_REG scr3
/*
-** First four bytes (host)
+** Last four bytes (host)
*/
#define actualquirks phys.header.status[0]
#define host_status phys.header.status[1]
#define parity_status phys.header.status[3]
/*
-** Last four bytes (script)
+** First four bytes (script)
*/
-#define xerr_st header.status[4] /* MUST be ==0 mod 4 */
-#define sync_st header.status[5] /* MUST be ==1 mod 4 */
-#define nego_st header.status[6]
-#define wide_st header.status[7] /* MUST be ==3 mod 4 */
+#define xerr_st header.scr_st[0]
+#define sync_st header.scr_st[1]
+#define nego_st header.scr_st[2]
+#define wide_st header.scr_st[3]
/*
-** Last four bytes (host)
+** First four bytes (host)
*/
#define xerr_status phys.xerr_st
#define sync_status phys.sync_st
/*
** Timeout handler
*/
+#if 0
u_long heartbeat;
u_short ticks;
u_short latetime;
+#endif
u_long lasttime;
/*-----------------------------------------------
** of 825A, 875 and 895 chips.
*/
struct script {
- ncrcmd start [ 7];
+ ncrcmd start [ 4];
ncrcmd start0 [ 2];
ncrcmd start1 [ 3];
ncrcmd startpos [ 1];
ncrcmd prepare2 [ 24];
ncrcmd setmsg [ 5];
ncrcmd clrack [ 2];
- ncrcmd dispatch [ 33];
+ ncrcmd dispatch [ 38];
ncrcmd no_data [ 17];
ncrcmd checkatn [ 10];
ncrcmd command [ 15];
static void ncr_timeout (ncb_p np);
static void ncr_wakeup (ncb_p np, u_long code);
static void ncr_start_reset (ncb_p np, int settle_delay);
+static int ncr_reset_scsi_bus (ncb_p np, int enab_int, int settle_delay);
#ifdef SCSI_NCR_USER_COMMAND_SUPPORT
static void ncr_usercmd (ncb_p np);
**==========================================================
*/
-#if 0
-static char ident[] =
- "\n$Id: ncr.c,v 1.67 1996/03/11 19:36:07 se Exp $\n";
-static u_long ncr_version = NCR_VERSION * 11
- + (u_long) sizeof (struct ncb) * 7
- + (u_long) sizeof (struct ccb) * 5
- + (u_long) sizeof (struct lcb) * 3
- + (u_long) sizeof (struct tcb) * 2;
-#endif
-
#ifdef SCSI_NCR_DEBUG_INFO_SUPPORT
static int ncr_debug = SCSI_NCR_DEBUG_FLAGS;
#endif
static struct script script0 __initdata = {
/*--------------------------< START >-----------------------*/ {
+#if 0
/*
** Claim to be still alive ...
*/
SCR_COPY (sizeof (((struct ncb *)0)->heartbeat)),
KVAR(SCRIPT_KVAR_JIFFIES),
NADDR (heartbeat),
-
+#endif
/*
** Make data structure address invalid.
** clear SIGP.
0,
SCR_RETURN ^ IFTRUE (WHEN (SCR_DATA_OUT)),
0,
- SCR_RETURN ^ IFTRUE (IF (SCR_DATA_IN)),
+ /*
+ ** DEL 397 - 53C875 Rev 3 - Part Number 609-0392410 - ITEM 4.
+ ** Possible data corruption during Memory Write and Invalidate.
+ ** This work-around resets the addressing logic prior to the
+ ** start of the first MOVE of a DATA IN phase.
+ ** (See README.ncr53c8xx for more information)
+ */
+ SCR_JUMPR ^ IFFALSE (IF (SCR_DATA_IN)),
+ 20,
+ SCR_COPY (4),
+ RADDR (scratcha),
+ RADDR (scratcha),
+ SCR_RETURN,
0,
+
SCR_JUMP ^ IFTRUE (IF (SCR_MSG_OUT)),
PADDR (msg_out),
SCR_JUMP ^ IFTRUE (IF (SCR_MSG_IN)),
while (src < end) {
- *dst++ = opcode = *src++;
+ opcode = *src++;
+ *dst++ = cpu_to_scr(opcode);
/*
** If we forget to change the length
** the NO FLUSH bit if present.
*/
if ((opcode & SCR_NO_FLUSH) && !(np->features & FE_PFEN)) {
- dst[-1] = (opcode & ~SCR_NO_FLUSH);
+ dst[-1] = cpu_to_scr(opcode & ~SCR_NO_FLUSH);
++opchanged;
}
break;
break;
}
- *dst++ = new;
+ *dst++ = cpu_to_scr(new);
}
} else
- *dst++ = *src++;
+ *dst++ = cpu_to_scr(*src++);
};
if (bootverbose > 1 && opchanged)
period = (11 * div_10M[np->clock_divn - 1]) / (4 * np->clock_khz);
np->maxsync = period > 2540 ? 254 : period / 10;
- /*
- ** Get on-board RAM bus address when supported
- */
- if (np->features & FE_RAM) {
- OUTONB(nc_ctest2, 0x8);
- np->paddr2 = INL(nc_scr0);
- OUTOFFB(nc_ctest2, 0x8);
- }
-
/*
** Prepare initial value of other IO registers
*/
** virtual and physical memory.
*/
- np->paddr = device->slot.base;
+ np->paddr = device->slot.base;
+ np->paddr2 = (np->features & FE_RAM)? device->slot.base_2 : 0;
#ifndef NCR_IOMAPPED
np->vaddr = remap_pci_mem((u_long) np->paddr, (u_long) 128);
*/
if (np->features & FE_LED0) {
- np->script0->reselect[0] = SCR_REG_REG(gpreg, SCR_OR, 0x01);
- np->script0->reselect1[0] = SCR_REG_REG(gpreg, SCR_AND, 0xfe);
- np->script0->reselect2[0] = SCR_REG_REG(gpreg, SCR_AND, 0xfe);
+ np->script0->reselect[0] =
+ cpu_to_scr(SCR_REG_REG(gpreg, SCR_OR, 0x01));
+ np->script0->reselect1[0] =
+ cpu_to_scr(SCR_REG_REG(gpreg, SCR_AND, 0xfe));
+ np->script0->reselect2[0] =
+ cpu_to_scr(SCR_REG_REG(gpreg, SCR_AND, 0xfe));
}
/*
** init data structure
*/
- np->jump_tcb.l_cmd = SCR_JUMP;
- np->jump_tcb.l_paddr = NCB_SCRIPTH_PHYS (np, abort);
+ np->jump_tcb.l_cmd = cpu_to_scr(SCR_JUMP);
+ np->jump_tcb.l_paddr = cpu_to_scr(NCB_SCRIPTH_PHYS (np, abort));
/*
** Reset chip.
SA_INTERRUPT|SA_SHIRQ, "ncr53c8xx", np)) {
#else
if (request_irq(device->slot.irq, ncr53c8xx_intr,
- SA_INTERRUPT, "ncr53c8xx", NULL)) {
+ SA_INTERRUPT, "ncr53c8xx", np)) {
#endif
#else
if (request_irq(device->slot.irq, ncr53c8xx_intr,
** Then enable disconnects.
*/
save_flags(flags); cli();
- ncr_start_reset(np, driver_setup.settle_delay);
+ if (ncr_reset_scsi_bus(np, 0, driver_setup.settle_delay) != 0) {
+ printf("%s: FATAL ERROR: CHECK SCSI BUS - CABLES, TERMINATION, DEVICE POWER etc.!\n", ncr_name(np));
+ restore_flags(flags);
+ goto attach_error;
+ }
ncr_exception (np);
restore_flags(flags);
#endif
release_region(np->port, 128);
}
+ if (np->irq) {
+#ifdef DEBUG_NCR53C8XX
+ printf("%s: freeing irq %d\n", ncr_name(np), np->irq);
+#endif
+#if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,70)
+ free_irq(np->irq, np);
+#else
+ free_irq(np->irq);
+#endif
+ }
scsi_unregister(instance);
return -1;
/*---------------------------------------------------
**
- ** Assign a ccb / bind cmd
+ ** Assign a ccb / bind cmd.
+ ** If resetting, shorten settle_time if necessary
+ ** in order to avoid spurious timeouts.
** If resetting or no free ccb,
** insert cmd into the waiting list.
**
*/
save_flags(flags); cli();
+ if (np->settle_time && cmd->timeout_per_command >= HZ &&
+ np->settle_time > jiffies + cmd->timeout_per_command - HZ) {
+ np->settle_time = jiffies + cmd->timeout_per_command - HZ;
+ }
+
if (np->settle_time || !(cp=ncr_get_ccb (np, cmd->target, cmd->lun))) {
insert_into_waiting_list(np, cmd);
restore_flags(flags);
u_long endp;
default:
case XferBoth:
- cp->phys.header.savep = NCB_SCRIPT_PHYS (np, data_io);
+ cp->phys.header.savep =
+ cpu_to_scr(NCB_SCRIPT_PHYS (np, data_io));
cp->phys.header.goalp = cp->phys.header.savep;
break;
case XferIn:
endp = NCB_SCRIPT_PHYS (np, data_in) + MAX_SCATTER*16;
- cp->phys.header.goalp = endp + 8;
- cp->phys.header.savep = endp - segments*16;
+ cp->phys.header.goalp = cpu_to_scr(endp + 8);
+ cp->phys.header.savep = cpu_to_scr(endp - segments*16);
break;
case XferOut:
endp = NCB_SCRIPTH_PHYS (np, data_out) + MAX_SCATTER*16;
- cp->phys.header.goalp = endp + 8;
- cp->phys.header.savep = endp - segments*16;
+ cp->phys.header.goalp = cpu_to_scr(endp + 8);
+ cp->phys.header.savep = cpu_to_scr(endp - segments*16);
break;
case XferNone:
- cp->phys.header.savep = NCB_SCRIPT_PHYS (np, no_data);
+ cp->phys.header.savep =
+ cpu_to_scr(NCB_SCRIPT_PHYS (np, no_data));
cp->phys.header.goalp = cp->phys.header.savep;
break;
}
/*
** Startqueue
*/
- cp->phys.header.launch.l_paddr = NCB_SCRIPT_PHYS (np, select);
- cp->phys.header.launch.l_cmd = SCR_JUMP;
+ cp->phys.header.launch.l_paddr =
+ cpu_to_scr(NCB_SCRIPT_PHYS (np, select));
+ cp->phys.header.launch.l_cmd = cpu_to_scr(SCR_JUMP);
/*
** select
*/
/*
** message
*/
- cp->phys.smsg.addr = CCB_PHYS (cp, scsi_smsg);
- cp->phys.smsg.size = msglen;
+ cp->phys.smsg.addr = cpu_to_scr(CCB_PHYS (cp, scsi_smsg));
+ cp->phys.smsg.size = cpu_to_scr(msglen);
- cp->phys.smsg2.addr = CCB_PHYS (cp, scsi_smsg2);
- cp->phys.smsg2.size = msglen2;
+ cp->phys.smsg2.addr = cpu_to_scr(CCB_PHYS (cp, scsi_smsg2));
+ cp->phys.smsg2.size = cpu_to_scr(msglen2);
/*
** command
*/
- cp->phys.cmd.addr = vtophys (&cmd->cmnd[0]);
- cp->phys.cmd.size = cmd->cmd_len;
+ cp->phys.cmd.addr = cpu_to_scr(vtophys (&cmd->cmnd[0]));
+ cp->phys.cmd.size = cpu_to_scr(cmd->cmd_len);
/*
** sense command
*/
- cp->phys.scmd.addr = CCB_PHYS (cp, sensecmd);
- cp->phys.scmd.size = 6;
+ cp->phys.scmd.addr = cpu_to_scr(CCB_PHYS (cp, sensecmd));
+ cp->phys.scmd.size = cpu_to_scr(6);
/*
** patch requested size into sense command
*/
/*
** sense data
*/
- cp->phys.sense.addr = vtophys (&cmd->sense_buffer[0]);
- cp->phys.sense.size = sizeof(cmd->sense_buffer);
+ cp->phys.sense.addr =
+ cpu_to_scr(vtophys (&cmd->sense_buffer[0]));
+ cp->phys.sense.size = cpu_to_scr(sizeof(cmd->sense_buffer));
/*
** status
*/
** reselect pattern and activate this job.
*/
- cp->jump_ccb.l_cmd = (SCR_JUMP ^ IFFALSE (DATA (cp->tag)));
- /* Compute a time limit bigger than the middle-level driver one */
+ cp->jump_ccb.l_cmd =
+ cpu_to_scr((SCR_JUMP ^ IFFALSE (DATA (cp->tag))));
+
+ /* Compute a time limit greater than the middle-level driver one */
if (cmd->timeout_per_command > 0)
cp->tlimit = jiffies + cmd->timeout_per_command + NCR_TIMEOUT_INCREASE;
else
qidx = np->squeueput + 1;
if (qidx >= MAX_START) qidx=0;
- np->squeue [qidx ] = NCB_SCRIPT_PHYS (np, idle);
- np->squeue [np->squeueput] = CCB_PHYS (cp, phys);
+ np->squeue [qidx ] = cpu_to_scr(NCB_SCRIPT_PHYS (np, idle));
+ np->squeue [np->squeueput] = cpu_to_scr(CCB_PHYS (cp, phys));
np->squeueput = qidx;
if(DEBUG_FLAGS & DEBUG_QUEUE)
printf ("%s: queuepos=%d tryoffset=%d.\n", ncr_name (np),
np->squeueput,
- (unsigned)(np->script->startpos[0]-
+ (unsigned)(scr_to_cpu(np->script->startpos[0]) -
(NCB_SCRIPTH_PHYS (np, tryloop))));
/*
save_flags(flags); cli();
if (!np->settle_time) {
- if (bootverbose > 1)
- printf("%s: resetting, command processing suspended for %d seconds\n",
- ncr_name(np), settle_delay);
- np->settle_time = jiffies + settle_delay * HZ;
- OUTB (nc_istat, SRST);
- DELAY (1000);
- OUTB (nc_istat, 0);
- OUTW (nc_sien, RST);
- OUTB (nc_scntl1, CRST);
- DELAY (100);
+ (void) ncr_reset_scsi_bus(np, 1, settle_delay);
}
-
restore_flags(flags);
}
+static int ncr_reset_scsi_bus(ncb_p np, int enab_int, int settle_delay)
+{
+ u_int32 term;
+ int retv = 0;
+
+ np->settle_time = jiffies + settle_delay * HZ;
+
+ if (bootverbose > 1)
+ printf("%s: resetting, "
+ "command processing suspended for %d seconds\n",
+ ncr_name(np), settle_delay);
+
+ OUTB (nc_istat, SRST);
+ DELAY (1000);
+ OUTB (nc_istat, 0);
+ if (enab_int)
+ OUTW (nc_sien, RST);
+ OUTB (nc_scntl1, CRST);
+ DELAY (100);
+
+ if (!driver_setup.bus_check)
+ goto out;
+ /*
+ ** Check for no terminators or SCSI bus shorts to ground.
+ ** Read SCSI data bus, data parity bits and control signals.
+ ** We are expecting RESET to be TRUE and other signals to be
+ ** FALSE.
+ */
+ term = INB(nc_sstat0); /* rst, sdp0 */
+ term = ((term & 2) << 7) + ((term & 1) << 16);
+ term |= ((INB(nc_sstat2) & 0x01) << 25) | /* sdp1 */
+ (INW(nc_sbdl) << 9) | /* d15-0 */
+ INB(nc_sbcl); /* req, ack, bsy, sel, atn, msg, cd, io */
+
+ if (!(np->features & FE_WIDE))
+ term &= 0x3ffff;
+
+ if (term != (2<<7)) {
+ printf("%s: suspicious SCSI data while resetting the BUS.\n",
+ ncr_name(np));
+ printf("%s: %sdp0,d7-0,rst,req,ack,bsy,sel,atn,msg,c/d,i/o = "
+ "0x%lx, expecting 0x%lx\n",
+ ncr_name(np),
+ (np->features & FE_WIDE) ? "dp1,d15-8," : "",
+ (u_long)term, (u_long)(2<<7));
+ if (driver_setup.bus_check == 1)
+ retv = 1;
+ }
+out:
+ OUTB (nc_scntl1, 0);
+ return retv;
+}
+
/*==========================================================
**
**
* Commands will now be queued in the waiting list until a settle
* delay of 2 seconds will be completed.
*/
- ncr_start_reset(np, 2);
+ ncr_start_reset(np, driver_setup.settle_delay);
/*
* First, look in the wakeup list
*/
** this condition in order to complete the canceled command
** after the script skipped the ccb, if necessary.
*/
- cp->jump_ccb.l_cmd = (SCR_JUMP);
- if (cp->phys.header.launch.l_paddr == NCB_SCRIPT_PHYS (np, select)) {
+ cp->jump_ccb.l_cmd = cpu_to_scr(SCR_JUMP);
+ if (cp->phys.header.launch.l_paddr ==
+ cpu_to_scr(NCB_SCRIPT_PHYS (np, select))) {
printf ("%s: abort ccb=%p (skip)\n", ncr_name (np), cp);
- cp->phys.header.launch.l_paddr = NCB_SCRIPT_PHYS (np, skip);
+ cp->phys.header.launch.l_paddr =
+ cpu_to_scr(NCB_SCRIPT_PHYS (np, skip));
}
cp->tlimit = 0;
*/
#ifdef MODULE
-static int ncr_detach(ncb_p np, int irq)
+static int ncr_detach(ncb_p np)
{
ccb_p cp;
tcb_p tp;
*/
#ifdef DEBUG_NCR53C8XX
- printf("%s: freeing irq %d\n", ncr_name(np), irq);
+ printf("%s: freeing irq %d\n", ncr_name(np), np->irq);
#endif
#if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,70)
-# ifdef SCSI_NCR_SHARE_IRQ
- free_irq(irq, np);
-# else
- free_irq(irq, NULL);
-# endif
+ free_irq(np->irq, np);
#else
- free_irq(irq);
+ free_irq(np->irq);
#endif
/*
/*
** No Reselect anymore.
*/
- cp->jump_ccb.l_cmd = (SCR_JUMP);
+ cp->jump_ccb.l_cmd = cpu_to_scr(SCR_JUMP);
/*
** No starting.
*/
- cp->phys.header.launch.l_paddr= NCB_SCRIPT_PHYS (np, idle);
+ cp->phys.header.launch.l_paddr = cpu_to_scr(NCB_SCRIPT_PHYS (np, idle));
/*
** timestamp
** Check the status.
*/
if ( (cp->host_status == HS_COMPLETE)
- && (cp->scsi_status == S_GOOD)) {
-
+ && (cp->scsi_status == S_GOOD ||
+ cp->scsi_status == S_COND_MET)) {
/*
- ** All went well.
+ ** All went well (GOOD status).
+ ** CONDITION MET status is returned on
+ ** `Pre-Fetch' or `Search data' success.
*/
cmd->result = ScsiResult(DID_OK, cp->scsi_status);
}
} else if ((cp->host_status == HS_COMPLETE)
- && (cp->scsi_status == S_BUSY)) {
+ && (cp->scsi_status == S_BUSY ||
+ cp->scsi_status == S_CONFLICT)) {
/*
** Target is busy.
** Clear Start Queue
*/
for (i=0;i<MAX_START;i++)
- np -> squeue [i] = NCB_SCRIPT_PHYS (np, idle);
+ np -> squeue [i] = cpu_to_scr(NCB_SCRIPT_PHYS (np, idle));
/*
** Start at first entry.
*/
np->squeueput = 0;
- np->script0->startpos[0] = NCB_SCRIPTH_PHYS (np, tryloop);
- np->script0->start0 [0] = SCR_INT ^ IFFALSE (0);
+ np->script0->startpos[0] = cpu_to_scr(NCB_SCRIPTH_PHYS (np, tryloop));
+ np->script0->start0 [0] = cpu_to_scr(SCR_INT ^ IFFALSE (0));
/*
** Wakeup all pending jobs.
if (np->vaddr2) {
if (bootverbose)
printf ("%s: copying script fragments into the on-board RAM ...\n", ncr_name(np));
- bcopy(np->script0, np->script, sizeof(struct script));
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,0,0)
+ memcpy_toio(np->script, np->script0, sizeof(struct script));
+#else
+ memcpy(np->script, np->script0, sizeof(struct script));
+#endif
}
/*
if (tp->period < 500) scsi = "FAST-40";
else if (tp->period < 1000) scsi = "FAST-20";
else if (tp->period < 2000) scsi = "FAST-10";
- else scsi = "SLOW";
+ else scsi = "FAST-5";
printf ("%s %sSCSI %d.%d MB/s (%d ns, offset %d)\n", scsi,
tp->widedone > 1 ? "WIDE " : "",
{
u_long thistime = jiffies;
u_long count = 0;
- long signed t;
ccb_p cp;
u_long flags;
**
**----------------------------------------------------
*/
-
- t = (thistime - np->heartbeat) / HZ;
-
- if (t<2) np->latetime=0; else np->latetime++;
+#if 0
+ if (thistime < np->heartbeat + HZ + HZ)
+ np->latetime = 0;
+ else
+ np->latetime++;
+#endif
/*----------------------------------------------------
**
** still in start queue ?
*/
if (cp->phys.header.launch.l_paddr ==
- NCB_SCRIPT_PHYS (np, skip))
+ cpu_to_scr(NCB_SCRIPT_PHYS (np, skip)))
continue;
/* fall through */
script_base = (u_char *) np->script;
script_name = "script";
}
- else {
+ else if (np->p_scripth < dsp &&
+ dsp <= np->p_scripth + sizeof(struct scripth)) {
script_ofs = dsp - np->p_scripth;
script_size = sizeof(struct scripth);
script_base = (u_char *) np->scripth;
script_name = "scripth";
+ } else {
+ script_ofs = dsp;
+ script_size = 0;
+ script_base = 0;
+ script_name = "mem";
}
- printf ("%s:%d: ERROR (%x:%x) (%x-%x-%x) (%x/%x) @ %s (%x:%08x).\n",
+ printf ("%s:%d: ERROR (%x:%x) (%x-%x-%x) (%x/%x) @ (%s %x:%08x).\n",
ncr_name (np), (unsigned)INB (nc_ctest0)&0x0f, dstat, sist,
(unsigned)INB (nc_socl), (unsigned)INB (nc_sbcl), (unsigned)INB (nc_sbdl),
(unsigned)INB (nc_sxfer),(unsigned)INB (nc_scntl3), script_name, script_ofs,
/*
** interrupt on the fly ?
- */
- while ((istat = INB (nc_istat)) & INTF) {
+ ** Since the global header may be copied back to a CCB
+ ** using a posted PCI memory write, the last operation on
+ ** the istat register is a READ in order to flush posted
+ ** PCI commands (Btw, the 'do' loop is probably useless).
+ */
+ istat = INB (nc_istat);
+ if (istat & INTF) {
+ do {
+ OUTB (nc_istat, (istat & SIGP) | INTF);
+ istat = INB (nc_istat);
+ } while (istat & INTF);
if (DEBUG_FLAGS & DEBUG_TINY) printf ("F ");
-#ifdef SCSI_NCR_DEBUG_ERROR_RECOVERY_SUPPORT
- if (np->stalling)
- OUTB (nc_istat, INTF);
- else
-#endif
- OUTB (nc_istat, (istat & SIGP) | INTF);
np->profile.num_fly++;
ncr_wakeup (np, 0);
};
ncr_int_sir (np);
return;
}
- if (!(sist & (SBMC|PAR)) && !(dstat & SSI))
- printf("%s: unknown interrupt(s) ignored sist=%x dstat=%x\n",
- ncr_name(np), sist, dstat);
+ /*
+ ** DEL 397 - 53C875 Rev 3 - Part Number 609-0392410 - ITEM 2.
+ */
+ if (!(sist & (SBMC|PAR)) && !(dstat & SSI)) {
+ printf( "%s: unknown interrupt(s) ignored, "
+ "ISTAT=%x DSTAT=%x SIST=%x\n",
+ ncr_name(np), istat, dstat, sist);
+ return;
+ }
+
OUTONB (nc_dcntl, (STD|NOCOM));
return;
};
if ((sist & STO) &&
!(dstat & (MDPE|BF|ABRT))) {
+ /*
+ ** DEL 397 - 53C875 Rev 3 - Part Number 609-0392410 - ITEM 1.
+ */
+ OUTONB (nc_ctest3, CLF);
+
ncr_int_sto (np);
return;
};
if ((sist & (SGE)) ||
(dstat & (MDPE|BF|ABORT|IID))) {
- ncr_start_reset(np, 2);
+ ncr_start_reset(np, driver_setup.settle_delay);
return;
};
if (sist & HTH) {
printf ("%s: handshake timeout\n", ncr_name(np));
- ncr_start_reset(np, 2);
+ ncr_start_reset(np, driver_setup.settle_delay);
return;
};
OUTB (nc_scr1, HS_UNEXPECTED);
OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, cleanup));
};
- ncr_start_reset(np, 2);
+ ncr_start_reset(np, driver_setup.settle_delay);
return;
};
/* assert ((diff <= MAX_START * 20) && !(diff % 20));*/
if ((diff <= MAX_START * 20) && !(diff % 20)) {
- np->script->startpos[0] = scratcha;
+ np->script->startpos[0] = cpu_to_scr(scratcha);
OUTL (nc_dsp, NCB_SCRIPT_PHYS (np, start));
return;
};
ncr_name(np), np->scsi_mode, scsi_mode);
np->scsi_mode = scsi_mode;
- ncr_start_reset(np, 2);
+ ncr_start_reset(np, driver_setup.settle_delay);
return 1;
}
** get old startaddress and old length.
*/
- oadr = vdsp[1];
+ oadr = scr_to_cpu(vdsp[1]);
if (cmd & 0x10) { /* Table indirect */
tblp = (u_int32 *) ((char*) &cp->phys + oadr);
- olen = tblp[0];
- oadr = tblp[1];
+ olen = scr_to_cpu(tblp[0]);
+ oadr = scr_to_cpu(tblp[1]);
} else {
tblp = (u_int32 *) 0;
- olen = vdsp[0] & 0xffffff;
+ olen = scr_to_cpu(vdsp[0]) & 0xffffff;
};
if (DEBUG_FLAGS & DEBUG_PHASE) {
printf ("OCMD=%x\nTBLP=%p OLEN=%x OADR=%x\n",
- (unsigned) (vdsp[0] >> 24),
+ (unsigned) (scr_to_cpu(vdsp[0]) >> 24),
tblp,
(unsigned) olen,
(unsigned) oadr);
** check cmd against assumed interrupted script command.
*/
- if (cmd != (vdsp[0] >> 24)) {
+ if (cmd != (scr_to_cpu(vdsp[0]) >> 24)) {
PRINT_ADDR(cp->cmd);
printf ("internal error: cmd=%02x != %02x=(vdsp[0] >> 24)\n",
- (unsigned)cmd, (unsigned)vdsp[0] >> 24);
+ (unsigned)cmd, (unsigned)scr_to_cpu(vdsp[0]) >> 24);
return;
}
*/
newcmd = cp->patch;
- if (cp->phys.header.savep == vtophys (newcmd)) newcmd+=4;
+ if (cp->phys.header.savep == cpu_to_scr(vtophys (newcmd))) newcmd+=4;
/*
** fillin the commands
*/
- newcmd[0] = ((cmd & 0x0f) << 24) | rest;
- newcmd[1] = oadr + olen - rest;
- newcmd[2] = SCR_JUMP;
- newcmd[3] = nxtdsp;
+ newcmd[0] = cpu_to_scr(((cmd & 0x0f) << 24) | rest);
+ newcmd[1] = cpu_to_scr(oadr + olen - rest);
+ newcmd[2] = cpu_to_scr(SCR_JUMP);
+ newcmd[3] = cpu_to_scr(nxtdsp);
if (DEBUG_FLAGS & DEBUG_PHASE) {
PRINT_ADDR(cp->cmd);
printf ("newcmd[%d] %x %x %x %x.\n",
(int) (newcmd - cp->patch),
- (unsigned)newcmd[0],
- (unsigned)newcmd[1],
- (unsigned)newcmd[2],
- (unsigned)newcmd[3]);
+ (unsigned)scr_to_cpu(newcmd[0]),
+ (unsigned)scr_to_cpu(newcmd[1]),
+ (unsigned)scr_to_cpu(newcmd[2]),
+ (unsigned)scr_to_cpu(newcmd[3]));
}
/*
** fake the return address (to the patch).
*/
if (num == SIR_DATA_IO_IS_OUT) {
endp = NCB_SCRIPTH_PHYS (np, data_out) + MAX_SCATTER*16;
- cp->phys.header.goalp = endp + 8;
- cp->phys.header.savep = endp - cp->segments*16;
+ cp->phys.header.goalp = cpu_to_scr(endp + 8);
+ cp->phys.header.savep =
+ cpu_to_scr(endp - cp->segments*16);
} else {
endp = NCB_SCRIPT_PHYS (np, data_in) + MAX_SCATTER*16;
- cp->phys.header.goalp = endp + 8;
- cp->phys.header.savep = endp - cp->segments*16;
+ cp->phys.header.goalp = cpu_to_scr(endp + 8);
+ cp->phys.header.savep =
+ cpu_to_scr(endp - cp->segments*16);
}
cp->phys.header.lastp = cp->phys.header.savep;
np->header.savep = cp->phys.header.savep;
np->header.goalp = cp->phys.header.goalp;
np->header.lastp = cp->phys.header.lastp;
- OUTL (nc_temp, np->header.savep);
- OUTL (nc_dsp, np->header.savep);
+
+ OUTL (nc_temp, scr_to_cpu(np->header.savep));
+ OUTL (nc_dsp, scr_to_cpu(np->header.savep));
return;
/* break; */
** no job, resume normal processing
*/
if (DEBUG_FLAGS & DEBUG_RESTART) printf (" -- remove trap\n");
- np->script->start0[0] = SCR_INT ^ IFFALSE (0);
+ np->script->start0[0] = cpu_to_scr(SCR_INT ^ IFFALSE (0));
break;
case SIR_SENSE_FAILED:
/*
** And patch code to restart it.
*/
- np->script->start0[0] = SCR_INT;
+ np->script->start0[0] = cpu_to_scr(SCR_INT);
break;
/*-----------------------------------------------------------------------------
PRINT_ADDR(cp->cmd);
printf ("M_REJECT received (%x:%x).\n",
- (unsigned)np->lastmsg, np->msgout[0]);
+ (unsigned)scr_to_cpu(np->lastmsg), np->msgout[0]);
break;
case SIR_REJECT_SENT:
printf ("M_DISCONNECT received, but datapointer not saved: "
"data=%x save=%x goal=%x.\n",
(unsigned) INL (nc_temp),
- (unsigned) np->header.savep,
- (unsigned) np->header.goalp);
+ (unsigned) scr_to_cpu(np->header.savep),
+ (unsigned) scr_to_cpu(np->header.goalp));
break;
#if 0 /* This stuff does not work */
PRINT_ADDR(cp->cmd);
printf ("queue full.\n");
- np->script->start1[0] = SCR_INT;
+ np->script->start1[0] = cpu_to_scr(SCR_INT);
/*
** Try to disable tagged transfers.
*/
printf ("%s: queue empty.\n", ncr_name (np));
- np->script->start1[0] = SCR_INT ^ IFFALSE (0);
+ np->script->start1[0] = cpu_to_scr(SCR_INT ^ IFFALSE (0));
break;
#endif /* This stuff does not work */
};
tp=&np->target[target];
if (!tp->jump_tcb.l_cmd) {
-
/*
** initialize it.
*/
- tp->jump_tcb.l_cmd = (SCR_JUMP^IFFALSE (DATA (0x80 + target)));
+ tp->jump_tcb.l_cmd =
+ cpu_to_scr((SCR_JUMP^IFFALSE (DATA (0x80 + target))));
tp->jump_tcb.l_paddr = np->jump_tcb.l_paddr;
- tp->getscr[0] =
- (np->features & FE_PFEN)? SCR_COPY(1) : SCR_COPY_F(1);
- tp->getscr[1] = vtophys (&tp->sval);
- tp->getscr[2] = np->paddr + offsetof (struct ncr_reg, nc_sxfer);
- tp->getscr[3] =
- (np->features & FE_PFEN)? SCR_COPY(1) : SCR_COPY_F(1);
- tp->getscr[4] = vtophys (&tp->wval);
- tp->getscr[5] = np->paddr + offsetof (struct ncr_reg, nc_scntl3);
+ tp->getscr[0] = (np->features & FE_PFEN) ?
+ cpu_to_scr(SCR_COPY(1)):cpu_to_scr(SCR_COPY_F(1));
+ tp->getscr[1] = cpu_to_scr(vtophys (&tp->sval));
+ tp->getscr[2] =
+ cpu_to_scr(np->paddr + offsetof (struct ncr_reg, nc_sxfer));
+
+ tp->getscr[3] = (np->features & FE_PFEN) ?
+ cpu_to_scr(SCR_COPY(1)):cpu_to_scr(SCR_COPY_F(1));
+ tp->getscr[4] = cpu_to_scr(vtophys (&tp->wval));
+ tp->getscr[5] =
+ cpu_to_scr(np->paddr + offsetof (struct ncr_reg, nc_scntl3));
assert (( (offsetof(struct ncr_reg, nc_sxfer) ^
offsetof(struct tcb , sval )) &3) == 0);
assert (( (offsetof(struct ncr_reg, nc_scntl3) ^
offsetof(struct tcb , wval )) &3) == 0);
- tp->call_lun.l_cmd = (SCR_CALL);
- tp->call_lun.l_paddr = NCB_SCRIPT_PHYS (np, resel_lun);
+ tp->call_lun.l_cmd = cpu_to_scr(SCR_CALL);
+ tp->call_lun.l_paddr =
+ cpu_to_scr(NCB_SCRIPT_PHYS (np, resel_lun));
- tp->jump_lcb.l_cmd = (SCR_JUMP);
- tp->jump_lcb.l_paddr = NCB_SCRIPTH_PHYS (np, abort);
- np->jump_tcb.l_paddr = vtophys (&tp->jump_tcb);
+ tp->jump_lcb.l_cmd = cpu_to_scr(SCR_JUMP);
+ tp->jump_lcb.l_paddr = cpu_to_scr(NCB_SCRIPTH_PHYS (np, abort));
+ np->jump_tcb.l_paddr = cpu_to_scr(vtophys (&tp->jump_tcb));
}
/*
** Initialize it
*/
bzero (lp, sizeof (*lp));
- lp->jump_lcb.l_cmd = (SCR_JUMP ^ IFFALSE (DATA (lun)));
+ lp->jump_lcb.l_cmd =
+ cpu_to_scr(SCR_JUMP ^ IFFALSE (DATA (lun)));
lp->jump_lcb.l_paddr = tp->jump_lcb.l_paddr;
- lp->call_tag.l_cmd = (SCR_CALL);
- lp->call_tag.l_paddr = NCB_SCRIPT_PHYS (np, resel_tag);
+ lp->call_tag.l_cmd = cpu_to_scr(SCR_CALL);
+ lp->call_tag.l_paddr =
+ cpu_to_scr(NCB_SCRIPT_PHYS (np, resel_tag));
- lp->jump_ccb.l_cmd = (SCR_JUMP);
- lp->jump_ccb.l_paddr = NCB_SCRIPTH_PHYS (np, aborttag);
+ lp->jump_ccb.l_cmd = cpu_to_scr(SCR_JUMP);
+ lp->jump_ccb.l_paddr =
+ cpu_to_scr(NCB_SCRIPTH_PHYS (np, aborttag));
lp->actlink = 1;
/*
** Chain into LUN list
*/
- tp->jump_lcb.l_paddr = vtophys (&lp->jump_lcb);
+ tp->jump_lcb.l_paddr = cpu_to_scr(vtophys (&lp->jump_lcb));
tp->lp[lun] = lp;
ncr_setmaxtags (np, tp, driver_setup.default_tags);
/*
** Chain into reselect list
*/
- cp->jump_ccb.l_cmd = SCR_JUMP;
+ cp->jump_ccb.l_cmd = cpu_to_scr(SCR_JUMP);
cp->jump_ccb.l_paddr = lp->jump_ccb.l_paddr;
- lp->jump_ccb.l_paddr = CCB_PHYS (cp, jump_ccb);
- cp->call_tmp.l_cmd = SCR_CALL;
- cp->call_tmp.l_paddr = NCB_SCRIPT_PHYS (np, resel_tmp);
+ lp->jump_ccb.l_paddr = cpu_to_scr(CCB_PHYS (cp, jump_ccb));
+ cp->call_tmp.l_cmd = cpu_to_scr(SCR_CALL);
+ cp->call_tmp.l_paddr = cpu_to_scr(NCB_SCRIPT_PHYS (np, resel_tmp));
/*
** Chain into wakeup list
if (!use_sg) {
if (cmd->request_bufflen) {
data = &data[MAX_SCATTER - 1];
- data[0].addr = vtophys(cmd->request_buffer);
- data[0].size = cmd->request_bufflen;
- cp->data_len = data[0].size;
+ data[0].addr = cpu_to_scr(vtophys(cmd->request_buffer));
+ data[0].size = cpu_to_scr(cmd->request_bufflen);
+ cp->data_len = cmd->request_bufflen;
segment = 1;
}
}
data = &data[MAX_SCATTER - use_sg];
while (segment < use_sg) {
- data[segment].addr = vtophys(scatter[segment].address);
- data[segment].size = scatter[segment].length;
- cp->data_len += data[segment].size;
+ data[segment].addr =
+ cpu_to_scr(vtophys(scatter[segment].address));
+ data[segment].size =
+ cpu_to_scr(scatter[segment].length);
+ cp->data_len += scatter[segment].length;
++segment;
}
}
/*
** Set memory and register.
*/
- np->ncr_cache = host_wr;
+ np->ncr_cache = cpu_to_scr(host_wr);
OUTL (nc_temp, ncr_wr);
/*
** Start script (exchange values)
/*
** Read memory and register.
*/
- host_rd = np->ncr_cache;
+ host_rd = scr_to_cpu(np->ncr_cache);
ncr_rd = INL (nc_scratcha);
ncr_bk = INL (nc_temp);
/*
driver_setup.irqm = val;
else if (!strncmp(cur, "pcifix:", 7))
driver_setup.pci_fix_up = val;
+ else if (!strncmp(cur, "buschk:", 7))
+ driver_setup.bus_check = val;
#ifdef SCSI_NCR_NVRAM_SUPPORT
else if (!strncmp(cur, "nvram:", 6))
driver_setup.use_nvram = val;
else
printf("ncr53c8xx_setup: unexpected boot option '%.*s' ignored\n", (int)(pc-cur+1), cur);
+#ifdef MODULE
+ if ((cur = strchr(cur, ' ')) != NULL)
+#else
if ((cur = strchr(cur, ',')) != NULL)
+#endif
++cur;
}
#endif /* SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT */
)
{
#define YesNo(y) y ? 'y' : 'n'
- printk("ncr53c8xx: setup=disc:%c,specf:%c,ultra:%c,tags:%d,sync:%d,burst:%d,wide:%c,diff:%d\n",
+ printk("ncr53c8xx: setup=disc:%c,specf:%d,ultra:%c,tags:%d,sync:%d,burst:%d,wide:%c,diff:%d\n",
YesNo(driver_setup.disconnection),
- YesNo(driver_setup.special_features),
+ driver_setup.special_features,
YesNo(driver_setup.ultra_scsi),
driver_setup.default_tags,
driver_setup.default_sync,
h->device_id == devp->chip.device_id)
break;
}
- if (j < count && !devp->attached &&
- !ncr_attach (tpnt, attach_count, devp)) {
- attach_count++;
- devp->attached = 1;
+ if (j < count && !devp->attach_done) {
+ if (!ncr_attach (tpnt, attach_count, devp))
+ attach_count++;
+ devp->attach_done = 1;
}
}
# endif
#endif
+#if defined(SCSI_NCR_BOOT_COMMAND_LINE_SUPPORT) && defined(MODULE)
+if (ncr53c8xx)
+ ncr53c8xx_setup(ncr53c8xx, (int *) 0);
+#endif
+
/*
** Detect all 53c8xx hosts and then attach them.
**
#endif
printf(KERN_INFO "ncr53c8xx: 53c%s detected %s\n",
device[count].chip.name, msg);
-
- device[count].attached = 0;
++count;
}
}
** so try to attach them here.
*/
for (i= 0; i < count; i++) {
- if ((!device[i].attached) && (!ncr_attach (tpnt, attach_count, &device[i]))) {
+ if (!device[i].attach_done &&
+ !ncr_attach (tpnt, attach_count, &device[i])) {
attach_count++;
- device[i].attached = 1;
}
}
uchar cache_line_size, latency_timer;
uchar irq, revision;
#if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,0)
- uint base, io_port;
+ uint base, base_2, io_port;
#else
- ulong base, io_port;
+ ulong base, base_2;
#endif
- int i, error;
+ int i;
#ifdef SCSI_NCR_NVRAM_SUPPORT
ncr_nvram *nvram = device->nvram;
printk(KERN_INFO "ncr53c8xx: at PCI bus %d, device %d, function %d\n",
bus, (int) (device_fn & 0xf8) >> 3, (int) device_fn & 7);
/*
- * Read info from the PCI config space
+ * Read info from the PCI config space.
+ * pcibios_read_config_xxx() functions are assumed to be used for
+ * successfully detected PCI devices.
+ * Expecting error conditions from them is just paranoia,
+ * thus void cast.
*/
- if (
- (error=pcibios_read_config_word(bus, device_fn, PCI_VENDOR_ID, &vendor_id)) ||
- (error=pcibios_read_config_word(bus, device_fn, PCI_DEVICE_ID, &device_id)) ||
- (error=pcibios_read_config_word( bus, device_fn, PCI_COMMAND, &command)) ||
- (error=pcibios_read_config_dword(bus, device_fn, PCI_BASE_ADDRESS_0,&io_port)) ||
- (error=pcibios_read_config_dword(bus, device_fn, PCI_BASE_ADDRESS_1, &base)) ||
- (error=pcibios_read_config_byte(bus, device_fn, PCI_CLASS_REVISION,&revision)) ||
- (error=pcibios_read_config_byte(bus, device_fn, PCI_INTERRUPT_LINE, &irq)) ||
- (error=pcibios_read_config_byte(bus, device_fn, PCI_CACHE_LINE_SIZE, &cache_line_size)) ||
- (error=pcibios_read_config_byte(bus, device_fn, PCI_LATENCY_TIMER, &latency_timer))
- )
- goto err_pcibios;
+ (void) pcibios_read_config_word(bus, device_fn,
+ PCI_VENDOR_ID, &vendor_id);
+ (void) pcibios_read_config_word(bus, device_fn,
+ PCI_DEVICE_ID, &device_id);
+ (void) pcibios_read_config_word(bus, device_fn,
+ PCI_COMMAND, &command);
+ (void) pcibios_read_config_dword(bus, device_fn,
+ PCI_BASE_ADDRESS_0, &io_port);
+ (void) pcibios_read_config_dword(bus, device_fn,
+ PCI_BASE_ADDRESS_1, &base);
+ (void) pcibios_read_config_dword(bus, device_fn,
+ PCI_BASE_ADDRESS_2, &base_2);
+ (void) pcibios_read_config_byte(bus, device_fn,
+ PCI_CLASS_REVISION,&revision);
+ (void) pcibios_read_config_byte(bus, device_fn,
+ PCI_INTERRUPT_LINE, &irq);
+ (void) pcibios_read_config_byte(bus, device_fn,
+ PCI_CACHE_LINE_SIZE, &cache_line_size);
+ (void) pcibios_read_config_byte(bus, device_fn,
+ PCI_LATENCY_TIMER, &latency_timer);
/*
* Check if the chip is supported
return -1;
}
+#ifdef __powerpc__
+ /*
+ * Severall fix-up for power/pc.
+ * Should not be performed by the driver.
+ */
+ if ((command &
+ (PCI_COMMAND_MASTER|PCI_COMMAND_IO|PCI_COMMAND_MEMORY)) !=
+ (PCI_COMMAND_MASTER|PCI_COMMAND_IO|PCI_COMMAND_MEMORY)) {
+ printk("ncr53c8xx : setting PCI master/io/command bit\n");
+ command |= PCI_COMMAND_MASTER|PCI_COMMAND_IO|PCI_COMMAND_MEMORY;
+ pcibios_write_config_word(bus, device_fn, PCI_COMMAND, command);
+ }
+ if (io_port >= 0x10000000) {
+ io_port = (io_port & 0x00FFFFFF) | 0x01000000;
+ pcibios_write_config_dword(bus, device_fn, PCI_BASE_ADDRESS_0, io_port);
+ }
+ if (base >= 0x10000000) {
+ base = (base & 0x00FFFFFF) | 0x01000000;
+ pcibios_write_config_dword(bus, device_fn, PCI_BASE_ADDRESS_1, base);
+ }
+#endif
+
/*
* Check availability of IO space, memory space and master capability.
*/
return -1;
}
+ base_2 &= PCI_BASE_ADDRESS_MEM_MASK;
+
if (io_port && check_region (io_port, 128)) {
printk("ncr53c8xx: IO region 0x%x to 0x%x is in use\n",
(int) io_port, (int) (io_port + 127));
/*
* Fix some features according to driver setup.
*/
- if (!driver_setup.special_features)
+ if (!(driver_setup.special_features & 1))
chip->features &= ~FE_SPECIAL_SET;
+ else {
+ if (driver_setup.special_features & 2)
+ chip->features &= ~FE_WRIE;
+ }
if (driver_setup.ultra_scsi < 2 && (chip->features & FE_ULTRA2)) {
chip->features |= FE_ULTRA;
chip->features &= ~FE_ULTRA2;
#if defined(__i386) && !defined(MODULE)
if ((driver_setup.pci_fix_up & 1) &&
(chip->features & FE_CLSE) && cache_line_size == 0) {
+#if LINUX_VERSION_CODE < LinuxVersionCode(2,1,75)
extern char x86;
switch(x86) {
+#else
+ switch(boot_cpu_data.x86) {
+#endif
case 4: cache_line_size = 4; break;
case 5: cache_line_size = 8; break;
}
if (cache_line_size)
- error = pcibios_write_config_byte(bus, device_fn, PCI_CACHE_LINE_SIZE, cache_line_size);
- if (error)
- goto err_pcibios;
+ (void) pcibios_write_config_byte(bus, device_fn,
+ PCI_CACHE_LINE_SIZE, cache_line_size);
if (initverbose)
printk("ncr53c8xx: setting PCI_CACHE_LINE_SIZE to %d (fix-up).\n", cache_line_size);
}
if ((driver_setup.pci_fix_up & 2) && cache_line_size &&
(chip->features & FE_WRIE) && !(command & PCI_COMMAND_INVALIDATE)) {
command |= PCI_COMMAND_INVALIDATE;
- error=pcibios_write_config_word(bus, device_fn, PCI_COMMAND, command);
- if (error)
- goto err_pcibios;
+ (void) pcibios_write_config_word(bus, device_fn,
+ PCI_COMMAND, command);
if (initverbose)
printk("ncr53c8xx: setting PCI_COMMAND_INVALIDATE bit (fix-up).\n");
}
latency_timer = lt;
if (initverbose)
printk("ncr53c8xx: setting PCI_LATENCY_TIMER to %d bus clocks (fix-up).\n", latency_timer);
- error = pcibios_write_config_byte(bus, device_fn,
+ (void) pcibios_write_config_byte(bus, device_fn,
PCI_LATENCY_TIMER, latency_timer);
- if (error)
- goto err_pcibios;
}
}
device->slot.bus = bus;
device->slot.device_fn = device_fn;
device->slot.base = base;
+ device->slot.base_2 = base_2;
device->slot.io_port = io_port;
device->slot.irq = irq;
- device->attached = 0;
+ device->attach_done = 0;
#ifdef SCSI_NCR_NVRAM_SUPPORT
if (!nvram)
goto out;
#endif /* SCSI_NCR_NVRAM_SUPPORT */
return 0;
-
-err_pcibios:
- printk("ncr53c8xx: error %s reading configuration space\n",
- pcibios_strerror(error));
- return -1;
}
#if LINUX_VERSION_CODE >= LinuxVersionCode(2,0,0)
}
/*
-** Linux entry point of the interrupt handler
+** Linux entry point of the interrupt handler.
+** Fort linux versions > 1.3.70, we trust the kernel for
+** passing the internal host descriptor as 'dev_id'.
+** Otherwise, we scan the host list and call the interrupt
+** routine for each host that uses this IRQ.
*/
#if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,70)
static void ncr53c8xx_intr(int irq, void *dev_id, struct pt_regs * regs)
+{
+#ifdef DEBUG_NCR53C8XX
+ printk("ncr53c8xx : interrupt received\n");
+#endif
+
+ if (DEBUG_FLAGS & DEBUG_TINY) printf ("[");
+ ncr_exception((ncb_p) dev_id);
+ if (DEBUG_FLAGS & DEBUG_TINY) printf ("]\n");
+}
+
#else
static void ncr53c8xx_intr(int irq, struct pt_regs * regs)
-#endif
{
struct Scsi_Host *host;
struct host_data *host_data;
-#if 0
- u_long flags;
-
- save_flags(flags); cli();
-#endif
-
-#ifdef DEBUG_NCR53C8XX
-printk("ncr53c8xx : interrupt received\n");
-#endif
for (host = first_host; host; host = host->next) {
if (host->hostt == the_template && host->irq == irq) {
host_data = (struct host_data *) host->hostdata;
-#if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,70)
-# ifdef SCSI_NCR_SHARE_IRQ
- if (dev_id == host_data->ncb) {
-#else
- if (1) {
-# endif
-#endif
- if (DEBUG_FLAGS & DEBUG_TINY) printf ("[");
- ncr_exception(host_data->ncb);
- if (DEBUG_FLAGS & DEBUG_TINY) printf ("]\n");
- }
+ if (DEBUG_FLAGS & DEBUG_TINY) printf ("[");
+ ncr_exception(host_data->ncb);
+ if (DEBUG_FLAGS & DEBUG_TINY) printf ("]\n");
}
}
-#if 0
- restore_flags(flags);
-#endif
}
+#endif
/*
** Linux entry point of the timer handler
#ifdef MODULE
int ncr53c8xx_release(struct Scsi_Host *host)
{
- struct host_data *host_data;
#ifdef DEBUG_NCR53C8XX
printk("ncr53c8xx : release\n");
#endif
-
- for (host = first_host; host; host = host->next) {
- if (host->hostt == the_template) {
- host_data = (struct host_data *) host->hostdata;
- ncr_detach(host_data->ncb, host->irq);
- }
- }
+ ncr_detach(((struct host_data *) host->hostdata)->ncb);
return 1;
}
/*
** Name and revision of the driver
*/
-#define SCSI_NCR_DRIVER_NAME "ncr53c8xx - revision 2.4a"
+#define SCSI_NCR_DRIVER_NAME "ncr53c8xx - revision 2.5f"
/*
** Check supported Linux versions
* For Ultra2 SCSI support option, use special features and allow 40Mhz
* synchronous data transfers.
*/
-#define SCSI_NCR_SETUP_SPECIAL_FEATURES (1)
+#define SCSI_NCR_SETUP_SPECIAL_FEATURES (3)
#define SCSI_NCR_SETUP_ULTRA_SCSI (2)
#define SCSI_NCR_MAX_SYNC (40)
#endif
/*
- * Use normal IO if configured. Forced for alpha.
+ * Use normal IO if configured. Forced for alpha and ppc.
*/
-#if defined(CONFIG_SCSI_NCR53C8XX_IOMAPPED) || defined(__alpha__)
+#if defined(CONFIG_SCSI_NCR53C8XX_IOMAPPED)
+#define SCSI_NCR_IOMAPPED
+#elif defined(__alpha__) || defined(__powerpc__)
#define SCSI_NCR_IOMAPPED
#endif
#define ncr53c8xx_release NULL
#endif
-#if LINUX_VERSION_CODE >= LinuxVersionCode(1,3,0)
-
-#define NCR53C8XX {NULL,NULL,NULL,NULL,SCSI_NCR_DRIVER_NAME, ncr53c8xx_detect,\
- ncr53c8xx_release, /* info */ NULL, /* command, deprecated */ NULL, \
- ncr53c8xx_queue_command, ncr53c8xx_abort, ncr53c8xx_reset, \
- NULL /* slave attach */, scsicam_bios_param, /* can queue */ SCSI_NCR_CAN_QUEUE,\
- /* id */ 7, SCSI_NCR_SG_TABLESIZE /* SG */, /* cmd per lun */ SCSI_NCR_CMD_PER_LUN, \
- /* present */ 0, /* unchecked isa dma */ 0, DISABLE_CLUSTERING}
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,75)
+
+#define NCR53C8XX { name: SCSI_NCR_DRIVER_NAME, \
+ detect: ncr53c8xx_detect, \
+ release: ncr53c8xx_release, \
+ queuecommand: ncr53c8xx_queue_command,\
+ abort: ncr53c8xx_abort, \
+ reset: ncr53c8xx_reset, \
+ bios_param: scsicam_bios_param, \
+ can_queue: SCSI_NCR_CAN_QUEUE, \
+ this_id: 7, \
+ sg_tablesize: SCSI_NCR_SG_TABLESIZE, \
+ cmd_per_lun: SCSI_NCR_CMD_PER_LUN, \
+ use_clustering: DISABLE_CLUSTERING}
+
+#elif LINUX_VERSION_CODE >= LinuxVersionCode(1,3,0)
+#define NCR53C8XX { NULL, NULL, NULL, NULL, \
+ SCSI_NCR_DRIVER_NAME, ncr53c8xx_detect, \
+ ncr53c8xx_release, NULL, NULL, \
+ ncr53c8xx_queue_command,ncr53c8xx_abort, \
+ ncr53c8xx_reset, NULL, scsicam_bios_param, \
+ SCSI_NCR_CAN_QUEUE, 7, \
+ SCSI_NCR_SG_TABLESIZE, SCSI_NCR_CMD_PER_LUN, \
+ 0, 0, DISABLE_CLUSTERING}
#else
+#define NCR53C8XX { NULL, NULL, \
+ SCSI_NCR_DRIVER_NAME, ncr53c8xx_detect, \
+ ncr53c8xx_release, NULL, NULL, \
+ ncr53c8xx_queue_command,ncr53c8xx_abort, \
+ ncr53c8xx_reset, NULL, scsicam_bios_param, \
+ SCSI_NCR_CAN_QUEUE, 7, \
+ SCSI_NCR_SG_TABLESIZE, SCSI_NCR_CMD_PER_LUN, \
+ 0, 0, DISABLE_CLUSTERING}
-#define NCR53C8XX {NULL, NULL, SCSI_NCR_DRIVER_NAME, ncr53c8xx_detect,\
- ncr53c8xx_release, /* info */ NULL, /* command, deprecated */ NULL, \
- ncr53c8xx_queue_command, ncr53c8xx_abort, ncr53c8xx_reset, \
- NULL /* slave attach */, scsicam_bios_param, /* can queue */ SCSI_NCR_CAN_QUEUE,\
- /* id */ 7, SCSI_NCR_SG_TABLESIZE /* SG */, /* cmd per lun */ SCSI_NCR_CMD_PER_LUN, \
- /* present */ 0, /* unchecked isa dma */ 0, DISABLE_CLUSTERING}
-
-#endif /* LINUX_VERSION_CODE >= LinuxVersionCode(1,3,0) */
+#endif /* LINUX_VERSION_CODE */
#endif /* defined(HOSTS_C) || defined(MODULE) */
#ifndef HOSTS_C
+/*
+** IO functions definition for big/little endian support.
+** For now, the NCR is only supported in little endian addressing mode,
+** and big endian byte ordering is only supported for the PPC.
+** MMIO is not used on PPC.
+*/
+
+#ifdef __BIG_ENDIAN
+
+#if LINUX_VERSION_CODE < LinuxVersionCode(2,1,0)
+#error "BIG ENDIAN byte ordering needs kernel version >= 2.1.0"
+#endif
+
+#ifdef __powerpc__
+#define inw_l2b inw
+#define inl_l2b inl
+#define outw_b2l outw
+#define outl_b2l outl
+#else
+#error "Support for BIG ENDIAN is only available for the PowerPC"
+#endif
+
+#else /* Assumed x86 or alpha */
+
+#define inw_raw inw
+#define inl_raw inl
+#define outw_raw outw
+#define outl_raw outl
+#define readw_raw readw
+#define readl_raw readl
+#define writew_raw writew
+#define writel_raw writel
+
+#endif
+
+#ifdef SCSI_NCR_BIG_ENDIAN
+#error "The NCR in BIG ENDIAN adressing mode is not (yet) supported"
+#endif
+
/*
** NCR53C8XX Device Ids
*/
#define FE_SPECIAL_SET (FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM)
} ncr_chip;
+/*
+** DEL 397 - 53C875 Rev 3 - Part Number 609-0392410 - ITEM 3.
+** Memory Read transaction terminated by a retry followed by
+** Memory Read Line command.
+*/
+#define FE_CACHE0_SET (FE_CACHE_SET & ~FE_ERL)
+
+/*
+** DEL 397 - 53C875 Rev 3 - Part Number 609-0392410 - ITEM 5.
+** On paper, this errata is harmless. But it is a good reason for
+** using a shorter programmed burst length (64 DWORDS instead of 128).
+*/
+
#define SCSI_NCR_CHIP_TABLE \
{ \
{PCI_DEVICE_ID_NCR_53C810, 0x0f, "810", 4, 8, 4, \
{PCI_DEVICE_ID_NCR_53C825, 0x0f, "825", 4, 8, 4, \
FE_WIDE|FE_ERL|FE_BOF} \
, \
- {PCI_DEVICE_ID_NCR_53C825, 0xff, "825a", 7, 8, 4, \
- FE_WIDE|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM} \
+ {PCI_DEVICE_ID_NCR_53C825, 0xff, "825a", 6, 8, 4, \
+ FE_WIDE|FE_CACHE0_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM} \
, \
{PCI_DEVICE_ID_NCR_53C860, 0xff, "860", 4, 8, 5, \
FE_ULTRA|FE_CLK80|FE_CACHE_SET|FE_BOF|FE_LDSTR|FE_PFEN} \
, \
- {PCI_DEVICE_ID_NCR_53C875, 0x01, "875", 7, 16, 5, \
- FE_WIDE|FE_ULTRA|FE_CLK80|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}\
+ {PCI_DEVICE_ID_NCR_53C875, 0x01, "875", 6, 16, 5, \
+ FE_WIDE|FE_ULTRA|FE_CLK80|FE_CACHE0_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}\
, \
- {PCI_DEVICE_ID_NCR_53C875, 0xff, "875", 7, 16, 5, \
- FE_WIDE|FE_ULTRA|FE_DBLR|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}\
+ {PCI_DEVICE_ID_NCR_53C875, 0xff, "875", 6, 16, 5, \
+ FE_WIDE|FE_ULTRA|FE_DBLR|FE_CACHE0_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}\
, \
- {PCI_DEVICE_ID_NCR_53C875J, 0xff, "875J", 7, 16, 5, \
- FE_WIDE|FE_ULTRA|FE_DBLR|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}\
+ {PCI_DEVICE_ID_NCR_53C875J,0xff, "875J", 6, 16, 5, \
+ FE_WIDE|FE_ULTRA|FE_DBLR|FE_CACHE0_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}\
, \
- {PCI_DEVICE_ID_NCR_53C885, 0xff, "885", 7, 16, 5, \
- FE_WIDE|FE_ULTRA|FE_DBLR|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}\
+ {PCI_DEVICE_ID_NCR_53C885, 0xff, "885", 6, 16, 5, \
+ FE_WIDE|FE_ULTRA|FE_DBLR|FE_CACHE0_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}\
, \
{PCI_DEVICE_ID_NCR_53C895, 0xff, "895", 7, 31, 7, \
FE_WIDE|FE_ULTRA2|FE_QUAD|FE_CACHE_SET|FE_BOF|FE_DFS|FE_LDSTR|FE_PFEN|FE_RAM}\
1, \
SCSI_NCR_SETUP_SETTLE_TIME, \
SCSI_NCR_SETUP_DIFF_SUPPORT, \
- 0 \
+ 0, \
+ 1 \
}
/*
0, \
10, \
1, \
+ 1, \
1 \
}
/*
* u14-34f.c - Low-level driver for UltraStor 14F/34F SCSI host adapters.
*
+ * 4 Apr 1998 rev. 4.02 for linux 2.0.33 and 2.1.92
+ * io_port is now unsigned long.
+ *
+ * 17 Mar 1998 rev. 4.01 for linux 2.0.33 and 2.1.88
+ * Use new scsi error handling code (if linux version >= 2.1.88).
+ * Use new interrupt code.
+ *
* 12 Sep 1997 rev. 3.11 for linux 2.0.30 and 2.1.55
* Use of udelay inside the wait loops to avoid timeout
* problems with fast cpus.
*
* Multiple U14F and/or U34F host adapters are supported.
*
- * Copyright (C) 1994-1997 Dario Ballabio (dario@milano.europe.dg.com)
+ * Copyright (C) 1994-1998 Dario Ballabio (dario@milano.europe.dg.com)
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that redistributions of source
#undef DEBUG_LINKED_COMMANDS
#undef DEBUG_DETECT
#undef DEBUG_INTERRUPT
-#undef DEBUG_STATISTICS
#undef DEBUG_RESET
-#undef DEBUG_SMP
#define MAX_ISA 3
#define MAX_VESA 1
#define MAX_CHANNEL 1
#define MAX_LUN 8
#define MAX_TARGET 8
-#define MAX_IRQ 16
#define MAX_MAILBOXES 16
#define MAX_SGLIST 32
#define MAX_SAFE_SGLIST 16
#define MAX_CMD_PER_LUN 2
#define MAX_TAGGED_CMD_PER_LUN (MAX_MAILBOXES - MAX_CMD_PER_LUN)
-#define SKIP UINT_MAX
+#define SKIP ULONG_MAX
#define FALSE 0
#define TRUE 1
#define FREE 0
unsigned int cp_stat[MAX_MAILBOXES]; /* FREE, IN_USE, LOCKED, IN_RESET */
unsigned int last_cp_used; /* Index of last mailbox used */
unsigned int iocount; /* Total i/o done for this board */
- unsigned int multicount; /* Total ... in second ihdlr loop */
int board_number; /* Number of this board */
char board_name[16]; /* Name of this board */
char board_id[256]; /* data from INQUIRY on this board */
static struct Scsi_Host *sh[MAX_BOARDS + 1];
static const char *driver_name = "Ux4F";
-static unsigned int irqlist[MAX_IRQ], calls[MAX_IRQ];
+static char sha[MAX_BOARDS];
-static unsigned int io_port[] __initdata = {
+/* Initialize num_boards so that ihdlr can work while detect is in progress */
+static unsigned int num_boards = MAX_BOARDS;
+
+static unsigned long io_port[] __initdata = {
/* Space for MAX_INT_PARAM ports usable while loading as a module */
SKIP, SKIP, SKIP, SKIP, SKIP, SKIP, SKIP, SKIP,
#define BN(board) (HD(board)->board_name)
#define SWAP_BYTE(x) ((unsigned long)( \
- (((unsigned long)(x) & 0x000000ffU) << 24) | \
- (((unsigned long)(x) & 0x0000ff00U) << 8) | \
- (((unsigned long)(x) & 0x00ff0000U) >> 8) | \
- (((unsigned long)(x) & 0xff000000U) >> 24)))
+ (((unsigned long)(x) & 0x000000ffU) << 24) | \
+ (((unsigned long)(x) & 0x0000ff00U) << 8) | \
+ (((unsigned long)(x) & 0x00ff0000U) >> 8) | \
+ (((unsigned long)(x) & 0xff000000U) >> 24)))
#if defined(__BIG_ENDIAN)
#define H2DEV(x) SWAP_BYTE(x)
return;
}
-static inline int wait_on_busy(unsigned int iobase, unsigned int loop) {
+static inline int wait_on_busy(unsigned long iobase, unsigned int loop) {
while (inb(iobase + REG_LCL_INTR) & BSY_ASSERTED) {
udelay(1L);
}
__initfunc (static inline int port_detect \
- (unsigned int port_base, unsigned int j, Scsi_Host_Template *tpnt)) {
+ (unsigned long port_base, unsigned int j, Scsi_Host_Template *tpnt)) {
unsigned char irq, dma_channel, subversion, i;
unsigned char in_byte;
char *bus_type, dma_name[16];
unsigned char heads;
unsigned char sectors;
} mapping_table[4] = {
- { 16, 63 }, { 64, 32 }, { 64, 63 }, { 64, 32 }
- };
+ { 16, 63 }, { 64, 32 }, { 64, 63 }, { 64, 32 }
+ };
struct config_1 {
unsigned char bios_segment: 3;
sprintf(name, "%s%d", driver_name, j);
if(check_region(port_base, REGION_SIZE)) {
- printk("%s: address 0x%03x in use, skipping probe.\n", name, port_base);
+ printk("%s: address 0x%03lx in use, skipping probe.\n", name, port_base);
return FALSE;
}
dma_channel = dma_channel_table[config_1.dma_channel];
subversion = (in_byte & 0x0f);
- /* Board detected, allocate its IRQ if not already done */
- if ((irq >= MAX_IRQ) || (!irqlist[irq] && request_irq(irq,
- u14_34f_interrupt_handler, SA_INTERRUPT, driver_name, NULL))) {
+ /* Board detected, allocate its IRQ */
+ if (request_irq(irq, u14_34f_interrupt_handler,
+ SA_INTERRUPT | ((subversion == ESA) ? SA_SHIRQ : 0),
+ driver_name, (void *) &sha[j])) {
printk("%s: unable to allocate IRQ %u, detaching.\n", name, irq);
return FALSE;
}
if (subversion == ISA && request_dma(dma_channel, driver_name)) {
printk("%s: unable to allocate DMA channel %u, detaching.\n",
- name, dma_channel);
- free_irq(irq, NULL);
+ name, dma_channel);
+ free_irq(irq, &sha[j]);
return FALSE;
}
if (sh[j] == NULL) {
printk("%s: unable to register host, detaching.\n", name);
- if (!irqlist[irq]) free_irq(irq, NULL);
+ free_irq(irq, &sha[j]);
if (subversion == ISA) free_dma(dma_channel);
HD(j)->sectors = mapping_table[config_2.mapping_mode].sectors;
HD(j)->subversion = subversion;
HD(j)->board_number = j;
- irqlist[irq]++;
if (have_old_firmware) sh[j]->sg_tablesize = MAX_SAFE_SGLIST;
HD(j)->board_id[40] = 0;
if (strcmp(&HD(j)->board_id[32], "06000600")) {
- printk("%s: %s.\n", BN(j), &HD(j)->board_id[8]);
- printk("%s: firmware %s is outdated, FW PROM should be 28004-006.\n",
- BN(j), &HD(j)->board_id[32]);
- sh[j]->hostt->use_clustering = DISABLE_CLUSTERING;
- sh[j]->sg_tablesize = MAX_SAFE_SGLIST;
- }
+ printk("%s: %s.\n", BN(j), &HD(j)->board_id[8]);
+ printk("%s: firmware %s is outdated, FW PROM should be 28004-006.\n",
+ BN(j), &HD(j)->board_id[32]);
+ sh[j]->hostt->use_clustering = DISABLE_CLUSTERING;
+ sh[j]->sg_tablesize = MAX_SAFE_SGLIST;
+ }
}
if (dma_channel == NO_DMA) sprintf(dma_name, "%s", "BMST");
if (max_queue_depth < MAX_CMD_PER_LUN) max_queue_depth = MAX_CMD_PER_LUN;
- printk("%s: %s 0x%03x, BIOS 0x%05x, IRQ %u, %s, SG %d, MB %d, of:%c, "\
- "lc:%c, mq:%d.\n", BN(j), bus_type, sh[j]->io_port, (int)sh[j]->base,
- sh[j]->irq, dma_name, sh[j]->sg_tablesize, sh[j]->can_queue,
- YESNO(have_old_firmware), YESNO(linked_comm), max_queue_depth);
+ printk("%s: %s 0x%03lx, BIOS 0x%05x, IRQ %u, %s, SG %d, MB %d, of:%c, "\
+ "lc:%c, mq:%d.\n", BN(j), bus_type, (unsigned long)sh[j]->io_port,
+ (int)sh[j]->base, sh[j]->irq, dma_name, sh[j]->sg_tablesize,
+ sh[j]->can_queue, YESNO(have_old_firmware), YESNO(linked_comm),
+ max_queue_depth);
if (sh[j]->max_id > 8 || sh[j]->max_lun > 8)
printk("%s: wide SCSI support enabled, max_id %u, max_lun %u.\n",
}
#endif
- for (k = 0; k < MAX_IRQ; k++) {
- irqlist[k] = 0;
- calls[k] = 0;
- }
-
for (k = 0; k < MAX_BOARDS + 1; k++) sh[k] = NULL;
for (k = 0; io_port[k]; k++) {
}
if (j > 0)
- printk("UltraStor 14F/34F: Copyright (C) 1994-1997 Dario Ballabio.\n");
+ printk("UltraStor 14F/34F: Copyright (C) 1994-1998 Dario Ballabio.\n");
+ num_boards = j;
restore_flags(flags);
return j;
}
if (i >= sh[j]->can_queue) i = 0;
if (HD(j)->cp_stat[i] == FREE) {
- HD(j)->last_cp_used = i;
- break;
- }
+ HD(j)->last_cp_used = i;
+ break;
+ }
}
if (k == sh[j]->can_queue) {
printk("%s: qcomm, no free mailbox, resetting.\n", BN(j));
if (HD(j)->in_reset)
- printk("%s: qcomm, already in reset.\n", BN(j));
+ printk("%s: qcomm, already in reset.\n", BN(j));
else if (u14_34f_reset(SCpnt, SCSI_RESET_SUGGEST_BUS_RESET)
== SCSI_RESET_SUCCESS)
- panic("%s: qcomm, SCSI_RESET_SUCCESS.\n", BN(j));
+ panic("%s: qcomm, SCSI_RESET_SUCCESS.\n", BN(j));
SCpnt->result = DID_BUS_BUSY << 16;
SCpnt->host_scribble = NULL;
printk("%s: qcomm, pid %ld, DID_BUS_BUSY, done.\n", BN(j), SCpnt->pid);
restore_flags(flags);
done(SCpnt);
- return 0;
+ return 1;
}
/* Set pointer to control packet structure */
SCpnt->host_scribble = (unsigned char *) &cpp->index;
if (do_trace) printk("%s: qcomm, mbox %d, target %d.%d:%d, pid %ld.\n",
- BN(j), i, SCpnt->channel, SCpnt->target,
+ BN(j), i, SCpnt->channel, SCpnt->target,
SCpnt->lun, SCpnt->pid);
cpp->xdir = DTD_IN;
for (k = 0; k < ARRAY_SIZE(data_out_cmds); k++)
if (SCpnt->cmnd[0] == data_out_cmds[k]) {
- cpp->xdir = DTD_OUT;
- break;
- }
+ cpp->xdir = DTD_OUT;
+ break;
+ }
if (cpp->xdir == DTD_IN)
for (k = 0; k < ARRAY_SIZE(data_none_cmds); k++)
SCpnt->pid);
restore_flags(flags);
done(SCpnt);
- return 0;
+ return 1;
}
/* Store pointer in OGM address bytes */
if (SCarg->host_scribble == NULL
|| SCarg->serial_number != SCarg->serial_number_at_timeout) {
printk("%s: abort, target %d.%d:%d, pid %ld inactive.\n",
- BN(j), SCarg->channel, SCarg->target, SCarg->lun, SCarg->pid);
+ BN(j), SCarg->channel, SCarg->target, SCarg->lun, SCarg->pid);
restore_flags(flags);
return SCSI_ABORT_NOT_RUNNING;
}
i = *(unsigned int *)SCarg->host_scribble;
printk("%s: abort, mbox %d, target %d.%d:%d, pid %ld.\n",
- BN(j), i, SCarg->channel, SCarg->target, SCarg->lun, SCarg->pid);
+ BN(j), i, SCarg->channel, SCarg->target, SCarg->lun, SCarg->pid);
if (i >= sh[j]->can_queue)
panic("%s: abort, invalid SCarg->host_scribble.\n", BN(j));
printk("%s: abort, mbox %d is in use.\n", BN(j), i);
if (SCarg != HD(j)->cp[i].SCpnt)
- panic("%s: abort, mbox %d, SCarg %p, cp SCpnt %p.\n",
- BN(j), i, SCarg, HD(j)->cp[i].SCpnt);
+ panic("%s: abort, mbox %d, SCarg %p, cp SCpnt %p.\n",
+ BN(j), i, SCarg, HD(j)->cp[i].SCpnt);
if (inb(sh[j]->io_port + REG_SYS_INTR) & IRQ_ASSERTED)
printk("%s: abort, mbox %d, interrupt pending.\n", BN(j), i);
SCarg->host_scribble = NULL;
HD(j)->cp_stat[i] = FREE;
printk("%s, abort, mbox %d ready, DID_ABORT, pid %ld done.\n",
- BN(j), i, SCarg->pid);
+ BN(j), i, SCarg->pid);
SCarg->scsi_done(SCarg);
restore_flags(flags);
return SCSI_ABORT_SUCCESS;
cli();
j = ((struct hostdata *) SCarg->host->hostdata)->board_number;
printk("%s: reset, enter, target %d.%d:%d, pid %ld, reset_flags %u.\n",
- BN(j), SCarg->channel, SCarg->target, SCarg->lun, SCarg->pid,
+ BN(j), SCarg->channel, SCarg->target, SCarg->lun, SCarg->pid,
reset_flags);
if (SCarg->host_scribble == NULL)
if (HD(j)->cp_stat[i] == FREE) continue;
if (HD(j)->cp_stat[i] == LOCKED) {
- HD(j)->cp_stat[i] = FREE;
- printk("%s: reset, locked mbox %d forced free.\n", BN(j), i);
- continue;
- }
+ HD(j)->cp_stat[i] = FREE;
+ printk("%s: reset, locked mbox %d forced free.\n", BN(j), i);
+ continue;
+ }
if (!(SCpnt = HD(j)->cp[i].SCpnt))
- panic("%s: reset, mbox %d, SCpnt == NULL.\n", BN(j), i);
+ panic("%s: reset, mbox %d, SCpnt == NULL.\n", BN(j), i);
if (HD(j)->cp_stat[i] == READY || HD(j)->cp_stat[i] == ABORTING) {
HD(j)->cp_stat[i] = ABORTING;
}
if (SCpnt->host_scribble == NULL)
- panic("%s: reset, mbox %d, garbled SCpnt.\n", BN(j), i);
+ panic("%s: reset, mbox %d, garbled SCpnt.\n", BN(j), i);
if (*(unsigned int *)SCpnt->host_scribble != i)
- panic("%s: reset, mbox %d, index mismatch.\n", BN(j), i);
+ panic("%s: reset, mbox %d, index mismatch.\n", BN(j), i);
if (SCpnt->scsi_done == NULL)
- panic("%s: reset, mbox %d, SCpnt->scsi_done == NULL.\n", BN(j), i);
+ panic("%s: reset, mbox %d, SCpnt->scsi_done == NULL.\n", BN(j), i);
if (SCpnt == SCarg) arg_done = TRUE;
}
HD(j)->cp_stat[i] = LOCKED;
printk("%s, reset, mbox %d locked, DID_RESET, pid %ld done.\n",
- BN(j), i, SCpnt->pid);
+ BN(j), i, SCpnt->pid);
}
else if (HD(j)->cp_stat[i] == ABORTING) {
HD(j)->cp_stat[i] = FREE;
printk("%s, reset, mbox %d aborting, DID_RESET, pid %ld done.\n",
- BN(j), i, SCpnt->pid);
+ BN(j), i, SCpnt->pid);
}
else
}
}
-static void u14_34f_interrupt_handler(int irq, void *dev_id,
- struct pt_regs *regs) {
+static void u14_34f_interrupt_handler(int irq, void *shap,
+ struct pt_regs *regs) {
Scsi_Cmnd *SCpnt;
- unsigned long flags;
- unsigned int i, j, k, c, status, tstatus, loops, total_loops = 0, reg, ret;
+ unsigned int i, j, k, c, status, tstatus, reg, ret;
struct mscp *spp;
- save_flags(flags);
- cli();
+ /* Check if the interrupt must be processed by this handler */
+ if ((j = (unsigned int)((char *)shap - sha)) >= num_boards) return;
- if (!irqlist[irq]) {
- printk("%s, ihdlr, irq %d, unexpected interrupt.\n", driver_name, irq);
- restore_flags(flags);
+ if (sh[j]->irq != irq)
+ panic("%s: ihdlr, irq %d, sh[j]->irq %d.\n", BN(j), irq, sh[j]->irq);
+
+ if (do_trace) printk("%s: ihdlr, enter, irq %d, count %d.\n", BN(j), irq,
+ HD(j)->iocount);
+
+ /* Check if this board need to be serviced */
+ if (!((reg = inb(sh[j]->io_port + REG_SYS_INTR)) & IRQ_ASSERTED)) return;
+
+ spp = (struct mscp *)DEV2V(ret = inl(sh[j]->io_port + REG_ICM));
+
+ /* Clear interrupt pending flag */
+ outb(CMD_CLR_INTR, sh[j]->io_port + REG_SYS_INTR);
+
+ i = spp - HD(j)->cp;
+
+ if (spp < HD(j)->cp || spp >= HD(j)->cp + sh[j]->can_queue
+ || i >= sh[j]->can_queue)
+ panic("%s: ihdlr, invalid mscp bus address %p, cp0 %p.\n", BN(j),
+ (void *)ret, HD(j)->cp);
+
+ if (HD(j)->cp_stat[i] == IGNORE) {
+ HD(j)->cp_stat[i] = FREE;
+ return;
+ }
+ else if (HD(j)->cp_stat[i] == LOCKED) {
+ HD(j)->cp_stat[i] = FREE;
+ printk("%s: ihdlr, mbox %d unlocked, count %d.\n", BN(j), i,
+ HD(j)->iocount);
+ return;
+ }
+ else if (HD(j)->cp_stat[i] == FREE) {
+ printk("%s: ihdlr, mbox %d is free, count %d.\n", BN(j), i,
+ HD(j)->iocount);
return;
}
+ else if (HD(j)->cp_stat[i] == IN_RESET)
+ printk("%s: ihdlr, mbox %d is in reset.\n", BN(j), i);
+ else if (HD(j)->cp_stat[i] != IN_USE)
+ panic("%s: ihdlr, mbox %d, invalid cp_stat.\n", BN(j), i);
- if (do_trace) printk("%s: ihdlr, enter, irq %d, calls %d.\n",
- driver_name, irq, calls[irq]);
-
- /* Service all the boards configured on this irq */
- for (j = 0; sh[j] != NULL; j++) {
+ HD(j)->cp_stat[i] = FREE;
+ SCpnt = spp->SCpnt;
- if (sh[j]->irq != irq) continue;
+ if (SCpnt == NULL) panic("%s: ihdlr, mbox %d, SCpnt == NULL.\n", BN(j), i);
- loops = 0;
+ if (SCpnt->host_scribble == NULL)
+ panic("%s: ihdlr, mbox %d, pid %ld, SCpnt %p garbled.\n", BN(j), i,
+ SCpnt->pid, SCpnt);
- /* Loop until all interrupts for a board are serviced */
- while ((reg = inb(sh[j]->io_port + REG_SYS_INTR)) & IRQ_ASSERTED) {
- total_loops++;
- loops++;
-
- if (do_trace) printk("%s: ihdlr, start service, count %d.\n",
- BN(j), HD(j)->iocount);
+ if (*(unsigned int *)SCpnt->host_scribble != i)
+ panic("%s: ihdlr, mbox %d, pid %ld, index mismatch %d, irq %d.\n",
+ BN(j), i, SCpnt->pid, *(unsigned int *)SCpnt->host_scribble, irq);
- spp = (struct mscp *)DEV2V(ret = inl(sh[j]->io_port + REG_ICM));
+ if (linked_comm && SCpnt->device->queue_depth > 2
+ && TLDEV(SCpnt->device->type))
+ flush_dev(SCpnt->device, SCpnt->request.sector, j, TRUE);
- /* Clear interrupt pending flag */
- outb(CMD_CLR_INTR, sh[j]->io_port + REG_SYS_INTR);
-
- i = spp - HD(j)->cp;
+ tstatus = status_byte(spp->target_status);
- if (spp < HD(j)->cp || spp >= HD(j)->cp + sh[j]->can_queue
- || i >= sh[j]->can_queue)
- panic("%s: ihdlr, invalid mscp bus address %p, cp0 %p.\n",
- BN(j), (void *)ret, HD(j)->cp);
-
- if (HD(j)->cp_stat[i] == IGNORE) {
- HD(j)->cp_stat[i] = FREE;
- continue;
- }
- else if (HD(j)->cp_stat[i] == LOCKED) {
- HD(j)->cp_stat[i] = FREE;
- printk("%s: ihdlr, mbox %d unlocked, count %d.\n",
- BN(j), i, HD(j)->iocount);
- continue;
- }
- else if (HD(j)->cp_stat[i] == FREE) {
- printk("%s: ihdlr, mbox %d is free, count %d.\n",
- BN(j), i, HD(j)->iocount);
- continue;
- }
- else if (HD(j)->cp_stat[i] == IN_RESET)
- printk("%s: ihdlr, mbox %d is in reset.\n", BN(j), i);
- else if (HD(j)->cp_stat[i] != IN_USE)
- panic("%s: ihdlr, mbox %d, invalid cp_stat.\n", BN(j), i);
-
- HD(j)->cp_stat[i] = FREE;
- SCpnt = spp->SCpnt;
-
- if (SCpnt == NULL)
- panic("%s: ihdlr, mbox %d, SCpnt == NULL.\n", BN(j), i);
-
- if (SCpnt->host_scribble == NULL)
- panic("%s: ihdlr, mbox %d, pid %ld, SCpnt %p garbled.\n",
- BN(j), i, SCpnt->pid, SCpnt);
-
- if (*(unsigned int *)SCpnt->host_scribble != i)
- panic("%s: ihdlr, mbox %d, pid %ld, index mismatch %d,"\
- " irq %d.\n", BN(j), i, SCpnt->pid,
- *(unsigned int *)SCpnt->host_scribble, irq);
-
- if (linked_comm && SCpnt->device->queue_depth > 2
- && TLDEV(SCpnt->device->type))
- flush_dev(SCpnt->device, SCpnt->request.sector, j, TRUE);
-
- tstatus = status_byte(spp->target_status);
-
- switch (spp->adapter_status) {
- case ASOK: /* status OK */
-
- /* Forces a reset if a disk drive keeps returning BUSY */
- if (tstatus == BUSY && SCpnt->device->type != TYPE_TAPE)
- status = DID_ERROR << 16;
-
- /* If there was a bus reset, redo operation on each target */
- else if (tstatus != GOOD && SCpnt->device->type == TYPE_DISK
- && HD(j)->target_redo[SCpnt->target][SCpnt->channel])
- status = DID_BUS_BUSY << 16;
-
- /* Works around a flaw in scsi.c */
- else if (tstatus == CHECK_CONDITION
- && SCpnt->device->type == TYPE_DISK
- && (SCpnt->sense_buffer[2] & 0xf) == RECOVERED_ERROR)
- status = DID_BUS_BUSY << 16;
-
- else
- status = DID_OK << 16;
-
- if (tstatus == GOOD)
- HD(j)->target_redo[SCpnt->target][SCpnt->channel] = FALSE;
-
- if (spp->target_status && SCpnt->device->type == TYPE_DISK)
- printk("%s: ihdlr, target %d.%d:%d, pid %ld, "\
- "target_status 0x%x, sense key 0x%x.\n", BN(j),
- SCpnt->channel, SCpnt->target, SCpnt->lun,
- SCpnt->pid, spp->target_status,
- SCpnt->sense_buffer[2]);
-
- HD(j)->target_to[SCpnt->target][SCpnt->channel] = 0;
-
- if (HD(j)->last_retried_pid == SCpnt->pid) HD(j)->retries = 0;
-
- break;
- case ASST: /* Selection Time Out */
-
- if (HD(j)->target_to[SCpnt->target][SCpnt->channel] > 1)
- status = DID_ERROR << 16;
- else {
- status = DID_TIME_OUT << 16;
- HD(j)->target_to[SCpnt->target][SCpnt->channel]++;
- }
-
- break;
-
- /* Perform a limited number of internal retries */
- case 0x93: /* Unexpected bus free */
- case 0x94: /* Target bus phase sequence failure */
- case 0x96: /* Illegal SCSI command */
- case 0xa3: /* SCSI bus reset error */
-
- for (c = 0; c <= sh[j]->max_channel; c++)
- for (k = 0; k < sh[j]->max_id; k++)
- HD(j)->target_redo[k][c] = TRUE;
-
+ switch (spp->adapter_status) {
+ case ASOK: /* status OK */
- case 0x92: /* Data over/under-run */
-
- if (SCpnt->device->type != TYPE_TAPE
- && HD(j)->retries < MAX_INTERNAL_RETRIES) {
- status = DID_BUS_BUSY << 16;
- HD(j)->retries++;
- HD(j)->last_retried_pid = SCpnt->pid;
- }
- else
- status = DID_ERROR << 16;
-
- break;
- case 0x01: /* Invalid command */
- case 0x02: /* Invalid parameters */
- case 0x03: /* Invalid data list */
- case 0x84: /* SCSI bus abort error */
- case 0x9b: /* Auto request sense error */
- case 0x9f: /* Unexpected command complete message error */
- case 0xff: /* Invalid parameter in the S/G list */
- default:
- status = DID_ERROR << 16;
- break;
- }
-
- SCpnt->result = status | spp->target_status;
- HD(j)->iocount++;
-
- if (loops > 1) HD(j)->multicount++;
+ /* Forces a reset if a disk drive keeps returning BUSY */
+ if (tstatus == BUSY && SCpnt->device->type != TYPE_TAPE)
+ status = DID_ERROR << 16;
-#if defined (DEBUG_INTERRUPT)
- if (SCpnt->result || do_trace)
-#else
- if ((spp->adapter_status != ASOK && HD(j)->iocount > 1000) ||
- (spp->adapter_status != ASOK &&
- spp->adapter_status != ASST && HD(j)->iocount <= 1000) ||
- do_trace || msg_byte(spp->target_status))
-#endif
- printk("%s: ihdlr, mbox %2d, err 0x%x:%x,"\
- " target %d.%d:%d, pid %ld, reg 0x%x, count %d.\n",
- BN(j), i, spp->adapter_status, spp->target_status,
- SCpnt->channel, SCpnt->target, SCpnt->lun, SCpnt->pid,
- reg, HD(j)->iocount);
+ /* If there was a bus reset, redo operation on each target */
+ else if (tstatus != GOOD && SCpnt->device->type == TYPE_DISK
+ && HD(j)->target_redo[SCpnt->target][SCpnt->channel])
+ status = DID_BUS_BUSY << 16;
+
+ /* Works around a flaw in scsi.c */
+ else if (tstatus == CHECK_CONDITION
+ && SCpnt->device->type == TYPE_DISK
+ && (SCpnt->sense_buffer[2] & 0xf) == RECOVERED_ERROR)
+ status = DID_BUS_BUSY << 16;
- /* Set the command state to inactive */
- SCpnt->host_scribble = NULL;
+ else
+ status = DID_OK << 16;
- restore_flags(flags);
- SCpnt->scsi_done(SCpnt);
- cli();
+ if (tstatus == GOOD)
+ HD(j)->target_redo[SCpnt->target][SCpnt->channel] = FALSE;
- } /* Multiple command loop */
+ if (spp->target_status && SCpnt->device->type == TYPE_DISK)
+ printk("%s: ihdlr, target %d.%d:%d, pid %ld, "\
+ "target_status 0x%x, sense key 0x%x.\n", BN(j),
+ SCpnt->channel, SCpnt->target, SCpnt->lun,
+ SCpnt->pid, spp->target_status,
+ SCpnt->sense_buffer[2]);
- } /* Boards loop */
+ HD(j)->target_to[SCpnt->target][SCpnt->channel] = 0;
- calls[irq]++;
+ if (HD(j)->last_retried_pid == SCpnt->pid) HD(j)->retries = 0;
-#if defined (DEBUG_SMP)
- if (total_loops == 0)
- printk("%s: ihdlr, irq %d, no command completed, calls %d.\n",
- driver_name, irq, calls[irq]);
-#endif
+ break;
+ case ASST: /* Selection Time Out */
+
+ if (HD(j)->target_to[SCpnt->target][SCpnt->channel] > 1)
+ status = DID_ERROR << 16;
+ else {
+ status = DID_TIME_OUT << 16;
+ HD(j)->target_to[SCpnt->target][SCpnt->channel]++;
+ }
- if (do_trace) printk("%s: ihdlr, exit, irq %d, calls %d.\n",
- driver_name, irq, calls[irq]);
+ break;
-#if defined (DEBUG_STATISTICS)
- if ((calls[irq] % 100000) == 10000)
- for (j = 0; sh[j] != NULL; j++)
- printk("%s: ihdlr, calls %d, count %d, multi %d.\n", BN(j),
- calls[(sh[j]->irq)], HD(j)->iocount, HD(j)->multicount);
+ /* Perform a limited number of internal retries */
+ case 0x93: /* Unexpected bus free */
+ case 0x94: /* Target bus phase sequence failure */
+ case 0x96: /* Illegal SCSI command */
+ case 0xa3: /* SCSI bus reset error */
+
+ for (c = 0; c <= sh[j]->max_channel; c++)
+ for (k = 0; k < sh[j]->max_id; k++)
+ HD(j)->target_redo[k][c] = TRUE;
+
+
+ case 0x92: /* Data over/under-run */
+
+ if (SCpnt->device->type != TYPE_TAPE
+ && HD(j)->retries < MAX_INTERNAL_RETRIES) {
+ status = DID_BUS_BUSY << 16;
+ HD(j)->retries++;
+ HD(j)->last_retried_pid = SCpnt->pid;
+ }
+ else
+ status = DID_ERROR << 16;
+
+ break;
+ case 0x01: /* Invalid command */
+ case 0x02: /* Invalid parameters */
+ case 0x03: /* Invalid data list */
+ case 0x84: /* SCSI bus abort error */
+ case 0x9b: /* Auto request sense error */
+ case 0x9f: /* Unexpected command complete message error */
+ case 0xff: /* Invalid parameter in the S/G list */
+ default:
+ status = DID_ERROR << 16;
+ break;
+ }
+
+ SCpnt->result = status | spp->target_status;
+ HD(j)->iocount++;
+
+#if defined (DEBUG_INTERRUPT)
+ if (SCpnt->result || do_trace)
+#else
+ if ((spp->adapter_status != ASOK && HD(j)->iocount > 1000) ||
+ (spp->adapter_status != ASOK &&
+ spp->adapter_status != ASST && HD(j)->iocount <= 1000) ||
+ do_trace || msg_byte(spp->target_status))
#endif
+ printk("%s: ihdlr, mbox %2d, err 0x%x:%x,"\
+ " target %d.%d:%d, pid %ld, reg 0x%x, count %d.\n",
+ BN(j), i, spp->adapter_status, spp->target_status,
+ SCpnt->channel, SCpnt->target, SCpnt->lun, SCpnt->pid,
+ reg, HD(j)->iocount);
+
+ /* Set the command state to inactive */
+ SCpnt->host_scribble = NULL;
+
+ SCpnt->scsi_done(SCpnt);
+
+ if (do_trace) printk("%s: ihdlr, exit, irq %d, count %d.\n", BN(j), irq,
+ HD(j)->iocount);
- restore_flags(flags);
return;
}
for (i = 0; i < sh[j]->can_queue; i++)
if ((&HD(j)->cp[i])->sglist) kfree((&HD(j)->cp[i])->sglist);
- if (! --irqlist[sh[j]->irq]) free_irq(sh[j]->irq, NULL);
+ free_irq(sh[j]->irq, &sha[j]);
if (sh[j]->dma_channel != NO_DMA) free_dma(sh[j]->dma_channel);
int u14_34f_reset(Scsi_Cmnd *, unsigned int);
int u14_34f_biosparam(Disk *, kdev_t, int *);
-#define U14_34F_VERSION "3.11.00"
-
-#define ULTRASTOR_14_34F { \
- NULL, /* Ptr for modules */ \
- NULL, /* usage count for modules */ \
- NULL, \
- NULL, \
- "UltraStor 14F/34F rev. " U14_34F_VERSION " ", \
- u14_34f_detect, \
- u14_34f_release, \
- NULL, \
- NULL, \
- u14_34f_queuecommand, \
- u14_34f_abort, \
- u14_34f_reset, \
- NULL, \
- u14_34f_biosparam, \
- 0, /* can_queue, reset by detect */ \
- 7, /* this_id, reset by detect */ \
- 0, /* sg_tablesize, reset by detect */ \
- 0, /* cmd_per_lun, reset by detect */ \
- 0, /* number of boards present */ \
- 1, /* unchecked isa dma, reset by detect */ \
- ENABLE_CLUSTERING \
- }
+#define U14_34F_VERSION "4.02.00"
+
+#define LinuxVersionCode(v, p, s) (((v)<<16)+((p)<<8)+(s))
+
+#if LINUX_VERSION_CODE >= LinuxVersionCode(2,1,88)
+
+#define ULTRASTOR_14_34F { \
+ name: "UltraStor 14F/34F rev. " U14_34F_VERSION " ", \
+ detect: u14_34f_detect, \
+ release: u14_34f_release, \
+ queuecommand: u14_34f_queuecommand, \
+ abort: u14_34f_abort, \
+ reset: u14_34f_reset, \
+ bios_param: u14_34f_biosparam, \
+ this_id: 7, \
+ unchecked_isa_dma: 1, \
+ use_clustering: ENABLE_CLUSTERING, \
+ use_new_eh_code: 1 /* Enable new error code */ \
+ }
+
+#else /* Use old scsi code */
+
+#define ULTRASTOR_14_34F { \
+ name: "UltraStor 14F/34F rev. " U14_34F_VERSION " ", \
+ detect: u14_34f_detect, \
+ release: u14_34f_release, \
+ queuecommand: u14_34f_queuecommand, \
+ abort: u14_34f_abort, \
+ reset: u14_34f_reset, \
+ bios_param: u14_34f_biosparam, \
+ this_id: 7, \
+ unchecked_isa_dma: 1, \
+ use_clustering: ENABLE_CLUSTERING, \
+ }
+
+#endif
+
#endif
pte_t * pte = pte_alloc_kernel(pmd, address);
if (!pte)
return -ENOMEM;
- if (alloc_area_pte(pte, address, end - address)) {
- pte_free_kernel(pte);
+ if (alloc_area_pte(pte, address, end - address))
return -ENOMEM;
- }
address = (address + PMD_SIZE) & PMD_MASK;
pmd++;
}
pmd_t *pmd = pmd_alloc_kernel(dir, address);
if (!pmd)
return -ENOMEM;
- if (alloc_area_pmd(pmd, address, end - address)) {
- pmd_free_kernel(pmd);
+ if (alloc_area_pmd(pmd, address, end - address))
return -ENOMEM;
- }
set_pgdir(address, *dir);
address = (address + PGDIR_SIZE) & PGDIR_MASK;
dir++;
pmd_t *pmd = pmd_alloc_kernel(dir, address);
if (!pmd)
return -ENOMEM;
- if (remap_area_pmd(pmd, address, end - address, offset + address)) {
- pmd_free_kernel(pmd);
+ if (remap_area_pmd(pmd, address, end - address, offset + address))
return -ENOMEM;
- }
set_pgdir(address, *dir);
address = (address + PGDIR_SIZE) & PGDIR_MASK;
dir++;
*
* Some devices want to be initialized early..
*/
-#if defined(CONFIG_LANCE)
- lance_init();
-#endif
#if defined(CONFIG_PI)
pi_init();
#endif