11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* znet.c: An Zenith Z-Note ethernet driver for linux. */
21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	Written by Donald Becker.
51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	The author may be reached as becker@scyld.com.
71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	This driver is based on the Linux skeleton driver.  The copyright of the
81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	skeleton driver is held by the United States Government, as represented
91da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	by DIRNSA, and it is released under the GPL.
101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	Thanks to Mike Hollick for alpha testing and suggestions.
121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  References:
141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   The Crynwr packet driver.
151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  "82593 CSMA/CD Core LAN Controller" Intel datasheet, 1992
171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  Intel Microcommunications Databook, Vol. 1, 1990.
181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds    As usual with Intel, the documentation is incomplete and inaccurate.
191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	I had to read the Crynwr packet driver to figure out how to actually
201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	use the i82593, and guess at what register bits matched the loosely
211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	related i82586.
221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					Theory of Operation
241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	The i82593 used in the Zenith Z-Note series operates using two(!) slave
261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	DMA	channels, one interrupt, and one 8-bit I/O port.
271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	While there	several ways to configure '593 DMA system, I chose the one
291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	that seemed commensurate with the highest system performance in the face
301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	of moderate interrupt latency: Both DMA channels are configured as
311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	recirculating ring buffers, with one channel (#0) dedicated to Rx and
321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	the other channel (#1) to Tx and configuration.  (Note that this is
331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	different than the Crynwr driver, where the Tx DMA channel is initialized
341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	before each operation.  That approach simplifies operation and Tx error
351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	recovery, but requires additional I/O in normal operation and precludes
361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	transmit buffer	chaining.)
371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	Both rings are set to 8192 bytes using {TX,RX}_RING_SIZE.  This provides
391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	a reasonable ring size for Rx, while simplifying DMA buffer allocation --
401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	DMA buffers must not cross a 128K boundary.  (In truth the size selection
411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	was influenced by my lack of '593 documentation.  I thus was constrained
421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	to use the Crynwr '593 initialization table, which sets the Rx ring size
431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	to 8K.)
441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	Despite my usual low opinion about Intel-designed parts, I must admit
461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	that the bulk data handling of the i82593 is a good design for
471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	an integrated system, like a laptop, where using two slave DMA channels
481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	doesn't pose a problem.  I still take issue with using only a single I/O
491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	port.  In the same controlled environment there are essentially no
501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	limitations on I/O space, and using multiple locations would eliminate
511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	the	need for multiple operations when looking at status registers,
521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	setting the Rx ring boundary, or switching to promiscuous mode.
531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	I also question Zenith's selection of the '593: one of the advertised
551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	advantages of earlier Intel parts was that if you figured out the magic
561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	initialization incantation you could use the same part on many different
571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	network types.  Zenith's use of the "FriendlyNet" (sic) connector rather
581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	than an	on-board transceiver leads me to believe that they were planning
591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	to take advantage of this.  But, uhmmm, the '593 omits all but ethernet
601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	functionality from the serial subsystem.
611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* 10/2002
641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   o Resurected for Linux 2.5+ by Marc Zyngier <maz@wild-wind.fr.eu.org> :
661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   - Removed strange DMA snooping in znet_sent_packet, which lead to
681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds     TX buffer corruption on my laptop.
691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   - Use init_etherdev stuff.
701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   - Use kmalloc-ed DMA buffers.
711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   - Use as few global variables as possible.
721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   - Use proper resources management.
731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   - Use wireless/i82593.h as much as possible (structure, constants)
741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   - Compiles as module or build-in.
751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   - Now survives unplugging/replugging cable.
761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   Some code was taken from wavelan_cs.
786aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik
791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   Tested on a vintage Zenith Z-Note 433Lnp+. Probably broken on
801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   anything else. Testers (and detailed bug reports) are welcome :-).
811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   o TODO :
831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   - Properly handle multicast
851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   - Understand why some traffic patterns add a 1s latency...
861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h>
891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/kernel.h>
901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/string.h>
915a0e3ad6af8660be21ca98a971cd00f331318c05Tejun Heo#include <linux/slab.h>
921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/errno.h>
931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/interrupt.h>
941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/ioport.h>
951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h>
961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/delay.h>
971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/netdevice.h>
981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/etherdevice.h>
991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/skbuff.h>
1001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/if_arp.h>
1011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/bitops.h>
1021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/io.h>
1041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/dma.h>
1051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
106c85e9d7739fc8d879c4293ea020760926d6f87cdJohn W. Linville#include <linux/i82593.h>
1071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic char version[] __initdata = "znet.c:v1.02 9/23/94 becker@scyld.com\n";
1091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifndef ZNET_DEBUG
1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define ZNET_DEBUG 1
1121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
1131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic unsigned int znet_debug = ZNET_DEBUG;
1141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_param (znet_debug, int, 0);
1151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_PARM_DESC (znet_debug, "ZNet debug level");
1161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus TorvaldsMODULE_LICENSE("GPL");
1171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* The DMA modes we need aren't in <dma.h>. */
1191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define DMA_RX_MODE		0x14	/* Auto init, I/O to mem, ++, demand. */
1201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define DMA_TX_MODE		0x18	/* Auto init, Mem to I/O, ++, demand. */
1211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define dma_page_eq(ptr1, ptr2) ((long)(ptr1)>>17 == (long)(ptr2)>>17)
1221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define RX_BUF_SIZE 8192
1231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define TX_BUF_SIZE 8192
1241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define DMA_BUF_SIZE (RX_BUF_SIZE + 16)	/* 8k + 16 bytes for trailers */
1251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
126c63fdf46ad0a7f8fe3c0252a0e763515617e0ea7Eric Dumazet#define TX_TIMEOUT	(HZ/10)
1271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstruct znet_private {
1291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int rx_dma, tx_dma;
1301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spinlock_t lock;
1311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	short sia_base, sia_size, io_size;
1321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct i82593_conf_block i593_init;
1331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* The starting, current, and end pointers for the packet buffers. */
1341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ushort *rx_start, *rx_cur, *rx_end;
1351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ushort *tx_start, *tx_cur, *tx_end;
1361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ushort tx_buf_len;			/* Tx buffer length, in words. */
1371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
1381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Only one can be built-in;-> */
1401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct net_device *znet_dev;
1411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstruct netidblk {
1431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	char magic[8];		/* The magic number (string) "NETIDBLK" */
1441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned char netid[8]; /* The physical station address */
1451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	char nettype, globalopt;
1461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	char vendor[8];		/* The machine vendor and product name. */
1471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	char product[8];
1481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	char irq1, irq2;		/* Interrupts, only one is currently used.	*/
1491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	char dma1, dma2;
1501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	short dma_mem_misc[8];		/* DMA buffer locations (unused in Linux). */
1511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	short iobase1, iosize1;
1521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	short iobase2, iosize2;		/* Second iobase unused. */
1531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	char driver_options;			/* Misc. bits */
1541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	char pad;
1551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
1561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int	znet_open(struct net_device *dev);
15861357325f377889a1daffa14962d705dc814dd0eStephen Hemmingerstatic netdev_tx_t znet_send_packet(struct sk_buff *skb,
15961357325f377889a1daffa14962d705dc814dd0eStephen Hemminger				    struct net_device *dev);
1607d12e780e003f93433d49ce78cfedf4b4c52adc5David Howellsstatic irqreturn_t znet_interrupt(int irq, void *dev_id);
1611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void	znet_rx(struct net_device *dev);
1621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int	znet_close(struct net_device *dev);
1631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void hardware_init(struct net_device *dev);
1641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void update_stop_hit(short ioaddr, unsigned short rx_stop_offset);
1651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void znet_tx_timeout (struct net_device *dev);
1661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Request needed resources */
1681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int znet_request_resources (struct net_device *dev)
1691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
170524ad0a79126efabf58d0a49eace6155ab5b4549Wang Chen	struct znet_private *znet = netdev_priv(dev);
1716aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik
172a0607fd3a25ba1848a63a0d925e36d914735ab47Joe Perches	if (request_irq (dev->irq, znet_interrupt, 0, "ZNet", dev))
1731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto failed;
1741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (request_dma (znet->rx_dma, "ZNet rx"))
1751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto free_irq;
1761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (request_dma (znet->tx_dma, "ZNet tx"))
1771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto free_rx_dma;
1781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!request_region (znet->sia_base, znet->sia_size, "ZNet SIA"))
1791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto free_tx_dma;
1801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!request_region (dev->base_addr, znet->io_size, "ZNet I/O"))
1811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto free_sia;
1821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;				/* Happy ! */
1841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds free_sia:
1861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	release_region (znet->sia_base, znet->sia_size);
1871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds free_tx_dma:
1881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	free_dma (znet->tx_dma);
1891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds free_rx_dma:
1901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	free_dma (znet->rx_dma);
1911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds free_irq:
1921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	free_irq (dev->irq, dev);
1931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds failed:
1941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return -1;
1951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void znet_release_resources (struct net_device *dev)
1981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
199524ad0a79126efabf58d0a49eace6155ab5b4549Wang Chen	struct znet_private *znet = netdev_priv(dev);
2006aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik
2011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	release_region (znet->sia_base, znet->sia_size);
2021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	release_region (dev->base_addr, znet->io_size);
2031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	free_dma (znet->tx_dma);
2041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	free_dma (znet->rx_dma);
2051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	free_irq (dev->irq, dev);
2061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Keep the magical SIA stuff in a single function... */
2091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void znet_transceiver_power (struct net_device *dev, int on)
2101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
211524ad0a79126efabf58d0a49eace6155ab5b4549Wang Chen	struct znet_private *znet = netdev_priv(dev);
2121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned char v;
2131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Turn on/off the 82501 SIA, using zenith-specific magic. */
2151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Select LAN control register */
2161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(0x10, znet->sia_base);
2171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (on)
2191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		v = inb(znet->sia_base + 1) | 0x84;
2201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	else
2211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		v = inb(znet->sia_base + 1) & ~0x84;
2226aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik
2231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(v, znet->sia_base+1); /* Turn on/off LAN power (bit 2). */
2241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Init the i82593, with current promisc/mcast configuration.
2271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   Also used from hardware_init. */
2281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void znet_set_multicast_list (struct net_device *dev)
2291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
230524ad0a79126efabf58d0a49eace6155ab5b4549Wang Chen	struct znet_private *znet = netdev_priv(dev);
2311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	short ioaddr = dev->base_addr;
2321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct i82593_conf_block *cfblk = &znet->i593_init;
2331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	memset(cfblk, 0x00, sizeof(struct i82593_conf_block));
2356aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik
2361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds        /* The configuration block.  What an undocumented nightmare.
2371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   The first set of values are those suggested (without explanation)
2381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   for ethernet in the Intel 82586 databook.  The rest appear to be
2391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   completely undocumented, except for cryptic notes in the Crynwr
2401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   packet driver.  This driver uses the Crynwr values verbatim. */
2411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* maz : Rewritten to take advantage of the wanvelan includes.
2431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   At least we have names, not just blind values */
2446aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik
2451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Byte 0 */
2461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	cfblk->fifo_limit = 10;	/* = 16 B rx and 80 B tx fifo thresholds */
2471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	cfblk->forgnesi = 0;	/* 0=82C501, 1=AMD7992B compatibility */
2481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	cfblk->fifo_32 = 1;
2491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	cfblk->d6mod = 0;  	/* Run in i82593 advanced mode */
2501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	cfblk->throttle_enb = 1;
2511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Byte 1 */
2531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	cfblk->throttle = 8;	/* Continuous w/interrupts, 128-clock DMA. */
2541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	cfblk->cntrxint = 0;	/* enable continuous mode receive interrupts */
2551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	cfblk->contin = 1;	/* enable continuous mode */
2561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Byte 2 */
2581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	cfblk->addr_len = ETH_ALEN;
2591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	cfblk->acloc = 1;	/* Disable source addr insertion by i82593 */
2601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	cfblk->preamb_len = 2;	/* 8 bytes preamble */
2611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	cfblk->loopback = 0;	/* Loopback off */
2626aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik
2631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Byte 3 */
2641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	cfblk->lin_prio = 0;	/* Default priorities & backoff methods. */
2651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	cfblk->tbofstop = 0;
2661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	cfblk->exp_prio = 0;
2671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	cfblk->bof_met = 0;
2686aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik
2691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Byte 4 */
2701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	cfblk->ifrm_spc = 6;	/* 96 bit times interframe spacing */
2716aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik
2721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Byte 5 */
2731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	cfblk->slottim_low = 0; /* 512 bit times slot time (low) */
2746aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik
2751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Byte 6 */
2761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	cfblk->slottim_hi = 2;	/* 512 bit times slot time (high) */
2771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	cfblk->max_retr = 15;	/* 15 collisions retries */
2786aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik
2791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Byte 7 */
2801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	cfblk->prmisc = ((dev->flags & IFF_PROMISC) ? 1 : 0); /* Promiscuous mode */
2811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	cfblk->bc_dis = 0;	/* Enable broadcast reception */
2821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	cfblk->crs_1 = 0;	/* Don't transmit without carrier sense */
2831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	cfblk->nocrc_ins = 0;	/* i82593 generates CRC */
2841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	cfblk->crc_1632 = 0;	/* 32-bit Autodin-II CRC */
2851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	cfblk->crs_cdt = 0;	/* CD not to be interpreted as CS */
2866aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik
2871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Byte 8 */
2881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	cfblk->cs_filter = 0;  	/* CS is recognized immediately */
2891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	cfblk->crs_src = 0;	/* External carrier sense */
2901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	cfblk->cd_filter = 0;  	/* CD is recognized immediately */
2916aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik
2921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Byte 9 */
2931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	cfblk->min_fr_len = ETH_ZLEN >> 2; /* Minimum frame length */
2946aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik
2951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Byte A */
2961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	cfblk->lng_typ = 1;	/* Type/length checks OFF */
2971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	cfblk->lng_fld = 1; 	/* Disable 802.3 length field check */
2981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	cfblk->rxcrc_xf = 1;	/* Don't transfer CRC to memory */
2991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	cfblk->artx = 1;	/* Disable automatic retransmission */
3001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	cfblk->sarec = 1;	/* Disable source addr trig of CD */
3011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	cfblk->tx_jabber = 0;	/* Disable jabber jam sequence */
3021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	cfblk->hash_1 = 1; 	/* Use bits 0-5 in mc address hash */
3031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	cfblk->lbpkpol = 0; 	/* Loopback pin active high */
3046aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik
3051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Byte B */
3061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	cfblk->fdx = 0;		/* Disable full duplex operation */
3076aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik
3081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Byte C */
3091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	cfblk->dummy_6 = 0x3f; 	/* all ones, Default multicast addresses & backoff. */
3101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	cfblk->mult_ia = 0;	/* No multiple individual addresses */
3111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	cfblk->dis_bof = 0;	/* Disable the backoff algorithm ?! */
3126aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik
3131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Byte D */
3141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	cfblk->dummy_1 = 1; 	/* set to 1 */
3151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	cfblk->tx_ifs_retrig = 3; /* Hmm... Disabled */
316567ec874d15b478c8eda7e9a5d2dcb05f13f1fb5Jiri Pirko	cfblk->mc_all = (!netdev_mc_empty(dev) ||
317567ec874d15b478c8eda7e9a5d2dcb05f13f1fb5Jiri Pirko			(dev->flags & IFF_ALLMULTI)); /* multicast all mode */
3181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	cfblk->rcv_mon = 0;	/* Monitor mode disabled */
3191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	cfblk->frag_acpt = 0;	/* Do not accept fragments */
3201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	cfblk->tstrttrs = 0;	/* No start transmission threshold */
3216aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik
3221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Byte E */
3231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	cfblk->fretx = 1;	/* FIFO automatic retransmission */
3241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	cfblk->runt_eop = 0;	/* drop "runt" packets */
3251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	cfblk->hw_sw_pin = 0;	/* ?? */
3261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	cfblk->big_endn = 0;	/* Big Endian ? no... */
3271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	cfblk->syncrqs = 1;	/* Synchronous DRQ deassertion... */
3281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	cfblk->sttlen = 1;  	/* 6 byte status registers */
3291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	cfblk->rx_eop = 0;  	/* Signal EOP on packet reception */
3301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	cfblk->tx_eop = 0;  	/* Signal EOP on packet transmission */
3311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Byte F */
3331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	cfblk->rbuf_size = RX_BUF_SIZE >> 12; /* Set receive buffer size */
3341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	cfblk->rcvstop = 1; 	/* Enable Receive Stop Register */
3351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (znet_debug > 2) {
3371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		int i;
3381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		unsigned char *c;
3391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		for (i = 0, c = (char *) cfblk; i < sizeof (*cfblk); i++)
3411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			printk ("%02X ", c[i]);
3421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk ("\n");
3431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3446aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik
3451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	*znet->tx_cur++ = sizeof(struct i82593_conf_block);
3461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	memcpy(znet->tx_cur, cfblk, sizeof(struct i82593_conf_block));
3471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	znet->tx_cur += sizeof(struct i82593_conf_block)/2;
3481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(OP0_CONFIGURE | CR0_CHNL, ioaddr);
3491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* XXX FIXME maz : Add multicast addresses here, so having a
3511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * multicast address configured isn't equal to IFF_ALLMULTI */
3521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3536aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik
354bc0443fc38f802c5b7a7489b4a31577f1fadd4e4Stephen Hemmingerstatic const struct net_device_ops znet_netdev_ops = {
355bc0443fc38f802c5b7a7489b4a31577f1fadd4e4Stephen Hemminger	.ndo_open		= znet_open,
356bc0443fc38f802c5b7a7489b4a31577f1fadd4e4Stephen Hemminger	.ndo_stop		= znet_close,
357bc0443fc38f802c5b7a7489b4a31577f1fadd4e4Stephen Hemminger	.ndo_start_xmit		= znet_send_packet,
358afc4b13df143122f99a0eb10bfefb216c2806de0Jiri Pirko	.ndo_set_rx_mode	= znet_set_multicast_list,
359bc0443fc38f802c5b7a7489b4a31577f1fadd4e4Stephen Hemminger	.ndo_tx_timeout		= znet_tx_timeout,
360bc0443fc38f802c5b7a7489b4a31577f1fadd4e4Stephen Hemminger	.ndo_change_mtu		= eth_change_mtu,
361bc0443fc38f802c5b7a7489b4a31577f1fadd4e4Stephen Hemminger	.ndo_set_mac_address 	= eth_mac_addr,
362bc0443fc38f802c5b7a7489b4a31577f1fadd4e4Stephen Hemminger	.ndo_validate_addr	= eth_validate_addr,
363bc0443fc38f802c5b7a7489b4a31577f1fadd4e4Stephen Hemminger};
364bc0443fc38f802c5b7a7489b4a31577f1fadd4e4Stephen Hemminger
3651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* The Z-Note probe is pretty easy.  The NETIDBLK exists in the safe-to-probe
3661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   BIOS area.  We just scan for the signature, and pull the vital parameters
3671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   out of the structure. */
3681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init znet_probe (void)
3701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i;
3721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct netidblk *netinfo;
3731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct znet_private *znet;
3741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct net_device *dev;
3751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	char *p;
3761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int err = -ENOMEM;
3771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* This code scans the region 0xf0000 to 0xfffff for a "NETIDBLK". */
3791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for(p = (char *)phys_to_virt(0xf0000); p < (char *)phys_to_virt(0x100000); p++)
3801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (*p == 'N'  &&  strncmp(p, "NETIDBLK", 8) == 0)
3811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
3821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (p >= (char *)phys_to_virt(0x100000)) {
3841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (znet_debug > 1)
3851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			printk(KERN_INFO "No Z-Note ethernet adaptor found.\n");
3861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENODEV;
3871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev = alloc_etherdev(sizeof(struct znet_private));
3901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!dev)
3911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENOMEM;
3921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
393524ad0a79126efabf58d0a49eace6155ab5b4549Wang Chen	znet = netdev_priv(dev);
3941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	netinfo = (struct netidblk *)p;
3961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev->base_addr = netinfo->iobase1;
3971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev->irq = netinfo->irq1;
3981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* The station address is in the "netidblk" at 0x0f0000. */
4001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = 0; i < 6; i++)
4010795af5729b18218767fab27c44b1384f72dc9adJoe Perches		dev->dev_addr[i] = netinfo->netid[i];
4021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
403e174961ca1a0b28f7abf0be47973ad57cb74e5f0Johannes Berg	printk(KERN_INFO "%s: ZNET at %#3lx, %pM"
4040795af5729b18218767fab27c44b1384f72dc9adJoe Perches	       ", using IRQ %d DMA %d and %d.\n",
405e174961ca1a0b28f7abf0be47973ad57cb74e5f0Johannes Berg	       dev->name, dev->base_addr, dev->dev_addr,
4060795af5729b18218767fab27c44b1384f72dc9adJoe Perches	       dev->irq, netinfo->dma1, netinfo->dma2);
4071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (znet_debug > 1) {
4091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk(KERN_INFO "%s: vendor '%16.16s' IRQ1 %d IRQ2 %d DMA1 %d DMA2 %d.\n",
4101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		       dev->name, netinfo->vendor,
4111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		       netinfo->irq1, netinfo->irq2,
4121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		       netinfo->dma1, netinfo->dma2);
4131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk(KERN_INFO "%s: iobase1 %#x size %d iobase2 %#x size %d net type %2.2x.\n",
4141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		       dev->name, netinfo->iobase1, netinfo->iosize1,
4151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		       netinfo->iobase2, netinfo->iosize2, netinfo->nettype);
4161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (znet_debug > 0)
4191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk(KERN_INFO "%s", version);
4201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	znet->rx_dma = netinfo->dma1;
4221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	znet->tx_dma = netinfo->dma2;
4231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock_init(&znet->lock);
4241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	znet->sia_base = 0xe6;	/* Magic address for the 82501 SIA */
4251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	znet->sia_size = 2;
4261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* maz: Despite the '593 being advertised above as using a
4271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * single 8bits I/O port, this driver does many 16bits
4281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * access. So set io_size accordingly */
4291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	znet->io_size  = 2;
4301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!(znet->rx_start = kmalloc (DMA_BUF_SIZE, GFP_KERNEL | GFP_DMA)))
4321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto free_dev;
4331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!(znet->tx_start = kmalloc (DMA_BUF_SIZE, GFP_KERNEL | GFP_DMA)))
4341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto free_rx;
4351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!dma_page_eq (znet->rx_start, znet->rx_start + (RX_BUF_SIZE/2-1)) ||
4371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    !dma_page_eq (znet->tx_start, znet->tx_start + (TX_BUF_SIZE/2-1))) {
4381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk (KERN_WARNING "tx/rx crossing DMA frontiers, giving up\n");
4391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto free_tx;
4401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4416aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik
4421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	znet->rx_end = znet->rx_start + RX_BUF_SIZE/2;
4431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	znet->tx_buf_len = TX_BUF_SIZE/2;
4441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	znet->tx_end = znet->tx_start + znet->tx_buf_len;
4451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* The ZNET-specific entries in the device structure. */
447bc0443fc38f802c5b7a7489b4a31577f1fadd4e4Stephen Hemminger	dev->netdev_ops = &znet_netdev_ops;
4481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	dev->watchdog_timeo = TX_TIMEOUT;
4491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	err = register_netdev(dev);
4501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (err)
4511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto free_tx;
4521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	znet_dev = dev;
4531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
4541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds free_tx:
4561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree(znet->tx_start);
4571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds free_rx:
4581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	kfree(znet->rx_start);
4591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds free_dev:
4601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	free_netdev(dev);
4611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return err;
4621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4646aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik
4651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int znet_open(struct net_device *dev)
4661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
4671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int ioaddr = dev->base_addr;
4681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (znet_debug > 2)
4701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk(KERN_DEBUG "%s: znet_open() called.\n", dev->name);
4711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* These should never fail.  You can't add devices to a sealed box! */
4731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (znet_request_resources (dev)) {
4741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk(KERN_WARNING "%s: Not opened -- resource busy?!?\n", dev->name);
4751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EBUSY;
4761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	znet_transceiver_power (dev, 1);
4796aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik
4801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* According to the Crynwr driver we should wait 50 msec. for the
4811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   LAN clock to stabilize.  My experiments indicates that the '593 can
4821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   be initialized immediately.  The delay is probably needed for the
4831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   DC-to-DC converter to come up to full voltage, and for the oscillator
4841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   to be spot-on at 20Mhz before transmitting.
4851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   Until this proves to be a problem we rely on the higher layers for the
4861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   delay and save allocating a timer entry. */
4871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* maz : Well, I'm getting every time the following message
4891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * without the delay on a 486@33. This machine is much too
4901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * fast... :-) So maybe the Crynwr driver wasn't wrong after
4911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * all, even if the message is completly harmless on my
4921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * setup. */
4931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	mdelay (50);
4946aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik
4951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* This follows the packet driver's lead, and checks for success. */
4961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (inb(ioaddr) != 0x10 && inb(ioaddr) != 0x00)
4971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk(KERN_WARNING "%s: Problem turning on the transceiver power.\n",
4981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		       dev->name);
4991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	hardware_init(dev);
5011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	netif_start_queue (dev);
5021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
5041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void znet_tx_timeout (struct net_device *dev)
5081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int ioaddr = dev->base_addr;
5101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ushort event, tx_status, rx_offset, state;
5111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb (CR0_STATUS_0, ioaddr);
5131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	event = inb (ioaddr);
5141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb (CR0_STATUS_1, ioaddr);
5151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tx_status = inw (ioaddr);
5161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb (CR0_STATUS_2, ioaddr);
5171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rx_offset = inw (ioaddr);
5181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb (CR0_STATUS_3, ioaddr);
5191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	state = inb (ioaddr);
5201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	printk (KERN_WARNING "%s: transmit timed out, status %02x %04x %04x %02x,"
5211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 " resetting.\n", dev->name, event, tx_status, rx_offset, state);
5221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (tx_status == TX_LOST_CRS)
5231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk (KERN_WARNING "%s: Tx carrier error, check transceiver cable.\n",
5241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			dev->name);
5251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb (OP0_RESET, ioaddr);
5261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	hardware_init (dev);
5271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	netif_wake_queue (dev);
5281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
53061357325f377889a1daffa14962d705dc814dd0eStephen Hemmingerstatic netdev_tx_t znet_send_packet(struct sk_buff *skb, struct net_device *dev)
5311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int ioaddr = dev->base_addr;
533524ad0a79126efabf58d0a49eace6155ab5b4549Wang Chen	struct znet_private *znet = netdev_priv(dev);
5341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long flags;
5351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	short length = skb->len;
5361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (znet_debug > 4)
5381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk(KERN_DEBUG "%s: ZNet_send_packet.\n", dev->name);
5391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (length < ETH_ZLEN) {
5415b057c6b1a25d57edf2b4d1e956e50936480a9ffHerbert Xu		if (skb_padto(skb, ETH_ZLEN))
5426ed106549d17474ca17a16057f4c0ed4eba5a7caPatrick McHardy			return NETDEV_TX_OK;
5431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		length = ETH_ZLEN;
5441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
5456aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik
5461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	netif_stop_queue (dev);
5476aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik
5481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Check that the part hasn't reset itself, probably from suspend. */
5491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(CR0_STATUS_0, ioaddr);
5501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (inw(ioaddr) == 0x0010 &&
5511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    inw(ioaddr) == 0x0000 &&
5521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    inw(ioaddr) == 0x0010) {
5531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (znet_debug > 1)
5541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			printk (KERN_WARNING "%s : waking up\n", dev->name);
5551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		hardware_init(dev);
5561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		znet_transceiver_power (dev, 1);
5571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
5581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (1) {
5601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		unsigned char *buf = (void *)skb->data;
5611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ushort *tx_link = znet->tx_cur - 1;
5621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ushort rnd_len = (length + 1)>>1;
5636aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik
56409f75cd7bf13720738e6a196cc0107ce9a5bd5a0Jeff Garzik		dev->stats.tx_bytes+=length;
5651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (znet->tx_cur >= znet->tx_end)
5671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		  znet->tx_cur = znet->tx_start;
5681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		*znet->tx_cur++ = length;
5691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (znet->tx_cur + rnd_len + 1 > znet->tx_end) {
5701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			int semi_cnt = (znet->tx_end - znet->tx_cur)<<1; /* Cvrt to byte cnt. */
5711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			memcpy(znet->tx_cur, buf, semi_cnt);
5721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			rnd_len -= semi_cnt>>1;
5731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			memcpy(znet->tx_start, buf + semi_cnt, length - semi_cnt);
5741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			znet->tx_cur = znet->tx_start + rnd_len;
5751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		} else {
5761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			memcpy(znet->tx_cur, buf, skb->len);
5771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			znet->tx_cur += rnd_len;
5781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
5791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		*znet->tx_cur++ = 0;
5801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		spin_lock_irqsave(&znet->lock, flags);
5821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		{
5831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			*tx_link = OP0_TRANSMIT | CR0_CHNL;
5841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* Is this always safe to do? */
5851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			outb(OP0_TRANSMIT | CR0_CHNL, ioaddr);
5861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
5871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		spin_unlock_irqrestore (&znet->lock, flags);
5881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		netif_start_queue (dev);
5901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (znet_debug > 4)
5921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		  printk(KERN_DEBUG "%s: Transmitter queued, length %d.\n", dev->name, length);
5931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
5946aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik	dev_kfree_skb(skb);
5956ed106549d17474ca17a16057f4c0ed4eba5a7caPatrick McHardy	return NETDEV_TX_OK;
5961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* The ZNET interrupt handler. */
5997d12e780e003f93433d49ce78cfedf4b4c52adc5David Howellsstatic irqreturn_t znet_interrupt(int irq, void *dev_id)
6001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct net_device *dev = dev_id;
602524ad0a79126efabf58d0a49eace6155ab5b4549Wang Chen	struct znet_private *znet = netdev_priv(dev);
6031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int ioaddr;
6041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int boguscnt = 20;
6051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int handled = 0;
6061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_lock (&znet->lock);
6086aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik
6091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	ioaddr = dev->base_addr;
6101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(CR0_STATUS_0, ioaddr);
6121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	do {
6131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ushort status = inb(ioaddr);
6141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (znet_debug > 5) {
6151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			ushort result, rx_ptr, running;
6161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			outb(CR0_STATUS_1, ioaddr);
6171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			result = inw(ioaddr);
6181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			outb(CR0_STATUS_2, ioaddr);
6191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			rx_ptr = inw(ioaddr);
6201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			outb(CR0_STATUS_3, ioaddr);
6211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			running = inb(ioaddr);
6221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			printk(KERN_DEBUG "%s: interrupt, status %02x, %04x %04x %02x serial %d.\n",
6231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				 dev->name, status, result, rx_ptr, running, boguscnt);
6241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
6251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if ((status & SR0_INTERRUPT) == 0)
6261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
6271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		handled = 1;
6291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if ((status & SR0_EVENT_MASK) == SR0_TRANSMIT_DONE ||
6311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		    (status & SR0_EVENT_MASK) == SR0_RETRANSMIT_DONE ||
6321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		    (status & SR0_EVENT_MASK) == SR0_TRANSMIT_NO_CRC_DONE) {
6331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			int tx_status;
6341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			outb(CR0_STATUS_1, ioaddr);
6351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			tx_status = inw(ioaddr);
6361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* It's undocumented, but tx_status seems to match the i82586. */
6371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (tx_status & TX_OK) {
63809f75cd7bf13720738e6a196cc0107ce9a5bd5a0Jeff Garzik				dev->stats.tx_packets++;
63909f75cd7bf13720738e6a196cc0107ce9a5bd5a0Jeff Garzik				dev->stats.collisions += tx_status & TX_NCOL_MASK;
6401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			} else {
6411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				if (tx_status & (TX_LOST_CTS | TX_LOST_CRS))
64209f75cd7bf13720738e6a196cc0107ce9a5bd5a0Jeff Garzik					dev->stats.tx_carrier_errors++;
6431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				if (tx_status & TX_UND_RUN)
64409f75cd7bf13720738e6a196cc0107ce9a5bd5a0Jeff Garzik					dev->stats.tx_fifo_errors++;
6451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				if (!(tx_status & TX_HRT_BEAT))
64609f75cd7bf13720738e6a196cc0107ce9a5bd5a0Jeff Garzik					dev->stats.tx_heartbeat_errors++;
6471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				if (tx_status & TX_MAX_COL)
64809f75cd7bf13720738e6a196cc0107ce9a5bd5a0Jeff Garzik					dev->stats.tx_aborted_errors++;
6491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				/* ...and the catch-all. */
6501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				if ((tx_status | (TX_LOST_CRS | TX_LOST_CTS | TX_UND_RUN | TX_HRT_BEAT | TX_MAX_COL)) != (TX_LOST_CRS | TX_LOST_CTS | TX_UND_RUN | TX_HRT_BEAT | TX_MAX_COL))
65109f75cd7bf13720738e6a196cc0107ce9a5bd5a0Jeff Garzik					dev->stats.tx_errors++;
6521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				/* Transceiver may be stuck if cable
65425985edcedea6396277003854657b5f3cb31a628Lucas De Marchi				 * was removed while emitting a
6551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				 * packet. Flip it off, then on to
6561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				 * reset it. This is very empirical,
6571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				 * but it seems to work. */
6586aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik
6591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				znet_transceiver_power (dev, 0);
6601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				znet_transceiver_power (dev, 1);
6611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
6621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			netif_wake_queue (dev);
6631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
6641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if ((status & SR0_RECEPTION) ||
6661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		    (status & SR0_EVENT_MASK) == SR0_STOP_REG_HIT) {
6671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			znet_rx(dev);
6681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
6691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* Clear the interrupts we've handled. */
6701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		outb(CR0_INT_ACK, ioaddr);
6711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} while (boguscnt--);
6721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	spin_unlock (&znet->lock);
6746aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik
6751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return IRQ_RETVAL(handled);
6761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void znet_rx(struct net_device *dev)
6791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
680524ad0a79126efabf58d0a49eace6155ab5b4549Wang Chen	struct znet_private *znet = netdev_priv(dev);
6811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int ioaddr = dev->base_addr;
6821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int boguscount = 1;
6831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	short next_frame_end_offset = 0; 		/* Offset of next frame start. */
6841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	short *cur_frame_end;
6851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	short cur_frame_end_offset;
6861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(CR0_STATUS_2, ioaddr);
6881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	cur_frame_end_offset = inw(ioaddr);
6891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (cur_frame_end_offset == znet->rx_cur - znet->rx_start) {
6911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk(KERN_WARNING "%s: Interrupted, but nothing to receive, offset %03x.\n",
6921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			   dev->name, cur_frame_end_offset);
6931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
6941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
6951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Use same method as the Crynwr driver: construct a forward list in
6971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   the same area of the backwards links we now have.  This allows us to
6981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   pass packets to the upper layers in the order they were received --
6991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   important for fast-path sequential operations. */
7008e95a2026f3b43f7c3d676adaccd2de9532e8dccJoe Perches	while (znet->rx_start + cur_frame_end_offset != znet->rx_cur &&
7018e95a2026f3b43f7c3d676adaccd2de9532e8dccJoe Perches	       ++boguscount < 5) {
7021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		unsigned short hi_cnt, lo_cnt, hi_status, lo_status;
7031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		int count, status;
7041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (cur_frame_end_offset < 4) {
7061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* Oh no, we have a special case: the frame trailer wraps around
7071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			   the end of the ring buffer.  We've saved space at the end of
7081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			   the ring buffer for just this problem. */
7091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			memcpy(znet->rx_end, znet->rx_start, 8);
7101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			cur_frame_end_offset += (RX_BUF_SIZE/2);
7111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
7121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		cur_frame_end = znet->rx_start + cur_frame_end_offset - 4;
7131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		lo_status = *cur_frame_end++;
7151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		hi_status = *cur_frame_end++;
7161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		status = ((hi_status & 0xff) << 8) + (lo_status & 0xff);
7171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		lo_cnt = *cur_frame_end++;
7181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		hi_cnt = *cur_frame_end++;
7191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		count = ((hi_cnt & 0xff) << 8) + (lo_cnt & 0xff);
7201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (znet_debug > 5)
7221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		  printk(KERN_DEBUG "Constructing trailer at location %03x, %04x %04x %04x %04x"
7231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				 " count %#x status %04x.\n",
7241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				 cur_frame_end_offset<<1, lo_status, hi_status, lo_cnt, hi_cnt,
7251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				 count, status);
7261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		cur_frame_end[-4] = status;
7271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		cur_frame_end[-3] = next_frame_end_offset;
7281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		cur_frame_end[-2] = count;
7291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		next_frame_end_offset = cur_frame_end_offset;
7301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		cur_frame_end_offset -= ((count + 1)>>1) + 3;
7311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (cur_frame_end_offset < 0)
7321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		  cur_frame_end_offset += RX_BUF_SIZE/2;
7336403eab143205a45a5493166ff8bf7e3646f4a77Joe Perches	}
7341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Now step  forward through the list. */
7361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	do {
7371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ushort *this_rfp_ptr = znet->rx_start + next_frame_end_offset;
7381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		int status = this_rfp_ptr[-4];
7391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		int pkt_len = this_rfp_ptr[-2];
7406aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik
7411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (znet_debug > 5)
7421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		  printk(KERN_DEBUG "Looking at trailer ending at %04x status %04x length %03x"
7431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				 " next %04x.\n", next_frame_end_offset<<1, status, pkt_len,
7441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				 this_rfp_ptr[-3]<<1);
7451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/* Once again we must assume that the i82586 docs apply. */
7461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if ( ! (status & RX_RCV_OK)) { /* There was an error. */
74709f75cd7bf13720738e6a196cc0107ce9a5bd5a0Jeff Garzik			dev->stats.rx_errors++;
74809f75cd7bf13720738e6a196cc0107ce9a5bd5a0Jeff Garzik			if (status & RX_CRC_ERR) dev->stats.rx_crc_errors++;
74909f75cd7bf13720738e6a196cc0107ce9a5bd5a0Jeff Garzik			if (status & RX_ALG_ERR) dev->stats.rx_frame_errors++;
7501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#if 0
75109f75cd7bf13720738e6a196cc0107ce9a5bd5a0Jeff Garzik			if (status & 0x0200) dev->stats.rx_over_errors++; /* Wrong. */
75209f75cd7bf13720738e6a196cc0107ce9a5bd5a0Jeff Garzik			if (status & 0x0100) dev->stats.rx_fifo_errors++;
7531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#else
7541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* maz : Wild guess... */
75509f75cd7bf13720738e6a196cc0107ce9a5bd5a0Jeff Garzik			if (status & RX_OVRRUN) dev->stats.rx_over_errors++;
7561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
75709f75cd7bf13720738e6a196cc0107ce9a5bd5a0Jeff Garzik			if (status & RX_SRT_FRM) dev->stats.rx_length_errors++;
7581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		} else if (pkt_len > 1536) {
75909f75cd7bf13720738e6a196cc0107ce9a5bd5a0Jeff Garzik			dev->stats.rx_length_errors++;
7601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		} else {
7611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* Malloc up new buffer. */
7621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			struct sk_buff *skb;
7631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
764c056b734e54e12f38f34a2583a4824e6cecc16c1Pradeep A Dalvi			skb = netdev_alloc_skb(dev, pkt_len);
7651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (skb == NULL) {
7661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				if (znet_debug)
7671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				  printk(KERN_WARNING "%s: Memory squeeze, dropping packet.\n", dev->name);
76809f75cd7bf13720738e6a196cc0107ce9a5bd5a0Jeff Garzik				dev->stats.rx_dropped++;
7691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				break;
7701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
7711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (&znet->rx_cur[(pkt_len+1)>>1] > znet->rx_end) {
7731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				int semi_cnt = (znet->rx_end - znet->rx_cur)<<1;
7741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				memcpy(skb_put(skb,semi_cnt), znet->rx_cur, semi_cnt);
7751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				memcpy(skb_put(skb,pkt_len-semi_cnt), znet->rx_start,
7761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					   pkt_len - semi_cnt);
7771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			} else {
7781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				memcpy(skb_put(skb,pkt_len), znet->rx_cur, pkt_len);
7791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				if (znet_debug > 6) {
7801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					unsigned int *packet = (unsigned int *) skb->data;
7811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					printk(KERN_DEBUG "Packet data is %08x %08x %08x %08x.\n", packet[0],
7821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						   packet[1], packet[2], packet[3]);
7831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				}
7841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		  }
7851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		  skb->protocol=eth_type_trans(skb,dev);
7861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		  netif_rx(skb);
78709f75cd7bf13720738e6a196cc0107ce9a5bd5a0Jeff Garzik		  dev->stats.rx_packets++;
78809f75cd7bf13720738e6a196cc0107ce9a5bd5a0Jeff Garzik		  dev->stats.rx_bytes += pkt_len;
7891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
7901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		znet->rx_cur = this_rfp_ptr;
7911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (znet->rx_cur >= znet->rx_end)
7921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			znet->rx_cur -= RX_BUF_SIZE/2;
7931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		update_stop_hit(ioaddr, (znet->rx_cur - znet->rx_start)<<1);
7941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		next_frame_end_offset = this_rfp_ptr[-3];
7951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (next_frame_end_offset == 0)		/* Read all the frames? */
7961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;			/* Done for now */
7971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		this_rfp_ptr = znet->rx_start + next_frame_end_offset;
7981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	} while (--boguscount);
7991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* If any worth-while packets have been received, dev_rint()
8011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   has done a mark_bh(INET_BH) for us and will work on them
8021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   when we get to the bottom-half routine. */
8031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
8041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* The inverse routine to znet_open(). */
8061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int znet_close(struct net_device *dev)
8071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
8081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int ioaddr = dev->base_addr;
8091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	netif_stop_queue (dev);
8111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(OP0_RESET, ioaddr);			/* CMD0_RESET */
8131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (znet_debug > 1)
8151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk(KERN_DEBUG "%s: Shutting down ethercard.\n", dev->name);
8161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Turn off transceiver power. */
8171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	znet_transceiver_power (dev, 0);
8186aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik
8191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	znet_release_resources (dev);
8206aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik
8211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
8221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
8231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void show_dma(struct net_device *dev)
8251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
8261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	short ioaddr = dev->base_addr;
8271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned char stat = inb (ioaddr);
828524ad0a79126efabf58d0a49eace6155ab5b4549Wang Chen	struct znet_private *znet = netdev_priv(dev);
8291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long flags;
8301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	short dma_port = ((znet->tx_dma&3)<<2) + IO_DMA2_BASE;
8311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned addr = inb(dma_port);
8321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	short residue;
8331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	addr |= inb(dma_port) << 8;
8351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	residue = get_dma_residue(znet->tx_dma);
8366aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik
8371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (znet_debug > 1) {
8381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		flags=claim_dma_lock();
8391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk(KERN_DEBUG "Stat:%02x Addr: %04x cnt:%3x\n",
8401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		       stat, addr<<1, residue);
8411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		release_dma_lock(flags);
8421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
8431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
8441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/* Initialize the hardware.  We have to do this when the board is open()ed
8461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds   or when we come out of suspend mode. */
8471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void hardware_init(struct net_device *dev)
8481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
8491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long flags;
8501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	short ioaddr = dev->base_addr;
851524ad0a79126efabf58d0a49eace6155ab5b4549Wang Chen	struct znet_private *znet = netdev_priv(dev);
8521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	znet->rx_cur = znet->rx_start;
8541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	znet->tx_cur = znet->tx_start;
8551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Reset the chip, and start it up. */
8571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(OP0_RESET, ioaddr);
8581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	flags=claim_dma_lock();
8601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	disable_dma(znet->rx_dma); 		/* reset by an interrupting task. */
8611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	clear_dma_ff(znet->rx_dma);
8621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	set_dma_mode(znet->rx_dma, DMA_RX_MODE);
8631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	set_dma_addr(znet->rx_dma, (unsigned int) znet->rx_start);
8641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	set_dma_count(znet->rx_dma, RX_BUF_SIZE);
8651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	enable_dma(znet->rx_dma);
8661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Now set up the Tx channel. */
8671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	disable_dma(znet->tx_dma);
8681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	clear_dma_ff(znet->tx_dma);
8691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	set_dma_mode(znet->tx_dma, DMA_TX_MODE);
8701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	set_dma_addr(znet->tx_dma, (unsigned int) znet->tx_start);
8711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	set_dma_count(znet->tx_dma, znet->tx_buf_len<<1);
8721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	enable_dma(znet->tx_dma);
8731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	release_dma_lock(flags);
8746aa20a2235535605db6d6d2bd850298b2fe7f31eJeff Garzik
8751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (znet_debug > 1)
8761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  printk(KERN_DEBUG "%s: Initializing the i82593, rx buf %p tx buf %p\n",
8771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 dev->name, znet->rx_start,znet->tx_start);
8781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Do an empty configure command, just like the Crynwr driver.  This
8791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	   resets to chip to its default values. */
8801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	*znet->tx_cur++ = 0;
8811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	*znet->tx_cur++ = 0;
8821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	show_dma(dev);
8831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(OP0_CONFIGURE | CR0_CHNL, ioaddr);
8841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	znet_set_multicast_list (dev);
8861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	*znet->tx_cur++ = 6;
8881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	memcpy(znet->tx_cur, dev->dev_addr, 6);
8891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	znet->tx_cur += 3;
8901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	show_dma(dev);
8911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(OP0_IA_SETUP | CR0_CHNL, ioaddr);
8921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	show_dma(dev);
8931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	update_stop_hit(ioaddr, 8192);
8951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (znet_debug > 1)  printk(KERN_DEBUG "enabling Rx.\n");
8961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(OP0_RCV_ENABLE, ioaddr);
8971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	netif_start_queue (dev);
8981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
8991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void update_stop_hit(short ioaddr, unsigned short rx_stop_offset)
9011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
9021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(OP0_SWIT_TO_PORT_1 | CR0_CHNL, ioaddr);
9031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (znet_debug > 5)
9041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  printk(KERN_DEBUG "Updating stop hit with value %02x.\n",
9051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			 (rx_stop_offset >> 6) | CR1_STOP_REG_UPDATE);
9061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb((rx_stop_offset >> 6) | CR1_STOP_REG_UPDATE, ioaddr);
9071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	outb(OP1_SWIT_TO_PORT_0, ioaddr);
9081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
9091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic __exit void znet_cleanup (void)
9111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
9121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (znet_dev) {
913524ad0a79126efabf58d0a49eace6155ab5b4549Wang Chen		struct znet_private *znet = netdev_priv(znet_dev);
9141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		unregister_netdev (znet_dev);
9161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		kfree (znet->rx_start);
9171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		kfree (znet->tx_start);
9181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		free_netdev (znet_dev);
9191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
9201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
9211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_init (znet_probe);
9231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsmodule_exit (znet_cleanup);
924